diff --git a/openmetadata-ui/src/main/resources/ui/cypress/common/advancedSearch.js b/openmetadata-ui/src/main/resources/ui/cypress/common/advancedSearch.js index 2a23a91ac07..ab9ae5b0d95 100644 --- a/openmetadata-ui/src/main/resources/ui/cypress/common/advancedSearch.js +++ b/openmetadata-ui/src/main/resources/ui/cypress/common/advancedSearch.js @@ -116,6 +116,7 @@ export const OPERATOR = { }; export const searchForField = (condition, fieldid, searchCriteria, index) => { + interceptURL('GET', '/api/v1/search/suggest?q=*', 'suggestApi'); // Click on field dropdown cy.get('.rule--field > .ant-select > .ant-select-selector') .eq(index) @@ -134,7 +135,6 @@ export const searchForField = (condition, fieldid, searchCriteria, index) => { cy.get('.rule--operator .ant-select-selection-item') .should('be.visible') .should('contain', `${condition}`); - cy.wait(500); // Verify the search criteria for the condition cy.get('body').then(($body) => { @@ -148,14 +148,9 @@ export const searchForField = (condition, fieldid, searchCriteria, index) => { .eq(index) .should('be.visible') .type(searchCriteria); - } - }); - - cy.wait(1000); - // if condition has a dropdown then select value from dropdown - cy.get('body').then(($body) => { - if ($body.find(`.ant-select-dropdown [title="${searchCriteria}"]`).length) { - cy.get(`[title = '${searchCriteria}']`) + // select value from dropdown + verifyResponseStatusCode('@suggestApi', 200); + cy.get(`.ant-select-dropdown [title = '${searchCriteria}']`) .should('be.visible') .trigger('mouseover') .trigger('click'); @@ -179,11 +174,19 @@ export const goToAdvanceSearch = () => { .scrollIntoView() .should('exist') .and('be.visible'); - cy.wait(1000); - // Click on advance search button - cy.get('[data-testid="advance-search-button"]').should('be.visible').click(); - cy.wait(1000); + cy.wait('@explorePage').then(() => { + // Click on advance search button + cy.get('[data-testid="advance-search-button"]') + .should('be.visible') + .click(); + + cy.get('.ant-btn') + .contains('Reset') + .scrollIntoView() + .should('be.visible') + .click(); + }); }; export const checkmustPaths = ( @@ -316,16 +319,15 @@ export const addOwner = (searchTerm, ownerName) => { verifyResponseStatusCode('@searchOwner', 200); - interceptURL('PATCH', '/api/v1/tables/*', 'validateOwner'); + interceptURL('PATCH', '/api/v1/tables/*', 'tablePatch'); // Selecting the user - cy.get('[data-testid="user-tag"]') + cy.get(`[data-testid="user-tag"]`) .contains(ownerName) .should('exist') + .scrollIntoView() .and('be.visible') .click(); - verifyResponseStatusCode('@validateOwner', 200); - cy.get('[data-testid="owner-link"]') .scrollIntoView() .invoke('text') @@ -336,6 +338,7 @@ export const addOwner = (searchTerm, ownerName) => { export const addTier = (tier) => { cy.get('[data-testid="appbar-item-explore"]') + .scrollIntoView() .should('exist') .and('be.visible') .click(); @@ -352,14 +355,12 @@ export const addTier = (tier) => { .should('be.visible') .click(); - cy.get('[data-testid="select-tier-buuton"]') + cy.get('[data-testid="select-tier-button"]') .first() .should('exist') .should('be.visible') .click(); - cy.wait(1000); - cy.get('[data-testid="tags"] > [data-testid="add-tag"]').should( 'contain', 'Tier1' @@ -367,6 +368,9 @@ export const addTier = (tier) => { }; export const addTag = (tag) => { + cy.intercept('/api/v1/testCase?fields=testCaseResult*').as('testCaseResults'); + cy.intercept('/api/v1/feed?entityLink=*').as('entityLink'); + cy.get('[data-testid="appbar-item-explore"]') .should('exist') .and('be.visible') @@ -377,24 +381,29 @@ export const addTag = (tag) => { .scrollIntoView() .should('be.visible') .click(); - cy.get('[data-testid="tags"] > [data-testid="add-tag"]') - .eq(0) - .should('be.visible') - .scrollIntoView() - .click(); - cy.wait(500); - cy.get('[data-testid="tag-selector"]').should('be.visible').click().type(tag); - cy.wait(500); - cy.get('.ant-select-item-option-content').should('be.visible').click(); - cy.get( - '[data-testid="tags-wrapper"] > [data-testid="tag-container"]' - ).contains(tag); - cy.get('[data-testid="saveAssociatedTag"]').should('be.visible').click(); - cy.get('[data-testid="entity-tags"]') - .scrollIntoView() - .should('be.visible') - .contains(tag); + cy.wait(['@testCaseResults', '@entityLink']).then(() => { + cy.get('[data-testid="tags"] > [data-testid="add-tag"]') + .eq(0) + .should('be.visible') + .scrollIntoView() + .click(); + + cy.get('[data-testid="tag-selector"]') + .should('be.visible') + .click() + .type(tag); + + cy.get('.ant-select-item-option-content').should('be.visible').click(); + cy.get( + '[data-testid="tags-wrapper"] > [data-testid="tag-container"]' + ).contains(tag); + cy.get('[data-testid="saveAssociatedTag"]').should('be.visible').click(); + cy.get('[data-testid="entity-tags"]') + .scrollIntoView() + .should('be.visible') + .contains(tag); + }); }; export const checkAddGroupWithOperator = ( @@ -430,7 +439,6 @@ export const checkAddGroupWithOperator = ( cy.get('.rule--operator .ant-select-selection-item') .should('be.visible') .should('contain', `${condition_1}`); - cy.wait(500); // Verify the search criteria for the condition cy.get('body').then(($body) => { @@ -440,30 +448,31 @@ export const checkAddGroupWithOperator = ( .should('be.visible') .type(searchCriteria_1); } else { + interceptURL('GET', '/api/v1/search/suggest?q=*', 'suggestApi'); cy.get('.widget--widget > .ant-select > .ant-select-selector') .eq(index_1) .should('be.visible') .type(searchCriteria_1); - } - }); - cy.wait(1000); - // if condition has a dropdown then select value from dropdown - cy.get('body').then(($body) => { - if ( - $body.find(`.ant-select-dropdown [title="${searchCriteria_1}"]`).length - ) { - cy.get(`[title = '${searchCriteria_1}']`) + verifyResponseStatusCode('@suggestApi', 200); + cy.get('.ant-select-dropdown') + .not('.ant-select-dropdown-hidden') + .find(`[title="${searchCriteria_1}"]`) .should('be.visible') .trigger('mouseover') .trigger('click'); } }); + // To close the dropdown for anyin and notin condition cy.get('.ant-modal-header').click(); // Select add-group button - cy.get('.action--ADD-GROUP').eq(0).should('be.visible').click(); + cy.get('.action--ADD-GROUP') + .eq(0) + .scrollIntoView() + .should('be.visible') + .click(); // Select the AND/OR condition cy.get( @@ -483,7 +492,6 @@ export const checkAddGroupWithOperator = ( cy.get('.rule--operator .ant-select-selection-item') .should('be.visible') .should('contain', `${condition_2}`); - cy.wait(500); // Verify the search criteria for the condition cy.get('body').then(($body) => { @@ -493,38 +501,25 @@ export const checkAddGroupWithOperator = ( .should('be.visible') .type(searchCriteria_2); } else { + interceptURL('GET', '/api/v1/search/suggest?q=*', 'suggestApi'); cy.get('.widget--widget > .ant-select > .ant-select-selector') .eq(index_2) .should('be.visible') .type(searchCriteria_2); - } - }); + verifyResponseStatusCode('@suggestApi', 200); - cy.wait(1000); - // if condition has a dropdown then select value from dropdown - cy.get('body').then(($body) => { - if ( - $body.find(`.ant-select-dropdown [title="${searchCriteria_2}"]`).length && - searchCriteria_2 === 'Tier.Tier2' - ) { - cy.get(`[title = "${searchCriteria_2}"]`) - .eq(1) - .contains(searchCriteria_2) - .click({ force: true }); - } else if ( - $body.find(`.ant-select-dropdown [title="${searchCriteria_2}"]`).length - ) { - cy.get(`[title = "${searchCriteria_2}"]`) - .contains(searchCriteria_2) - .click(); + cy.get('.ant-select-dropdown') + .not('.ant-select-dropdown-hidden') + .find(`[title="${searchCriteria_2}"]`) + .should('be.visible') + .trigger('mouseover') + .trigger('click'); } }); interceptURL( 'GET', - `/api/v1/search/query?q=&index=*&from=0&size=10&deleted=false&query_filter=*${filter_1}*${encodeURI( - searchCriteria_1 - )}*${filter_2}*${encodeURI(response_2)}*&sort_field=_score&sort_order=desc`, + `/api/v1/search/query?q=&index=*&from=0&size=10&deleted=false&query_filter=*${searchCriteria_1}*&sort_field=_score&sort_order=desc`, 'search' ); @@ -566,7 +561,6 @@ export const checkAddRuleWithOperator = ( cy.get('.rule--operator .ant-select-selection-item') .should('be.visible') .should('contain', `${condition_1}`); - cy.wait(500); // Verify the search criteria for the condition cy.get('body').then(($body) => { @@ -576,25 +570,22 @@ export const checkAddRuleWithOperator = ( .should('be.visible') .type(searchCriteria_1); } else { + interceptURL('GET', '/api/v1/search/suggest?q=*', 'suggestApi'); + cy.get('.widget--widget > .ant-select > .ant-select-selector') .eq(index_1) .should('be.visible') .type(searchCriteria_1); - } - }); - cy.wait(1000); - // if condition has a dropdown then select value from dropdown - cy.get('body').then(($body) => { - if ( - $body.find(`.ant-select-dropdown [title="${searchCriteria_1}"]`).length - ) { + verifyResponseStatusCode('@suggestApi', 200); + cy.get(`[title = '${searchCriteria_1}']`) .should('be.visible') .trigger('mouseover') .trigger('click'); } }); + // To close the dropdown for anyin and notin condition cy.get('.ant-modal-header').click(); @@ -619,7 +610,6 @@ export const checkAddRuleWithOperator = ( cy.get('.rule--operator .ant-select-selection-item') .should('be.visible') .should('contain', `${condition_2}`); - cy.wait(500); // Verify the search criteria for the condition cy.get('body').then(($body) => { @@ -629,28 +619,18 @@ export const checkAddRuleWithOperator = ( .should('be.visible') .type(searchCriteria_2); } else { + interceptURL('GET', '/api/v1/search/suggest?q=*', 'suggestApi'); cy.get('.widget--widget > .ant-select > .ant-select-selector') .eq(index_2) .should('be.visible') .type(searchCriteria_2); - } - }); - cy.wait(1000); - // if condition has a dropdown then select value from dropdown - cy.get('body').then(($body) => { - if ( - $body.find(`.ant-select-dropdown [title="${searchCriteria_2}"]`).length && - searchCriteria_2 === 'Tier.Tier2' - ) { - cy.get(`[title = "${searchCriteria_2}"]`) - .eq(1) - .contains(searchCriteria_2) - .click({ force: true }); - } else if ( - $body.find(`.ant-select-dropdown [title="${searchCriteria_2}"]`).length - ) { - cy.get(`[title = "${searchCriteria_2}"]`) + verifyResponseStatusCode('@suggestApi', 200); + + cy.get('.ant-select-dropdown') + .not('.ant-select-dropdown-hidden') + .find(`[title="${searchCriteria_2}"]`) + .should('be.visible') .contains(searchCriteria_2) .click(); } diff --git a/openmetadata-ui/src/main/resources/ui/cypress/common/common.js b/openmetadata-ui/src/main/resources/ui/cypress/common/common.js index bb86eb699d0..ba2918b09d4 100644 --- a/openmetadata-ui/src/main/resources/ui/cypress/common/common.js +++ b/openmetadata-ui/src/main/resources/ui/cypress/common/common.js @@ -158,7 +158,7 @@ export const testServiceCreationAndIngestion = ( 'api/v1/services/ingestionPipelines/*', 'getIngestionPipelineStatus' ); - cy.get('[data-testid="next-button"]').click(); + cy.get('[data-testid="next-button"]').should('exist').click(); verifyResponseStatusCode('@getIngestionPipelineStatus', 200); // Connection Details in step 3 cy.get('[data-testid="add-new-service-container"]') diff --git a/openmetadata-ui/src/main/resources/ui/cypress/e2e/Flow/SearchFlow.spec.js b/openmetadata-ui/src/main/resources/ui/cypress/e2e/Flow/SearchFlow.spec.js index 6d06b7368eb..ec49f9df1e3 100644 --- a/openmetadata-ui/src/main/resources/ui/cypress/e2e/Flow/SearchFlow.spec.js +++ b/openmetadata-ui/src/main/resources/ui/cypress/e2e/Flow/SearchFlow.spec.js @@ -26,7 +26,6 @@ import { FIELDS, OPERATOR, } from '../../common/advancedSearch'; - import { deleteCreatedService, interceptURL, @@ -34,7 +33,6 @@ import { testServiceCreationAndIngestion, verifyResponseStatusCode, } from '../../common/common'; - import { API_SERVICE } from '../../constants/constants'; import { MYSQL } from '../../constants/service.constants'; @@ -111,7 +109,7 @@ describe('Single filed search', () => { field.responseValueFirstGroup ); }); - cy.wait(1000); + Object.values(CONDITIONS_MUST_NOT).forEach((condition) => { checkmust_notPaths( condition.name, diff --git a/openmetadata-ui/src/main/resources/ui/cypress/e2e/Pages/EntityDetails.spec.js b/openmetadata-ui/src/main/resources/ui/cypress/e2e/Pages/EntityDetails.spec.js index e8a2683d7a8..da8233d58ce 100644 --- a/openmetadata-ui/src/main/resources/ui/cypress/e2e/Pages/EntityDetails.spec.js +++ b/openmetadata-ui/src/main/resources/ui/cypress/e2e/Pages/EntityDetails.spec.js @@ -147,7 +147,7 @@ describe('Entity Details Page', () => { .should('be.visible') .click(); - cy.get('[data-testid="select-tier-buuton"]') + cy.get('[data-testid="select-tier-button"]') .first() .should('exist') .should('be.visible') diff --git a/openmetadata-ui/src/main/resources/ui/cypress/e2e/Pages/Glossary.spec.js b/openmetadata-ui/src/main/resources/ui/cypress/e2e/Pages/Glossary.spec.js index e807bac62cf..0d24e6890c6 100644 --- a/openmetadata-ui/src/main/resources/ui/cypress/e2e/Pages/Glossary.spec.js +++ b/openmetadata-ui/src/main/resources/ui/cypress/e2e/Pages/Glossary.spec.js @@ -781,8 +781,8 @@ describe('Glossary page should work properly', () => { 'have.value', GLOSSARY_TERM_WITH_DETAILS.relatedTerms ); - verifyResponseStatusCode('@searchGlossaryTerm', 200); cy.get('[data-testid="loader"]').should('be.visible'); + verifyResponseStatusCode('@searchGlossaryTerm', 200); cy.get('[data-testid="user-card-container"]').should('be.visible'); cy.get('[data-testid="checkboxAddUser"]').should('be.visible').click(); cy.get('[data-testid="saveButton"]').should('be.visible').click(); @@ -798,8 +798,8 @@ describe('Glossary page should work properly', () => { cy.get('[data-testid="searchbar"]') .should('be.visible') .type(GLOSSARY_TERM_WITH_DETAILS.reviewer); - verifyResponseStatusCode('@searchGlossaryTerm', 200); cy.get('[data-testid="loader"]').should('be.visible'); + verifyResponseStatusCode('@searchGlossaryTerm', 200); cy.get('[data-testid="user-card-container"]').should('be.visible'); cy.get('[data-testid="checkboxAddUser"]').should('be.visible').click(); cy.get('[data-testid="save-button"]').should('be.visible').click(); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/AdvancedSearch/AdvancedSearch.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/AdvancedSearch/AdvancedSearch.component.tsx index da3edbbf864..878117a1921 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/AdvancedSearch/AdvancedSearch.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/AdvancedSearch/AdvancedSearch.component.tsx @@ -11,7 +11,7 @@ * limitations under the License. */ -import React, { useEffect, useMemo, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { Builder, Config, @@ -30,7 +30,6 @@ const AdvancedSearch: React.FC = ({ onChangeJsonTree, onChangeQueryFilter, searchIndex, - onAppliedFilterChange, }) => { const [config, setConfig] = useState(getQbConfigs(searchIndex)); @@ -42,12 +41,22 @@ const AdvancedSearch: React.FC = ({ useEffect(() => setConfig(getQbConfigs(searchIndex)), [searchIndex]); useEffect(() => { - onAppliedFilterChange(QbUtils.sqlFormat(immutableTree, config) ?? ''); - onChangeQueryFilter({ - query: elasticSearchFormat(immutableTree, config), - }); + onChangeQueryFilter( + { + query: elasticSearchFormat(immutableTree, config), + }, + QbUtils.sqlFormat(immutableTree, config) ?? '' + ); }, [immutableTree, config]); + const handleChange = useCallback( + (nTree, nConfig) => { + setConfig(nConfig); + onChangeJsonTree(QbUtils.getTree(nTree)); + }, + [setConfig, onChangeJsonTree] + ); + return ( = ({ )} value={immutableTree} - onChange={(nTree, nConfig) => { - setConfig(nConfig); - onChangeJsonTree(QbUtils.getTree(nTree)); - }} + onChange={handleChange} /> ); }; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/AdvancedSearch/AdvancedSearch.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/AdvancedSearch/AdvancedSearch.interface.ts index 9adc3a22d3a..5914a508343 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/AdvancedSearch/AdvancedSearch.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/AdvancedSearch/AdvancedSearch.interface.ts @@ -19,9 +19,9 @@ export interface AdvancedSearchProps { searchIndex: SearchIndex; onChangeJsonTree: (tree: JsonTree) => void; onChangeQueryFilter: ( - queryFilter: Record | undefined + queryFilter: Record | undefined, + sqlFilter: string ) => void; - onAppliedFilterChange: (value: string) => void; } export type FilterObject = Record; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/AdvanceSearchModal.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/AdvanceSearchModal.component.tsx index 54c33b7b4a7..9a36381c6f3 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/AdvanceSearchModal.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/AdvanceSearchModal.component.tsx @@ -12,21 +12,28 @@ */ import { Button, Modal, Space, Typography } from 'antd'; -import { delay } from 'lodash'; -import React, { FunctionComponent, useState } from 'react'; -import { JsonTree } from 'react-awesome-query-builder'; +import { debounce, delay, isString } from 'lodash'; +import Qs from 'qs'; +import React, { + FunctionComponent, + useCallback, + useMemo, + useState, +} from 'react'; +import { JsonTree, Utils } from 'react-awesome-query-builder'; import { useTranslation } from 'react-i18next'; +import { useHistory, useLocation } from 'react-router-dom'; import { SearchIndex } from '../../enums/search.enum'; import AdvancedSearch from '../AdvancedSearch/AdvancedSearch.component'; interface Props { visible: boolean; - onSubmit: (filter?: Record) => void; + onSubmit: ( + filter: Record | undefined, + sqlFilter: string + ) => void; onCancel: () => void; searchIndex: SearchIndex; - onChangeJsonTree: (tree?: JsonTree) => void; - jsonTree?: JsonTree; - onAppliedFilterChange: (value: string) => void; } export const AdvancedSearchModal: FunctionComponent = ({ @@ -34,24 +41,79 @@ export const AdvancedSearchModal: FunctionComponent = ({ onSubmit, onCancel, searchIndex, - onChangeJsonTree, - jsonTree, - onAppliedFilterChange, }: Props) => { const [queryFilter, setQueryFilter] = useState< Record | undefined >(); + const [sqlFilter, setSQLFilter] = useState(''); const { t } = useTranslation(); + const history = useHistory(); + const location = useLocation(); + + const parsedSearch = useMemo( + () => + Qs.parse( + location.search.startsWith('?') + ? location.search.substr(1) + : location.search + ), + [location.search] + ); + + const jsonTree = useMemo(() => { + if (!isString(parsedSearch.queryFilter)) { + return undefined; + } + + try { + const queryFilter = JSON.parse(parsedSearch.queryFilter); + const immutableTree = Utils.loadTree(queryFilter as JsonTree); + if (Utils.isValidTree(immutableTree)) { + return queryFilter as JsonTree; + } + } catch { + return undefined; + } + + return undefined; + }, [location.search]); + + const [treeInternal, setTreeInternal] = useState( + jsonTree + ); + + const handleTreeUpdate = useCallback( + (tree?: JsonTree) => { + history.push({ + pathname: history.location.pathname, + search: Qs.stringify({ + ...parsedSearch, + queryFilter: tree ? JSON.stringify(tree) : undefined, + page: 1, + }), + }); + setTreeInternal(undefined); + }, + [history, parsedSearch] + ); const handleAdvanceSearchReset = () => { - delay(onChangeJsonTree, 100); + delay(handleTreeUpdate, 100); }; + const handleQueryFilterUpdate = useCallback( + (queryFilter: Record | undefined, sqlFilter: string) => { + setQueryFilter(queryFilter); + setSQLFilter(sqlFilter); + }, + [setQueryFilter, setSQLFilter] + ); return (