/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/
import React, { Component, Fragment } from "react";
import _ from "lodash";
import {
EuiButton,
EuiButtonEmpty,
EuiModal,
EuiModalBody,
EuiModalFooter,
EuiModalHeader,
EuiModalHeaderTitle,
EuiOverlayMask,
EuiComboBox,
EuiFormRow,
EuiFieldText,
EuiCallOut,
EuiText,
EuiSpacer,
EuiCodeBlock,
EuiLink,
EuiIcon,
} from "@elastic/eui";
import { BrowserServices } from "../../../../models/interfaces";
import { PolicyOption } from "../../models/interfaces";
import { DocumentPolicy, State } from "../../../../../models/interfaces";
import { getErrorMessage } from "../../../../utils/helpers";
import { DOCUMENTATION_URL } from "../../../../utils/constants";
import { CoreServicesContext } from "../../../../components/core_services";
interface ApplyPolicyModalProps {
onClose: () => void;
services: BrowserServices;
indices: string[];
}
interface ApplyPolicyModalState {
isLoading: boolean;
selectedPolicy: PolicyOption | null;
selectedPolicyError: string;
hasRolloverAction: boolean;
policyOptions: PolicyOption[];
rolloverAlias: string;
rolloverAliasError: string;
hasSubmitted: boolean;
}
export default class ApplyPolicyModal extends Component {
static contextType = CoreServicesContext;
state: ApplyPolicyModalState = {
isLoading: false,
selectedPolicy: null,
selectedPolicyError: "",
hasRolloverAction: false,
policyOptions: [],
rolloverAlias: "",
rolloverAliasError: "",
hasSubmitted: false,
};
async componentDidMount(): Promise {
await this.onPolicySearchChange("");
}
onApplyPolicy = async (selectedPolicy: PolicyOption, hasRolloverAction: boolean, rolloverAlias: string): Promise => {
try {
const {
onClose,
indices,
services: { indexService },
} = this.props;
const policyId = selectedPolicy.label;
const applyPolicyResponse = await indexService.applyPolicy(indices, policyId);
if (applyPolicyResponse.ok) {
const { updatedIndices, failedIndices, failures } = applyPolicyResponse.response;
if (updatedIndices) {
this.context.notifications.toasts.addSuccess(`Applied policy to ${updatedIndices} indices`);
if (hasRolloverAction && rolloverAlias && indices.length === 1) {
await this.onEditRolloverAlias(indices[0], rolloverAlias);
}
}
if (failures) {
this.context.notifications.toasts.addDanger(
`Failed to apply policy to ${failedIndices
.map((failedIndex) => `[${failedIndex.indexName}, ${failedIndex.reason}]`)
.join(", ")}`
);
}
onClose();
} else {
this.context.notifications.toasts.addDanger(applyPolicyResponse.error);
}
} catch (err) {
this.context.notifications.toasts.addDanger(getErrorMessage(err, "There was a problem adding policy to indices"));
}
};
onEditRolloverAlias = async (index: string, rolloverAlias: string): Promise => {
const {
services: { indexService },
} = this.props;
try {
const response = await indexService.editRolloverAlias(index, rolloverAlias);
if (response.ok) {
if (response.response.acknowledged) {
this.context.notifications.toasts.addSuccess(`Edited rollover alias on ${index}`);
} else {
this.context.notifications.toasts.addDanger(`Failed to edit rollover alias on ${index}`);
}
} else {
this.context.notifications.toasts.addDanger(response.error);
}
} catch (err) {
this.context.notifications.toasts.addDanger(getErrorMessage(err, `There was a problem editing rollover alias on ${index}`));
}
};
onPolicySearchChange = async (searchValue: string): Promise => {
const {
services: { indexService },
} = this.props;
this.setState({ isLoading: true, policyOptions: [] });
try {
const searchPoliciesResponse = await indexService.searchPolicies(searchValue, true);
if (searchPoliciesResponse.ok) {
const policies = searchPoliciesResponse.response.policies.map((p: DocumentPolicy) => ({
label: p.id,
policy: p.policy,
}));
this.setState({ policyOptions: policies });
} else {
if (searchPoliciesResponse.error.startsWith("[index_not_found_exception]")) {
this.context.notifications.toasts.addDanger("You have not created a policy yet");
} else {
this.context.notifications.toasts.addDanger(searchPoliciesResponse.error);
}
}
} catch (err) {
if (this.context != null) this.context.notifications.toasts.addDanger(err.message);
}
this.setState({ isLoading: false });
};
onChangeSelectedPolicy = (selectedPolicies: PolicyOption[]): void => {
const selectedPolicy = selectedPolicies.length ? selectedPolicies[0] : null;
const hasRolloverAction = this.hasRolloverAction(selectedPolicy);
this.setState({
selectedPolicy,
hasRolloverAction,
selectedPolicyError: this.getSelectedPolicyError(selectedPolicy),
});
};
onChangeRolloverAlias = (e: React.ChangeEvent): void => {
const rolloverAlias = e.target.value;
this.setState({
rolloverAlias,
rolloverAliasError: this.getRolloverAliasError(rolloverAlias),
});
};
onSubmit = async (): Promise => {
const { selectedPolicy, rolloverAlias } = this.state;
const selectedPolicyError = this.getSelectedPolicyError(selectedPolicy);
const rolloverAliasError = this.getRolloverAliasError(rolloverAlias);
const hasSubmitError = !!selectedPolicyError || !!rolloverAliasError;
if (hasSubmitError) {
this.setState({ selectedPolicyError, rolloverAliasError, hasSubmitted: true });
} else {
// @ts-ignore
await this.onApplyPolicy(selectedPolicy, this.hasRolloverAction(selectedPolicy), rolloverAlias);
}
};
getRolloverAliasError = (rolloverAlias: string): string => {
const { hasRolloverAction } = this.state;
const { indices } = this.props;
const isDataStream = indices[0].includes(".ds");
const hasSingleIndexSelected = indices.length === 1;
const requiresAlias = hasRolloverAction && hasSingleIndexSelected && !isDataStream;
const hasAliasError = requiresAlias && !rolloverAlias;
return hasAliasError ? "Required" : "";
};
getSelectedPolicyError = (selectedPolicy: PolicyOption | null): string => (selectedPolicy ? "" : "You must select a policy");
hasRolloverAction = (selectedPolicy: PolicyOption | null): boolean =>
_.get(selectedPolicy, "policy.states", []).some((state: State) => state.actions.some((action) => action.hasOwnProperty("rollover")));
renderRollover = (): React.ReactNode | null => {
const { rolloverAlias, hasRolloverAction, rolloverAliasError, hasSubmitted } = this.state;
const { indices } = this.props;
const hasSingleIndexSelected = indices.length === 1;
if (!hasRolloverAction) return null;
if (hasSingleIndexSelected) {
return (
This policy includes a rollover action. Specify a rollover alias.{" "}
Learn more
}
isInvalid={hasSubmitted && !!rolloverAliasError}
error={rolloverAliasError}
fullWidth
>
);
}
return (
You are applying a policy with rollover to multiple indices. You will need to add a unique rollover_alias setting to each index.
}
iconType="alert"
size="s"
color="warning"
/>
);
};
renderPreview = (): React.ReactNode | null => {
const { selectedPolicy } = this.state;
if (!selectedPolicy) return null;
let policyString = "";
try {
policyString = JSON.stringify({ policy: selectedPolicy.policy }, null, 4);
} catch (err) {
console.error(err);
}
if (!policyString) {
return null;
}
return (
Preview
{policyString}
);
};
render() {
const { policyOptions, selectedPolicy, selectedPolicyError, isLoading, hasSubmitted } = this.state;
const { onClose } = this.props;
const selectedOptions = selectedPolicy ? [selectedPolicy] : [];
return (
{/*
// @ts-ignore */}
Apply policy
Choose the policy you want to use for the selected indices. A copy of the policy will be created and applied to the indices.
{this.renderPreview()}
{this.renderRollover()}
Cancel
Apply
);
}
}