From d579e6744089a2ddbff9513a49e7bbe9ec89194a Mon Sep 17 00:00:00 2001 From: Hailong-amzn Date: Tue, 3 Jan 2023 16:49:59 +0800 Subject: [PATCH] Merge/index operation reindex (#526) * Feature/common 2.5 (#506) * feat: split to common change Signed-off-by: suzhou * feat: update Signed-off-by: suzhou Signed-off-by: suzhou * enable fullwidth for JSON editor (#479) * enable fullwidth for JSON editor Signed-off-by: Hailong Cui * update width of import settings & mappings Signed-off-by: Hailong Cui * wording change Signed-off-by: Hailong Cui Signed-off-by: Hailong Cui * advanced settings Signed-off-by: Hailong Cui * fix integration test Signed-off-by: Hailong Cui * wording change Signed-off-by: Hailong Cui * filter system index and alias from destination Signed-off-by: Hailong Cui * fix code merge issue Signed-off-by: Hailong Cui Signed-off-by: suzhou Signed-off-by: Hailong Cui Co-authored-by: suzhou --- cypress/integration/reindex_spec.js | 225 +++++ public/pages/Main/Main.tsx | 9 + .../CreateIndexFlyout.test.tsx | 33 + .../CreateIndexFlyout/CreateIndexFlyout.tsx | 48 + .../CreateIndexFlyout.test.tsx.snap | 918 ++++++++++++++++++ .../components/CreateIndexFlyout/index.ts | 8 + .../IndexSelect/IndexSelect.test.tsx | 64 ++ .../components/IndexSelect/IndexSelect.tsx | 73 ++ .../__snapshots__/IndexSelect.test.tsx.snap | 190 ++++ .../Reindex/components/IndexSelect/index.ts | 8 + .../ReindexAdvancedOptions.test.tsx | 93 ++ .../ReindexAdvancedOptions.tsx | 167 ++++ .../ReindexAdvancedOptions.test.tsx.snap | 648 +++++++++++++ .../ReindexAdvancedOptions/index.ts | 8 + .../container/Reindex/Reindex.test.tsx | 627 ++++++++++++ .../Reindex/container/Reindex/Reindex.tsx | 645 ++++++++++++ .../__snapshots__/Reindex.test.tsx.snap | 449 +++++++++ .../pages/Reindex/container/Reindex/index.ts | 7 + public/pages/Reindex/index.ts | 8 + public/pages/Reindex/models/interfaces.ts | 39 + public/pages/Reindex/utils/constants.ts | 17 + public/pages/Reindex/utils/helper.test.ts | 102 ++ public/pages/Reindex/utils/helper.ts | 69 ++ .../components/FlyoutFooter/FlyoutFooter.tsx | 5 +- test/mocks/coreServicesMock.ts | 1 + 25 files changed, 4459 insertions(+), 2 deletions(-) create mode 100644 cypress/integration/reindex_spec.js create mode 100644 public/pages/Reindex/components/CreateIndexFlyout/CreateIndexFlyout.test.tsx create mode 100644 public/pages/Reindex/components/CreateIndexFlyout/CreateIndexFlyout.tsx create mode 100644 public/pages/Reindex/components/CreateIndexFlyout/__snapshots__/CreateIndexFlyout.test.tsx.snap create mode 100644 public/pages/Reindex/components/CreateIndexFlyout/index.ts create mode 100644 public/pages/Reindex/components/IndexSelect/IndexSelect.test.tsx create mode 100644 public/pages/Reindex/components/IndexSelect/IndexSelect.tsx create mode 100644 public/pages/Reindex/components/IndexSelect/__snapshots__/IndexSelect.test.tsx.snap create mode 100644 public/pages/Reindex/components/IndexSelect/index.ts create mode 100644 public/pages/Reindex/components/ReindexAdvancedOptions/ReindexAdvancedOptions.test.tsx create mode 100644 public/pages/Reindex/components/ReindexAdvancedOptions/ReindexAdvancedOptions.tsx create mode 100644 public/pages/Reindex/components/ReindexAdvancedOptions/__snapshots__/ReindexAdvancedOptions.test.tsx.snap create mode 100644 public/pages/Reindex/components/ReindexAdvancedOptions/index.ts create mode 100644 public/pages/Reindex/container/Reindex/Reindex.test.tsx create mode 100644 public/pages/Reindex/container/Reindex/Reindex.tsx create mode 100644 public/pages/Reindex/container/Reindex/__snapshots__/Reindex.test.tsx.snap create mode 100644 public/pages/Reindex/container/Reindex/index.ts create mode 100644 public/pages/Reindex/index.ts create mode 100644 public/pages/Reindex/models/interfaces.ts create mode 100644 public/pages/Reindex/utils/constants.ts create mode 100644 public/pages/Reindex/utils/helper.test.ts create mode 100644 public/pages/Reindex/utils/helper.ts diff --git a/cypress/integration/reindex_spec.js b/cypress/integration/reindex_spec.js new file mode 100644 index 000000000..4f31b0270 --- /dev/null +++ b/cypress/integration/reindex_spec.js @@ -0,0 +1,225 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { PLUGIN_NAME } from "../support/constants"; +const REINDEX_DEST = "test-ecomm-rdx"; +const REINDEX_DEST_NO_SOURCE = "test-reindex-nosource"; +const REINDEX_NEW_CREATED = "test-logs-new"; + +describe("Reindex", () => { + beforeEach(() => { + // Set welcome screen tracking to false + localStorage.setItem("home:welcome:show", "false"); + + // Visit ISM OSD + cy.visit(`${Cypress.env("opensearch_dashboards")}/app/${PLUGIN_NAME}#/indices`); + + // Common text to wait for to confirm page loaded, give up to 60 seconds for initial load + cy.contains("Rows per page", { timeout: 60000 }); + }); + + describe("Reindex validation error", () => { + before(() => { + cy.deleteAllIndices(); + // Load ecommerce data + cy.request({ + method: "POST", + url: `${Cypress.env("opensearch_dashboards")}/api/sample_data/ecommerce`, + headers: { + "osd-xsrf": true, + }, + }).then((response) => { + expect(response.status).equal(200); + }); + + cy.createIndex(REINDEX_DEST_NO_SOURCE, null, { + mappings: { + _source: { + enabled: false, + }, + properties: { + name: { + type: "keyword", + }, + }, + }, + }); + }); + + it("source validation failed", () => { + // Confirm we have our initial index + cy.contains(REINDEX_DEST_NO_SOURCE); + + cy.get(`[data-test-subj="checkboxSelectRow-${REINDEX_DEST_NO_SOURCE}"]`).check({ force: true }); + + // Click actions button + cy.get('[data-test-subj="moreAction"]').click(); + // Reindex should show as activate + cy.get('[data-test-subj="Reindex Action"]').should("exist").should("not.have.class", "euiContextMenuItem-isDisabled").click(); + + cy.contains(/_sources is not enabled/); + }); + }); + + describe("Reindex successfully", () => { + before(() => { + cy.deleteAllIndices(); + // Load ecommerce data + cy.request({ + method: "POST", + url: `${Cypress.env("opensearch_dashboards")}/api/sample_data/ecommerce`, + headers: { + "osd-xsrf": true, + }, + }).then((response) => { + expect(response.status).equal(200); + }); + + cy.createIndex(REINDEX_DEST, null, { settings: { "index.number_of_replicas": 0 } }); + + cy.createPipeline("bumpOrderId", { + description: "sample description", + processors: [ + { + set: { + field: "order_id", + value: "200{{order_id}}", + }, + }, + ], + }); + }); + + it("successfully", () => { + // Confirm we have our initial index + cy.contains("opensearch_dashboards_sample_data_ecommerce"); + + // Click actions button + cy.get('[data-test-subj="moreAction"]').click(); + // Reindex should show as activate + cy.get('[data-test-subj="Reindex Action"]').should("exist").should("not.have.class", "euiContextMenuItem-isDisabled").click(); + + cy.get(`div[data-test-subj="sourceSelector"]`) + .find(`input[data-test-subj="comboBoxSearchInput"]`) + .type(`opensearch_dashboards_sample_data_ecommerce{downArrow}{enter}`); + + cy.get(`div[data-test-subj="destinationSelector"]`) + .find(`input[data-test-subj="comboBoxSearchInput"]`) + .type(`${REINDEX_DEST}{downArrow}{enter}`); + + // open advance option + cy.get('[data-test-subj="advanceOptionToggle"]').click(); + + // enable subset query + cy.get('[data-test-subj="subsetOption"] #subset').click({ force: true }); + + // input query to reindex subset + cy.get('[data-test-subj="queryJsonEditor"] textarea') + .focus() + .clear() + .type('{"query":{"match":{"category":"Men\'s Clothing"}}}', { parseSpecialCharSequences: false }); + + // set slices to auto + cy.get('[data-test-subj="sliceEnabled"]').click({ force: true }); + + // input pipeline + cy.get(`div[data-test-subj="pipelineCombobox"]`).find(`input[data-test-subj="comboBoxSearchInput"]`).type("bumpOrderId{enter}"); + + // click to perform reindex + cy.get('[data-test-subj="reindexConfirmButton"]').click(); + cy.wait(10); + cy.contains(/Successfully started reindexing/); + + cy.wait(10000); + // Type in REINDEX_DEST in search input + cy.get(`input[type="search"]`).focus().type(REINDEX_DEST); + + // Confirm we only see REINDEX_DEST in table + cy.get("tbody > tr").should(($tr) => { + expect($tr, "1 row").to.have.length(1); + expect($tr, "item").to.contain(REINDEX_DEST); + }); + }); + }); + + describe("Reindex successfully for newly created index", () => { + before(() => { + cy.deleteAllIndices(); + // Load logs data + cy.request({ + method: "POST", + url: `${Cypress.env("opensearch_dashboards")}/api/sample_data/logs`, + headers: { + "osd-xsrf": true, + }, + }).then((response) => { + expect(response.status).equal(200); + }); + }); + + it("successfully", () => { + // search + cy.get(`input[type="search"]`).focus().type("opensearch_dashboards_sample_data_logs"); + + cy.wait(1000); + + // Confirm we have our initial index + cy.contains("opensearch_dashboards_sample_data_logs"); + + // select logs index + cy.get("#_selection_column_opensearch_dashboards_sample_data_logs-checkbox").click(); + + // Click actions button + cy.get('[data-test-subj="moreAction"]').click(); + // Reindex should show as activate + cy.get('[data-test-subj="Reindex Action"]').click(); + + // open advance option + cy.get('[data-test-subj="advanceOptionToggle"]').click(); + + // enable subset query + cy.get('[data-test-subj="subsetOption"] #subset').click({ force: true }); + + // input query to reindex subset + cy.get('[data-test-subj="queryJsonEditor"] textarea') + .focus() + .clear() + .type('{"query":{"match":{"ip":"135.201.60.64"}}}', { parseSpecialCharSequences: false }); + + // create destination + cy.get('[data-test-subj="createIndexButton"]').click(); + cy.contains("Create Index"); + + cy.get('[placeholder="Specify a name for the new index."]').type(REINDEX_NEW_CREATED).blur(); + cy.wait(1000); + + // import setting and mapping + cy.get('[data-test-subj="importSettingMappingBtn"]').click(); + cy.get('[data-test-subj="import-settings-opensearch_dashboards_sample_data_logs"]').click(); + + cy.wait(10); + cy.contains(/have been import successfully/); + + cy.get('[data-test-subj="flyout-footer-action-button"]').click({ force: true }); + + // click to perform reindex + cy.get('[data-test-subj="reindexConfirmButton"]').click(); + cy.wait(10); + cy.contains(/Successfully started reindexing/); + + cy.wait(10000); + // Type in REINDEX_DEST in search input + cy.get(`input[type="search"]`).focus().type(REINDEX_NEW_CREATED); + + // Confirm we only see REINDEX_DEST in table + cy.get("tbody > tr").should(($tr) => { + expect($tr, "1 row").to.have.length(1); + expect($tr, "item").to.contain(REINDEX_NEW_CREATED); + // subset data number + expect($tr, "item").to.contain(13); + }); + }); + }); +}); diff --git a/public/pages/Main/Main.tsx b/public/pages/Main/Main.tsx index 4bd38c983..4d7351f76 100644 --- a/public/pages/Main/Main.tsx +++ b/public/pages/Main/Main.tsx @@ -38,6 +38,7 @@ import Templates from "../Templates"; import CreateIndexTemplate from "../CreateIndexTemplate"; import CreateIndex from "../CreateIndex"; import IndexDetail from "../IndexDetail"; +import Reindex from "../Reindex/container/Reindex"; enum Navigation { IndexManagement = "Index Management", @@ -471,6 +472,14 @@ export default class Main extends Component { )} /> + ( +
+ +
+ )} + /> diff --git a/public/pages/Reindex/components/CreateIndexFlyout/CreateIndexFlyout.test.tsx b/public/pages/Reindex/components/CreateIndexFlyout/CreateIndexFlyout.test.tsx new file mode 100644 index 000000000..585d09a87 --- /dev/null +++ b/public/pages/Reindex/components/CreateIndexFlyout/CreateIndexFlyout.test.tsx @@ -0,0 +1,33 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { render, waitFor } from "@testing-library/react"; +import React from "react"; +import CreateIndexFlyout from "./CreateIndexFlyout"; +import { coreServicesMock, browserServicesMock, apiCallerMock } from "../../../../../test/mocks"; +import { CoreServicesContext } from "../../../../components/core_services"; +import { ServicesContext } from "../../../../services"; + +describe(" spec", () => { + beforeEach(() => { + apiCallerMock(browserServicesMock); + }); + it("renders the component", async () => { + const component = render( + + + {}} />, + + + ); + await waitFor( + () => expect((document.querySelector("#accordionForCreateIndexSettings") as HTMLDivElement).style.height).toEqual("0px"), + { + timeout: 3000, + } + ); + expect(component).toMatchSnapshot(); + }); +}); diff --git a/public/pages/Reindex/components/CreateIndexFlyout/CreateIndexFlyout.tsx b/public/pages/Reindex/components/CreateIndexFlyout/CreateIndexFlyout.tsx new file mode 100644 index 000000000..9683e5ce8 --- /dev/null +++ b/public/pages/Reindex/components/CreateIndexFlyout/CreateIndexFlyout.tsx @@ -0,0 +1,48 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from "react"; +import CreateIndex, { IndexFormProps, IndexForm } from "../../../CreateIndex/containers/IndexForm"; +import { CoreServicesContext } from "../../../../components/core_services"; +import { EuiFlyout, EuiFlyoutBody, EuiFlyoutFooter, EuiFlyoutHeader, EuiTitle } from "@elastic/eui"; +import FlyoutFooter from "../../../VisualCreatePolicy/components/FlyoutFooter"; + +interface CreateIndexFlyoutProps extends IndexFormProps { + sourceIndices: string[]; + onCloseFlyout: () => void; +} + +export default class CreateIndexFlyout extends React.Component { + static contextType = CoreServicesContext; + + createIndexRef: IndexForm | null = null; + + render() { + const { onCloseFlyout } = this.props; + return ( + {}} hideCloseButton> + + +

Create Index

+
+
+ + + (this.createIndexRef = ref)} hideButtons={true} {...this.props} /> + + + + this.createIndexRef?.onSubmit()} + /> + +
+ ); + } +} diff --git a/public/pages/Reindex/components/CreateIndexFlyout/__snapshots__/CreateIndexFlyout.test.tsx.snap b/public/pages/Reindex/components/CreateIndexFlyout/__snapshots__/CreateIndexFlyout.test.tsx.snap new file mode 100644 index 000000000..5e533a1bf --- /dev/null +++ b/public/pages/Reindex/components/CreateIndexFlyout/__snapshots__/CreateIndexFlyout.test.tsx.snap @@ -0,0 +1,918 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` spec renders the component 1`] = ` +Object { + "asFragment": [Function], + "baseElement": + +
+