From c506daa69ae12f7dc30f48428fa1f1f7a4e97585 Mon Sep 17 00:00:00 2001 From: Qxisylolo Date: Wed, 11 Dec 2024 17:44:45 +0800 Subject: [PATCH] separately fetch dqc Signed-off-by: Qxisylolo --- .../association_data_source_modal.test.tsx | 66 ++++++++++------- .../association_data_source_modal.tsx | 73 +++++++++++-------- .../workspace_creator.test.tsx | 4 +- .../select_data_source_panel.test.tsx | 20 +---- src/plugins/workspace/public/utils.ts | 6 +- 5 files changed, 86 insertions(+), 83 deletions(-) diff --git a/src/plugins/workspace/public/components/data_source_association/association_data_source_modal.test.tsx b/src/plugins/workspace/public/components/data_source_association/association_data_source_modal.test.tsx index fa29283e56bc..72e1aae42d6e 100644 --- a/src/plugins/workspace/public/components/data_source_association/association_data_source_modal.test.tsx +++ b/src/plugins/workspace/public/components/data_source_association/association_data_source_modal.test.tsx @@ -2,7 +2,7 @@ * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 */ -import { fireEvent, render, screen, waitFor } from '@testing-library/react'; +import { fireEvent, render, screen, waitFor, act } from '@testing-library/react'; import React from 'react'; import { IntlProvider } from 'react-intl'; @@ -15,6 +15,32 @@ import { AssociationDataSourceModalProps, } from './association_data_source_modal'; import { AssociationDataSourceModalMode } from 'src/plugins/workspace/common/constants'; +import { DataSourceEngineType } from '../../../../data_source/common/data_sources'; +const dataSourcesList = [ + { + id: 'ds1', + title: 'Data Source 1', + description: 'Description of data source 1', + auth: '', + dataSourceEngineType: '' as DataSourceEngineType, + workspaces: [], + // This is used for mocking saved object function + get: () => { + return 'Data Source 1'; + }, + }, + { + id: 'dqs1', + title: 'Data Connection 1', + description: 'Description of data connection 1', + auth: '', + dataSourceEngineType: '' as DataSourceEngineType, + workspaces: [], + get: () => { + return 'Data Connection 1'; + }, + }, +]; const openSearchAndDataConnectionsMock = { openSearchConnections: [ @@ -41,37 +67,18 @@ const setupAssociationDataSourceModal = ({ handleAssignDataSourceConnections, }: Partial = {}) => { const coreServices = coreMock.createStart(); - jest.spyOn(utilsExports, 'getDataSourcesList').mockResolvedValue([]); + jest.spyOn(utilsExports, 'getDataSourcesList').mockResolvedValue(dataSourcesList); jest .spyOn(utilsExports, 'convertDataSourcesToOpenSearchAndDataConnections') .mockReturnValue(openSearchAndDataConnectionsMock); - jest.spyOn(utilsExports, 'fulfillRelatedConnections').mockReturnValue([ - { - id: 'ds1', - name: 'Data Source 1', - type: 'OpenSearch', - connectionType: DataSourceConnectionType.OpenSearchConnection, - relatedConnections: [ - { - id: 'ds1-dqc1', - name: 'dqc1', - type: 'Amazon S3', - connectionType: DataSourceConnectionType.DirectQueryConnection, - parentId: 'ds1', - }, - ], - }, - ]); - - jest.spyOn(utilsExports, 'fetchDirectQueryConnections').mockResolvedValue([ + jest.spyOn(utilsExports, 'fetchDirectQueryConnectionsByIDs').mockResolvedValue([ { id: 'ds1-dqc1', name: 'dqc1', type: 'Amazon S3', connectionType: 1, - description: 'direct_query_connections_1', parentId: 'ds1', }, ]); @@ -165,12 +172,14 @@ describe('AssociationDataSourceModal', () => { setupAssociationDataSourceModal({ handleAssignDataSourceConnections: handleAssignDataSourceConnectionsMock, }); - await waitFor(() => { - fireEvent.click(screen.getByRole('option', { name: 'Data Source 1' })); - fireEvent.click(screen.getByRole('button', { name: 'Associate data sources' })); + expect(screen.getByText('Data Source 1')).toBeInTheDocument(); + expect(screen.getByText('Associate data sources')).toBeInTheDocument(); }); + fireEvent.click(screen.getByText('Data Source 1')); + fireEvent.click(screen.getByText('Associate data sources')); + expect(handleAssignDataSourceConnectionsMock).toHaveBeenCalledWith([ { id: 'ds1', @@ -196,11 +205,12 @@ describe('AssociationDataSourceModal', () => { handleAssignDataSourceConnections: handleAssignDataSourceConnectionsMock, mode: AssociationDataSourceModalMode.DirectQueryConnections, }); - await waitFor(() => { - fireEvent.click(screen.getByRole('option', { name: 'Data Connection 1' })); - fireEvent.click(screen.getByRole('button', { name: 'Associate data sources' })); + expect(screen.getByText('Data Connection 1')).toBeInTheDocument(); + expect(screen.getByText('Associate data sources')).toBeInTheDocument(); }); + fireEvent.click(screen.getByText('Data Connection 1')); + fireEvent.click(screen.getByText('Associate data sources')); expect(handleAssignDataSourceConnectionsMock).toHaveBeenCalledWith([ { diff --git a/src/plugins/workspace/public/components/data_source_association/association_data_source_modal.tsx b/src/plugins/workspace/public/components/data_source_association/association_data_source_modal.tsx index 732f9e388207..dc61fc0ef358 100644 --- a/src/plugins/workspace/public/components/data_source_association/association_data_source_modal.tsx +++ b/src/plugins/workspace/public/components/data_source_association/association_data_source_modal.tsx @@ -27,8 +27,7 @@ import { i18n } from '@osd/i18n'; import { getDataSourcesList, - fulfillRelatedConnections, - fetchDirectQueryConnections, + fetchDirectQueryConnectionsByIDs, convertDataSourcesToOpenSearchAndDataConnections, } from '../../utils'; import { DataSourceConnection, DataSourceConnectionType } from '../../../common/types'; @@ -93,18 +92,18 @@ const convertConnectionToOption = ({ connection, selectedConnectionIds, logos, - isRelatedConnectionsLoaded, + loadingStatus, }: { connection: DataSourceConnection; selectedConnectionIds: string[]; logos: Logos; - isRelatedConnectionsLoaded: boolean; + loadingStatus: Record; }) => { return { label: connection.name, key: connection.id, description: connection.description, - append: isRelatedConnectionsLoaded ? ( + append: !loadingStatus[connection.id] ? ( connection.relatedConnections && connection.relatedConnections.length > 0 ? ( {i18n.translate('workspace.form.selectDataSource.optionBadge', { @@ -141,14 +140,14 @@ const convertConnectionsToOptions = ({ selectedConnectionIds, excludedConnectionIds, logos, - isRelatedConnectionsLoaded, + loadingStatus, }: { connections: DataSourceConnection[]; excludedConnectionIds: string[]; showDirectQueryConnections: boolean; selectedConnectionIds: string[]; logos: Logos; - isRelatedConnectionsLoaded: boolean; + loadingStatus: Record; }) => { return connections .flatMap((connection) => { @@ -186,7 +185,7 @@ const convertConnectionsToOptions = ({ connection, selectedConnectionIds, logos, - isRelatedConnectionsLoaded, + loadingStatus, }) ); }; @@ -222,11 +221,11 @@ export const AssociationDataSourceModalContent = ({ }: AssociationDataSourceModalProps) => { const [allConnections, setAllConnections] = useState([]); const [selectedConnectionIds, setSelectedConnectionIds] = useState([]); - const [isRelatedConnectionsLoaded, setIsRelatedConnectionsLoaded] = useState(false); const [options, setOptions] = useState([]); const [isLoading, setIsLoading] = useState(false); const [isSaving, setIsSaving] = useState(false); const mountedRef = useRef(false); + const [loadingStatus, setLoadingStatus] = useState>({}); useEffect(() => { mountedRef.current = true; @@ -260,21 +259,40 @@ export const AssociationDataSourceModalContent = ({ openSearchConnections, dataConnections, } = convertDataSourcesToOpenSearchAndDataConnections(dataSourcesList); + + const initialLoadingStatus = dataSourcesList.reduce((acc, ds) => { + acc[ds.id] = true; + return acc; + }, {} as Record); + + setLoadingStatus(initialLoadingStatus); + + // display data sources connections first while loading direct query connection setAllConnections([...openSearchConnections, ...dataConnections]); - return { openSearchConnections, dataConnections, dataSourcesList }; + return { openSearchConnections }; }) - .then(({ openSearchConnections, dataConnections, dataSourcesList }) => { - fetchDirectQueryConnections(dataSourcesList, http, notifications).then( - (directQueryConnections) => { - const updatedOpenSearchConnections = fulfillRelatedConnections( - openSearchConnections, - directQueryConnections - ); - - setAllConnections([...updatedOpenSearchConnections, ...dataConnections]); - setIsRelatedConnectionsLoaded(true); // related connections are completely loaded - } - ); + .then(({ openSearchConnections }) => { + // Only data source saved object type needs to fetch data source connections, data connection type object not. + openSearchConnections.forEach((ds) => { + // fetch direct query connections for each data source, and set loading status accordingly + fetchDirectQueryConnectionsByIDs([ds.id], http, notifications) + .then((directQueryConnections) => { + setAllConnections((prev) => { + return prev.map((connection) => { + if (connection.id === ds.id) { + return { + ...connection, + relatedConnections: directQueryConnections, + }; + } + return connection; + }); + }); + }) + .finally(() => { + setLoadingStatus((prev) => ({ ...prev, [ds.id]: false })); + }); + }); }) .finally(() => { setIsLoading(false); @@ -291,18 +309,11 @@ export const AssociationDataSourceModalContent = ({ showDirectQueryConnections: mode === AssociationDataSourceModalMode.DirectQueryConnections, logos, - isRelatedConnectionsLoaded, + loadingStatus, }) ); } - }, [ - excludedConnectionIds, - selectedConnectionIds, - mode, - allConnections, - logos, - isRelatedConnectionsLoaded, - ]); + }, [excludedConnectionIds, selectedConnectionIds, mode, allConnections, logos, loadingStatus]); return ( <> diff --git a/src/plugins/workspace/public/components/workspace_creator/workspace_creator.test.tsx b/src/plugins/workspace/public/components/workspace_creator/workspace_creator.test.tsx index 3e833dd0602e..adaa0f184208 100644 --- a/src/plugins/workspace/public/components/workspace_creator/workspace_creator.test.tsx +++ b/src/plugins/workspace/public/components/workspace_creator/workspace_creator.test.tsx @@ -16,7 +16,7 @@ import { WorkspaceCreatorProps, } from './workspace_creator'; import { DataSourceEngineType } from '../../../../data_source/common/data_sources'; -import { DataSourceConnectionType, DataSourceConnection } from '../../../common/types'; +import { DataSourceConnectionType } from '../../../common/types'; import * as utils from '../../utils'; import * as workspaceUtilsExports from '../utils/workspace'; @@ -140,7 +140,7 @@ jest.spyOn(utils, 'fulfillRelatedConnections').mockReturnValue([ }, ]); -jest.spyOn(utils, 'fetchDirectQueryConnections').mockResolvedValue(directQueryConnectionsMock); +jest.spyOn(utils, 'fetchDirectQueryConnectionsByIDs').mockResolvedValue(directQueryConnectionsMock); const mockCoreStart = coreMock.createStart(); diff --git a/src/plugins/workspace/public/components/workspace_form/select_data_source_panel.test.tsx b/src/plugins/workspace/public/components/workspace_form/select_data_source_panel.test.tsx index e66b6ba06bd2..bf6f4705d57e 100644 --- a/src/plugins/workspace/public/components/workspace_form/select_data_source_panel.test.tsx +++ b/src/plugins/workspace/public/components/workspace_form/select_data_source_panel.test.tsx @@ -63,25 +63,7 @@ jest .spyOn(utils, 'convertDataSourcesToOpenSearchAndDataConnections') .mockReturnValue({ openSearchConnections: [...dataSourceConnectionsMock], dataConnections: [] }); -jest.spyOn(utils, 'fetchDirectQueryConnections').mockResolvedValue(directQueryConnectionsMock); - -jest.spyOn(utils, 'fulfillRelatedConnections').mockReturnValue([ - { - id: 'ds1', - name: 'Data Source 1', - type: 'OpenSearch', - connectionType: DataSourceConnectionType.OpenSearchConnection, - relatedConnections: [ - { - id: 'ds1-dqc1', - name: 'dqc1', - type: 'Amazon S3', - connectionType: DataSourceConnectionType.DirectQueryConnection, - parentId: 'ds1', - }, - ], - }, -]); +jest.spyOn(utils, 'fetchDirectQueryConnectionsByIDs').mockResolvedValue(directQueryConnectionsMock); const mockCoreStart = coreMock.createStart(); diff --git a/src/plugins/workspace/public/utils.ts b/src/plugins/workspace/public/utils.ts index 0f3a7dbbc006..c0225a571367 100644 --- a/src/plugins/workspace/public/utils.ts +++ b/src/plugins/workspace/public/utils.ts @@ -539,15 +539,15 @@ export const fetchDataSourceConnections = async ( } }; -export const fetchDirectQueryConnections = async ( - dataSources: DataSource[], +export const fetchDirectQueryConnectionsByIDs = async ( + dataSourceIds: string[], http: HttpSetup | undefined, notifications: NotificationsStart | undefined ) => { try { const directQueryConnections = await fetchDataSourceConnectionsByDataSourceIds( // Only data source saved object type needs to fetch data source connections, data connection type object not. - dataSources.filter((ds) => ds.type === DATA_SOURCE_SAVED_OBJECT_TYPE).map((ds) => ds.id), + dataSourceIds, http );