diff --git a/openmetadata-ui/src/main/resources/ui/cypress/e2e/Pages/DataInsightSettings.spec.js b/openmetadata-ui/src/main/resources/ui/cypress/e2e/Pages/DataInsightSettings.spec.js new file mode 100644 index 00000000000..02d9635ed5b --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/cypress/e2e/Pages/DataInsightSettings.spec.js @@ -0,0 +1,249 @@ +/* + * Copyright 2023 Collate. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { interceptURL, verifyResponseStatusCode } from '../../common/common'; +import { BASE_URL } from '../../constants/constants'; + +const PIPELINE_NAME = 'cypress_dataInsight_pipeline'; +const REGION_NAME = 'US'; + +describe('Data Insight settings page should work properly', () => { + beforeEach(() => { + cy.login(); + interceptURL('GET', '/api/v1/teams/name/*', 'settingsPage'); + + cy.get('[data-testid="appbar-item-settings"]').click(); + verifyResponseStatusCode('@settingsPage', 200); + cy.get('[data-testid="settings-left-panel"]').should('be.visible'); + + interceptURL( + 'GET', + 'api/v1/services/ingestionPipelines?fields=pipelineStatuses&service=OpenMetadata&pipelineType=dataInsight', + 'ingestionPipelines' + ); + interceptURL( + 'GET', + '/api/v1/services/ingestionPipelines/OpenMetadata.OpenMetadata_dataInsight/pipelineStatus?startTs=*', + 'pipelineStatus' + ); + + cy.get(`[data-menu-id*="openMetadata.dataInsight"]`) + .scrollIntoView() + .click(); + + verifyResponseStatusCode('@ingestionPipelines', 200); + verifyResponseStatusCode('@pipelineStatus', 200); + }); + + it('Add data insight pipeline should work properly', () => { + interceptURL( + 'GET', + '/api/v1/services/metadataServices/name/OpenMetadata', + 'serviceDetails' + ); + + cy.get('[data-testid="add-new-ingestion-button"]').click(); + + verifyResponseStatusCode('@serviceDetails', 200); + + cy.get('[data-testid="name"]').clear().type(PIPELINE_NAME); + + cy.get('[data-testid="next-button"]').click(); + + cy.get('#root\\/regionName').type(REGION_NAME); + + cy.get('#root\\/useAwsCredentials').click({ waitForAnimations: true }); + + cy.get('#root\\/useAwsCredentials') + .invoke('attr', 'aria-checked') + .should('eq', 'true'); + + cy.get('[data-testid="next-button"]').click(); + + interceptURL( + 'POST', + '/api/v1/services/ingestionPipelines', + 'postIngestionPipeline' + ); + + cy.get('[data-testid="deploy-button"]').click(); + + cy.wait('@postIngestionPipeline').then(({ request, response }) => { + expect(request.body.sourceConfig.config).to.deep.equal({ + regionName: 'US', + useAwsCredentials: true, + useSSL: false, + verifyCerts: false, + type: 'MetadataToElasticSearch', + }); + expect(request.body.loggerLevel).to.equal('INFO'); + + expect(response.statusCode).to.equal(201); + }); + + cy.get('[data-testid="view-service-button"]').click(); + + verifyResponseStatusCode('@ingestionPipelines', 200); + verifyResponseStatusCode('@pipelineStatus', 200); + + cy.get(`[data-row-key="${PIPELINE_NAME}"]`).should('be.visible'); + }); + + it('Edit data insight pipeline should work properly', () => { + interceptURL( + 'GET', + '/api/v1/services/metadataServices/name/OpenMetadata', + 'serviceDetails' + ); + + cy.get(`[data-row-key="${PIPELINE_NAME}"] [data-testid="edit"]`).click(); + + verifyResponseStatusCode('@serviceDetails', 200); + + cy.get('#root\\/loggerLevel').click({ waitForAnimations: true }); + + cy.get('#root\\/loggerLevel') + .invoke('attr', 'aria-checked') + .should('eq', 'true'); + + cy.get('[data-testid="next-button"]').click(); + + cy.get('#root\\/useSSL').click({ waitForAnimations: true }); + + cy.get('#root\\/useSSL') + .invoke('attr', 'aria-checked') + .should('eq', 'true'); + + cy.get('[data-testid="next-button"]').click(); + + interceptURL( + 'PUT', + '/api/v1/services/ingestionPipelines', + 'putIngestionPipeline' + ); + + cy.get('[data-testid="deploy-button"]').click(); + + cy.wait('@putIngestionPipeline').then(({ request, response }) => { + expect(request.body.sourceConfig.config).to.deep.equal({ + regionName: 'US', + useAwsCredentials: true, + useSSL: true, + verifyCerts: false, + type: 'MetadataToElasticSearch', + }); + expect(request.body.loggerLevel).to.equal('DEBUG'); + + expect(response.statusCode).to.equal(200); + }); + + cy.get('[data-testid="view-service-button"]').click(); + + verifyResponseStatusCode('@ingestionPipelines', 200); + verifyResponseStatusCode('@pipelineStatus', 200); + + cy.get(`[data-row-key="${PIPELINE_NAME}"]`).should('be.visible'); + }); + + it('Run and kill data insight pipeline should work properly', () => { + interceptURL( + 'POST', + '/api/v1/services/ingestionPipelines/trigger/*', + 'runPipelineDag' + ); + cy.get(`[data-row-key="${PIPELINE_NAME}"] [data-testid="run"]`).click(); + + verifyResponseStatusCode('@runPipelineDag', 200); + interceptURL( + 'POST', + '/api/v1/services/ingestionPipelines/kill/*', + 'killPipelineDag' + ); + cy.get(`[data-row-key="${PIPELINE_NAME}"] [data-testid="kill"]`).click(); + + cy.get('[data-testid="kill-modal"]').contains('Confirm').click(); + + verifyResponseStatusCode('@killPipelineDag', 200); + }); + + it('Re deploy data insight pipeline should work properly', () => { + interceptURL( + 'POST', + '/api/v1/services/ingestionPipelines/deploy/*', + 'reDeployPipelineDag' + ); + cy.get( + `[data-row-key="${PIPELINE_NAME}"] [data-testid="re-deploy-btn"]` + ).click(); + + verifyResponseStatusCode('@reDeployPipelineDag', 200); + }); + + it('Pause and unpause data insight pipeline should work properly', () => { + interceptURL( + 'POST', + '/api/v1/services/ingestionPipelines/toggleIngestion/*', + 'togglePipelineDag' + ); + cy.get(`[data-row-key="${PIPELINE_NAME}"] [data-testid="pause"]`).click(); + + verifyResponseStatusCode('@togglePipelineDag', 200); + + cy.get(`[data-row-key="${PIPELINE_NAME}"] [data-testid="unpause"]`).click(); + + verifyResponseStatusCode('@togglePipelineDag', 200); + }); + + it('Logs action button for the data insight pipeline should redirect to the logs page', () => { + interceptURL( + 'GET', + `/api/v1/services/ingestionPipelines/name/OpenMetadata.${PIPELINE_NAME}?fields=owner,pipelineStatuses`, + 'getServiceDetails' + ); + interceptURL( + 'GET', + '/api/v1/services/ingestionPipelines/logs/*/*last?after=', + 'getLogs' + ); + interceptURL( + 'GET', + `/api/v1/services/ingestionPipelines/OpenMetadata.cypress_dataInsight_pipeline/pipelineStatus?*`, + 'getPipelineStatus' + ); + cy.get(`[data-row-key="${PIPELINE_NAME}"] [data-testid="logs"]`).click(); + + verifyResponseStatusCode('@getServiceDetails', 200); + verifyResponseStatusCode('@getLogs', 200); + verifyResponseStatusCode('@getPipelineStatus', 200); + + cy.url().should( + 'eq', + `${BASE_URL}/metadataServices/OpenMetadata.${PIPELINE_NAME}/logs` + ); + }); + + it('Delete data insight pipeline should work properly', () => { + interceptURL( + 'DELETE', + '/api/v1/services/ingestionPipelines/*?hardDelete=true', + 'deletePipelineDag' + ); + cy.get(`[data-row-key="${PIPELINE_NAME}"] [data-testid="delete"]`).click(); + + cy.get('[data-testid="confirmation-text-input"]').type('DELETE'); + + cy.get('[data-testid="confirm-button"]').click(); + + verifyResponseStatusCode('@deletePipelineDag', 200); + }); +}); diff --git a/openmetadata-ui/src/main/resources/ui/cypress/e2e/Pages/Teams.spec.js b/openmetadata-ui/src/main/resources/ui/cypress/e2e/Pages/Teams.spec.js index 96a23c47d17..5a6c8a6321d 100644 --- a/openmetadata-ui/src/main/resources/ui/cypress/e2e/Pages/Teams.spec.js +++ b/openmetadata-ui/src/main/resources/ui/cypress/e2e/Pages/Teams.spec.js @@ -357,12 +357,25 @@ describe('Teams flow should work properly', () => { `/api/v1/teams/name/${TEAM_DETAILS.name}*`, 'getSelectedTeam' ); + interceptURL( + 'GET', + `/api/v1/teams?limit=100000&parentTeam=${TEAM_DETAILS.name}&include=all`, + 'getTeamParent' + ); + interceptURL( + 'GET', + `/api/v1/teams?fields=userCount%2CchildrenCount%2Cowns%2Cparents&limit=100000&parentTeam=${TEAM_DETAILS.name}&include=all`, + 'getChildrenCount' + ); cy.get('table').should('contain', TEAM_DETAILS.name).click(); cy.get('table').find('.ant-table-row').contains(TEAM_DETAILS.name).click(); verifyResponseStatusCode('@getSelectedTeam', 200); + verifyResponseStatusCode('@getTeamParent', 200); + verifyResponseStatusCode('@getChildrenCount', 200); + cy.get('[data-testid="team-heading"]') .should('be.visible') .contains(TEAM_DETAILS.updatedname); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/AddIngestion/Steps/DataInsightMetadataToESConfigForm/DataInsightMetadataToESConfigForm.tsx b/openmetadata-ui/src/main/resources/ui/src/components/AddIngestion/Steps/DataInsightMetadataToESConfigForm/DataInsightMetadataToESConfigForm.tsx index 389b0f1e8f1..31b82ae4281 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/AddIngestion/Steps/DataInsightMetadataToESConfigForm/DataInsightMetadataToESConfigForm.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/AddIngestion/Steps/DataInsightMetadataToESConfigForm/DataInsightMetadataToESConfigForm.tsx @@ -118,12 +118,12 @@ const DataInsightMetadataToESConfigForm = ({ - - diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Ingestion/Ingestion.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Ingestion/Ingestion.test.tsx index 26105eba739..a642d32ad01 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Ingestion/Ingestion.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Ingestion/Ingestion.test.tsx @@ -307,7 +307,7 @@ describe('Test Ingestion page', () => { } ); - const viewButton = await findByTestId(container, 'airflow-tree-view'); + const viewButton = await findByTestId(container, 'ingestion-dag-link'); expect(viewButton).toBeInTheDocument(); }); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Ingestion/IngestionListTable.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Ingestion/IngestionListTable.component.tsx index bb3638c5dab..72d3cfcab5a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Ingestion/IngestionListTable.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Ingestion/IngestionListTable.component.tsx @@ -13,6 +13,7 @@ import { Table, Tooltip, Typography } from 'antd'; import { ColumnsType } from 'antd/lib/table'; +import NextPrevious from 'components/common/next-previous/NextPrevious'; import Loader from 'components/Loader/Loader'; import cronstrue from 'cronstrue'; import { Paging } from 'generated/type/paging'; @@ -23,7 +24,6 @@ import { getEntityName } from 'utils/EntityUtils'; import { getErrorPlaceHolder } from 'utils/IngestionUtils'; import { PAGE_SIZE } from '../../constants/constants'; import { IngestionPipeline } from '../../generated/entity/services/ingestionPipelines/ingestionPipeline'; -import NextPrevious from '../common/next-previous/NextPrevious'; import { IngestionListTableProps } from './IngestionListTable.interface'; import { IngestionRecentRuns } from './IngestionRecentRun/IngestionRecentRuns.component'; import PipelineActions from './PipelineActions.component'; @@ -72,7 +72,7 @@ function IngestionListTable({ }> + jest.fn().mockImplementation(() =>
nextPrevious
) +); +jest.mock('components/Loader/Loader', () => + jest.fn().mockImplementation(() =>
loader
) +); +jest.mock('./PipelineActions.component', () => + jest.fn().mockImplementation(() =>
pipelineActions
) +); +jest.mock('./IngestionRecentRun/IngestionRecentRuns.component', () => ({ + IngestionRecentRuns: jest + .fn() + .mockImplementation(() =>
ingestionRecentRuns
), +})); + +describe('IngestionListTable tests', () => { + it('Should display the loader if the isLoading is true', () => { + render(); + + const ingestionListTable = screen.getByTestId('ingestion-list-table'); + const loader = screen.getByText('loader'); + + expect(ingestionListTable).toBeInTheDocument(); + expect(loader).toBeInTheDocument(); + }); + + it('Should not display the loader if the isLoading is false', () => { + render( + + ); + + const ingestionListTable = screen.getByTestId('ingestion-list-table'); + const loader = screen.queryByText('loader'); + + expect(ingestionListTable).toBeInTheDocument(); + expect(loader).toBeNull(); + }); + + it('Should not display the loader if the isLoading is undefined', () => { + render( + + ); + + const ingestionListTable = screen.getByTestId('ingestion-list-table'); + const loader = screen.queryByText('loader'); + + expect(ingestionListTable).toBeInTheDocument(); + expect(loader).toBeNull(); + }); + + it('Should display NexPrevious component for list size more than 10 and paging object has after field', () => { + render( + + ); + + const nextPrevious = screen.getByText('nextPrevious'); + + expect(nextPrevious).toBeInTheDocument(); + }); + + it('Should not display NexPrevious component for list size less than 10', () => { + render( + + ); + + const nextPrevious = screen.queryByText('nextPrevious'); + + expect(nextPrevious).toBeNull(); + }); + + it('Should render the ingestion link if airflowEndpoint is provided', () => { + render(); + + const ingestionDagLink = screen.getByTestId('ingestion-dag-link'); + + expect(ingestionDagLink).toBeInTheDocument(); + }); + + it('Should not render the ingestion link if airflowEndpoint is not provided', () => { + render( + + ); + + const ingestionDagLink = screen.queryByTestId('ingestion-dag-link'); + + expect(ingestionDagLink).toBeNull(); + }); +}); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Modals/KillIngestionPipelineModal/KillIngestionModal.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Modals/KillIngestionPipelineModal/KillIngestionModal.test.tsx index b122048c955..ec62b8e2f3d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Modals/KillIngestionPipelineModal/KillIngestionModal.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Modals/KillIngestionPipelineModal/KillIngestionModal.test.tsx @@ -43,8 +43,8 @@ describe('Test Kill Ingestion Modal component', () => { const container = await screen.findByTestId('kill-modal'); const body = await screen.findByTestId('kill-modal-body'); - const cancelButton = await screen.findByText('Cancel'); - const confirmButton = await screen.findByText('Confirm'); + const cancelButton = await screen.findByText('label.cancel'); + const confirmButton = await screen.findByText('label.confirm'); expect(container).toBeInTheDocument(); expect(body).toBeInTheDocument(); @@ -55,7 +55,7 @@ describe('Test Kill Ingestion Modal component', () => { it('Should close modal on click of cancel button', async () => { render(); - const cancelButton = await screen.findByText('Cancel'); + const cancelButton = await screen.findByText('label.cancel'); expect(cancelButton).toBeInTheDocument(); @@ -68,7 +68,7 @@ describe('Test Kill Ingestion Modal component', () => { await act(async () => { render(); - const confirmButton = await screen.findByText('Confirm'); + const confirmButton = await screen.findByText('label.confirm'); expect(confirmButton).toBeInTheDocument(); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Modals/KillIngestionPipelineModal/KillIngestionPipelineModal.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Modals/KillIngestionPipelineModal/KillIngestionPipelineModal.tsx index 9dbd2bba3de..543979b138f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Modals/KillIngestionPipelineModal/KillIngestionPipelineModal.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Modals/KillIngestionPipelineModal/KillIngestionPipelineModal.tsx @@ -57,11 +57,12 @@ const KillIngestionModal: FC = ({ return ( { if (isAirflowAvailable) { getAllIngestionWorkflows(); + getAirflowEndpoint(); } }, [isAirflowAvailable]); diff --git a/openmetadata-ui/src/main/resources/ui/src/mocks/IngestionListTable.mock.ts b/openmetadata-ui/src/main/resources/ui/src/mocks/IngestionListTable.mock.ts new file mode 100644 index 00000000000..703a973a0e7 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/mocks/IngestionListTable.mock.ts @@ -0,0 +1,168 @@ +/* + * Copyright 2023 Collate. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { IngestionListTableProps } from 'components/Ingestion/IngestionListTable.interface'; +import { ServiceCategory } from 'enums/service.enum'; +import { + AuthProvider, + ConfigType, + IngestionPipeline, + LogLevels, + OpenmetadataType, + PipelineState, + PipelineType, + ProviderType, + SearchIndexMappingLanguage, + SecretsManagerClientLoader, + SecretsManagerProvider, + VerifySSL, +} from 'generated/entity/services/ingestionPipelines/ingestionPipeline'; +import { ENTITY_PERMISSIONS } from './Permissions.mock'; + +const mockTriggerIngestion = jest.fn(); +const mockDeployIngestion = jest.fn(); +const mockHandleEnableDisableIngestion = jest.fn(); +const mockOnIngestionWorkflowsUpdate = jest.fn(); +const mockHandleDeleteSelection = jest.fn(); +const mockHandleIsConfirmationModalOpen = jest.fn(); + +const mockESIngestionData: IngestionPipeline[] = [ + { + id: '5ff66f1c-9809-4333-836e-ba4dadda11f2', + name: 'OpenMetadata_elasticSearchReindex', + displayName: 'OpenMetadata_elasticSearchReindex', + description: 'Elastic Search Reindexing Pipeline', + pipelineType: PipelineType.ElasticSearchReindex, + fullyQualifiedName: 'OpenMetadata.OpenMetadata_elasticSearchReindex', + sourceConfig: { + config: { + type: ConfigType.MetadataToElasticSearch, + useSSL: false, + timeout: 30, + batchSize: 1000, + verifyCerts: false, + recreateIndex: true, + useAwsCredentials: false, + searchIndexMappingLanguage: SearchIndexMappingLanguage.En, + }, + }, + openMetadataServerConnection: { + clusterName: 'sandbox-beta', + type: OpenmetadataType.OpenMetadata, + hostPort: 'http://openmetadata-server:8585/api', + authProvider: AuthProvider.Openmetadata, + verifySSL: VerifySSL.NoSSL, + securityConfig: { + jwtToken: 'eyJraWQiOiJHYjM4OWEtOWY3Ni1nZGpzLWE5MmotMDI0MmJrO', + }, + secretsManagerProvider: SecretsManagerProvider.Noop, + secretsManagerLoader: SecretsManagerClientLoader.Noop, + apiVersion: 'v1', + includeTopics: true, + includeTables: true, + includeDashboards: true, + includePipelines: true, + includeMlModels: true, + includeUsers: true, + includeTeams: true, + includeGlossaryTerms: true, + includeTags: true, + includePolicy: true, + includeMessagingServices: true, + enableVersionValidation: true, + includeDatabaseServices: true, + includePipelineServices: true, + limitRecords: 1000, + forceEntityOverwriting: false, + supportsDataInsightExtraction: true, + supportsElasticSearchReindexingExtraction: true, + }, + airflowConfig: { + pausePipeline: false, + concurrency: 1, + pipelineTimezone: 'UTC', + retries: 3, + retryDelay: 300, + pipelineCatchup: false, + scheduleInterval: '*/30 * * * *', + maxActiveRuns: 1, + workflowDefaultView: 'tree', + workflowDefaultViewOrientation: 'LR', + }, + service: { + id: 'd520c9bb-a517-4f1e-8962-d8518de71279', + type: 'metadataService', + name: 'OpenMetadata', + fullyQualifiedName: 'OpenMetadata', + description: + 'Service Used for creating OpenMetadata Ingestion Pipelines.', + displayName: 'OpenMetadata Service', + deleted: false, + href: 'http://sandbox-beta.open-metadata.org/api/v1/services/databaseServices/d520c9bb-a517-4f1e-8962-d8518de71279', + }, + pipelineStatuses: { + runId: '8bd07fbd-a356-45c1-8621-7bb6a4dff5b2', + pipelineState: PipelineState.Success, + startDate: 1690885805006, + timestamp: 1690885805006, + endDate: 1690885844106, + }, + loggerLevel: LogLevels.Info, + deployed: true, + enabled: false, + href: 'http://sandbox-beta.open-metadata.org/api/v1/services/ingestionPipelines/5ff66f1c-9809-4333-836e-ba4dadda11f2', + version: 0.5, + updatedAt: 1687854372726, + updatedBy: 'teddy', + changeDescription: { + fieldsAdded: [], + fieldsUpdated: [ + { + name: 'enabled', + oldValue: true, + newValue: false, + }, + ], + fieldsDeleted: [], + previousVersion: 0.4, + }, + deleted: false, + provider: ProviderType.User, + }, +]; + +export const mockIngestionListTableProps: IngestionListTableProps = { + airflowEndpoint: 'http://localhost:8080', + triggerIngestion: mockTriggerIngestion, + deployIngestion: mockDeployIngestion, + isRequiredDetailsAvailable: true, + paging: { total: 2 }, + handleEnableDisableIngestion: mockHandleEnableDisableIngestion, + onIngestionWorkflowsUpdate: mockOnIngestionWorkflowsUpdate, + ingestionPipelinesPermission: { + OpenMetadata_elasticSearchReindex: ENTITY_PERMISSIONS, + }, + serviceCategory: ServiceCategory.METADATA_SERVICES, + serviceName: 'OpenMetadata', + handleDeleteSelection: mockHandleDeleteSelection, + handleIsConfirmationModalOpen: mockHandleIsConfirmationModalOpen, + ingestionData: mockESIngestionData, + deleteSelection: { + id: '', + name: '', + state: '', + }, + permissions: ENTITY_PERMISSIONS, + pipelineType: PipelineType.ElasticSearchReindex, + isLoading: false, +};