diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index bb9d39962..f88c93599 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,18 +1,11 @@ ### Description - [Describe what this change achieves] ### Issues Resolved - [List any issues this PR will resolve] ### Check List - -- [ ] New functionality includes testing. - - [ ] All tests pass -- [ ] New functionality has been documented. -- [ ] Commits are signed per the DCO using `--signoff` +- [ ] Commits are signed per the DCO using --signoff By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. For more information on following Developer Certificate of Origin and signing off your commits, please check [here](https://github.com/opensearch-project/OpenSearch/blob/main/CONTRIBUTING.md#developer-certificate-of-origin). - diff --git a/.github/draft-release-notes-config.yml b/.github/draft-release-notes-config.yml index 0a1cb473e..116bbda94 100644 --- a/.github/draft-release-notes-config.yml +++ b/.github/draft-release-notes-config.yml @@ -14,7 +14,7 @@ replacers: - search: '##' replace: '###' -# Organizing the tagged PRs into unified ODFE categories +# Organizing the tagged PRs into unified OpenSearch categories categories: - title: 'Breaking changes' labels: @@ -41,7 +41,6 @@ categories: - 'documentation' - title: 'Maintenance' labels: - - 'security' - 'version support' - 'backwards-compatibility' - title: 'Refactoring' diff --git a/.github/workflows/cypress-workflow.yml b/.github/workflows/cypress-workflow.yml index 982db8d80..9aee51ec0 100644 --- a/.github/workflows/cypress-workflow.yml +++ b/.github/workflows/cypress-workflow.yml @@ -3,10 +3,11 @@ on: pull_request: branches: - main + - 1.* push: branches: - main - - development-* + - 1.* env: OPENSEARCH_DASHBOARDS_VERSION: '1.x' OPENSEARCH_VERSION: '1.1.0-SNAPSHOT' diff --git a/.github/workflows/links.yml b/.github/workflows/links.yml index 423dca420..6766fb7d6 100644 --- a/.github/workflows/links.yml +++ b/.github/workflows/links.yml @@ -1,9 +1,13 @@ name: Link Checker on: push: - branches: [ main ] + branches: + - main + - 1.* pull_request: - branches: [ main ] + branches: + - main + - 1.* jobs: linkchecker: diff --git a/.github/workflows/unit-tests-workflow.yml b/.github/workflows/unit-tests-workflow.yml index df0852668..b0db729c1 100644 --- a/.github/workflows/unit-tests-workflow.yml +++ b/.github/workflows/unit-tests-workflow.yml @@ -3,10 +3,11 @@ on: pull_request: branches: - main + - 1.* push: branches: - main - - development-* + - 1.* jobs: tests: diff --git a/NOTICE b/NOTICE index 850f3c505..731cb6006 100644 --- a/NOTICE +++ b/NOTICE @@ -1,2 +1,2 @@ -index-management-dashboards-plugin -Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors diff --git a/README.md b/README.md index e309d4e81..5c3b5f017 100644 --- a/README.md +++ b/README.md @@ -37,8 +37,8 @@ If you discover a potential security issue in this project we ask that you notif ## License -This project is licensed under the [Apache v2.0 License](LICENSE.txt). +This project is licensed under the [Apache v2.0 License](LICENSE). ## Copyright -Copyright 2020-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. \ No newline at end of file +Copyright OpenSearch Contributors. See [NOTICE](NOTICE) for details. \ No newline at end of file diff --git a/models/interfaces.ts b/models/interfaces.ts index 13a98dec9..2b358846a 100644 --- a/models/interfaces.ts +++ b/models/interfaces.ts @@ -150,6 +150,7 @@ export interface UIAction { id: string; type: ActionType | string; render: (uiAction: UIAction, onChangeAction: (uiAction: UIAction) => void) => JSX.Element | null; + isValid: () => boolean; clone: (action: Data) => UIAction; content: () => JSX.Element | string | null; toAction: () => Action; @@ -157,7 +158,7 @@ export interface UIAction { export interface ForceMergeAction extends Action { force_merge: { - max_num_segments: number; + max_num_segments?: number; }; } @@ -171,7 +172,7 @@ export interface ReadWriteAction extends Action { export interface ReplicaCountAction extends Action { replica_count: { - number_of_replicas: number; + number_of_replicas?: number; }; } @@ -207,7 +208,7 @@ export interface Transition { export interface Condition { min_index_age?: string; - min_doc_count?: number; + min_doc_count?: number | undefined; min_size?: string; cron?: Cron; } @@ -238,22 +239,24 @@ export interface SnapshotAction extends Action { export interface IndexPriorityAction extends Action { index_priority: { - priority: number; + priority?: number; }; } export interface AllocationAction extends Action { allocation: { - require: { - [key: string]: string; - }; - include: { - [key: string]: string; - }; - exclude: { - [key: string]: string; - }; - wait_for: boolean; + // TODO: These require a complete UI and we are only supporting JSON editor for allocation for now + // require: { + // [key: string]: string; + // }; + // include: { + // [key: string]: string; + // }; + // exclude: { + // [key: string]: string; + // }; + // wait_for: boolean; + jsonString: string; }; } diff --git a/opensearch_dashboards.json b/opensearch_dashboards.json index 5eb7af329..a3c749eb0 100644 --- a/opensearch_dashboards.json +++ b/opensearch_dashboards.json @@ -1,6 +1,6 @@ { "id": "indexManagementDashboards", - "version": "1.0.0.0", + "version": "1.1.0.0", "opensearchDashboardsVersion": "1.1.0", "configPath": ["opensearch_index_management"], "requiredPlugins": ["navigation"], diff --git a/package.json b/package.json index 63e0ed7a3..d4adbad2e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "opensearch_index_management_dashboards", - "version": "1.0.0.0", + "version": "1.1.0.0", "description": "Opensearch Dashboards plugin for Index Management", "main": "index.js", "license": "Apache-2.0", diff --git a/public/app.scss b/public/app.scss index ec2074508..a9eb69f53 100644 --- a/public/app.scss +++ b/public/app.scss @@ -50,3 +50,7 @@ $euiTextColor: $euiColorDarkestShade !default; } } } + +.state-accordion:hover { + text-decoration: none; +} diff --git a/public/components/JSONModal/JSONModal.tsx b/public/components/JSONModal/JSONModal.tsx index d5f523c63..6e9433cbf 100644 --- a/public/components/JSONModal/JSONModal.tsx +++ b/public/components/JSONModal/JSONModal.tsx @@ -30,13 +30,13 @@ interface JSONModalProps { const JSONModal: React.SFC = ({ title, json, onClose }) => { return ( - + {title} - + {JSON.stringify(json, null, 4)} diff --git a/public/components/JSONModal/__snapshots__/JSONModal.test.tsx.snap b/public/components/JSONModal/__snapshots__/JSONModal.test.tsx.snap index 0160721eb..45989e2c1 100644 --- a/public/components/JSONModal/__snapshots__/JSONModal.test.tsx.snap +++ b/public/components/JSONModal/__snapshots__/JSONModal.test.tsx.snap @@ -23,7 +23,6 @@ exports[` spec renders the component 1`] = ` >
diff --git a/public/pages/PolicyDetails/components/DeleteModal/DeleteModal.tsx b/public/pages/PolicyDetails/components/DeleteModal/DeleteModal.tsx index ed8198c74..71a639a35 100644 --- a/public/pages/PolicyDetails/components/DeleteModal/DeleteModal.tsx +++ b/public/pages/PolicyDetails/components/DeleteModal/DeleteModal.tsx @@ -13,50 +13,49 @@ import React, { ChangeEvent, Component, Fragment } from "react"; import { EuiConfirmModal, EuiForm, EuiFormRow, EuiFieldText, EuiOverlayMask, EuiSpacer } from "@elastic/eui"; interface DeleteModalProps { - policyId: string; - closeDeleteModal: (event?: any) => void; - onClickDelete: (event?: any) => void; + policyId: string; + closeDeleteModal: (event?: any) => void; + onClickDelete: (event?: any) => void; } interface DeleteModalState { - confirmDeleteText: string; + confirmDeleteText: string; } export default class DeleteModal extends Component { - state = { confirmDeleteText: "" }; + state = { confirmDeleteText: "" }; - onChange = (e: ChangeEvent): void => { - this.setState({ confirmDeleteText: e.target.value }); - }; + onChange = (e: ChangeEvent): void => { + this.setState({ confirmDeleteText: e.target.value }); + }; - render() { - const { policyId, closeDeleteModal, onClickDelete } = this.props; - const { confirmDeleteText } = this.state; + render() { + const { policyId, closeDeleteModal, onClickDelete } = this.props; + const { confirmDeleteText } = this.state; - return ( - - - - - Delete "{policyId}" permanently? Indices will no - longer be managed using this policy - - - - - - - - - ); - } + return ( + + + + + Delete "{policyId}" permanently? Indices will no longer be managed using this policy. + + + + + + + + + ); + } } diff --git a/public/pages/PolicyDetails/components/DeleteModal/__snapshots__/DeleteModal.test.tsx.snap b/public/pages/PolicyDetails/components/DeleteModal/__snapshots__/DeleteModal.test.tsx.snap index 6dbe84f6f..344ec0d2c 100644 --- a/public/pages/PolicyDetails/components/DeleteModal/__snapshots__/DeleteModal.test.tsx.snap +++ b/public/pages/PolicyDetails/components/DeleteModal/__snapshots__/DeleteModal.test.tsx.snap @@ -69,7 +69,7 @@ exports[` spec renders the component 1`] = ` some_id - " permanently? Indices will no longer be managed using this policy + " permanently? Indices will no longer be managed using this policy.
@@ -101,7 +101,7 @@ exports[` spec renders the component 1`] = ` class="euiFormHelpText euiFormRow__text" id="some_html_id-help" > - To confirm deletion, type delete + To confirm deletion, type "delete".
diff --git a/public/pages/PolicyDetails/components/PolicySettings/PolicySettings.tsx b/public/pages/PolicyDetails/components/PolicySettings/PolicySettings.tsx index 59c3ffac2..81a8af0b5 100644 --- a/public/pages/PolicyDetails/components/PolicySettings/PolicySettings.tsx +++ b/public/pages/PolicyDetails/components/PolicySettings/PolicySettings.tsx @@ -11,10 +11,9 @@ import React, { Component } from "react"; import { EuiLink, EuiFlexGrid, EuiSpacer, EuiFlexItem, EuiText } from "@elastic/eui"; -import { ContentPanel, ContentPanelActions } from "../../../../components/ContentPanel"; +import { ContentPanel } from "../../../../components/ContentPanel"; import { ModalConsumer } from "../../../../components/Modal"; import { ErrorNotification, ISMTemplate } from "../../../../../models/interfaces"; -import CreatePolicyModal from "../../../../components/CreatePolicyModal"; import JSONModal from "../../../../components/JSONModal"; interface PolicySettingsProps { @@ -25,14 +24,13 @@ interface PolicySettingsProps { description: string; sequenceNumber: number; ismTemplates: ISMTemplate[] | ISMTemplate | null; - onEdit: (visual: boolean) => void; } interface PolicySettingsState {} export default class PolicySettings extends Component { render() { - const { policyId, errorNotification, primaryTerm, lastUpdated, description, sequenceNumber, onEdit } = this.props; + const { policyId, errorNotification, primaryTerm, lastUpdated, description, sequenceNumber } = this.props; const updatedDate = lastUpdated ? new Date(lastUpdated).toLocaleString() : "-"; @@ -58,31 +56,7 @@ export default class PolicySettings extends Component - {() => ( - void) => () => - onShow(CreatePolicyModal, { isEdit: true, onClickContinue: onEdit }), - }, - }, - ]} - /> - )} - - } - bodyStyles={{ padding: "10px" }} - title="Policy settings" - titleSize="s" - > +
diff --git a/public/pages/PolicyDetails/components/PolicySettings/__snapshots__/PolicySettings.test.tsx.snap b/public/pages/PolicyDetails/components/PolicySettings/__snapshots__/PolicySettings.test.tsx.snap index 2e402a98a..1d6020947 100644 --- a/public/pages/PolicyDetails/components/PolicySettings/__snapshots__/PolicySettings.test.tsx.snap +++ b/public/pages/PolicyDetails/components/PolicySettings/__snapshots__/PolicySettings.test.tsx.snap @@ -18,41 +18,6 @@ exports[` spec renders the component 1`] = ` Policy settings
-
-
-
-
-
- -
-
-
-
-

+ + + {({ onShow }) => ( + onShow(CreatePolicyModal, { isEdit: true, onClickContinue: this.onEdit })} + data-test-subj="policy-details-edit-button" + > + Edit + + )} + + Delete @@ -212,7 +226,6 @@ export default class PolicyDetails extends Component diff --git a/public/pages/VisualCreatePolicy/components/DraggableItem/DraggableItem.test.tsx b/public/pages/VisualCreatePolicy/components/DraggableItem/DraggableItem.test.tsx index 8e69fbc01..c5733b945 100644 --- a/public/pages/VisualCreatePolicy/components/DraggableItem/DraggableItem.test.tsx +++ b/public/pages/VisualCreatePolicy/components/DraggableItem/DraggableItem.test.tsx @@ -26,7 +26,15 @@ describe(" spec", () => { const { container } = render( {}}> - {}} onClickEdit={() => {}} /> + {}} + onClickEdit={() => {}} + draggableType="action" + /> ); @@ -40,7 +48,15 @@ describe(" spec", () => { const { getByTestId } = render( {}}> - {}} /> + {}} + draggableType="action" + /> ); @@ -56,7 +72,15 @@ describe(" spec", () => { const { getByTestId } = render( {}}> - {}} onClickEdit={onClickEdit} /> + {}} + onClickEdit={onClickEdit} + draggableType="action" + /> ); diff --git a/public/pages/VisualCreatePolicy/components/DraggableItem/DraggableItem.tsx b/public/pages/VisualCreatePolicy/components/DraggableItem/DraggableItem.tsx index 3111fa91f..c29cba407 100644 --- a/public/pages/VisualCreatePolicy/components/DraggableItem/DraggableItem.tsx +++ b/public/pages/VisualCreatePolicy/components/DraggableItem/DraggableItem.tsx @@ -10,7 +10,7 @@ */ import React from "react"; -import { EuiFlexGroup, EuiFlexItem, EuiDraggable, EuiPanel, EuiIcon, EuiButtonIcon } from "@elastic/eui"; +import { EuiFlexGroup, EuiFlexItem, EuiDraggable, EuiPanel, EuiIcon, EuiButtonIcon, EuiToolTip } from "@elastic/eui"; interface DraggableItemProps { content: string | JSX.Element | null; @@ -19,9 +19,10 @@ interface DraggableItemProps { isLast: boolean; onClickDelete: () => void; onClickEdit: () => void; + draggableType: "action" | "transition"; } -const DraggableItem = ({ content, id, idx, isLast, onClickDelete, onClickEdit }: DraggableItemProps) => ( +const DraggableItem = ({ content, id, idx, isLast, onClickDelete, onClickEdit, draggableType }: DraggableItemProps) => ( {(provided) => ( @@ -33,22 +34,26 @@ const DraggableItem = ({ content, id, idx, isLast, onClickDelete, onClickEdit }: {content} - + Delete {draggableType}

}> + +
- + Edit {draggableType}

}> + +
diff --git a/public/pages/VisualCreatePolicy/components/DraggableItem/__snapshots__/DraggableItem.test.tsx.snap b/public/pages/VisualCreatePolicy/components/DraggableItem/__snapshots__/DraggableItem.test.tsx.snap index 805325b21..04aa3e112 100644 --- a/public/pages/VisualCreatePolicy/components/DraggableItem/__snapshots__/DraggableItem.test.tsx.snap +++ b/public/pages/VisualCreatePolicy/components/DraggableItem/__snapshots__/DraggableItem.test.tsx.snap @@ -43,26 +43,34 @@ exports[` spec renders the component 1`] = `
- + +
- + +
diff --git a/public/pages/VisualCreatePolicy/components/EuiFormCustomLabel/EuiFormCustomLabel.tsx b/public/pages/VisualCreatePolicy/components/EuiFormCustomLabel/EuiFormCustomLabel.tsx index d0d7f84bc..7e25cdec9 100644 --- a/public/pages/VisualCreatePolicy/components/EuiFormCustomLabel/EuiFormCustomLabel.tsx +++ b/public/pages/VisualCreatePolicy/components/EuiFormCustomLabel/EuiFormCustomLabel.tsx @@ -15,23 +15,33 @@ import { EuiText } from "@elastic/eui"; interface EuiFormCustomLabelProps { title: string; helpText?: string; + learnMore?: JSX.Element | null | undefined; textStyle?: object; headerStyle?: object; + isInvalid?: boolean; } // New pattern for label and help text both being above the form row instead of label above and help below. const EuiFormCustomLabel = ({ title, helpText, + learnMore = null, textStyle = { marginBottom: "5px" }, headerStyle = { marginBottom: "2px" }, + isInvalid = false, }: EuiFormCustomLabelProps) => ( -
{title}
+
+ {title} +

