From 74faf51f8e3babc893c64e356351c674eac5b7af Mon Sep 17 00:00:00 2001 From: Michael Queyrichon Date: Tue, 10 Dec 2024 09:48:27 +0100 Subject: [PATCH 1/2] Invalidate LayoutSetsExtended queries --- .../WebSocketSyncWrapper.test.tsx | 4 +- .../WebSocketSyncWrapper.tsx | 4 +- .../mutations/useAddLayoutSetMutation.ts | 1 + .../mutations/useDeleteLayoutSetMutation.ts | 1 + .../mutations/useUpdateLayoutSetIdMutation.ts | 1 + .../useUpdateProcessDataTypesMutation.ts | 1 + .../SyncSuccessQueriesInvalidator.test.ts | 18 +++---- .../SyncSuccessQueriesInvalidator.ts | 51 +++++++++++-------- 8 files changed, 46 insertions(+), 35 deletions(-) diff --git a/frontend/app-development/components/WebSocketSyncWrapper/WebSocketSyncWrapper.test.tsx b/frontend/app-development/components/WebSocketSyncWrapper/WebSocketSyncWrapper.test.tsx index 735ff043fdf..ef733897a07 100644 --- a/frontend/app-development/components/WebSocketSyncWrapper/WebSocketSyncWrapper.test.tsx +++ b/frontend/app-development/components/WebSocketSyncWrapper/WebSocketSyncWrapper.test.tsx @@ -68,7 +68,7 @@ describe('WebSocketSyncWrapper', () => { const queryClientMock = createQueryClientMock(); const invalidator = SyncSuccessQueriesInvalidator.getInstance(queryClientMock, org, app); - invalidator.invalidateQueryByFileLocation = jest.fn(); + invalidator.invalidateQueriesByFileLocation = jest.fn(); const mockOnWSMessageReceived = jest .fn() .mockImplementation((callback: Function) => callback(syncSuccessMock)); @@ -80,7 +80,7 @@ describe('WebSocketSyncWrapper', () => { renderWebSocketSyncWrapper(); await waitFor(() => { - expect(invalidator.invalidateQueryByFileLocation).toHaveBeenCalledWith( + expect(invalidator.invalidateQueriesByFileLocation).toHaveBeenCalledWith( syncSuccessMock.source.name, ); }); diff --git a/frontend/app-development/components/WebSocketSyncWrapper/WebSocketSyncWrapper.tsx b/frontend/app-development/components/WebSocketSyncWrapper/WebSocketSyncWrapper.tsx index 733e8e5219a..53284a5a7db 100644 --- a/frontend/app-development/components/WebSocketSyncWrapper/WebSocketSyncWrapper.tsx +++ b/frontend/app-development/components/WebSocketSyncWrapper/WebSocketSyncWrapper.tsx @@ -48,8 +48,8 @@ export const WebSocketSyncWrapper = ({ const isSuccessMessage = 'source' in message; if (isSuccessMessage) { - // Please extend the "fileNameCacheKeyMap" inside the "SyncSuccessQueriesInvalidator" class. Do not add query-client invalidation directly here. - invalidator.invalidateQueryByFileLocation(message.source.name); + // Please extend the "fileNameCacheKeysMap" inside the "SyncSuccessQueriesInvalidator" class. Do not add query-client invalidation directly here. + invalidator.invalidateQueriesByFileLocation(message.source.name); } }); diff --git a/frontend/app-development/hooks/mutations/useAddLayoutSetMutation.ts b/frontend/app-development/hooks/mutations/useAddLayoutSetMutation.ts index 68a2d3ff174..cedff5138b8 100644 --- a/frontend/app-development/hooks/mutations/useAddLayoutSetMutation.ts +++ b/frontend/app-development/hooks/mutations/useAddLayoutSetMutation.ts @@ -46,6 +46,7 @@ export const useAddLayoutSetMutation = (org: string, app: string) => { // when process-editor renders the tasks and 'adds' them on first mount, when they already exists. if (isLayoutSets(layoutSets)) { queryClient.setQueryData([QueryKey.LayoutSets, org, app], layoutSets); + queryClient.invalidateQueries({ queryKey: [QueryKey.LayoutSetsExtended, org, app] }); } }, }); diff --git a/frontend/app-development/hooks/mutations/useDeleteLayoutSetMutation.ts b/frontend/app-development/hooks/mutations/useDeleteLayoutSetMutation.ts index 57cfa3b9a10..1b439b227e0 100644 --- a/frontend/app-development/hooks/mutations/useDeleteLayoutSetMutation.ts +++ b/frontend/app-development/hooks/mutations/useDeleteLayoutSetMutation.ts @@ -11,6 +11,7 @@ export const useDeleteLayoutSetMutation = (org: string, app: string) => { deleteLayoutSet(org, app, layoutSetIdToUpdate), onSuccess: () => { queryClient.invalidateQueries({ queryKey: [QueryKey.LayoutSets, org, app] }); + queryClient.invalidateQueries({ queryKey: [QueryKey.LayoutSetsExtended, org, app] }); queryClient.invalidateQueries({ queryKey: [QueryKey.AppMetadataModelIds, org, app] }); }, }); diff --git a/frontend/app-development/hooks/mutations/useUpdateLayoutSetIdMutation.ts b/frontend/app-development/hooks/mutations/useUpdateLayoutSetIdMutation.ts index 87baa945ea7..40a218cac0c 100644 --- a/frontend/app-development/hooks/mutations/useUpdateLayoutSetIdMutation.ts +++ b/frontend/app-development/hooks/mutations/useUpdateLayoutSetIdMutation.ts @@ -16,6 +16,7 @@ export const useUpdateLayoutSetIdMutation = (org: string, app: string) => { }) => updateLayoutSetId(org, app, layoutSetIdToUpdate, newLayoutSetId), onSuccess: () => { queryClient.invalidateQueries({ queryKey: [QueryKey.LayoutSets, org, app] }); + queryClient.invalidateQueries({ queryKey: [QueryKey.LayoutSetsExtended, org, app] }); }, }); }; diff --git a/frontend/app-development/hooks/mutations/useUpdateProcessDataTypesMutation.ts b/frontend/app-development/hooks/mutations/useUpdateProcessDataTypesMutation.ts index 8e2dea1b706..5aae6913df6 100644 --- a/frontend/app-development/hooks/mutations/useUpdateProcessDataTypesMutation.ts +++ b/frontend/app-development/hooks/mutations/useUpdateProcessDataTypesMutation.ts @@ -11,6 +11,7 @@ export const useUpdateProcessDataTypesMutation = (org: string, app: string) => { onSuccess: async () => { await queryClient.invalidateQueries({ queryKey: [QueryKey.AppMetadataModelIds, org, app] }); await queryClient.invalidateQueries({ queryKey: [QueryKey.LayoutSets, org, app] }); + await queryClient.invalidateQueries({ queryKey: [QueryKey.LayoutSetsExtended, org, app] }); }, }); }; diff --git a/frontend/packages/shared/src/queryInvalidator/SyncSuccessQueriesInvalidator.test.ts b/frontend/packages/shared/src/queryInvalidator/SyncSuccessQueriesInvalidator.test.ts index 87de0c60acd..1703079e69f 100644 --- a/frontend/packages/shared/src/queryInvalidator/SyncSuccessQueriesInvalidator.test.ts +++ b/frontend/packages/shared/src/queryInvalidator/SyncSuccessQueriesInvalidator.test.ts @@ -19,12 +19,12 @@ describe('SyncSuccessQueriesInvalidator', () => { jest.clearAllMocks(); }); - it('should invalidate query cache only once when invalidateQueryByFileLocation is called', async () => { + it('should invalidate query cache only once when invalidateQueriesByFileLocation is called', async () => { const queriesInvalidator = SyncSuccessQueriesInvalidator.getInstance(queryClientMock, org, app); const fileName = 'applicationmetadata.json'; - queriesInvalidator.invalidateQueryByFileLocation(fileName); - queriesInvalidator.invalidateQueryByFileLocation(fileName); + queriesInvalidator.invalidateQueriesByFileLocation(fileName); + queriesInvalidator.invalidateQueriesByFileLocation(fileName); await waitFor(() => expect(queryClientMock.invalidateQueries).toHaveBeenCalledWith({ queryKey: [QueryKey.AppMetadata, org, app], @@ -33,22 +33,22 @@ describe('SyncSuccessQueriesInvalidator', () => { expect(queryClientMock.invalidateQueries).toHaveBeenCalledTimes(1); }); - it('should not invalidate query cache when invalidateQueryByFileLocation is called with an unknown file name', async () => { + it('should not invalidate query cache when invalidateQueriesByFileLocation is called with an unknown file name', async () => { const queriesInvalidator = SyncSuccessQueriesInvalidator.getInstance(queryClientMock, org, app); const fileName = 'unknown.json'; - queriesInvalidator.invalidateQueryByFileLocation(fileName); + queriesInvalidator.invalidateQueriesByFileLocation(fileName); await new Promise((resolve) => setTimeout(resolve, 501)); expect(queryClientMock.invalidateQueries).not.toHaveBeenCalled(); }); - it('should invalidate query cache with layoutSetName identifier when invalidateQueryByFileLocation is called and layoutSetName has been set', async () => { + it('should invalidate query cache with layoutSetName identifier when invalidateQueriesByFileLocation is called and layoutSetName has been set', async () => { const queriesInvalidator = SyncSuccessQueriesInvalidator.getInstance(queryClientMock, org, app); queriesInvalidator.layoutSetName = selectedLayoutSet; const fileName = 'Settings.json'; - queriesInvalidator.invalidateQueryByFileLocation(fileName); + queriesInvalidator.invalidateQueriesByFileLocation(fileName); await waitFor(() => { expect(queryClientMock.invalidateQueries).toHaveBeenCalledWith({ @@ -58,12 +58,12 @@ describe('SyncSuccessQueriesInvalidator', () => { expect(queryClientMock.invalidateQueries).toHaveBeenCalledTimes(1); }); - it('should invalidate layouts query cache with layoutSetName identifier when invalidateQueryByFileLocation is called and layoutSetName has been set', async () => { + it('should invalidate layouts query cache with layoutSetName identifier when invalidateQueriesByFileLocation is called and layoutSetName has been set', async () => { const queriesInvalidator = SyncSuccessQueriesInvalidator.getInstance(queryClientMock, org, app); queriesInvalidator.layoutSetName = selectedLayoutSet; const folderName = 'layouts'; - queriesInvalidator.invalidateQueryByFileLocation(folderName); + queriesInvalidator.invalidateQueriesByFileLocation(folderName); await waitFor(() => expect(queryClientMock.invalidateQueries).toHaveBeenCalledWith({ diff --git a/frontend/packages/shared/src/queryInvalidator/SyncSuccessQueriesInvalidator.ts b/frontend/packages/shared/src/queryInvalidator/SyncSuccessQueriesInvalidator.ts index 0e240ae8590..3b88d565b4a 100644 --- a/frontend/packages/shared/src/queryInvalidator/SyncSuccessQueriesInvalidator.ts +++ b/frontend/packages/shared/src/queryInvalidator/SyncSuccessQueriesInvalidator.ts @@ -21,16 +21,19 @@ export class SyncSuccessQueriesInvalidator extends Queue { private _queryClient: QueryClient; // Maps file names to their cache keys for invalidation upon sync success - can be extended to include more files - private readonly fileNameCacheKeyMap: Record> = { - 'applicationmetadata.json': [QueryKey.AppMetadata, '[org]', '[app]'], - 'layout-sets.json': [QueryKey.LayoutSets, '[org]', '[app]'], - 'policy.xml': [QueryKey.AppPolicy, '[org]', '[app]'], - 'Settings.json': [QueryKey.FormLayoutSettings, '[org]', '[app]', '[layoutSetName]'], + private readonly fileNameCacheKeysMap: Record>> = { + 'applicationmetadata.json': [[QueryKey.AppMetadata, '[org]', '[app]']], + 'layout-sets.json': [ + [QueryKey.LayoutSets, '[org]', '[app]'], + [QueryKey.LayoutSetsExtended, '[org]', '[app]'], + ], + 'policy.xml': [[QueryKey.AppPolicy, '[org]', '[app]']], + 'Settings.json': [[QueryKey.FormLayoutSettings, '[org]', '[app]', '[layoutSetName]']], }; // Maps folder names to their cache keys for invalidation upon sync success - can be extended to include more folders - private readonly folderNameCacheKeyMap: Record> = { - layouts: [QueryKey.FormLayouts, '[org]', '[app]'], + private readonly folderNameCacheKeysMap: Record>> = { + layouts: [[QueryKey.FormLayouts, '[org]', '[app]']], }; public set layoutSetName(layoutSetName: string) { @@ -67,32 +70,36 @@ export class SyncSuccessQueriesInvalidator extends Queue { SyncSuccessQueriesInvalidator.instance = null; } - public invalidateQueryByFileLocation(fileOrFolderName: string): void { - const cacheKey = this.getCacheKeyByFileLocation(fileOrFolderName); - if (!cacheKey) return; + public invalidateQueriesByFileLocation(fileOrFolderName: string): void { + const cacheKeys = this.getCacheKeysByFileLocation(fileOrFolderName); + if (!cacheKeys) return; this.addTaskToQueue({ id: fileOrFolderName, callback: () => { - this._queryClient.invalidateQueries({ queryKey: cacheKey }); + cacheKeys.forEach((cacheKey) => { + this._queryClient.invalidateQueries({ queryKey: cacheKey }); + }); }, }); } - private getCacheKeyByFileLocation(fileOrFolderName: string): string[] { - const cacheKey = - this.fileNameCacheKeyMap[fileOrFolderName] || this.folderNameCacheKeyMap[fileOrFolderName]; - if (!cacheKey) return undefined; + private getCacheKeysByFileLocation(fileOrFolderName: string): Array { + const cacheKeys = + this.fileNameCacheKeysMap[fileOrFolderName] || this.folderNameCacheKeysMap[fileOrFolderName]; + if (!cacheKeys) return undefined; - return this.replaceCacheKeyPlaceholders(cacheKey); + return this.replaceCacheKeysPlaceholders(cacheKeys); } - private replaceCacheKeyPlaceholders(cacheKey: string[]): string[] { - return cacheKey.map((key) => - key - .replace('[org]', this._org) - .replace('[app]', this._app) - .replace('[layoutSetName]', this._layoutSetName), + private replaceCacheKeysPlaceholders(cacheKeys: Array): Array { + return cacheKeys.map((cacheKey) => + cacheKey.map((key) => + key + .replace('[org]', this._org) + .replace('[app]', this._app) + .replace('[layoutSetName]', this._layoutSetName), + ), ); } } From 60662db03709aa7c98419cf6d3c550d8c0d6d444 Mon Sep 17 00:00:00 2001 From: Michael Queyrichon Date: Tue, 10 Dec 2024 10:30:29 +0100 Subject: [PATCH 2/2] Fix tests --- .../hooks/mutations/useUpdateProcessDataTypesMutation.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/app-development/hooks/mutations/useUpdateProcessDataTypesMutation.test.ts b/frontend/app-development/hooks/mutations/useUpdateProcessDataTypesMutation.test.ts index fb3380ea275..7de62a67c34 100644 --- a/frontend/app-development/hooks/mutations/useUpdateProcessDataTypesMutation.test.ts +++ b/frontend/app-development/hooks/mutations/useUpdateProcessDataTypesMutation.test.ts @@ -43,7 +43,7 @@ describe('useUpdateProcessDataTypeMutation', () => { await renderHook({ queryClient }); - expect(invalidateQueriesSpy).toHaveBeenCalledTimes(2); + expect(invalidateQueriesSpy).toHaveBeenCalledTimes(3); expect(invalidateQueriesSpy).toHaveBeenCalledWith({ queryKey: [QueryKey.AppMetadataModelIds, org, app], });