{" "} {/* Keep the

tag even if no helpText to remove last child styling on h tags */} - {helpText && {helpText}} + {helpText && ( + + {helpText} {learnMore} + + )}

); diff --git a/public/pages/VisualCreatePolicy/components/EuiFormCustomLabel/__snapshots__/EuiFormCustomLabel.test.tsx.snap b/public/pages/VisualCreatePolicy/components/EuiFormCustomLabel/__snapshots__/EuiFormCustomLabel.test.tsx.snap index d216ca11f..352403080 100644 --- a/public/pages/VisualCreatePolicy/components/EuiFormCustomLabel/__snapshots__/EuiFormCustomLabel.test.tsx.snap +++ b/public/pages/VisualCreatePolicy/components/EuiFormCustomLabel/__snapshots__/EuiFormCustomLabel.test.tsx.snap @@ -6,6 +6,7 @@ exports[` spec renders the component 1`] = ` style="margin-bottom: 5px;" >
Some title @@ -13,9 +14,10 @@ exports[` spec renders the component 1`] = `

Some helpful text +

diff --git a/public/pages/VisualCreatePolicy/components/FlyoutFooter/FlyoutFooter.tsx b/public/pages/VisualCreatePolicy/components/FlyoutFooter/FlyoutFooter.tsx index f27c2baa4..66bf556b2 100644 --- a/public/pages/VisualCreatePolicy/components/FlyoutFooter/FlyoutFooter.tsx +++ b/public/pages/VisualCreatePolicy/components/FlyoutFooter/FlyoutFooter.tsx @@ -21,9 +21,9 @@ interface FlyoutFooterProps { } const FlyoutFooter = ({ edit, action, disabledAction = false, onClickCancel, onClickAction }: FlyoutFooterProps) => ( - + - + Cancel diff --git a/public/pages/VisualCreatePolicy/components/FlyoutFooter/__snapshots__/FlyoutFooter.test.tsx.snap b/public/pages/VisualCreatePolicy/components/FlyoutFooter/__snapshots__/FlyoutFooter.test.tsx.snap index f9e880945..d9490da11 100644 --- a/public/pages/VisualCreatePolicy/components/FlyoutFooter/__snapshots__/FlyoutFooter.test.tsx.snap +++ b/public/pages/VisualCreatePolicy/components/FlyoutFooter/__snapshots__/FlyoutFooter.test.tsx.snap @@ -2,7 +2,7 @@ exports[` spec renders the component 1`] = `
spec renders the component 1`] = ` - EuiIconMock diff --git a/public/pages/VisualCreatePolicy/components/ISMTemplate/ISMTemplate.tsx b/public/pages/VisualCreatePolicy/components/ISMTemplate/ISMTemplate.tsx index 11c79331e..522bb6de0 100644 --- a/public/pages/VisualCreatePolicy/components/ISMTemplate/ISMTemplate.tsx +++ b/public/pages/VisualCreatePolicy/components/ISMTemplate/ISMTemplate.tsx @@ -31,6 +31,7 @@ const ISMTemplate = ({ template, onUpdateTemplate, onRemoveTemplate, isFirst }: ({ label: pattern }))} @@ -64,7 +65,6 @@ const ISMTemplate = ({ template, onUpdateTemplate, onRemoveTemplate, isFirst }: //TODO setInvalid(false); }} - isClearable={true} isInvalid={isInvalid} data-test-subj="ism-template-index-pattern-input" /> diff --git a/public/pages/VisualCreatePolicy/components/ISMTemplate/__snapshots__/ISMTemplate.test.tsx.snap b/public/pages/VisualCreatePolicy/components/ISMTemplate/__snapshots__/ISMTemplate.test.tsx.snap index 35094f9a0..c5bb1d0dd 100644 --- a/public/pages/VisualCreatePolicy/components/ISMTemplate/__snapshots__/ISMTemplate.test.tsx.snap +++ b/public/pages/VisualCreatePolicy/components/ISMTemplate/__snapshots__/ISMTemplate.test.tsx.snap @@ -29,7 +29,7 @@ exports[` spec renders the component 1`] = ` class="euiFormControlLayout__childrenWrapper" >
@@ -71,18 +71,6 @@ exports[` spec renders the component 1`] = ` />
-
- -
diff --git a/public/pages/VisualCreatePolicy/components/ISMTemplates/ISMTemplates.tsx b/public/pages/VisualCreatePolicy/components/ISMTemplates/ISMTemplates.tsx index 017a3ba67..00705ac36 100644 --- a/public/pages/VisualCreatePolicy/components/ISMTemplates/ISMTemplates.tsx +++ b/public/pages/VisualCreatePolicy/components/ISMTemplates/ISMTemplates.tsx @@ -45,7 +45,7 @@ const ISMTemplates = ({ policy, onChangePolicy }: ISMTemplatesProps) => { -

ISM template

+

ISM templates

@@ -57,9 +57,9 @@ const ISMTemplates = ({ policy, onChangePolicy }: ISMTemplatesProps) => { } titleSize="s" subTitleText={ - -

- Specify an ISM template pattern that matches the index to apply the policy.{" "} + +

+ Specify ISM template patterns that match the index to apply the policy.{" "} Learn more @@ -109,6 +109,7 @@ const ISMTemplates = ({ policy, onChangePolicy }: ISMTemplatesProps) => { {!templates.length ? ( No ISM templates

} + style={{ maxWidth: "37em" }} titleSize="s" body={

diff --git a/public/pages/VisualCreatePolicy/components/ISMTemplates/__snapshots__/ISMTemplates.test.tsx.snap b/public/pages/VisualCreatePolicy/components/ISMTemplates/__snapshots__/ISMTemplates.test.tsx.snap index 37e5b99c1..2cecba2d3 100644 --- a/public/pages/VisualCreatePolicy/components/ISMTemplates/__snapshots__/ISMTemplates.test.tsx.snap +++ b/public/pages/VisualCreatePolicy/components/ISMTemplates/__snapshots__/ISMTemplates.test.tsx.snap @@ -22,7 +22,7 @@ exports[` spec renders the component 1`] = ` class="euiText euiText--medium" >

- ISM template + ISM templates

@@ -46,21 +46,25 @@ exports[` spec renders the component 1`] = ` class="euiText euiText--small" style="padding: 5px 0px;" > -

- Specify an ISM template pattern that matches the index to apply the policy. - - - Learn more - EuiIconMock - -

+ Specify ISM template patterns that match the index to apply the policy. + + + Learn more + EuiIconMock + +

+ @@ -74,186 +78,50 @@ exports[` spec renders the component 1`] = ` style="padding: 10px 0px 0px 10px;" >
-
-
-
- Index patterns -
-
-
-
+ No ISM templates +
-
- Priority -
-
-
-
-
-
+ class="euiSpacer euiSpacer--m" + />
-
- +

+ Your policy currently has no ISM templates defined. Add ISM templates to automatically apply the policy to indices created in the future. +

-
+
-
-
-
-
- -
-
-
-
-
+ class="euiSpacer euiSpacer--l" + />
+ -
-
-
- + +
diff --git a/public/pages/VisualCreatePolicy/components/LegacyNotification/LegacyNotification.tsx b/public/pages/VisualCreatePolicy/components/LegacyNotification/LegacyNotification.tsx index fe69c07bc..eaf69af21 100644 --- a/public/pages/VisualCreatePolicy/components/LegacyNotification/LegacyNotification.tsx +++ b/public/pages/VisualCreatePolicy/components/LegacyNotification/LegacyNotification.tsx @@ -20,16 +20,18 @@ interface LegacyNotificationProps { notificationJsonString: string; onChangeNotificationJsonString: (str: string) => void; actionNotification?: boolean; + isInvalid?: boolean; } const LegacyNotification = ({ notificationJsonString, onChangeNotificationJsonString, actionNotification = false, + isInvalid = false, // TODO: default to false for error notification for now, but add validation logic for it }: LegacyNotificationProps) => { return ( <> - + {(isDarkMode) => ( spec renders the component 1`] = `
diff --git a/public/pages/VisualCreatePolicy/components/PolicyInfo/PolicyInfo.tsx b/public/pages/VisualCreatePolicy/components/PolicyInfo/PolicyInfo.tsx index f9a64f928..abf03c418 100644 --- a/public/pages/VisualCreatePolicy/components/PolicyInfo/PolicyInfo.tsx +++ b/public/pages/VisualCreatePolicy/components/PolicyInfo/PolicyInfo.tsx @@ -44,7 +44,7 @@ const PolicyInfo = ({ isEdit, policyId, policyIdError, description, onChangePoli - + spec renders the component 1`] = ` style="margin-bottom: 5px;" >
Policy ID @@ -40,9 +41,10 @@ exports[` spec renders the component 1`] = `

Specify a unique and descriptive ID that is easy to recognize and remember. +

@@ -79,6 +81,7 @@ exports[` spec renders the component 1`] = ` style="margin-bottom: 5px;" >
Description @@ -86,9 +89,10 @@ exports[` spec renders the component 1`] = `

- Describe the policy + Describe the policy. +

diff --git a/public/pages/VisualCreatePolicy/components/States/State.tsx b/public/pages/VisualCreatePolicy/components/States/State.tsx index 4fc296934..2cb04fcb9 100644 --- a/public/pages/VisualCreatePolicy/components/States/State.tsx +++ b/public/pages/VisualCreatePolicy/components/States/State.tsx @@ -10,7 +10,7 @@ */ import React from "react"; -import { EuiAccordion, EuiText, EuiPanel, EuiFlexGroup, EuiFlexItem, EuiButtonIcon } from "@elastic/eui"; +import { EuiAccordion, EuiText, EuiPanel, EuiFlexGroup, EuiFlexItem, EuiButtonIcon, EuiToolTip } from "@elastic/eui"; import "brace/theme/github"; import "brace/mode/json"; import { State as StateData } from "../../../../../models/interfaces"; @@ -34,6 +34,7 @@ const State = ({ state, isInitialState, idx, onClickEditState, onClickDeleteStat @@ -68,34 +69,39 @@ const State = ({ state, isInitialState, idx, onClickEditState, onClickDeleteStat {({ onShow, onClose }) => ( - - onShow(ConfirmationModal, { - title: "Delete state", - bodyMessage: ( - - - Delete "{state.name}" permanently? Deleting the state will result in deleting all transitions. - - - ), - actionMessage: "Delete state", - actionProps: { color: "danger" }, - modalProps: { maxWidth: 600 }, - onAction: () => onClickDeleteState(idx), - onClose, - }) - } - data-test-subj="state-delete-button" - /> + Delete state

}> + + onShow(ConfirmationModal, { + title: "Delete state", + bodyMessage: ( + + + Delete "{state.name}" permanently? Deleting the state will result in deleting all + transitions. + + + ), + actionMessage: "Delete state", + actionProps: { color: "danger" }, + modalProps: { maxWidth: 600 }, + onAction: () => onClickDeleteState(idx), + onClose, + }) + } + data-test-subj="state-delete-button" + /> +
)}
- onClickEditState(state)} /> + Edit state

}> + onClickEditState(state)} /> +
) @@ -112,7 +118,7 @@ const State = ({ state, isInitialState, idx, onClickEditState, onClickDeleteStat {!state.actions?.length ? ( No actions. Edit state to add actions. ) : ( - + {state.actions.map((action) => ( {actionRepoSingleton.getUIActionFromData(action).content()} @@ -130,7 +136,7 @@ const State = ({ state, isInitialState, idx, onClickEditState, onClickDeleteStat {!state.transitions?.length ? ( No transitions. Edit state to add transitions. ) : ( - + {state.transitions.map((transition) => ( diff --git a/public/pages/VisualCreatePolicy/components/States/States.tsx b/public/pages/VisualCreatePolicy/components/States/States.tsx index c89ad88a1..6f9524565 100644 --- a/public/pages/VisualCreatePolicy/components/States/States.tsx +++ b/public/pages/VisualCreatePolicy/components/States/States.tsx @@ -27,7 +27,7 @@ import { ContentPanel } from "../../../../components/ContentPanel"; import "brace/theme/github"; import "brace/mode/json"; import { Policy, State as StateData } from "../../../../../models/interfaces"; -import { DOCUMENTATION_URL } from "../../../../utils/constants"; +import { STATES_DOCUMENTATION_URL } from "../../../../utils/constants"; import State from "./State"; interface StatesProps { @@ -46,12 +46,12 @@ const States = ({ onOpenFlyout, policy, onClickEditState, onClickDeleteState, on title={`States (${policy.states.length})`} titleSize="s" subTitleText={ - -

+ +

You can think of policies as state machines. "Actions" are the operations ISM performs when an index is in a certain state.
"Transitions" define when to move from one state to another.{" "} - + Learn more

diff --git a/public/pages/VisualCreatePolicy/components/States/__snapshots__/State.test.tsx.snap b/public/pages/VisualCreatePolicy/components/States/__snapshots__/State.test.tsx.snap index 67f99b7ac..30fa05919 100644 --- a/public/pages/VisualCreatePolicy/components/States/__snapshots__/State.test.tsx.snap +++ b/public/pages/VisualCreatePolicy/components/States/__snapshots__/State.test.tsx.snap @@ -11,7 +11,7 @@ exports[` spec renders the component 1`] = ` + +
- + +
@@ -163,7 +171,7 @@ exports[` spec renders the component 1`] = ` class="euiFlexItem" >
spec renders the component 1`] = ` class="euiFlexItem" >
spec renders the component 1`] = ` class="euiText euiText--small" style="padding: 5px 0px;" > -

- You can think of policies as state machines. "Actions" are the operations ISM performs when an index is in a certain state. -
- "Transitions" define when to move from one state to another. - - - Learn more - EuiIconMock - -

+ You can think of policies as state machines. "Actions" are the operations ISM performs when an index is in a certain state. +
+ "Transitions" define when to move from one state to another. + + + Learn more + EuiIconMock + +

+
@@ -120,7 +124,7 @@ exports[` spec renders the component 1`] = ` + +
- + +
@@ -272,7 +284,7 @@ exports[` spec renders the component 1`] = ` class="euiFlexItem" >
spec renders the component 1`] = ` class="euiFlexItem" >
spec renders the component 1`] = ` + +
- + +
@@ -465,7 +485,7 @@ exports[` spec renders the component 1`] = ` class="euiFlexItem" >
) => { const timeout = e.target.value; onChangeAction(action.clone({ ...action.action, timeout })); @@ -59,18 +59,23 @@ const TimeoutRetrySettings = ({ action, editAction, onChangeAction }: TimeoutRet - + ) => { const count = e.target.valueAsNumber; - onChangeAction(action.clone({ ...action.action, retry: { ...action.action.retry, count } })); + const retry = { ...action.action.retry, count }; + if (isNaN(count)) delete retry.count; + onChangeAction(action.clone({ ...action.action, retry })); }} /> @@ -81,10 +86,10 @@ const TimeoutRetrySettings = ({ action, editAction, onChangeAction }: TimeoutRet ) => { const backoff = e.target.value; onChangeAction(action.clone({ ...action.action, retry: { ...action.action.retry, backoff } })); @@ -98,9 +103,9 @@ const TimeoutRetrySettings = ({ action, editAction, onChangeAction }: TimeoutRet ) => { const delay = e.target.value; onChangeAction(action.clone({ ...action.action, retry: { ...action.action.retry, delay } })); diff --git a/public/pages/VisualCreatePolicy/components/TimeoutRetrySettings/__snapshots__/TimeoutRetrySettings.test.tsx.snap b/public/pages/VisualCreatePolicy/components/TimeoutRetrySettings/__snapshots__/TimeoutRetrySettings.test.tsx.snap index f2a3319ad..96f4576f6 100644 --- a/public/pages/VisualCreatePolicy/components/TimeoutRetrySettings/__snapshots__/TimeoutRetrySettings.test.tsx.snap +++ b/public/pages/VisualCreatePolicy/components/TimeoutRetrySettings/__snapshots__/TimeoutRetrySettings.test.tsx.snap @@ -61,6 +61,7 @@ exports[` spec renders the component 1`] = ` style="margin-bottom: 5px;" >
Timeout @@ -68,9 +69,10 @@ exports[` spec renders the component 1`] = `

The timeout period for the action. Accepts time units, e.g. "5h" or "1d". +

@@ -114,6 +116,7 @@ exports[` spec renders the component 1`] = ` style="margin-bottom: 5px;" >
Retry count @@ -121,9 +124,10 @@ exports[` spec renders the component 1`] = `

- The number of times the action should be retried if it fails. + The number of times the action should be retried if it fails. Must be greater than 0. +

@@ -168,6 +172,7 @@ exports[` spec renders the component 1`] = ` style="margin-bottom: 5px;" >
Retry backoff @@ -175,9 +180,10 @@ exports[` spec renders the component 1`] = `

The backoff policy type to use when retrying. +

@@ -236,6 +242,7 @@ exports[` spec renders the component 1`] = ` style="margin-bottom: 5px;" >
Retry delay @@ -243,9 +250,10 @@ exports[` spec renders the component 1`] = `

The time to wait between retries. Accepts time units, e.g. "2h" or "1d" +

diff --git a/public/pages/VisualCreatePolicy/components/Transition/Transition.tsx b/public/pages/VisualCreatePolicy/components/Transition/Transition.tsx index 8ac282502..24bbb6743 100644 --- a/public/pages/VisualCreatePolicy/components/Transition/Transition.tsx +++ b/public/pages/VisualCreatePolicy/components/Transition/Transition.tsx @@ -10,14 +10,16 @@ */ import React, { ChangeEvent } from "react"; -import { EuiFormRow, EuiSelect, EuiSpacer, EuiFieldText, EuiFieldNumber } from "@elastic/eui"; +import { EuiLink, EuiIcon, EuiFormRow, EuiSelect, EuiSpacer, EuiFieldText, EuiFieldNumber } from "@elastic/eui"; import moment from "moment-timezone"; import EuiFormCustomLabel from "../EuiFormCustomLabel"; import { UITransition } from "../../../../../models/interfaces"; +import { TRANSITION_DOCUMENTATION_URL } from "../../../../utils/constants"; const timezones = moment.tz.names().map((tz) => ({ label: tz, text: tz })); const conditionTypeOptions = [ + { value: "none", text: "No condition" }, { value: "min_index_age", text: "Minimum index age" }, { value: "min_doc_count", text: "Minimum doc count" }, { value: "min_size", text: "Minimum size" }, @@ -31,30 +33,30 @@ interface TransitionProps { const Transition = ({ uiTransition, onChangeTransition }: TransitionProps) => { // We currently only support one transition condition - const conditionType = Object.keys(uiTransition.transition?.conditions || []).pop() || "min_index_age"; + const conditionType = Object.keys(uiTransition.transition.conditions || []).pop() || "none"; + const conditions = uiTransition.transition.conditions; return ( <> - + { const selectedConditionType = e.target.value; - let condition = {}; - if (selectedConditionType === "min_index_age") condition = { min_index_age: "30d" }; - if (selectedConditionType === "min_doc_count") condition = { min_doc_count: 1000000 }; - if (selectedConditionType === "min_size") condition = { min_size: "50gb" }; + const transition = { ...uiTransition.transition }; + if (selectedConditionType === "none") delete transition.conditions; + if (selectedConditionType === "min_index_age") transition.conditions = { min_index_age: "30d" }; + if (selectedConditionType === "min_doc_count") transition.conditions = { min_doc_count: 1000000 }; + if (selectedConditionType === "min_size") transition.conditions = { min_size: "50gb" }; if (selectedConditionType === "cron") - condition = { cron: { cron: { expression: "* 17 * * SAT", timezone: "America/Los_Angeles" } } }; + transition.conditions = { cron: { cron: { expression: "* 17 * * SAT", timezone: "America/Los_Angeles" } } }; onChangeTransition({ ...uiTransition, - transition: { - ...uiTransition.transition, - conditions: condition, - }, + transition, }); }} data-test-subj="create-state-action-type" @@ -66,10 +68,10 @@ const Transition = ({ uiTransition, onChangeTransition }: TransitionProps) => { {conditionType === "min_index_age" && ( <> - + ) => { const minIndexAge = e.target.value; onChangeTransition({ @@ -94,19 +96,21 @@ const Transition = ({ uiTransition, onChangeTransition }: TransitionProps) => { title="Minimum doc count" helpText="The minimum number of documents required to transition to the next state." /> - + ) => { const minDocCount = e.target.valueAsNumber; + const conditions = { min_doc_count: minDocCount }; + // TODO: clean this up.. + // set it to undefined instead of deleting... as we use the presence of the key itself for the type of transition + if (isNaN(minDocCount)) conditions.min_doc_count = undefined; onChangeTransition({ ...uiTransition, transition: { ...uiTransition.transition, - conditions: { - min_doc_count: minDocCount, - }, + conditions, }, }); }} @@ -122,10 +126,10 @@ const Transition = ({ uiTransition, onChangeTransition }: TransitionProps) => { title="Minimum index size" helpText="The minimum size of the total primary shard storage required to transition to the next state." /> - + ) => { const minSize = e.target.value; onChangeTransition({ @@ -146,11 +150,19 @@ const Transition = ({ uiTransition, onChangeTransition }: TransitionProps) => { {conditionType === "cron" && ( <> - - + + Learn more + + } + /> + ) => { const expression = e.target.value; onChangeTransition({ @@ -175,11 +187,12 @@ const Transition = ({ uiTransition, onChangeTransition }: TransitionProps) => { - + ) => { const timezone = e.target.value; onChangeTransition({ diff --git a/public/pages/VisualCreatePolicy/components/Transition/__snapshots__/Transition.test.tsx.snap b/public/pages/VisualCreatePolicy/components/Transition/__snapshots__/Transition.test.tsx.snap index 66cc43b8d..a623686d1 100644 --- a/public/pages/VisualCreatePolicy/components/Transition/__snapshots__/Transition.test.tsx.snap +++ b/public/pages/VisualCreatePolicy/components/Transition/__snapshots__/Transition.test.tsx.snap @@ -6,6 +6,7 @@ exports[` spec renders the component 1`] = ` style="margin-bottom: 5px;" >
Condition @@ -13,9 +14,10 @@ exports[` spec renders the component 1`] = `

Specify the condition needed to be met to transition to the destination state. +

diff --git a/public/pages/VisualCreatePolicy/components/UIActions/AllocationUIAction.tsx b/public/pages/VisualCreatePolicy/components/UIActions/AllocationUIAction.tsx index d6822093e..eb6f9cae9 100644 --- a/public/pages/VisualCreatePolicy/components/UIActions/AllocationUIAction.tsx +++ b/public/pages/VisualCreatePolicy/components/UIActions/AllocationUIAction.tsx @@ -10,9 +10,11 @@ */ import React from "react"; +import { EuiFormRow, EuiCodeEditor } from "@elastic/eui"; import { AllocationAction, UIAction } from "../../../../../models/interfaces"; import { makeId } from "../../../../utils/helpers"; import { ActionType } from "../../utils/constants"; +import { DarkModeConsumer } from "../../../../components/DarkMode"; export default class AllocationUIAction implements UIAction { id: string; @@ -28,9 +30,54 @@ export default class AllocationUIAction implements UIAction { clone = (action: AllocationAction = this.action) => new AllocationUIAction(action, this.id); + isValid = () => { + try { + JSON.parse(this.getActionJsonString(this.action)); + return true; + } catch (err) { + return false; + } + }; + + getActionJsonString = (action: AllocationAction) => { + const allocation = action.allocation; + return allocation.hasOwnProperty("jsonString") ? allocation.jsonString : JSON.stringify(allocation, null, 4); + }; + + getActionJson = (action: AllocationAction) => { + const allocation = action.allocation; + return allocation.hasOwnProperty("jsonString") ? JSON.parse(allocation.jsonString) : allocation; + }; + render = (action: UIAction, onChangeAction: (action: UIAction) => void) => { - return
; + return ( + + + {(isDarkMode) => ( + { + onChangeAction( + this.clone({ + ...action.action, + allocation: { jsonString: str }, + }) + ); + }} + setOptions={{ fontSize: "14px" }} + aria-label="Code Editor" + /> + )} + + + ); }; - toAction = () => this.action; + toAction = () => ({ + ...this.action, + allocation: this.getActionJson(this.action), + }); } diff --git a/public/pages/VisualCreatePolicy/components/UIActions/CloseUIAction.tsx b/public/pages/VisualCreatePolicy/components/UIActions/CloseUIAction.tsx index 3f3a286fe..a474bea4e 100644 --- a/public/pages/VisualCreatePolicy/components/UIActions/CloseUIAction.tsx +++ b/public/pages/VisualCreatePolicy/components/UIActions/CloseUIAction.tsx @@ -28,6 +28,8 @@ export default class CloseUIAction implements UIAction { clone = (action: CloseAction) => new CloseUIAction(action, this.id); + isValid = () => true; + render = (action: UIAction, onChangeAction: (action: UIAction) => void) => { return
; }; diff --git a/public/pages/VisualCreatePolicy/components/UIActions/DeleteUIAction.tsx b/public/pages/VisualCreatePolicy/components/UIActions/DeleteUIAction.tsx index ece534f7a..35f1afeb7 100644 --- a/public/pages/VisualCreatePolicy/components/UIActions/DeleteUIAction.tsx +++ b/public/pages/VisualCreatePolicy/components/UIActions/DeleteUIAction.tsx @@ -28,6 +28,8 @@ export default class DeleteUIAction implements UIAction { clone = (action: DeleteAction) => new DeleteUIAction(action, this.id); + isValid = () => true; + render = (action: UIAction, onChangeAction: (action: UIAction) => void) => { return
; }; diff --git a/public/pages/VisualCreatePolicy/components/UIActions/ForceMergeUIAction.tsx b/public/pages/VisualCreatePolicy/components/UIActions/ForceMergeUIAction.tsx index c768940a8..a3bc01e12 100644 --- a/public/pages/VisualCreatePolicy/components/UIActions/ForceMergeUIAction.tsx +++ b/public/pages/VisualCreatePolicy/components/UIActions/ForceMergeUIAction.tsx @@ -30,23 +30,25 @@ export default class ForceMergeUIAction implements UIAction { clone = (action: ForceMergeAction) => new ForceMergeUIAction(action, this.id); + isValid = () => { + const segments = this.action.force_merge.max_num_segments; + return !!segments && segments > 0; + }; + render = (action: UIAction, onChangeAction: (action: UIAction) => void) => { + const segments = action.action.force_merge.max_num_segments; return ( <> - - + + ) => { const maxNumSegments = e.target.valueAsNumber; - onChangeAction( - this.clone({ - force_merge: { - max_num_segments: maxNumSegments, - }, - }) - ); + const forceMerge = { max_num_segments: maxNumSegments }; + if (isNaN(maxNumSegments)) delete forceMerge.max_num_segments; + onChangeAction(this.clone({ force_merge: forceMerge })); }} data-test-subj="action-render-force-merge" /> diff --git a/public/pages/VisualCreatePolicy/components/UIActions/IndexPriorityUIAction.tsx b/public/pages/VisualCreatePolicy/components/UIActions/IndexPriorityUIAction.tsx index 0b466f03e..fb47f8ed7 100644 --- a/public/pages/VisualCreatePolicy/components/UIActions/IndexPriorityUIAction.tsx +++ b/public/pages/VisualCreatePolicy/components/UIActions/IndexPriorityUIAction.tsx @@ -30,21 +30,29 @@ export default class IndexPriorityUIAction implements UIAction new IndexPriorityUIAction(action, this.id); + isValid = () => { + const priority = this.action.index_priority.priority; + return typeof priority !== "undefined" && priority >= 0; + }; + render = (action: UIAction, onChangeAction: (action: UIAction) => void) => { + const priority = action.action.index_priority.priority; return ( <> - - + + ) => { const priority = e.target.valueAsNumber; - onChangeAction( - this.clone({ - index_priority: { priority }, - }) - ); + const indexPriority = { priority }; + if (isNaN(priority)) delete indexPriority.priority; + onChangeAction(this.clone({ index_priority: indexPriority })); }} data-test-subj="action-render-index-priority" /> diff --git a/public/pages/VisualCreatePolicy/components/UIActions/NotificationUIAction.tsx b/public/pages/VisualCreatePolicy/components/UIActions/NotificationUIAction.tsx index 627f595a6..2641609d3 100644 --- a/public/pages/VisualCreatePolicy/components/UIActions/NotificationUIAction.tsx +++ b/public/pages/VisualCreatePolicy/components/UIActions/NotificationUIAction.tsx @@ -22,6 +22,7 @@ interface NotifUIProps { action: NotificationAction; clone: (action: NotificationAction) => NotificationUIAction; onChangeAction: (action: UIAction) => void; + isInvalid: boolean; } class NotifUI extends React.Component { onChangeNotificationJsonString = (str: string) => { @@ -35,12 +36,13 @@ class NotifUI extends React.Component { }; render() { - const { action } = this.props; + const { action, isInvalid } = this.props; return ( ); } @@ -61,11 +63,21 @@ export default class NotificationUIAction implements UIAction new NotificationUIAction(action, this.id); + isValid = () => { + try { + JSON.parse(this.action.notificationJsonString); + return true; + } catch (err) { + console.error(err); + return false; + } + }; + render = (action: UIAction, onChangeAction: (action: UIAction) => void) => { return ( {(services: BrowserServices | null) => - services && + services && } ); diff --git a/public/pages/VisualCreatePolicy/components/UIActions/OpenUIAction.tsx b/public/pages/VisualCreatePolicy/components/UIActions/OpenUIAction.tsx index 0f0443200..42e335122 100644 --- a/public/pages/VisualCreatePolicy/components/UIActions/OpenUIAction.tsx +++ b/public/pages/VisualCreatePolicy/components/UIActions/OpenUIAction.tsx @@ -28,6 +28,8 @@ export default class OpenUIAction implements UIAction { clone = (action: OpenAction) => new OpenUIAction(action, this.id); + isValid = () => true; + render = (action: UIAction, onChangeAction: (action: UIAction) => void) => { return
; }; diff --git a/public/pages/VisualCreatePolicy/components/UIActions/ReadOnlyUIAction.tsx b/public/pages/VisualCreatePolicy/components/UIActions/ReadOnlyUIAction.tsx index b50be8a51..ffe876050 100644 --- a/public/pages/VisualCreatePolicy/components/UIActions/ReadOnlyUIAction.tsx +++ b/public/pages/VisualCreatePolicy/components/UIActions/ReadOnlyUIAction.tsx @@ -28,6 +28,8 @@ export default class ReadOnlyUIAction implements UIAction { clone = (action: ReadOnlyAction) => new ReadOnlyUIAction(action, this.id); + isValid = () => true; + render = (action: UIAction, onChangeAction: (action: UIAction) => void) => { return
; }; diff --git a/public/pages/VisualCreatePolicy/components/UIActions/ReadWriteUIAction.tsx b/public/pages/VisualCreatePolicy/components/UIActions/ReadWriteUIAction.tsx index 5cf13b408..ea67fdb52 100644 --- a/public/pages/VisualCreatePolicy/components/UIActions/ReadWriteUIAction.tsx +++ b/public/pages/VisualCreatePolicy/components/UIActions/ReadWriteUIAction.tsx @@ -28,6 +28,8 @@ export default class ReadWriteUIAction implements UIAction { clone = (action: ReadWriteAction) => new ReadWriteUIAction(action, this.id); + isValid = () => true; + render = (action: UIAction, onChangeAction: (action: UIAction) => void) => { return
; }; diff --git a/public/pages/VisualCreatePolicy/components/UIActions/ReplicaCountUIAction.tsx b/public/pages/VisualCreatePolicy/components/UIActions/ReplicaCountUIAction.tsx index 45c1be5be..9ecde903a 100644 --- a/public/pages/VisualCreatePolicy/components/UIActions/ReplicaCountUIAction.tsx +++ b/public/pages/VisualCreatePolicy/components/UIActions/ReplicaCountUIAction.tsx @@ -14,6 +14,7 @@ import { EuiFormRow, EuiFieldNumber } from "@elastic/eui"; import { ReplicaCountAction, UIAction } from "../../../../../models/interfaces"; import { makeId } from "../../../../utils/helpers"; import { ActionType } from "../../utils/constants"; +import EuiFormCustomLabel from "../EuiFormCustomLabel"; export default class ReplicaCountUIAction implements UIAction { id: string; @@ -29,25 +30,34 @@ export default class ReplicaCountUIAction implements UIAction new ReplicaCountUIAction(action, this.id); + isValid = () => { + const numberOfReplicas = this.action.replica_count.number_of_replicas; + return typeof numberOfReplicas !== "undefined" && numberOfReplicas >= 0; + }; + render = (action: UIAction, onChangeAction: (action: UIAction) => void) => { + const replicas = action.action.replica_count.number_of_replicas; return ( - - ) => { - const numberOfReplicas = e.target.valueAsNumber; - onChangeAction( - this.clone({ - replica_count: { - number_of_replicas: numberOfReplicas, - }, - }) - ); - }} - data-test-subj="action-render-replica-count" + <> + - + + ) => { + const numberOfReplicas = e.target.valueAsNumber; + const replicaCount = { number_of_replicas: numberOfReplicas }; + if (isNaN(numberOfReplicas)) delete replicaCount.number_of_replicas; + onChangeAction(this.clone({ replica_count: replicaCount })); + }} + data-test-subj="action-render-replica-count" + /> + + ); }; diff --git a/public/pages/VisualCreatePolicy/components/UIActions/RolloverUIAction.tsx b/public/pages/VisualCreatePolicy/components/UIActions/RolloverUIAction.tsx index cde80fdff..94ac38c1b 100644 --- a/public/pages/VisualCreatePolicy/components/UIActions/RolloverUIAction.tsx +++ b/public/pages/VisualCreatePolicy/components/UIActions/RolloverUIAction.tsx @@ -10,10 +10,11 @@ */ import React, { ChangeEvent } from "react"; -import { EuiFormRow, EuiFieldNumber, EuiFieldText } from "@elastic/eui"; +import { EuiFormRow, EuiFieldNumber, EuiFieldText, EuiSpacer } from "@elastic/eui"; import { RolloverAction, UIAction } from "../../../../../models/interfaces"; import { makeId } from "../../../../utils/helpers"; import { ActionType } from "../../utils/constants"; +import EuiFormCustomLabel from "../EuiFormCustomLabel"; export default class RolloverUIAction implements UIAction { id: string; @@ -29,69 +30,78 @@ export default class RolloverUIAction implements UIAction { clone = (action: RolloverAction) => new RolloverUIAction(action, this.id); + isValid = () => { + const minIndexAge = this.action.rollover.min_index_age; + const minDocCount = this.action.rollover.min_doc_count; + const minSize = this.action.rollover.min_size; + if (typeof minDocCount !== "undefined") { + if (minDocCount <= 0) return false; + } + + // for minIndexAge and minSize just let them through and backend will fail the validation + // TODO -> add validation for index age and size.. but involves replicating checks for byte strings and time strings + return !!minIndexAge || minDocCount === 0 || !!minDocCount || !!minSize; + }; + render = (action: UIAction, onChangeAction: (action: UIAction) => void) => { + const rollover = action.action.rollover; return ( <> - + + ) => { const minIndexAge = e.target.value; - onChangeAction( - this.clone({ - rollover: { - ...action.action.rollover, - min_index_age: minIndexAge, - }, - }) - ); + const rollover = { ...action.action.rollover }; + if (minIndexAge) rollover.min_index_age = minIndexAge; + else delete rollover.min_index_age; + onChangeAction(this.clone({ rollover })); }} data-test-subj="action-render-rollover-min-index-age" /> - + + isInvalid={!this.isValid()} + /> + ) => { const minDocCount = e.target.valueAsNumber; - onChangeAction( - this.clone({ - rollover: { - ...action.action.rollover, - min_doc_count: minDocCount, - }, - }) - ); + const rollover = { ...action.action.rollover }; + if (!isNaN(minDocCount)) rollover.min_doc_count = minDocCount; + else delete rollover.min_doc_count; + onChangeAction(this.clone({ rollover })); }} data-test-subj="action-render-rollover-min-doc-count" /> - + + + ) => { const minSize = e.target.value; - onChangeAction( - this.clone({ - rollover: { - ...action.action.rollover, - min_size: minSize, - }, - }) - ); + const rollover = { ...action.action.rollover }; + if (minSize) rollover.min_size = minSize; + else delete rollover.min_size; + onChangeAction(this.clone({ rollover })); }} data-test-subj="action-render-rollover-min-size" /> diff --git a/public/pages/VisualCreatePolicy/components/UIActions/RollupUIAction.tsx b/public/pages/VisualCreatePolicy/components/UIActions/RollupUIAction.tsx index a8cb03e6f..7c873eddb 100644 --- a/public/pages/VisualCreatePolicy/components/UIActions/RollupUIAction.tsx +++ b/public/pages/VisualCreatePolicy/components/UIActions/RollupUIAction.tsx @@ -30,20 +30,40 @@ export default class RollupUIAction implements UIAction { clone = (action: RollupAction) => new RollupUIAction(action, this.id); + isValid = () => { + try { + JSON.parse(this.getActionJsonString(this.action)); + return true; + } catch (err) { + return false; + } + }; + + getActionJsonString = (action: RollupAction) => { + const rollup = action.rollup; + return rollup.hasOwnProperty("jsonString") ? rollup.jsonString : JSON.stringify(rollup, null, 4); + }; + + getActionJson = (action: RollupAction) => { + const rollup = action.rollup; + return rollup.hasOwnProperty("jsonString") ? JSON.parse(rollup.jsonString) : rollup; + }; + render = (action: UIAction, onChangeAction: (action: UIAction) => void) => { + // If we don't have a JSON string yet it just means we haven't converted the rollup to it yet return ( - + {(isDarkMode) => ( { onChangeAction( this.clone({ - ...action, + ...action.action, rollup: { jsonString: str }, }) ); @@ -59,7 +79,6 @@ export default class RollupUIAction implements UIAction { toAction = () => ({ ...this.action, - // TODO: validate this in UI before parsing here in case it failsZ - rollup: { ism_rollup: JSON.parse(this.action.rollup.jsonString) }, + rollup: this.getActionJson(this.action), }); } diff --git a/public/pages/VisualCreatePolicy/components/UIActions/SnapshotUIAction.tsx b/public/pages/VisualCreatePolicy/components/UIActions/SnapshotUIAction.tsx index 1916502e6..136c7b2b3 100644 --- a/public/pages/VisualCreatePolicy/components/UIActions/SnapshotUIAction.tsx +++ b/public/pages/VisualCreatePolicy/components/UIActions/SnapshotUIAction.tsx @@ -10,10 +10,11 @@ */ import React, { ChangeEvent } from "react"; -import { EuiFormRow, EuiFieldText } from "@elastic/eui"; +import { EuiFormRow, EuiFieldText, EuiSpacer } from "@elastic/eui"; import { SnapshotAction, UIAction } from "../../../../../models/interfaces"; import { makeId } from "../../../../utils/helpers"; import { ActionType } from "../../utils/constants"; +import EuiFormCustomLabel from "../EuiFormCustomLabel"; export default class SnapshotUIAction implements UIAction { id: string; @@ -29,18 +30,22 @@ export default class SnapshotUIAction implements UIAction { clone = (action: SnapshotAction) => new SnapshotUIAction(action, this.id); + isValid = () => { + return !!this.action.snapshot.snapshot && !!this.action.snapshot.repository; + }; + render = (action: UIAction, onChangeAction: (action: UIAction) => void) => { return ( <> - + isInvalid={!this.isValid()} + /> + ) => { const repository = e.target.value; onChangeAction( @@ -55,10 +60,12 @@ export default class SnapshotUIAction implements UIAction { data-test-subj="action-render-snapshot-repository" /> - + + + ) => { const snapshot = e.target.value; onChangeAction( diff --git a/public/pages/VisualCreatePolicy/containers/CreateAction/CreateAction.tsx b/public/pages/VisualCreatePolicy/containers/CreateAction/CreateAction.tsx index 9c927724e..0b2c27d8a 100644 --- a/public/pages/VisualCreatePolicy/containers/CreateAction/CreateAction.tsx +++ b/public/pages/VisualCreatePolicy/containers/CreateAction/CreateAction.tsx @@ -10,13 +10,13 @@ */ import React, { Component, ChangeEvent } from "react"; -import { EuiFlyoutBody, EuiFlyoutFooter, EuiTitle, EuiFormRow, EuiSelect, EuiSpacer } from "@elastic/eui"; +import { EuiText, EuiLink, EuiIcon, EuiFlyoutBody, EuiFlyoutFooter, EuiTitle, EuiFormRow, EuiSelect, EuiSpacer } from "@elastic/eui"; import { UIAction, Action } from "../../../../../models/interfaces"; -import { actions } from "../../utils/constants"; import TimeoutRetrySettings from "../../components/TimeoutRetrySettings"; import { actionRepoSingleton, capitalizeFirstLetter } from "../../utils/helpers"; import FlyoutFooter from "../../components/FlyoutFooter"; import EuiFormCustomLabel from "../../components/EuiFormCustomLabel"; +import { ACTIONS_DOCUMENTATION_URL } from "../../../../utils/constants"; interface CreateActionProps { stateName: string; @@ -71,7 +71,7 @@ export default class CreateAction extends Component{bodyTitle}
+ + Actions are the operations ISM performs when an index is in a certain state.{" "} + + Learn more + + + - + diff --git a/public/pages/VisualCreatePolicy/containers/CreateAction/__snapshots__/CreateAction.test.tsx.snap b/public/pages/VisualCreatePolicy/containers/CreateAction/__snapshots__/CreateAction.test.tsx.snap index 41cbe3618..af9693558 100644 --- a/public/pages/VisualCreatePolicy/containers/CreateAction/__snapshots__/CreateAction.test.tsx.snap +++ b/public/pages/VisualCreatePolicy/containers/CreateAction/__snapshots__/CreateAction.test.tsx.snap @@ -15,6 +15,22 @@ exports[` spec renders the component 1`] = ` > Edit action
+
+ Actions are the operations ISM performs when an index is in a certain state. + + + Learn more + EuiIconMock + +
@@ -23,6 +39,7 @@ exports[` spec renders the component 1`] = ` style="margin-bottom: 5px;" >
Action type @@ -30,27 +47,28 @@ exports[` spec renders the component 1`] = `

Select the action you want to add to this state. +

-
- The minimum age required to roll over the index. -
+
-
-
+ The minimum number of documents required to roll over the index. + + +

+
+
-
- The minimum number of documents required to roll over the index. -
+
-
-
+ The minimum size of the total primary shard storage required to roll over the index. Accepts byte units, e.g. "500mb" or "50gb". + + +

+
+
-
- The minimum size of the total primary shard storage required to roll over the index. -
spec renders the component 1`] = ` style="margin-bottom: 5px;" >
Timeout @@ -345,9 +373,10 @@ exports[` spec renders the component 1`] = `

The timeout period for the action. Accepts time units, e.g. "5h" or "1d". +

@@ -391,6 +420,7 @@ exports[` spec renders the component 1`] = ` style="margin-bottom: 5px;" >
Retry count @@ -398,9 +428,10 @@ exports[` spec renders the component 1`] = `

- The number of times the action should be retried if it fails. + The number of times the action should be retried if it fails. Must be greater than 0. +

@@ -445,6 +476,7 @@ exports[` spec renders the component 1`] = ` style="margin-bottom: 5px;" >
Retry backoff @@ -452,9 +484,10 @@ exports[` spec renders the component 1`] = `

The backoff policy type to use when retrying. +

@@ -513,6 +546,7 @@ exports[` spec renders the component 1`] = ` style="margin-bottom: 5px;" >
Retry delay @@ -520,9 +554,10 @@ exports[` spec renders the component 1`] = `

The time to wait between retries. Accepts time units, e.g. "2h" or "1d" +

diff --git a/public/pages/VisualCreatePolicy/containers/CreateState/Actions.tsx b/public/pages/VisualCreatePolicy/containers/CreateState/Actions.tsx index e62592e6a..25dd13952 100644 --- a/public/pages/VisualCreatePolicy/containers/CreateState/Actions.tsx +++ b/public/pages/VisualCreatePolicy/containers/CreateState/Actions.tsx @@ -41,6 +41,7 @@ const Actions = ({ actions, onClickDeleteAction, onClickEditAction, onDragEndAct isLast={actions.length - 1 === idx} onClickDelete={() => onClickDeleteAction(idx)} onClickEdit={() => onClickEditAction(action)} + draggableType="action" /> ))} diff --git a/public/pages/VisualCreatePolicy/containers/CreateState/CreateState.tsx b/public/pages/VisualCreatePolicy/containers/CreateState/CreateState.tsx index 0846101c6..326f5ec34 100644 --- a/public/pages/VisualCreatePolicy/containers/CreateState/CreateState.tsx +++ b/public/pages/VisualCreatePolicy/containers/CreateState/CreateState.tsx @@ -47,6 +47,7 @@ interface CreateStateProps { interface CreateStateState { name: string; + nameError: string; createAction: boolean; editAction: UIAction | null; createTransition: boolean; @@ -65,6 +66,7 @@ export default class CreateState extends Component) => { + const { state, policy } = this.props; const name = event.target.value; - this.setState((state) => ({ name })); + const isEditing = !!this.props.state; + let nameError = ""; + // If we are not editing a state or if we are editing and have changed the name + if (!isEditing || (isEditing && name !== state?.name)) { + // then check to make sure a state doesn't already exist in the policy with that name + if (!!policy.states.find((state) => state.name === name)) { + nameError = "A state with this name already exists."; + } + } + + this.setState({ name, nameError }); }; onDragEndActions = ({ source, destination }: DropResult) => { @@ -145,7 +158,9 @@ export default class CreateState extends Component action.id === id); + // Use edit action id instead of action id.. as current logic of editing an action can end + // up with a new id if you switch action types, i.e. rollover -> delete will create a new class w/ a new id + const foundActionIdx = actions.findIndex(({ id }) => editAction.id === id); if (foundActionIdx >= 0) { newActions = actions .slice(0, foundActionIdx) @@ -183,7 +198,7 @@ export default class CreateState extends Component { const { policy, state } = this.props; - const { actions, name, afterBeforeState, order, disableOrderSelections } = this.state; + const { actions, name, nameError, afterBeforeState, order, disableOrderSelections } = this.state; // If we are editing a state filter it out from the selectable options const stateOptions = policy.states.map((state) => ({ value: state.name, text: state.name })).filter((s) => s.value !== state?.name); return ( @@ -193,10 +208,10 @@ export default class CreateState extends Component {/* Dummy span to get rid of last child styling on h5 */} - + { const { onCloseFlyout, state } = this.props; - const { name } = this.state; + const { name, nameError } = this.state; const isEditing = !!state; return ( - Close + Cancel - + {isEditing ? "Update state" : "Save state"} @@ -288,8 +303,9 @@ export default class CreateState extends Component s.name); @@ -323,7 +339,7 @@ export default class CreateState extends Component - +

{title}

diff --git a/public/pages/VisualCreatePolicy/containers/CreateState/Transitions.tsx b/public/pages/VisualCreatePolicy/containers/CreateState/Transitions.tsx index 7268cb861..6d97adbe9 100644 --- a/public/pages/VisualCreatePolicy/containers/CreateState/Transitions.tsx +++ b/public/pages/VisualCreatePolicy/containers/CreateState/Transitions.tsx @@ -50,6 +50,7 @@ const Transitions = ({ isLast={transitions.length - 1 === idx} onClickDelete={() => onClickDeleteTransition(idx)} onClickEdit={() => onClickEditTransition(transition)} + draggableType="transition" /> ))} diff --git a/public/pages/VisualCreatePolicy/containers/CreateState/__snapshots__/Actions.test.tsx.snap b/public/pages/VisualCreatePolicy/containers/CreateState/__snapshots__/Actions.test.tsx.snap index 976076fb1..65a7eb7f8 100644 --- a/public/pages/VisualCreatePolicy/containers/CreateState/__snapshots__/Actions.test.tsx.snap +++ b/public/pages/VisualCreatePolicy/containers/CreateState/__snapshots__/Actions.test.tsx.snap @@ -6,6 +6,7 @@ exports[` spec renders the component 1`] = ` style="margin-bottom: 5px;" >
Actions @@ -13,9 +14,10 @@ exports[` spec renders the component 1`] = `

Actions are the operations ISM performs when an index is in a certain state. +

diff --git a/public/pages/VisualCreatePolicy/containers/CreateState/__snapshots__/CreateState.test.tsx.snap b/public/pages/VisualCreatePolicy/containers/CreateState/__snapshots__/CreateState.test.tsx.snap index 909a1d5a8..e26b63b15 100644 --- a/public/pages/VisualCreatePolicy/containers/CreateState/__snapshots__/CreateState.test.tsx.snap +++ b/public/pages/VisualCreatePolicy/containers/CreateState/__snapshots__/CreateState.test.tsx.snap @@ -27,14 +27,6 @@ exports[` spec renders the component 1`] = ` style="max-width: 600px;" tabindex="0" > -
@@ -42,7 +34,7 @@ exports[` spec renders the component 1`] = ` class="euiTitle euiTitle--medium" id="flyoutTitle" > - Create state: + Create state
spec renders the component 1`] = ` style="margin-bottom: 5px;" >
Actions @@ -211,9 +204,10 @@ exports[` spec renders the component 1`] = `

Actions are the operations ISM performs when an index is in a certain state. +

@@ -258,6 +252,7 @@ exports[` spec renders the component 1`] = ` style="margin-bottom: 5px;" >
Transitions @@ -265,9 +260,10 @@ exports[` spec renders the component 1`] = `

Transitions define the conditions that need to be met for a state to change. After all actions in the current state are completed, the policy starts checking the conditions for transitions. +

@@ -327,7 +323,7 @@ exports[` spec renders the component 1`] = ` - Close + Cancel diff --git a/public/pages/VisualCreatePolicy/containers/CreateState/__snapshots__/Transitions.test.tsx.snap b/public/pages/VisualCreatePolicy/containers/CreateState/__snapshots__/Transitions.test.tsx.snap index b8de9da93..e0b566034 100644 --- a/public/pages/VisualCreatePolicy/containers/CreateState/__snapshots__/Transitions.test.tsx.snap +++ b/public/pages/VisualCreatePolicy/containers/CreateState/__snapshots__/Transitions.test.tsx.snap @@ -6,6 +6,7 @@ exports[` spec renders the component 1`] = ` style="margin-bottom: 5px;" >
Transitions @@ -13,9 +14,10 @@ exports[` spec renders the component 1`] = `

Transitions define the conditions that need to be met for a state to change. After all actions in the current state are completed, the policy starts checking the conditions for transitions. +

diff --git a/public/pages/VisualCreatePolicy/containers/CreateTransition/CreateTransition.tsx b/public/pages/VisualCreatePolicy/containers/CreateTransition/CreateTransition.tsx index 4ba38e9f6..8543b5059 100644 --- a/public/pages/VisualCreatePolicy/containers/CreateTransition/CreateTransition.tsx +++ b/public/pages/VisualCreatePolicy/containers/CreateTransition/CreateTransition.tsx @@ -10,12 +10,13 @@ */ import React, { Component } from "react"; -import { EuiFlyoutBody, EuiFlyoutFooter, EuiTitle, EuiFormRow, EuiSpacer, EuiComboBox } from "@elastic/eui"; +import { EuiText, EuiLink, EuiIcon, EuiFlyoutBody, EuiFlyoutFooter, EuiTitle, EuiFormRow, EuiSpacer, EuiComboBox } from "@elastic/eui"; import { Transition as ITransition, UITransition } from "../../../../../models/interfaces"; import FlyoutFooter from "../../components/FlyoutFooter"; import EuiFormCustomLabel from "../../components/EuiFormCustomLabel"; import { makeId } from "../../../../utils/helpers"; import Transition from "../../components/Transition"; +import { TRANSITION_DOCUMENTATION_URL } from "../../../../utils/constants"; interface CreateTransitionProps { editTransition: UITransition | null; @@ -38,7 +39,6 @@ export default class CreateTransition extends Component @@ -93,6 +93,14 @@ export default class CreateTransition extends Component{title}
+ + Transitions define the conditions that need to be met for a state to change. After all actions in the current state are + completed, the policy starts checking the conditions for transitions.{" "} + + Learn more + + + - + ({ label: state }))} diff --git a/public/pages/VisualCreatePolicy/containers/CreateTransition/__snapshots__/CreateTransition.test.tsx.snap b/public/pages/VisualCreatePolicy/containers/CreateTransition/__snapshots__/CreateTransition.test.tsx.snap index f68ee8847..990e1888d 100644 --- a/public/pages/VisualCreatePolicy/containers/CreateTransition/__snapshots__/CreateTransition.test.tsx.snap +++ b/public/pages/VisualCreatePolicy/containers/CreateTransition/__snapshots__/CreateTransition.test.tsx.snap @@ -15,6 +15,22 @@ exports[` spec renders the component 1`] = ` > Edit transition
+
+ Transitions define the conditions that need to be met for a state to change. After all actions in the current state are completed, the policy starts checking the conditions for transitions. + + + Learn more + EuiIconMock + +
@@ -23,6 +39,7 @@ exports[` spec renders the component 1`] = ` style="margin-bottom: 5px;" >
Destination state @@ -30,14 +47,15 @@ exports[` spec renders the component 1`] = `

If entering a state that does not exist yet then you must create it before creating the policy. +

spec renders the component 1`] = `