From b828f76f187f0c6c687e6a87013f1e9c2c16a54b Mon Sep 17 00:00:00 2001 From: Erling Hauan Date: Tue, 28 Jan 2025 13:17:46 +0100 Subject: [PATCH 01/14] create useOrgCodeListsQuery --- .../hooks/queries/useOrgCodeListsQuery.test.ts | 18 ++++++++++++++++++ .../src/hooks/queries/useOrgCodeListsQuery.ts | 13 +++++++++++++ frontend/packages/shared/src/types/QueryKey.ts | 1 + 3 files changed, 32 insertions(+) create mode 100644 frontend/packages/shared/src/hooks/queries/useOrgCodeListsQuery.test.ts create mode 100644 frontend/packages/shared/src/hooks/queries/useOrgCodeListsQuery.ts diff --git a/frontend/packages/shared/src/hooks/queries/useOrgCodeListsQuery.test.ts b/frontend/packages/shared/src/hooks/queries/useOrgCodeListsQuery.test.ts new file mode 100644 index 00000000000..fdc6dd29d5a --- /dev/null +++ b/frontend/packages/shared/src/hooks/queries/useOrgCodeListsQuery.test.ts @@ -0,0 +1,18 @@ +import { waitFor } from '@testing-library/react'; +import { queriesMock } from 'app-shared/mocks/queriesMock'; +import { renderHookWithProviders } from 'app-shared/mocks/renderHookWithProviders'; +import { org } from '@studio/testing/testids'; +import { useOrgCodeListsQuery } from 'app-shared/hooks/queries/useOrgCodeListsQuery'; + +describe('useOrgCodeListsQuery', () => { + it('calls getCodeListsForOrg with the correct parameters', () => { + render(); + expect(queriesMock.getCodeListsForOrg).toHaveBeenCalledWith(org); + }); +}); + +const render = async () => { + const { result } = renderHookWithProviders(() => useOrgCodeListsQuery(org)); + await waitFor(() => expect(result.current.isSuccess).toBe(true)); + return result; +}; diff --git a/frontend/packages/shared/src/hooks/queries/useOrgCodeListsQuery.ts b/frontend/packages/shared/src/hooks/queries/useOrgCodeListsQuery.ts new file mode 100644 index 00000000000..a334b993d0e --- /dev/null +++ b/frontend/packages/shared/src/hooks/queries/useOrgCodeListsQuery.ts @@ -0,0 +1,13 @@ +import { useServicesContext } from 'app-shared/contexts/ServicesContext'; +import { QueryKey } from 'app-shared/types/QueryKey'; +import type { UseQueryResult } from '@tanstack/react-query'; +import { useQuery } from '@tanstack/react-query'; +import type { OptionListsResponse } from 'app-shared/types/api/OptionListsResponse'; + +export const useOrgCodeListsQuery = (org: string): UseQueryResult => { + const { getCodeListsForOrg } = useServicesContext(); + return useQuery({ + queryKey: [QueryKey.OrgCodeLists, org], + queryFn: () => getCodeListsForOrg(org), + }); +}; diff --git a/frontend/packages/shared/src/types/QueryKey.ts b/frontend/packages/shared/src/types/QueryKey.ts index 99bcd8b818e..f88f55ae18d 100644 --- a/frontend/packages/shared/src/types/QueryKey.ts +++ b/frontend/packages/shared/src/types/QueryKey.ts @@ -30,6 +30,7 @@ export enum QueryKey { OptionListsUsage = 'OptionListsUsage', OptionLists = 'OptionLists', OptionListIds = 'OptionListIds', + OrgCodeLists = 'OrgCodeLists', OrgList = 'OrgList', Organizations = 'Organizations', ProcessTaskDataType = 'ProcessTaskDataType', From bbf8ac54f099be3000f3c1b23e5e9fa6475e5385 Mon Sep 17 00:00:00 2001 From: Erling Hauan Date: Tue, 28 Jan 2025 14:41:40 +0100 Subject: [PATCH 02/14] create useCreateOrgCodeListMutation --- frontend/packages/shared/src/api/mutations.ts | 4 +- .../useCreateOrgCodeListMutation.test.ts | 61 +++++++++++++++++++ .../mutations/useCreateOrgCodeListMutation.ts | 19 ++++++ 3 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.test.ts create mode 100644 frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.ts diff --git a/frontend/packages/shared/src/api/mutations.ts b/frontend/packages/shared/src/api/mutations.ts index 465f8edc356..cd3c8a89cae 100644 --- a/frontend/packages/shared/src/api/mutations.ts +++ b/frontend/packages/shared/src/api/mutations.ts @@ -49,6 +49,7 @@ import { dataTypePath, optionListPath, undeployAppFromEnvPath, + orgCodeListPath, } from 'app-shared/api/paths'; import type { AddLanguagePayload } from 'app-shared/types/api/AddLanguagePayload'; import type { AddRepoParams } from 'app-shared/types/api'; @@ -75,7 +76,6 @@ import type { FormLayoutRequest } from 'app-shared/types/api/FormLayoutRequest'; import type { Option } from 'app-shared/types/Option'; import type { MaskinportenScopes } from 'app-shared/types/MaskinportenScope'; import type { DataType } from '../types/DataType'; -import type { CodeListData } from 'app-shared/types/CodeListData'; import type { CodeList } from 'app-shared/types/CodeList'; const headers = { @@ -172,7 +172,7 @@ export const updateSelectedMaskinportenScopes = (org: string, app: string, appSc // Organisation library: // Todo: Replace these with real API calls when endpoints are ready. https://github.com/Altinn/altinn-studio/issues/14505 -export const createCodeListForOrg = async (org: string, payload: CodeListData): Promise => Promise.resolve(); +export const createCodeListForOrg = (org: string, codeListId: string, payload: CodeList) => post(orgCodeListPath(org, codeListId), payload); export const updateCodeListForOrg = async (org: string, codeListId: string, payload: CodeList): Promise => Promise.resolve(); export const deleteCodeListForOrg = async (org: string, codeListId: string): Promise => Promise.resolve(); export const uploadCodeListForOrg = async (org: string, app: string, payload: FormData): Promise => Promise.resolve(); diff --git a/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.test.ts b/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.test.ts new file mode 100644 index 00000000000..545a29f1e45 --- /dev/null +++ b/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.test.ts @@ -0,0 +1,61 @@ +import { renderHookWithProviders } from 'app-shared/mocks/renderHookWithProviders'; +import { org } from '@studio/testing/testids'; +import { queriesMock } from 'app-shared/mocks/queriesMock'; +import { useCreateOrgCodeListMutation } from 'app-shared/hooks/mutations/useCreateOrgCodeListMutation'; +import type { CodeListData } from 'app-shared/types/CodeListData'; +import { createQueryClientMock } from 'app-shared/mocks/queryClientMock'; +import { QueryKey } from 'app-shared/types/QueryKey'; +import type { CodeList } from '@studio/components'; + +// Test data: +const codeList: CodeList = [ + { + value: 'test-value', + label: 'name', + }, +]; + +const codeListData: CodeListData = { + title: 'test', + data: codeList, +}; + +describe('useCreateOrgCodeListMutation', () => { + it('Calls useCreateOrgCodeListMutation with correct parameters', async () => { + const renderCreateOrgCodeListMutationResult = renderHookWithProviders(() => + useCreateOrgCodeListMutation(org), + ).result; + await renderCreateOrgCodeListMutationResult.current.mutateAsync({ + codeListId: codeListData.title, + payload: codeList, + }); + expect(queriesMock.createCodeListForOrg).toHaveBeenCalledTimes(1); + expect(queriesMock.createCodeListForOrg).toHaveBeenCalledWith( + org, + codeListData.title, + codeList, + ); + }); + + it('Sets the cache with the new option list', async () => { + const queryClient = createQueryClientMock(); + queryClient.setQueryData([QueryKey.OrgCodeLists, org], []); + const renderCreateOrgCodeListMutationResult = renderHookWithProviders( + () => useCreateOrgCodeListMutation(org), + { queryClient }, + ).result; + await renderCreateOrgCodeListMutationResult.current.mutateAsync({ + codeListId: codeListData.title, + payload: codeList, + }); + const cacheData = queryClient.getQueryData([QueryKey.OrgCodeLists, org]); + expect(cacheData).toEqual([ + { + codeListId: codeListData.title, + payload: codeList, + }, + ]); + }); + + it('Invalidates?', async () => {}); +}); diff --git a/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.ts b/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.ts new file mode 100644 index 00000000000..9d57d10c5ac --- /dev/null +++ b/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.ts @@ -0,0 +1,19 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { useServicesContext } from '../../contexts/ServicesContext'; +import type { CodeList } from '@studio/components'; +import { QueryKey } from 'app-shared/types/QueryKey'; + +type CreateOrgCodeListMutationArgs = { + codeListId: string; + payload: CodeList; +}; + +export const useCreateOrgCodeListMutation = (org: string) => { + const queryClient = useQueryClient(); + const { createCodeListForOrg } = useServicesContext(); + return useMutation({ + mutationFn: ({ codeListId, payload }: CreateOrgCodeListMutationArgs) => + createCodeListForOrg(org, codeListId, payload), + onSuccess: (data) => queryClient.setQueryData([QueryKey.OrgCodeLists, org], data), + }); +}; From b15cfa2a64d6884c0880ea909639637201b40595 Mon Sep 17 00:00:00 2001 From: Erling Hauan Date: Tue, 28 Jan 2025 20:31:15 +0100 Subject: [PATCH 03/14] create useCreateOrgCodeListMutation --- frontend/packages/shared/src/api/mutations.ts | 4 +- frontend/packages/shared/src/api/paths.js | 2 +- .../useCreateOrgCodeListMutation.test.ts | 43 +++---------------- .../mutations/useCreateOrgCodeListMutation.ts | 3 +- 4 files changed, 11 insertions(+), 41 deletions(-) diff --git a/frontend/packages/shared/src/api/mutations.ts b/frontend/packages/shared/src/api/mutations.ts index 99dda5e614f..8f72fe89c80 100644 --- a/frontend/packages/shared/src/api/mutations.ts +++ b/frontend/packages/shared/src/api/mutations.ts @@ -49,7 +49,6 @@ import { dataTypePath, optionListPath, undeployAppFromEnvPath, - orgCodeListPath, } from 'app-shared/api/paths'; import type { AddLanguagePayload } from 'app-shared/types/api/AddLanguagePayload'; import type { AddRepoParams } from 'app-shared/types/api'; @@ -76,6 +75,7 @@ import type { FormLayoutRequest } from 'app-shared/types/api/FormLayoutRequest'; import type { Option } from 'app-shared/types/Option'; import type { MaskinportenScopes } from 'app-shared/types/MaskinportenScope'; import type { DataType } from '../types/DataType'; +import type { CodeListData } from 'app-shared/types/CodeListData'; import type { CodeList } from 'app-shared/types/CodeList'; const headers = { @@ -172,7 +172,7 @@ export const updateSelectedMaskinportenScopes = (org: string, app: string, appSc // Organisation library code lists: // Todo: Replace these with real API calls when endpoints are ready. https://github.com/Altinn/altinn-studio/issues/14505 -export const createCodeListForOrg = (org: string, codeListId: string, payload: CodeList) => post(orgCodeListPath(org, codeListId), payload); +export const createCodeListForOrg = async (org: string, codeListId: string, payload: CodeList): Promise => Promise.resolve(); export const updateCodeListForOrg = async (org: string, codeListId: string, payload: CodeList): Promise => Promise.resolve(); export const deleteCodeListForOrg = async (org: string, codeListId: string): Promise => Promise.resolve(); export const uploadCodeListForOrg = async (org: string, app: string, payload: FormData): Promise => Promise.resolve(); diff --git a/frontend/packages/shared/src/api/paths.js b/frontend/packages/shared/src/api/paths.js index 7b875a973d3..31d4e0157dd 100644 --- a/frontend/packages/shared/src/api/paths.js +++ b/frontend/packages/shared/src/api/paths.js @@ -89,7 +89,7 @@ export const languagesPath = (org, app) => `${basePath}/${org}/${app}/languages` // Library - org-level export const orgCodeListsPath = (org) => `${basePath}/${org}/code-lists`; // Get -export const orgCodeListPath = (org, optionsListId) => `${basePath}/${org}/code-lists/${optionsListId}`; // Post, Put, Delete +export const orgCodeListPath = (org, codeListId) => `${basePath}/${org}/code-lists/${codeListId}`; // Post, Put, Delete export const orgCodeListUploadPath = (org) => `${basePath}/${org}/code-lists/upload`; // Post // Organizations diff --git a/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.test.ts b/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.test.ts index 545a29f1e45..4f443e46569 100644 --- a/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.test.ts +++ b/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.test.ts @@ -2,60 +2,29 @@ import { renderHookWithProviders } from 'app-shared/mocks/renderHookWithProvider import { org } from '@studio/testing/testids'; import { queriesMock } from 'app-shared/mocks/queriesMock'; import { useCreateOrgCodeListMutation } from 'app-shared/hooks/mutations/useCreateOrgCodeListMutation'; -import type { CodeListData } from 'app-shared/types/CodeListData'; import { createQueryClientMock } from 'app-shared/mocks/queryClientMock'; import { QueryKey } from 'app-shared/types/QueryKey'; import type { CodeList } from '@studio/components'; // Test data: -const codeList: CodeList = [ +const codeListId = 'testId'; +const payload: CodeList = [ { value: 'test-value', - label: 'name', + label: 'test-label', }, ]; -const codeListData: CodeListData = { - title: 'test', - data: codeList, -}; - describe('useCreateOrgCodeListMutation', () => { it('Calls useCreateOrgCodeListMutation with correct parameters', async () => { const renderCreateOrgCodeListMutationResult = renderHookWithProviders(() => useCreateOrgCodeListMutation(org), ).result; await renderCreateOrgCodeListMutationResult.current.mutateAsync({ - codeListId: codeListData.title, - payload: codeList, + codeListId, + payload, }); expect(queriesMock.createCodeListForOrg).toHaveBeenCalledTimes(1); - expect(queriesMock.createCodeListForOrg).toHaveBeenCalledWith( - org, - codeListData.title, - codeList, - ); + expect(queriesMock.createCodeListForOrg).toHaveBeenCalledWith(org, codeListId, payload); }); - - it('Sets the cache with the new option list', async () => { - const queryClient = createQueryClientMock(); - queryClient.setQueryData([QueryKey.OrgCodeLists, org], []); - const renderCreateOrgCodeListMutationResult = renderHookWithProviders( - () => useCreateOrgCodeListMutation(org), - { queryClient }, - ).result; - await renderCreateOrgCodeListMutationResult.current.mutateAsync({ - codeListId: codeListData.title, - payload: codeList, - }); - const cacheData = queryClient.getQueryData([QueryKey.OrgCodeLists, org]); - expect(cacheData).toEqual([ - { - codeListId: codeListData.title, - payload: codeList, - }, - ]); - }); - - it('Invalidates?', async () => {}); }); diff --git a/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.ts b/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.ts index 9d57d10c5ac..62ea1b7ca9a 100644 --- a/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.ts +++ b/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.ts @@ -2,6 +2,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useServicesContext } from '../../contexts/ServicesContext'; import type { CodeList } from '@studio/components'; import { QueryKey } from 'app-shared/types/QueryKey'; +import { ArrayUtils } from '@studio/pure-functions'; type CreateOrgCodeListMutationArgs = { codeListId: string; @@ -14,6 +15,6 @@ export const useCreateOrgCodeListMutation = (org: string) => { return useMutation({ mutationFn: ({ codeListId, payload }: CreateOrgCodeListMutationArgs) => createCodeListForOrg(org, codeListId, payload), - onSuccess: (data) => queryClient.setQueryData([QueryKey.OrgCodeLists, org], data), + onSuccess: () => queryClient.invalidateQueries([QueryKey.OrgCodeLists, org]), }); }; From 1663ea18561e731676e60abe10c342c874d70ffc Mon Sep 17 00:00:00 2001 From: Erling Hauan Date: Tue, 28 Jan 2025 20:50:39 +0100 Subject: [PATCH 04/14] create useUpdateOrgCodeListMutation --- frontend/packages/shared/src/api/mutations.ts | 1 - .../useCreateOrgCodeListMutation.test.ts | 8 ++---- .../mutations/useCreateOrgCodeListMutation.ts | 3 +- .../useUpdateOrgCodeListMutation.test.ts | 28 +++++++++++++++++++ .../mutations/useUpdateOrgCodeListMutation.ts | 19 +++++++++++++ 5 files changed, 51 insertions(+), 8 deletions(-) create mode 100644 frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.test.ts create mode 100644 frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.ts diff --git a/frontend/packages/shared/src/api/mutations.ts b/frontend/packages/shared/src/api/mutations.ts index 8f72fe89c80..e104a07f545 100644 --- a/frontend/packages/shared/src/api/mutations.ts +++ b/frontend/packages/shared/src/api/mutations.ts @@ -75,7 +75,6 @@ import type { FormLayoutRequest } from 'app-shared/types/api/FormLayoutRequest'; import type { Option } from 'app-shared/types/Option'; import type { MaskinportenScopes } from 'app-shared/types/MaskinportenScope'; import type { DataType } from '../types/DataType'; -import type { CodeListData } from 'app-shared/types/CodeListData'; import type { CodeList } from 'app-shared/types/CodeList'; const headers = { diff --git a/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.test.ts b/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.test.ts index 4f443e46569..361c9ce9b79 100644 --- a/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.test.ts +++ b/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.test.ts @@ -1,9 +1,7 @@ -import { renderHookWithProviders } from 'app-shared/mocks/renderHookWithProviders'; +import { renderHookWithProviders } from '../../mocks/renderHookWithProviders'; import { org } from '@studio/testing/testids'; -import { queriesMock } from 'app-shared/mocks/queriesMock'; -import { useCreateOrgCodeListMutation } from 'app-shared/hooks/mutations/useCreateOrgCodeListMutation'; -import { createQueryClientMock } from 'app-shared/mocks/queryClientMock'; -import { QueryKey } from 'app-shared/types/QueryKey'; +import { queriesMock } from '../../mocks/queriesMock'; +import { useCreateOrgCodeListMutation } from './useCreateOrgCodeListMutation'; import type { CodeList } from '@studio/components'; // Test data: diff --git a/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.ts b/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.ts index 62ea1b7ca9a..6603d219294 100644 --- a/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.ts +++ b/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.ts @@ -2,7 +2,6 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useServicesContext } from '../../contexts/ServicesContext'; import type { CodeList } from '@studio/components'; import { QueryKey } from 'app-shared/types/QueryKey'; -import { ArrayUtils } from '@studio/pure-functions'; type CreateOrgCodeListMutationArgs = { codeListId: string; @@ -15,6 +14,6 @@ export const useCreateOrgCodeListMutation = (org: string) => { return useMutation({ mutationFn: ({ codeListId, payload }: CreateOrgCodeListMutationArgs) => createCodeListForOrg(org, codeListId, payload), - onSuccess: () => queryClient.invalidateQueries([QueryKey.OrgCodeLists, org]), + onSuccess: () => queryClient.invalidateQueries({ queryKey: [QueryKey.OrgCodeLists, org] }), }); }; diff --git a/frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.test.ts b/frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.test.ts new file mode 100644 index 00000000000..75eee23d037 --- /dev/null +++ b/frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.test.ts @@ -0,0 +1,28 @@ +import { renderHookWithProviders } from '../../mocks/renderHookWithProviders'; +import { org } from '@studio/testing/testids'; +import { queriesMock } from '../../mocks/queriesMock'; +import type { CodeList } from '@studio/components'; +import { useUpdateOrgCodeListMutation } from './useUpdateOrgCodeListMutation'; + +// Test data: +const codeListId = 'testId'; +const payload: CodeList = [ + { + value: 'test-value', + label: 'test-label', + }, +]; + +describe('useUpdateOrgCodeListMutation', () => { + it('Calls useUpdateOrgCodeListMutation with correct parameters', async () => { + const renderUpdateOrgCodeListMutationResult = renderHookWithProviders(() => + useUpdateOrgCodeListMutation(org), + ).result; + await renderUpdateOrgCodeListMutationResult.current.mutateAsync({ + codeListId, + payload, + }); + expect(queriesMock.updateCodeListForOrg).toHaveBeenCalledTimes(1); + expect(queriesMock.updateCodeListForOrg).toHaveBeenCalledWith(org, codeListId, payload); + }); +}); diff --git a/frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.ts b/frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.ts new file mode 100644 index 00000000000..f60455c36be --- /dev/null +++ b/frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.ts @@ -0,0 +1,19 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { useServicesContext } from '../../contexts/ServicesContext'; +import type { CodeList } from '@studio/components'; +import { QueryKey } from 'app-shared/types/QueryKey'; + +type UpdateOrgCodeListMutationArgs = { + codeListId: string; + payload: CodeList; +}; + +export const useUpdateOrgCodeListMutation = (org: string) => { + const queryClient = useQueryClient(); + const { updateCodeListForOrg } = useServicesContext(); + return useMutation({ + mutationFn: ({ codeListId, payload }: UpdateOrgCodeListMutationArgs) => + updateCodeListForOrg(org, codeListId, payload), + onSuccess: () => queryClient.invalidateQueries({ queryKey: [QueryKey.OrgCodeLists, org] }), + }); +}; From 46d20ab9d14779e692e6f681551fabe1e15e503d Mon Sep 17 00:00:00 2001 From: Erling Hauan Date: Tue, 28 Jan 2025 20:56:36 +0100 Subject: [PATCH 05/14] create useUploadOrgCodeListMutation --- frontend/packages/shared/src/api/mutations.ts | 2 +- .../useUploadOrgCodeListMutation.test.ts | 26 +++++++++++++++++++ .../mutations/useUploadOrgCodeListMutation.ts | 16 ++++++++++++ 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.test.ts create mode 100644 frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.ts diff --git a/frontend/packages/shared/src/api/mutations.ts b/frontend/packages/shared/src/api/mutations.ts index e104a07f545..60b490423fa 100644 --- a/frontend/packages/shared/src/api/mutations.ts +++ b/frontend/packages/shared/src/api/mutations.ts @@ -174,7 +174,7 @@ export const updateSelectedMaskinportenScopes = (org: string, app: string, appSc export const createCodeListForOrg = async (org: string, codeListId: string, payload: CodeList): Promise => Promise.resolve(); export const updateCodeListForOrg = async (org: string, codeListId: string, payload: CodeList): Promise => Promise.resolve(); export const deleteCodeListForOrg = async (org: string, codeListId: string): Promise => Promise.resolve(); -export const uploadCodeListForOrg = async (org: string, app: string, payload: FormData): Promise => Promise.resolve(); +export const uploadCodeListForOrg = async (org: string, payload: FormData): Promise => Promise.resolve(); // Organisation text resources: // Todo: Replace these with real API calls when endpoints are ready. https://github.com/Altinn/altinn-studio/issues/14503 diff --git a/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.test.ts b/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.test.ts new file mode 100644 index 00000000000..3a91c665705 --- /dev/null +++ b/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.test.ts @@ -0,0 +1,26 @@ +import { renderHookWithProviders } from '../../mocks/renderHookWithProviders'; +import { org } from '@studio/testing/testids'; +import { queriesMock } from '../../mocks/queriesMock'; +import type { CodeList } from '@studio/components'; +import { useUploadOrgCodeListMutation } from './useUploadOrgCodeListMutation'; + +// Test data: +const payload: CodeList = [ + { + value: 'test-value', + label: 'test-label', + }, +]; + +describe('useUploadOrgCodeListMutation', () => { + it('Calls useUploadOrgCodeListMutation with correct parameters', async () => { + const renderUpdateOrgCodeListMutationResult = renderHookWithProviders(() => + useUploadOrgCodeListMutation(org), + ).result; + await renderUpdateOrgCodeListMutationResult.current.mutateAsync({ + payload, + }); + expect(queriesMock.uploadCodeListForOrg).toHaveBeenCalledTimes(1); + expect(queriesMock.uploadCodeListForOrg).toHaveBeenCalledWith(org, payload); + }); +}); diff --git a/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.ts b/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.ts new file mode 100644 index 00000000000..6ac93bebdd5 --- /dev/null +++ b/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.ts @@ -0,0 +1,16 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { useServicesContext } from '../../contexts/ServicesContext'; +import { QueryKey } from 'app-shared/types/QueryKey'; + +type UploadOrgCodeListMutationArgs = { + payload: FormData; +}; + +export const useUploadOrgCodeListMutation = (org: string) => { + const queryClient = useQueryClient(); + const { uploadCodeListForOrg } = useServicesContext(); + return useMutation({ + mutationFn: ({ payload }: UploadOrgCodeListMutationArgs) => uploadCodeListForOrg(org, payload), + onSuccess: () => queryClient.invalidateQueries({ queryKey: [QueryKey.OrgCodeLists, org] }), + }); +}; From d70ccf0870579da34622a8e19c8901550a3ad4ba Mon Sep 17 00:00:00 2001 From: Erling Hauan Date: Tue, 28 Jan 2025 21:07:23 +0100 Subject: [PATCH 06/14] create useDeleteOrgCodeListMutation --- .../useDeleteOrgCodeListMutation.test.ts | 20 +++++++++++++++++++ .../mutations/useDeleteOrgCodeListMutation.ts | 17 ++++++++++++++++ .../useUploadOrgCodeListMutation.test.ts | 4 ++-- 3 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 frontend/packages/shared/src/hooks/mutations/useDeleteOrgCodeListMutation.test.ts create mode 100644 frontend/packages/shared/src/hooks/mutations/useDeleteOrgCodeListMutation.ts diff --git a/frontend/packages/shared/src/hooks/mutations/useDeleteOrgCodeListMutation.test.ts b/frontend/packages/shared/src/hooks/mutations/useDeleteOrgCodeListMutation.test.ts new file mode 100644 index 00000000000..87b83a354d9 --- /dev/null +++ b/frontend/packages/shared/src/hooks/mutations/useDeleteOrgCodeListMutation.test.ts @@ -0,0 +1,20 @@ +import { renderHookWithProviders } from '../../mocks/renderHookWithProviders'; +import { org } from '@studio/testing/testids'; +import { queriesMock } from '../../mocks/queriesMock'; +import { useDeleteOrgCodeListMutation } from 'app-shared/hooks/mutations/useDeleteOrgCodeListMutation'; + +// Test data: +const codeListId = 'testId'; + +describe('useDeleteOrgCodeListMutation', () => { + it('Calls useDeleteOrgCodeListMutation with correct parameters', async () => { + const renderDeleteOrgCodeListMutationResult = renderHookWithProviders(() => + useDeleteOrgCodeListMutation(org), + ).result; + await renderDeleteOrgCodeListMutationResult.current.mutateAsync({ + codeListId, + }); + expect(queriesMock.deleteCodeListForOrg).toHaveBeenCalledTimes(1); + expect(queriesMock.deleteCodeListForOrg).toHaveBeenCalledWith(org, codeListId); + }); +}); diff --git a/frontend/packages/shared/src/hooks/mutations/useDeleteOrgCodeListMutation.ts b/frontend/packages/shared/src/hooks/mutations/useDeleteOrgCodeListMutation.ts new file mode 100644 index 00000000000..43202647fbe --- /dev/null +++ b/frontend/packages/shared/src/hooks/mutations/useDeleteOrgCodeListMutation.ts @@ -0,0 +1,17 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { useServicesContext } from '../../contexts/ServicesContext'; +import { QueryKey } from 'app-shared/types/QueryKey'; + +type DeleteOrgCodeListMutationArgs = { + codeListId: string; +}; + +export const useDeleteOrgCodeListMutation = (org: string) => { + const queryClient = useQueryClient(); + const { deleteCodeListForOrg } = useServicesContext(); + return useMutation({ + mutationFn: ({ codeListId }: DeleteOrgCodeListMutationArgs) => + deleteCodeListForOrg(org, codeListId), + onSuccess: () => queryClient.invalidateQueries({ queryKey: [QueryKey.OrgCodeLists, org] }), + }); +}; diff --git a/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.test.ts b/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.test.ts index 3a91c665705..20397bbfe42 100644 --- a/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.test.ts +++ b/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.test.ts @@ -14,10 +14,10 @@ const payload: CodeList = [ describe('useUploadOrgCodeListMutation', () => { it('Calls useUploadOrgCodeListMutation with correct parameters', async () => { - const renderUpdateOrgCodeListMutationResult = renderHookWithProviders(() => + const renderUploadOrgCodeListMutationResult = renderHookWithProviders(() => useUploadOrgCodeListMutation(org), ).result; - await renderUpdateOrgCodeListMutationResult.current.mutateAsync({ + await renderUploadOrgCodeListMutationResult.current.mutateAsync({ payload, }); expect(queriesMock.uploadCodeListForOrg).toHaveBeenCalledTimes(1); From a7c0f2ba12fa160cde394cde67dc278629cf41e2 Mon Sep 17 00:00:00 2001 From: Erling Hauan Date: Fri, 31 Jan 2025 09:24:53 +0100 Subject: [PATCH 07/14] fix some PR comments (correct import paths, clearAllMocks, destructure render fn, begin improving caching) --- frontend/packages/shared/src/api/mutations.ts | 4 ++- .../useCreateOrgCodeListMutation.test.ts | 29 ++++++++++++------- .../mutations/useCreateOrgCodeListMutation.ts | 22 +++++++++----- .../useDeleteOrgCodeListMutation.test.ts | 12 ++++---- .../mutations/useDeleteOrgCodeListMutation.ts | 2 +- .../useUpdateOrgCodeListMutation.test.ts | 13 ++++----- .../mutations/useUpdateOrgCodeListMutation.ts | 4 +-- .../useUploadOrgCodeListMutation.test.ts | 12 ++++---- .../mutations/useUploadOrgCodeListMutation.ts | 2 +- .../src/hooks/queries/useOrgCodeListsQuery.ts | 8 ++--- 10 files changed, 60 insertions(+), 48 deletions(-) diff --git a/frontend/packages/shared/src/api/mutations.ts b/frontend/packages/shared/src/api/mutations.ts index 60b490423fa..f3984b23adf 100644 --- a/frontend/packages/shared/src/api/mutations.ts +++ b/frontend/packages/shared/src/api/mutations.ts @@ -49,6 +49,7 @@ import { dataTypePath, optionListPath, undeployAppFromEnvPath, + orgCodeListPath, } from 'app-shared/api/paths'; import type { AddLanguagePayload } from 'app-shared/types/api/AddLanguagePayload'; import type { AddRepoParams } from 'app-shared/types/api'; @@ -76,6 +77,7 @@ import type { Option } from 'app-shared/types/Option'; import type { MaskinportenScopes } from 'app-shared/types/MaskinportenScope'; import type { DataType } from '../types/DataType'; import type { CodeList } from 'app-shared/types/CodeList'; +import type { CodeListData } from 'app-shared/types/CodeListData'; const headers = { Accept: 'application/json', @@ -171,7 +173,7 @@ export const updateSelectedMaskinportenScopes = (org: string, app: string, appSc // Organisation library code lists: // Todo: Replace these with real API calls when endpoints are ready. https://github.com/Altinn/altinn-studio/issues/14505 -export const createCodeListForOrg = async (org: string, codeListId: string, payload: CodeList): Promise => Promise.resolve(); +export const createCodeListForOrg = async (org: string, codeListId: string, payload: CodeList): Promise => post(orgCodeListPath(org, codeListId), payload); export const updateCodeListForOrg = async (org: string, codeListId: string, payload: CodeList): Promise => Promise.resolve(); export const deleteCodeListForOrg = async (org: string, codeListId: string): Promise => Promise.resolve(); export const uploadCodeListForOrg = async (org: string, payload: FormData): Promise => Promise.resolve(); diff --git a/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.test.ts b/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.test.ts index 361c9ce9b79..bdd7f58db16 100644 --- a/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.test.ts +++ b/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.test.ts @@ -2,27 +2,36 @@ import { renderHookWithProviders } from '../../mocks/renderHookWithProviders'; import { org } from '@studio/testing/testids'; import { queriesMock } from '../../mocks/queriesMock'; import { useCreateOrgCodeListMutation } from './useCreateOrgCodeListMutation'; -import type { CodeList } from '@studio/components'; +import type { CodeList } from '../../types/CodeList'; +import type { CodeListData } from '../../types/CodeListData'; // Test data: -const codeListId = 'testId'; -const payload: CodeList = [ +const codeList: CodeList = [ { value: 'test-value', label: 'test-label', }, ]; +const codeListData: CodeListData = { + title: 'test-title', + data: codeList, +}; + describe('useCreateOrgCodeListMutation', () => { + beforeEach(jest.clearAllMocks); + it('Calls useCreateOrgCodeListMutation with correct parameters', async () => { - const renderCreateOrgCodeListMutationResult = renderHookWithProviders(() => - useCreateOrgCodeListMutation(org), - ).result; - await renderCreateOrgCodeListMutationResult.current.mutateAsync({ - codeListId, - payload, + const { result } = renderHookWithProviders(() => useCreateOrgCodeListMutation(org)); + await result.current.mutateAsync({ + codeListTitle: codeListData.title, + codeList: codeListData.data, }); expect(queriesMock.createCodeListForOrg).toHaveBeenCalledTimes(1); - expect(queriesMock.createCodeListForOrg).toHaveBeenCalledWith(org, codeListId, payload); + expect(queriesMock.createCodeListForOrg).toHaveBeenCalledWith( + org, + codeListData.title, + codeListData.data, + ); }); }); diff --git a/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.ts b/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.ts index 6603d219294..0dbb6ab89e2 100644 --- a/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.ts +++ b/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.ts @@ -1,19 +1,27 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useServicesContext } from '../../contexts/ServicesContext'; -import type { CodeList } from '@studio/components'; -import { QueryKey } from 'app-shared/types/QueryKey'; +import type { CodeList } from '../../types/CodeList'; +import { QueryKey } from '../../types/QueryKey'; +import type { CodeListData } from '../../types/CodeListData'; type CreateOrgCodeListMutationArgs = { - codeListId: string; - payload: CodeList; + codeListTitle: string; + codeList: CodeList; }; export const useCreateOrgCodeListMutation = (org: string) => { const queryClient = useQueryClient(); const { createCodeListForOrg } = useServicesContext(); + + const mutationFn = ({ codeListTitle, codeList }: CreateOrgCodeListMutationArgs) => + createCodeListForOrg(org, codeListTitle, codeList); + return useMutation({ - mutationFn: ({ codeListId, payload }: CreateOrgCodeListMutationArgs) => - createCodeListForOrg(org, codeListId, payload), - onSuccess: () => queryClient.invalidateQueries({ queryKey: [QueryKey.OrgCodeLists, org] }), + mutationFn: mutationFn, + onSuccess: (newData: CodeListData) => { + queryClient.setQueryData([QueryKey.OrgCodeLists, org], (oldData: CodeListData[]) => + oldData ? [...oldData, newData] : [newData], + ); + }, }); }; diff --git a/frontend/packages/shared/src/hooks/mutations/useDeleteOrgCodeListMutation.test.ts b/frontend/packages/shared/src/hooks/mutations/useDeleteOrgCodeListMutation.test.ts index 87b83a354d9..f9eb4602e5f 100644 --- a/frontend/packages/shared/src/hooks/mutations/useDeleteOrgCodeListMutation.test.ts +++ b/frontend/packages/shared/src/hooks/mutations/useDeleteOrgCodeListMutation.test.ts @@ -1,19 +1,17 @@ import { renderHookWithProviders } from '../../mocks/renderHookWithProviders'; import { org } from '@studio/testing/testids'; import { queriesMock } from '../../mocks/queriesMock'; -import { useDeleteOrgCodeListMutation } from 'app-shared/hooks/mutations/useDeleteOrgCodeListMutation'; +import { useDeleteOrgCodeListMutation } from '../../hooks/mutations/useDeleteOrgCodeListMutation'; // Test data: const codeListId = 'testId'; describe('useDeleteOrgCodeListMutation', () => { + beforeEach(jest.clearAllMocks); + it('Calls useDeleteOrgCodeListMutation with correct parameters', async () => { - const renderDeleteOrgCodeListMutationResult = renderHookWithProviders(() => - useDeleteOrgCodeListMutation(org), - ).result; - await renderDeleteOrgCodeListMutationResult.current.mutateAsync({ - codeListId, - }); + const { result } = renderHookWithProviders(() => useDeleteOrgCodeListMutation(org)); + await result.current.mutateAsync({ codeListId }); expect(queriesMock.deleteCodeListForOrg).toHaveBeenCalledTimes(1); expect(queriesMock.deleteCodeListForOrg).toHaveBeenCalledWith(org, codeListId); }); diff --git a/frontend/packages/shared/src/hooks/mutations/useDeleteOrgCodeListMutation.ts b/frontend/packages/shared/src/hooks/mutations/useDeleteOrgCodeListMutation.ts index 43202647fbe..eb31e7781b9 100644 --- a/frontend/packages/shared/src/hooks/mutations/useDeleteOrgCodeListMutation.ts +++ b/frontend/packages/shared/src/hooks/mutations/useDeleteOrgCodeListMutation.ts @@ -1,6 +1,6 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useServicesContext } from '../../contexts/ServicesContext'; -import { QueryKey } from 'app-shared/types/QueryKey'; +import { QueryKey } from '../../types/QueryKey'; type DeleteOrgCodeListMutationArgs = { codeListId: string; diff --git a/frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.test.ts b/frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.test.ts index 75eee23d037..7a44b1fd06d 100644 --- a/frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.test.ts +++ b/frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.test.ts @@ -1,7 +1,7 @@ import { renderHookWithProviders } from '../../mocks/renderHookWithProviders'; import { org } from '@studio/testing/testids'; import { queriesMock } from '../../mocks/queriesMock'; -import type { CodeList } from '@studio/components'; +import type { CodeList } from '../../types/CodeList'; import { useUpdateOrgCodeListMutation } from './useUpdateOrgCodeListMutation'; // Test data: @@ -14,14 +14,11 @@ const payload: CodeList = [ ]; describe('useUpdateOrgCodeListMutation', () => { + beforeEach(jest.clearAllMocks); + it('Calls useUpdateOrgCodeListMutation with correct parameters', async () => { - const renderUpdateOrgCodeListMutationResult = renderHookWithProviders(() => - useUpdateOrgCodeListMutation(org), - ).result; - await renderUpdateOrgCodeListMutationResult.current.mutateAsync({ - codeListId, - payload, - }); + const { result } = renderHookWithProviders(() => useUpdateOrgCodeListMutation(org)); + await result.current.mutateAsync({ codeListId, payload }); expect(queriesMock.updateCodeListForOrg).toHaveBeenCalledTimes(1); expect(queriesMock.updateCodeListForOrg).toHaveBeenCalledWith(org, codeListId, payload); }); diff --git a/frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.ts b/frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.ts index f60455c36be..4a732391736 100644 --- a/frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.ts +++ b/frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.ts @@ -1,7 +1,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useServicesContext } from '../../contexts/ServicesContext'; -import type { CodeList } from '@studio/components'; -import { QueryKey } from 'app-shared/types/QueryKey'; +import type { CodeList } from '../../types/CodeList'; +import { QueryKey } from '../../types/QueryKey'; type UpdateOrgCodeListMutationArgs = { codeListId: string; diff --git a/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.test.ts b/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.test.ts index 20397bbfe42..590b1cc28ce 100644 --- a/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.test.ts +++ b/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.test.ts @@ -1,7 +1,7 @@ import { renderHookWithProviders } from '../../mocks/renderHookWithProviders'; import { org } from '@studio/testing/testids'; import { queriesMock } from '../../mocks/queriesMock'; -import type { CodeList } from '@studio/components'; +import type { CodeList } from '../../types/CodeList'; import { useUploadOrgCodeListMutation } from './useUploadOrgCodeListMutation'; // Test data: @@ -13,13 +13,11 @@ const payload: CodeList = [ ]; describe('useUploadOrgCodeListMutation', () => { + beforeEach(jest.clearAllMocks); + it('Calls useUploadOrgCodeListMutation with correct parameters', async () => { - const renderUploadOrgCodeListMutationResult = renderHookWithProviders(() => - useUploadOrgCodeListMutation(org), - ).result; - await renderUploadOrgCodeListMutationResult.current.mutateAsync({ - payload, - }); + const { result } = renderHookWithProviders(() => useUploadOrgCodeListMutation(org)); + await result.current.mutateAsync({ payload }); expect(queriesMock.uploadCodeListForOrg).toHaveBeenCalledTimes(1); expect(queriesMock.uploadCodeListForOrg).toHaveBeenCalledWith(org, payload); }); diff --git a/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.ts b/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.ts index 6ac93bebdd5..95f66b4a56a 100644 --- a/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.ts +++ b/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.ts @@ -1,6 +1,6 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useServicesContext } from '../../contexts/ServicesContext'; -import { QueryKey } from 'app-shared/types/QueryKey'; +import { QueryKey } from '../../types/QueryKey'; type UploadOrgCodeListMutationArgs = { payload: FormData; diff --git a/frontend/packages/shared/src/hooks/queries/useOrgCodeListsQuery.ts b/frontend/packages/shared/src/hooks/queries/useOrgCodeListsQuery.ts index a334b993d0e..0a180a244ef 100644 --- a/frontend/packages/shared/src/hooks/queries/useOrgCodeListsQuery.ts +++ b/frontend/packages/shared/src/hooks/queries/useOrgCodeListsQuery.ts @@ -1,12 +1,12 @@ import { useServicesContext } from 'app-shared/contexts/ServicesContext'; -import { QueryKey } from 'app-shared/types/QueryKey'; +import { QueryKey } from '../../types/QueryKey'; import type { UseQueryResult } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query'; -import type { OptionListsResponse } from 'app-shared/types/api/OptionListsResponse'; +import type { CodeListsResponse } from '../../types/api/CodeListsResponse'; -export const useOrgCodeListsQuery = (org: string): UseQueryResult => { +export const useOrgCodeListsQuery = (org: string): UseQueryResult => { const { getCodeListsForOrg } = useServicesContext(); - return useQuery({ + return useQuery({ queryKey: [QueryKey.OrgCodeLists, org], queryFn: () => getCodeListsForOrg(org), }); From 82e709d377f858099f829ab5e7b45911f7cab84a Mon Sep 17 00:00:00 2001 From: Erling Hauan Date: Fri, 31 Jan 2025 16:22:44 +0100 Subject: [PATCH 08/14] improve caching for rest of mutations --- .../src/ArrayUtils/ArrayUtils.test.ts | 27 +++++++++++++ .../src/ArrayUtils/ArrayUtils.ts | 5 +++ frontend/packages/shared/src/api/mutations.ts | 7 ++-- .../useCreateOrgCodeListMutation.test.ts | 39 ++++++++++++++++--- .../mutations/useCreateOrgCodeListMutation.ts | 10 +++-- .../mutations/useDeleteOrgCodeListMutation.ts | 14 ++++++- .../mutations/useUpdateOrgCodeListMutation.ts | 12 +++++- .../mutations/useUploadOrgCodeListMutation.ts | 8 +++- 8 files changed, 106 insertions(+), 16 deletions(-) diff --git a/frontend/libs/studio-pure-functions/src/ArrayUtils/ArrayUtils.test.ts b/frontend/libs/studio-pure-functions/src/ArrayUtils/ArrayUtils.test.ts index fdca9b8ccc8..dc2b570d222 100644 --- a/frontend/libs/studio-pure-functions/src/ArrayUtils/ArrayUtils.test.ts +++ b/frontend/libs/studio-pure-functions/src/ArrayUtils/ArrayUtils.test.ts @@ -289,4 +289,31 @@ describe('ArrayUtils', () => { expect(ArrayUtils.removeEmptyStrings(array)).toEqual(['0', '1', '2']); }); }); + + describe('appendOrCreate', () => { + it('Should create a new array with the new item if the old array is undefined', () => { + const result = ArrayUtils.appendOrCreate(undefined, 'test'); + expect(result).toEqual(['test']); + }); + + it('Should append the new item to the existing array if one is provided', () => { + const oldArray = ['existing']; + const result = ArrayUtils.appendOrCreate(oldArray, 'new'); + expect(result).toEqual(['existing', 'new']); + }); + + it('Should work with an empty array by returning a new array with the new item', () => { + const oldArray: string[] = []; + const result = ArrayUtils.appendOrCreate(oldArray, 'test'); + expect(result).toEqual(['test']); + }); + + it('Should not mutate the original array', () => { + const oldArray = ['original']; + const result = ArrayUtils.appendOrCreate(oldArray, 'added'); + + expect(result).toEqual(['original', 'added']); + expect(oldArray).toEqual(['original']); + }); + }); }); diff --git a/frontend/libs/studio-pure-functions/src/ArrayUtils/ArrayUtils.ts b/frontend/libs/studio-pure-functions/src/ArrayUtils/ArrayUtils.ts index ec5561d8537..f607d838be1 100644 --- a/frontend/libs/studio-pure-functions/src/ArrayUtils/ArrayUtils.ts +++ b/frontend/libs/studio-pure-functions/src/ArrayUtils/ArrayUtils.ts @@ -72,6 +72,11 @@ export class ArrayUtils { return [item, ...array]; } + /** Appends an item to the end of an array if the array exists, otherwise creates a new array with the item. */ + public static appendOrCreate(array: T[], item: T): T[] { + return array ? [...array, item] : [item]; + } + public static isDuplicate(value: T, valueList: T[]): boolean { return valueList.filter((item) => item === value).length > 1; } diff --git a/frontend/packages/shared/src/api/mutations.ts b/frontend/packages/shared/src/api/mutations.ts index f3984b23adf..99ffe1baf1d 100644 --- a/frontend/packages/shared/src/api/mutations.ts +++ b/frontend/packages/shared/src/api/mutations.ts @@ -50,6 +50,7 @@ import { optionListPath, undeployAppFromEnvPath, orgCodeListPath, + orgCodeListUploadPath, } from 'app-shared/api/paths'; import type { AddLanguagePayload } from 'app-shared/types/api/AddLanguagePayload'; import type { AddRepoParams } from 'app-shared/types/api'; @@ -174,9 +175,9 @@ export const updateSelectedMaskinportenScopes = (org: string, app: string, appSc // Organisation library code lists: // Todo: Replace these with real API calls when endpoints are ready. https://github.com/Altinn/altinn-studio/issues/14505 export const createCodeListForOrg = async (org: string, codeListId: string, payload: CodeList): Promise => post(orgCodeListPath(org, codeListId), payload); -export const updateCodeListForOrg = async (org: string, codeListId: string, payload: CodeList): Promise => Promise.resolve(); -export const deleteCodeListForOrg = async (org: string, codeListId: string): Promise => Promise.resolve(); -export const uploadCodeListForOrg = async (org: string, payload: FormData): Promise => Promise.resolve(); +export const updateCodeListForOrg = async (org: string, codeListId: string, payload: CodeList): Promise => put(orgCodeListPath(org, codeListId), payload); +export const deleteCodeListForOrg = async (org: string, codeListId: string): Promise => del(orgCodeListPath(org, codeListId)); +export const uploadCodeListForOrg = async (org: string, payload: FormData): Promise => post(orgCodeListUploadPath(org), payload); // Organisation text resources: // Todo: Replace these with real API calls when endpoints are ready. https://github.com/Altinn/altinn-studio/issues/14503 diff --git a/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.test.ts b/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.test.ts index bdd7f58db16..7d86e95b595 100644 --- a/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.test.ts +++ b/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.test.ts @@ -4,6 +4,8 @@ import { queriesMock } from '../../mocks/queriesMock'; import { useCreateOrgCodeListMutation } from './useCreateOrgCodeListMutation'; import type { CodeList } from '../../types/CodeList'; import type { CodeListData } from '../../types/CodeListData'; +import { createQueryClientMock } from 'app-shared/mocks/queryClientMock'; +import { QueryKey } from 'app-shared/types/QueryKey'; // Test data: const codeList: CodeList = [ @@ -13,25 +15,50 @@ const codeList: CodeList = [ }, ]; -const codeListData: CodeListData = { - title: 'test-title', +const newCodeList: CodeListData = { + title: 'new-title', data: codeList, }; +const existingCodeLists: CodeListData[] = [ + { + title: 'existing-title', + data: [...codeList], + }, + { + title: 'another-existing-title', + data: [...codeList], + }, +]; + describe('useCreateOrgCodeListMutation', () => { beforeEach(jest.clearAllMocks); it('Calls useCreateOrgCodeListMutation with correct parameters', async () => { const { result } = renderHookWithProviders(() => useCreateOrgCodeListMutation(org)); await result.current.mutateAsync({ - codeListTitle: codeListData.title, - codeList: codeListData.data, + title: newCodeList.title, + codeList: newCodeList.data, }); expect(queriesMock.createCodeListForOrg).toHaveBeenCalledTimes(1); expect(queriesMock.createCodeListForOrg).toHaveBeenCalledWith( org, - codeListData.title, - codeListData.data, + newCodeList.title, + newCodeList.data, ); }); + + it('Adds newly created data to existing cache', async () => { + const queryClient = createQueryClientMock(); + queryClient.setQueryData([QueryKey.OrgCodeLists, org], existingCodeLists); + const { result } = renderHookWithProviders(() => useCreateOrgCodeListMutation(org), { + queryClient, + }); + + await result.current.mutateAsync({ ...newCodeList }); + + const expectedUpdatedData = [...existingCodeLists, newCodeList]; + const updatedData = queryClient.getQueryData([QueryKey.OrgCodeLists, org]); + expect(updatedData).toEqual(expectedUpdatedData); + }); }); diff --git a/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.ts b/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.ts index 0dbb6ab89e2..cca28f0c10f 100644 --- a/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.ts +++ b/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.ts @@ -3,9 +3,10 @@ import { useServicesContext } from '../../contexts/ServicesContext'; import type { CodeList } from '../../types/CodeList'; import { QueryKey } from '../../types/QueryKey'; import type { CodeListData } from '../../types/CodeListData'; +import { ArrayUtils } from '@studio/pure-functions'; type CreateOrgCodeListMutationArgs = { - codeListTitle: string; + title: string; codeList: CodeList; }; @@ -13,14 +14,15 @@ export const useCreateOrgCodeListMutation = (org: string) => { const queryClient = useQueryClient(); const { createCodeListForOrg } = useServicesContext(); - const mutationFn = ({ codeListTitle, codeList }: CreateOrgCodeListMutationArgs) => - createCodeListForOrg(org, codeListTitle, codeList); + const mutationFn = ({ title, codeList }: CreateOrgCodeListMutationArgs) => + createCodeListForOrg(org, title, codeList); return useMutation({ mutationFn: mutationFn, onSuccess: (newData: CodeListData) => { + console.log('newData', newData); queryClient.setQueryData([QueryKey.OrgCodeLists, org], (oldData: CodeListData[]) => - oldData ? [...oldData, newData] : [newData], + ArrayUtils.appendOrCreate(oldData, newData), ); }, }); diff --git a/frontend/packages/shared/src/hooks/mutations/useDeleteOrgCodeListMutation.ts b/frontend/packages/shared/src/hooks/mutations/useDeleteOrgCodeListMutation.ts index eb31e7781b9..928d804caac 100644 --- a/frontend/packages/shared/src/hooks/mutations/useDeleteOrgCodeListMutation.ts +++ b/frontend/packages/shared/src/hooks/mutations/useDeleteOrgCodeListMutation.ts @@ -1,6 +1,8 @@ +import type { QueryClient } from '@tanstack/react-query'; import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useServicesContext } from '../../contexts/ServicesContext'; import { QueryKey } from '../../types/QueryKey'; +import type { CodeListData } from '../../types/CodeListData'; type DeleteOrgCodeListMutationArgs = { codeListId: string; @@ -12,6 +14,16 @@ export const useDeleteOrgCodeListMutation = (org: string) => { return useMutation({ mutationFn: ({ codeListId }: DeleteOrgCodeListMutationArgs) => deleteCodeListForOrg(org, codeListId), - onSuccess: () => queryClient.invalidateQueries({ queryKey: [QueryKey.OrgCodeLists, org] }), + onSuccess: (codeListId: string) => removeItemFromCache(queryClient, org, codeListId), }); }; + +const removeItemFromCache = (queryClient: QueryClient, org: string, codeListId: string): void => { + const currentCodeLists = queryClient.getQueryData([QueryKey.OrgCodeLists, org]); + if (currentCodeLists) { + const updatedCodeLists = currentCodeLists.filter((codeList) => codeList.title !== codeListId); + queryClient.setQueryData([QueryKey.OrgCodeLists, org], updatedCodeLists); + } else { + queryClient.setQueryData([QueryKey.OrgCodeLists, org], null); + } +}; diff --git a/frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.ts b/frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.ts index 4a732391736..b5205aa237c 100644 --- a/frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.ts +++ b/frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.ts @@ -1,7 +1,9 @@ +import type { QueryClient } from '@tanstack/react-query'; import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useServicesContext } from '../../contexts/ServicesContext'; import type { CodeList } from '../../types/CodeList'; import { QueryKey } from '../../types/QueryKey'; +import type { CodeListData } from '../../types/CodeListData'; type UpdateOrgCodeListMutationArgs = { codeListId: string; @@ -14,6 +16,14 @@ export const useUpdateOrgCodeListMutation = (org: string) => { return useMutation({ mutationFn: ({ codeListId, payload }: UpdateOrgCodeListMutationArgs) => updateCodeListForOrg(org, codeListId, payload), - onSuccess: () => queryClient.invalidateQueries({ queryKey: [QueryKey.OrgCodeLists, org] }), + onSuccess: (codeList: CodeListData) => updateCache(queryClient, org, codeList), }); }; + +const updateCache = (queryClient: QueryClient, org: string, codeList: CodeListData): void => { + const currentCodeLists = queryClient.getQueryData([QueryKey.OrgCodeLists, org]); + const updatedCodeLists = currentCodeLists.map((item) => + item.title === codeList.title ? codeList : item, + ); + queryClient.setQueryData([QueryKey.OrgCodeLists, org], updatedCodeLists); +}; diff --git a/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.ts b/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.ts index 95f66b4a56a..281ed9a66de 100644 --- a/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.ts +++ b/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.ts @@ -1,6 +1,8 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useServicesContext } from '../../contexts/ServicesContext'; import { QueryKey } from '../../types/QueryKey'; +import type { CodeListData } from 'app-shared/types/CodeListData'; +import { ArrayUtils } from '@studio/pure-functions'; type UploadOrgCodeListMutationArgs = { payload: FormData; @@ -11,6 +13,10 @@ export const useUploadOrgCodeListMutation = (org: string) => { const { uploadCodeListForOrg } = useServicesContext(); return useMutation({ mutationFn: ({ payload }: UploadOrgCodeListMutationArgs) => uploadCodeListForOrg(org, payload), - onSuccess: () => queryClient.invalidateQueries({ queryKey: [QueryKey.OrgCodeLists, org] }), + onSuccess: (newData: CodeListData) => { + queryClient.setQueryData([QueryKey.OrgCodeLists, org], (oldData: CodeListData[]) => + ArrayUtils.appendOrCreate(oldData, newData), + ); + }, }); }; From 19110ba45c80b990133451e41152da735849235d Mon Sep 17 00:00:00 2001 From: Erling Hauan Date: Mon, 3 Feb 2025 14:02:50 +0100 Subject: [PATCH 09/14] Make mutations expect to receive all codelists in org as response --- .../src/FileUtils/FileUtils.ts | 7 +++ frontend/packages/shared/src/api/mutations.ts | 11 ++--- .../useAddOptionListMutation.test.ts | 4 +- .../mutations/useAddOptionListMutation.ts | 9 +--- .../useCreateOrgCodeListMutation.test.ts | 32 ++++++------- .../mutations/useCreateOrgCodeListMutation.ts | 22 +++------ .../useDeleteOrgCodeListMutation.test.ts | 47 +++++++++++++++++-- .../mutations/useDeleteOrgCodeListMutation.ts | 28 ++++------- .../useUpdateOrgCodeListMutation.test.ts | 43 +++++++++++++++-- .../mutations/useUpdateOrgCodeListMutation.ts | 26 ++++------ .../useUploadOrgCodeListMutation.test.ts | 40 ++++++++++++++-- .../mutations/useUploadOrgCodeListMutation.ts | 22 ++++----- 12 files changed, 183 insertions(+), 108 deletions(-) create mode 100644 frontend/libs/studio-pure-functions/src/FileUtils/FileUtils.ts diff --git a/frontend/libs/studio-pure-functions/src/FileUtils/FileUtils.ts b/frontend/libs/studio-pure-functions/src/FileUtils/FileUtils.ts new file mode 100644 index 00000000000..618bfbc780a --- /dev/null +++ b/frontend/libs/studio-pure-functions/src/FileUtils/FileUtils.ts @@ -0,0 +1,7 @@ +export class FileUtils { + static convertToFormData = (file: File): FormData => { + const formData = new FormData(); + formData.append('file', file); + return formData; + }; +} diff --git a/frontend/packages/shared/src/api/mutations.ts b/frontend/packages/shared/src/api/mutations.ts index 99ffe1baf1d..2b19afe53a3 100644 --- a/frontend/packages/shared/src/api/mutations.ts +++ b/frontend/packages/shared/src/api/mutations.ts @@ -78,7 +78,7 @@ import type { Option } from 'app-shared/types/Option'; import type { MaskinportenScopes } from 'app-shared/types/MaskinportenScope'; import type { DataType } from '../types/DataType'; import type { CodeList } from 'app-shared/types/CodeList'; -import type { CodeListData } from 'app-shared/types/CodeListData'; +import type { CodeListsResponse } from 'app-shared/types/api/CodeListsResponse'; const headers = { Accept: 'application/json', @@ -173,11 +173,10 @@ export const updateProcessDataTypes = (org: string, app: string, dataTypesChange export const updateSelectedMaskinportenScopes = (org: string, app: string, appScopesUpsertRequest: MaskinportenScopes) => put(selectedMaskinportenScopesPath(org, app), appScopesUpsertRequest); // Organisation library code lists: -// Todo: Replace these with real API calls when endpoints are ready. https://github.com/Altinn/altinn-studio/issues/14505 -export const createCodeListForOrg = async (org: string, codeListId: string, payload: CodeList): Promise => post(orgCodeListPath(org, codeListId), payload); -export const updateCodeListForOrg = async (org: string, codeListId: string, payload: CodeList): Promise => put(orgCodeListPath(org, codeListId), payload); -export const deleteCodeListForOrg = async (org: string, codeListId: string): Promise => del(orgCodeListPath(org, codeListId)); -export const uploadCodeListForOrg = async (org: string, payload: FormData): Promise => post(orgCodeListUploadPath(org), payload); +export const createCodeListForOrg = async (org: string, codeListId: string, payload: CodeList): Promise => post(orgCodeListPath(org, codeListId), payload); +export const updateCodeListForOrg = async (org: string, codeListId: string, payload: CodeList): Promise => put(orgCodeListPath(org, codeListId), payload); +export const deleteCodeListForOrg = async (org: string, codeListId: string): Promise => del(orgCodeListPath(org, codeListId)); +export const uploadCodeListForOrg = async (org: string, payload: FormData): Promise => post(orgCodeListUploadPath(org), payload); // Organisation text resources: // Todo: Replace these with real API calls when endpoints are ready. https://github.com/Altinn/altinn-studio/issues/14503 diff --git a/frontend/packages/shared/src/hooks/mutations/useAddOptionListMutation.test.ts b/frontend/packages/shared/src/hooks/mutations/useAddOptionListMutation.test.ts index 201486e2cc3..b0d0d5bd803 100644 --- a/frontend/packages/shared/src/hooks/mutations/useAddOptionListMutation.test.ts +++ b/frontend/packages/shared/src/hooks/mutations/useAddOptionListMutation.test.ts @@ -2,11 +2,11 @@ import { queriesMock } from 'app-shared/mocks/queriesMock'; import { app, org } from '@studio/testing/testids'; import { useAddOptionListMutation } from './useAddOptionListMutation'; import { renderHookWithProviders } from 'app-shared/mocks/renderHookWithProviders'; +import { FileUtils } from '@studio/pure-functions/src/FileUtils/FileUtils'; // Test data: const file = new File(['hello'], 'hello.json', { type: 'text/json' }); -const formData = new FormData(); -formData.append('file', file); +const formData = FileUtils.convertToFormData(file); describe('useAddOptionsMutation', () => { afterEach(jest.clearAllMocks); diff --git a/frontend/packages/shared/src/hooks/mutations/useAddOptionListMutation.ts b/frontend/packages/shared/src/hooks/mutations/useAddOptionListMutation.ts index 50ab147c1e5..f1e81821f57 100644 --- a/frontend/packages/shared/src/hooks/mutations/useAddOptionListMutation.ts +++ b/frontend/packages/shared/src/hooks/mutations/useAddOptionListMutation.ts @@ -2,13 +2,14 @@ import type { MutationMeta } from '@tanstack/react-query'; import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useServicesContext } from 'app-shared/contexts/ServicesContext'; import { QueryKey } from 'app-shared/types/QueryKey'; +import { FileUtils } from '@studio/pure-functions/src/FileUtils/FileUtils'; export const useAddOptionListMutation = (org: string, app: string, meta?: MutationMeta) => { const queryClient = useQueryClient(); const { uploadOptionList } = useServicesContext(); const mutationFn = (file: File) => { - const formData = createFormDataWithFile(file); + const formData = FileUtils.convertToFormData(file); return uploadOptionList(org, app, formData); }; @@ -21,9 +22,3 @@ export const useAddOptionListMutation = (org: string, app: string, meta?: Mutati meta, }); }; - -const createFormDataWithFile = (file: File): FormData => { - const formData = new FormData(); - formData.append('file', file); - return formData; -}; diff --git a/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.test.ts b/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.test.ts index 7d86e95b595..a08f69c7727 100644 --- a/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.test.ts +++ b/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.test.ts @@ -6,6 +6,7 @@ import type { CodeList } from '../../types/CodeList'; import type { CodeListData } from '../../types/CodeListData'; import { createQueryClientMock } from 'app-shared/mocks/queryClientMock'; import { QueryKey } from 'app-shared/types/QueryKey'; +import type { CodeListsResponse } from 'app-shared/types/api/CodeListsResponse'; // Test data: const codeList: CodeList = [ @@ -20,26 +21,19 @@ const newCodeList: CodeListData = { data: codeList, }; -const existingCodeLists: CodeListData[] = [ - { - title: 'existing-title', - data: [...codeList], - }, - { - title: 'another-existing-title', - data: [...codeList], - }, -]; +const existingCodeList: CodeListData = { + title: 'existing-title', + data: codeList, +}; describe('useCreateOrgCodeListMutation', () => { beforeEach(jest.clearAllMocks); - it('Calls useCreateOrgCodeListMutation with correct parameters', async () => { + it('Calls createCodeListForOrg with correct parameters', async () => { const { result } = renderHookWithProviders(() => useCreateOrgCodeListMutation(org)); - await result.current.mutateAsync({ - title: newCodeList.title, - codeList: newCodeList.data, - }); + + await result.current.mutateAsync({ ...newCodeList }); + expect(queriesMock.createCodeListForOrg).toHaveBeenCalledTimes(1); expect(queriesMock.createCodeListForOrg).toHaveBeenCalledWith( org, @@ -48,16 +42,18 @@ describe('useCreateOrgCodeListMutation', () => { ); }); - it('Adds newly created data to existing cache', async () => { + it('Replaces cache with api response', async () => { const queryClient = createQueryClientMock(); - queryClient.setQueryData([QueryKey.OrgCodeLists, org], existingCodeLists); + queryClient.setQueryData([QueryKey.OrgCodeLists, org], [existingCodeList]); + const createCodeListForOrg = jest.fn(() => Promise.resolve([existingCodeList, newCodeList])); const { result } = renderHookWithProviders(() => useCreateOrgCodeListMutation(org), { queryClient, + queries: { createCodeListForOrg }, }); await result.current.mutateAsync({ ...newCodeList }); - const expectedUpdatedData = [...existingCodeLists, newCodeList]; + const expectedUpdatedData: CodeListsResponse = [existingCodeList, newCodeList]; const updatedData = queryClient.getQueryData([QueryKey.OrgCodeLists, org]); expect(updatedData).toEqual(expectedUpdatedData); }); diff --git a/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.ts b/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.ts index cca28f0c10f..ef23c644c50 100644 --- a/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.ts +++ b/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.ts @@ -1,29 +1,21 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useServicesContext } from '../../contexts/ServicesContext'; -import type { CodeList } from '../../types/CodeList'; import { QueryKey } from '../../types/QueryKey'; -import type { CodeListData } from '../../types/CodeListData'; -import { ArrayUtils } from '@studio/pure-functions'; +import type { CodeListsResponse } from 'app-shared/types/api/CodeListsResponse'; +import type { CodeListData } from 'app-shared/types/CodeListData'; -type CreateOrgCodeListMutationArgs = { - title: string; - codeList: CodeList; -}; +type mutationArgs = Pick; export const useCreateOrgCodeListMutation = (org: string) => { const queryClient = useQueryClient(); const { createCodeListForOrg } = useServicesContext(); - const mutationFn = ({ title, codeList }: CreateOrgCodeListMutationArgs) => - createCodeListForOrg(org, title, codeList); + const mutationFn = ({ title, data }: mutationArgs) => createCodeListForOrg(org, title, data); return useMutation({ - mutationFn: mutationFn, - onSuccess: (newData: CodeListData) => { - console.log('newData', newData); - queryClient.setQueryData([QueryKey.OrgCodeLists, org], (oldData: CodeListData[]) => - ArrayUtils.appendOrCreate(oldData, newData), - ); + mutationFn, + onSuccess: (newData: CodeListsResponse) => { + queryClient.setQueryData([QueryKey.OrgCodeLists, org], newData); }, }); }; diff --git a/frontend/packages/shared/src/hooks/mutations/useDeleteOrgCodeListMutation.test.ts b/frontend/packages/shared/src/hooks/mutations/useDeleteOrgCodeListMutation.test.ts index f9eb4602e5f..e2cf9a1576c 100644 --- a/frontend/packages/shared/src/hooks/mutations/useDeleteOrgCodeListMutation.test.ts +++ b/frontend/packages/shared/src/hooks/mutations/useDeleteOrgCodeListMutation.test.ts @@ -2,17 +2,56 @@ import { renderHookWithProviders } from '../../mocks/renderHookWithProviders'; import { org } from '@studio/testing/testids'; import { queriesMock } from '../../mocks/queriesMock'; import { useDeleteOrgCodeListMutation } from '../../hooks/mutations/useDeleteOrgCodeListMutation'; +import { createQueryClientMock } from 'app-shared/mocks/queryClientMock'; +import { QueryKey } from 'app-shared/types/QueryKey'; +import { useCreateOrgCodeListMutation } from 'app-shared/hooks/mutations/useCreateOrgCodeListMutation'; +import type { CodeListsResponse } from 'app-shared/types/api/CodeListsResponse'; +import type { CodeListData } from 'app-shared/types/CodeListData'; +import type { CodeList } from '../../types/CodeList'; // Test data: -const codeListId = 'testId'; +const title = 'testId'; + +const codeList: CodeList = [ + { + value: 'test-value', + label: 'test-label', + }, +]; + +const deletedCodeList: CodeListData = { + title: 'deleted-title', + data: codeList, +}; + +const otherCodeList: CodeListData = { + title: 'other-title', + data: codeList, +}; describe('useDeleteOrgCodeListMutation', () => { beforeEach(jest.clearAllMocks); - it('Calls useDeleteOrgCodeListMutation with correct parameters', async () => { + it('Calls deleteCodeListForOrg with correct parameters', async () => { const { result } = renderHookWithProviders(() => useDeleteOrgCodeListMutation(org)); - await result.current.mutateAsync({ codeListId }); + await result.current.mutateAsync({ title }); expect(queriesMock.deleteCodeListForOrg).toHaveBeenCalledTimes(1); - expect(queriesMock.deleteCodeListForOrg).toHaveBeenCalledWith(org, codeListId); + expect(queriesMock.deleteCodeListForOrg).toHaveBeenCalledWith(org, title); + }); + + it('Replaces cache with api response', async () => { + const queryClient = createQueryClientMock(); + queryClient.setQueryData([QueryKey.OrgCodeLists, org], [deletedCodeList, otherCodeList]); + const createCodeListForOrg = jest.fn(() => Promise.resolve([otherCodeList])); + const { result } = renderHookWithProviders(() => useCreateOrgCodeListMutation(org), { + queryClient, + queries: { createCodeListForOrg }, + }); + + await result.current.mutateAsync({ title: deletedCodeList.title }); + + const expectedUpdatedData: CodeListsResponse = [otherCodeList]; + const updatedData = queryClient.getQueryData([QueryKey.OrgCodeLists, org]); + expect(updatedData).toEqual(expectedUpdatedData); }); }); diff --git a/frontend/packages/shared/src/hooks/mutations/useDeleteOrgCodeListMutation.ts b/frontend/packages/shared/src/hooks/mutations/useDeleteOrgCodeListMutation.ts index 928d804caac..7d5ce451031 100644 --- a/frontend/packages/shared/src/hooks/mutations/useDeleteOrgCodeListMutation.ts +++ b/frontend/packages/shared/src/hooks/mutations/useDeleteOrgCodeListMutation.ts @@ -1,29 +1,21 @@ -import type { QueryClient } from '@tanstack/react-query'; import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useServicesContext } from '../../contexts/ServicesContext'; import { QueryKey } from '../../types/QueryKey'; -import type { CodeListData } from '../../types/CodeListData'; +import type { CodeListsResponse } from 'app-shared/types/api/CodeListsResponse'; +import type { CodeListData } from 'app-shared/types/CodeListData'; -type DeleteOrgCodeListMutationArgs = { - codeListId: string; -}; +type mutationArgs = Pick; export const useDeleteOrgCodeListMutation = (org: string) => { const queryClient = useQueryClient(); const { deleteCodeListForOrg } = useServicesContext(); + + const mutationFn = ({ title }: mutationArgs) => deleteCodeListForOrg(org, title); + return useMutation({ - mutationFn: ({ codeListId }: DeleteOrgCodeListMutationArgs) => - deleteCodeListForOrg(org, codeListId), - onSuccess: (codeListId: string) => removeItemFromCache(queryClient, org, codeListId), + mutationFn, + onSuccess: (newData: CodeListsResponse) => { + queryClient.setQueryData([QueryKey.OrgCodeLists, org], newData); + }, }); }; - -const removeItemFromCache = (queryClient: QueryClient, org: string, codeListId: string): void => { - const currentCodeLists = queryClient.getQueryData([QueryKey.OrgCodeLists, org]); - if (currentCodeLists) { - const updatedCodeLists = currentCodeLists.filter((codeList) => codeList.title !== codeListId); - queryClient.setQueryData([QueryKey.OrgCodeLists, org], updatedCodeLists); - } else { - queryClient.setQueryData([QueryKey.OrgCodeLists, org], null); - } -}; diff --git a/frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.test.ts b/frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.test.ts index 7a44b1fd06d..1de604c8044 100644 --- a/frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.test.ts +++ b/frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.test.ts @@ -3,23 +3,56 @@ import { org } from '@studio/testing/testids'; import { queriesMock } from '../../mocks/queriesMock'; import type { CodeList } from '../../types/CodeList'; import { useUpdateOrgCodeListMutation } from './useUpdateOrgCodeListMutation'; +import { createQueryClientMock } from 'app-shared/mocks/queryClientMock'; +import { QueryKey } from 'app-shared/types/QueryKey'; +import type { CodeListsResponse } from 'app-shared/types/api/CodeListsResponse'; +import type { CodeListData } from 'app-shared/types/CodeListData'; // Test data: -const codeListId = 'testId'; -const payload: CodeList = [ +const codeList: CodeList = [ { value: 'test-value', label: 'test-label', }, ]; +const oldCodeList: CodeListData = { + title: 'old-title', + data: codeList, +}; + +const updatedCodeList: CodeListData = { + title: 'updated-title', + data: codeList, +}; + describe('useUpdateOrgCodeListMutation', () => { beforeEach(jest.clearAllMocks); - it('Calls useUpdateOrgCodeListMutation with correct parameters', async () => { + it('Calls updateCodeListForOrg with correct parameters', async () => { const { result } = renderHookWithProviders(() => useUpdateOrgCodeListMutation(org)); - await result.current.mutateAsync({ codeListId, payload }); + await result.current.mutateAsync({ ...updatedCodeList }); expect(queriesMock.updateCodeListForOrg).toHaveBeenCalledTimes(1); - expect(queriesMock.updateCodeListForOrg).toHaveBeenCalledWith(org, codeListId, payload); + expect(queriesMock.updateCodeListForOrg).toHaveBeenCalledWith( + org, + updatedCodeList.title, + updatedCodeList.data, + ); + }); + + it('Replaces cache with api response', async () => { + const queryClient = createQueryClientMock(); + queryClient.setQueryData([QueryKey.OrgCodeLists, org], [oldCodeList]); + const updateCodeListForOrg = jest.fn(() => Promise.resolve([updatedCodeList])); + const { result } = renderHookWithProviders(() => useUpdateOrgCodeListMutation(org), { + queryClient, + queries: { updateCodeListForOrg }, + }); + + await result.current.mutateAsync({ ...updatedCodeList }); + + const expectedUpdatedData: CodeListsResponse = [updatedCodeList]; + const updatedData = queryClient.getQueryData([QueryKey.OrgCodeLists, org]); + expect(updatedData).toEqual(expectedUpdatedData); }); }); diff --git a/frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.ts b/frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.ts index b5205aa237c..602b1ec5381 100644 --- a/frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.ts +++ b/frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.ts @@ -1,29 +1,21 @@ -import type { QueryClient } from '@tanstack/react-query'; import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useServicesContext } from '../../contexts/ServicesContext'; -import type { CodeList } from '../../types/CodeList'; import { QueryKey } from '../../types/QueryKey'; import type { CodeListData } from '../../types/CodeListData'; +import type { CodeListsResponse } from 'app-shared/types/api/CodeListsResponse'; -type UpdateOrgCodeListMutationArgs = { - codeListId: string; - payload: CodeList; -}; +type mutationArgs = Pick; export const useUpdateOrgCodeListMutation = (org: string) => { const queryClient = useQueryClient(); const { updateCodeListForOrg } = useServicesContext(); + + const mutationFn = ({ title, data }: mutationArgs) => updateCodeListForOrg(org, title, data); + return useMutation({ - mutationFn: ({ codeListId, payload }: UpdateOrgCodeListMutationArgs) => - updateCodeListForOrg(org, codeListId, payload), - onSuccess: (codeList: CodeListData) => updateCache(queryClient, org, codeList), + mutationFn, + onSuccess: (newData: CodeListsResponse) => { + queryClient.setQueryData([QueryKey.OrgCodeLists, org], newData); + }, }); }; - -const updateCache = (queryClient: QueryClient, org: string, codeList: CodeListData): void => { - const currentCodeLists = queryClient.getQueryData([QueryKey.OrgCodeLists, org]); - const updatedCodeLists = currentCodeLists.map((item) => - item.title === codeList.title ? codeList : item, - ); - queryClient.setQueryData([QueryKey.OrgCodeLists, org], updatedCodeLists); -}; diff --git a/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.test.ts b/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.test.ts index 590b1cc28ce..9d58db70886 100644 --- a/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.test.ts +++ b/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.test.ts @@ -1,24 +1,54 @@ import { renderHookWithProviders } from '../../mocks/renderHookWithProviders'; import { org } from '@studio/testing/testids'; import { queriesMock } from '../../mocks/queriesMock'; -import type { CodeList } from '../../types/CodeList'; import { useUploadOrgCodeListMutation } from './useUploadOrgCodeListMutation'; +import { createQueryClientMock } from 'app-shared/mocks/queryClientMock'; +import { QueryKey } from 'app-shared/types/QueryKey'; +import type { CodeListsResponse } from 'app-shared/types/api/CodeListsResponse'; +import type { CodeList } from 'app-shared/types/CodeList'; +import { FileUtils } from '@studio/pure-functions/src/FileUtils/FileUtils'; // Test data: -const payload: CodeList = [ +const fileName = 'fileName'; +const fileData = [ { value: 'test-value', label: 'test-label', }, ]; +const file = new File(fileData, `${fileName}.json`, { type: 'text/json' }); +const formData = FileUtils.convertToFormData(file); + +const codeListResponse: CodeListsResponse = [ + { + title: fileName, + data: fileData, + }, +]; + describe('useUploadOrgCodeListMutation', () => { beforeEach(jest.clearAllMocks); - it('Calls useUploadOrgCodeListMutation with correct parameters', async () => { + it('Calls uploadCodeListForOrg with correct parameters', async () => { const { result } = renderHookWithProviders(() => useUploadOrgCodeListMutation(org)); - await result.current.mutateAsync({ payload }); + await result.current.mutateAsync(file); expect(queriesMock.uploadCodeListForOrg).toHaveBeenCalledTimes(1); - expect(queriesMock.uploadCodeListForOrg).toHaveBeenCalledWith(org, payload); + expect(queriesMock.uploadCodeListForOrg).toHaveBeenCalledWith(org, formData); + }); + + it('Replaces cache with api response', async () => { + const queryClient = createQueryClientMock(); + const uploadCodeListForOrg = jest.fn(() => Promise.resolve([codeListResponse])); + const { result } = renderHookWithProviders(() => useUploadOrgCodeListMutation(org), { + queryClient, + queries: { uploadCodeListForOrg }, + }); + + await result.current.mutateAsync(file); + + const expectedUpdatedData: CodeListsResponse = [codeListResponse]; + const updatedData = queryClient.getQueryData([QueryKey.OrgCodeLists, org]); + expect(updatedData).toEqual(expectedUpdatedData); }); }); diff --git a/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.ts b/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.ts index 281ed9a66de..00860ed0269 100644 --- a/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.ts +++ b/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.ts @@ -1,22 +1,22 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useServicesContext } from '../../contexts/ServicesContext'; import { QueryKey } from '../../types/QueryKey'; -import type { CodeListData } from 'app-shared/types/CodeListData'; -import { ArrayUtils } from '@studio/pure-functions'; - -type UploadOrgCodeListMutationArgs = { - payload: FormData; -}; +import type { CodeListsResponse } from 'app-shared/types/api/CodeListsResponse'; +import { FileUtils } from '@studio/pure-functions/src/FileUtils/FileUtils'; export const useUploadOrgCodeListMutation = (org: string) => { const queryClient = useQueryClient(); const { uploadCodeListForOrg } = useServicesContext(); + + const mutationFn = (file: File) => { + const formData = FileUtils.convertToFormData(file); + return uploadCodeListForOrg(org, formData); + }; + return useMutation({ - mutationFn: ({ payload }: UploadOrgCodeListMutationArgs) => uploadCodeListForOrg(org, payload), - onSuccess: (newData: CodeListData) => { - queryClient.setQueryData([QueryKey.OrgCodeLists, org], (oldData: CodeListData[]) => - ArrayUtils.appendOrCreate(oldData, newData), - ); + mutationFn, + onSuccess: (newData: CodeListsResponse) => { + queryClient.setQueryData([QueryKey.OrgCodeLists, org], newData); }, }); }; From 40a613efb9be0ae388f382784074bf2a12eff213 Mon Sep 17 00:00:00 2001 From: Erling Hauan Date: Mon, 3 Feb 2025 14:14:34 +0100 Subject: [PATCH 10/14] Remove unused ArrayUtil class --- .../src/ArrayUtils/ArrayUtils.test.ts | 27 ------------------- .../src/ArrayUtils/ArrayUtils.ts | 5 ---- 2 files changed, 32 deletions(-) diff --git a/frontend/libs/studio-pure-functions/src/ArrayUtils/ArrayUtils.test.ts b/frontend/libs/studio-pure-functions/src/ArrayUtils/ArrayUtils.test.ts index dc2b570d222..fdca9b8ccc8 100644 --- a/frontend/libs/studio-pure-functions/src/ArrayUtils/ArrayUtils.test.ts +++ b/frontend/libs/studio-pure-functions/src/ArrayUtils/ArrayUtils.test.ts @@ -289,31 +289,4 @@ describe('ArrayUtils', () => { expect(ArrayUtils.removeEmptyStrings(array)).toEqual(['0', '1', '2']); }); }); - - describe('appendOrCreate', () => { - it('Should create a new array with the new item if the old array is undefined', () => { - const result = ArrayUtils.appendOrCreate(undefined, 'test'); - expect(result).toEqual(['test']); - }); - - it('Should append the new item to the existing array if one is provided', () => { - const oldArray = ['existing']; - const result = ArrayUtils.appendOrCreate(oldArray, 'new'); - expect(result).toEqual(['existing', 'new']); - }); - - it('Should work with an empty array by returning a new array with the new item', () => { - const oldArray: string[] = []; - const result = ArrayUtils.appendOrCreate(oldArray, 'test'); - expect(result).toEqual(['test']); - }); - - it('Should not mutate the original array', () => { - const oldArray = ['original']; - const result = ArrayUtils.appendOrCreate(oldArray, 'added'); - - expect(result).toEqual(['original', 'added']); - expect(oldArray).toEqual(['original']); - }); - }); }); diff --git a/frontend/libs/studio-pure-functions/src/ArrayUtils/ArrayUtils.ts b/frontend/libs/studio-pure-functions/src/ArrayUtils/ArrayUtils.ts index f607d838be1..ec5561d8537 100644 --- a/frontend/libs/studio-pure-functions/src/ArrayUtils/ArrayUtils.ts +++ b/frontend/libs/studio-pure-functions/src/ArrayUtils/ArrayUtils.ts @@ -72,11 +72,6 @@ export class ArrayUtils { return [item, ...array]; } - /** Appends an item to the end of an array if the array exists, otherwise creates a new array with the item. */ - public static appendOrCreate(array: T[], item: T): T[] { - return array ? [...array, item] : [item]; - } - public static isDuplicate(value: T, valueList: T[]): boolean { return valueList.filter((item) => item === value).length > 1; } From 30e8edacdbcd2d751585738692a21ade9aaac3e6 Mon Sep 17 00:00:00 2001 From: Erling Hauan Date: Mon, 3 Feb 2025 14:30:01 +0100 Subject: [PATCH 11/14] Add test and index.ts for FileUtils --- .../src/FileUtils/FileUtils.test.ts | 17 +++++++++++++++++ .../src/FileUtils/index.ts | 1 + .../libs/studio-pure-functions/src/index.ts | 1 + .../mutations/useAddOptionListMutation.test.ts | 2 +- .../hooks/mutations/useAddOptionListMutation.ts | 2 +- .../useUploadOrgCodeListMutation.test.ts | 2 +- .../mutations/useUploadOrgCodeListMutation.ts | 2 +- 7 files changed, 23 insertions(+), 4 deletions(-) create mode 100644 frontend/libs/studio-pure-functions/src/FileUtils/FileUtils.test.ts create mode 100644 frontend/libs/studio-pure-functions/src/FileUtils/index.ts diff --git a/frontend/libs/studio-pure-functions/src/FileUtils/FileUtils.test.ts b/frontend/libs/studio-pure-functions/src/FileUtils/FileUtils.test.ts new file mode 100644 index 00000000000..b421f4d8d39 --- /dev/null +++ b/frontend/libs/studio-pure-functions/src/FileUtils/FileUtils.test.ts @@ -0,0 +1,17 @@ +import { FileUtils } from './FileUtils'; + +describe('FileUtils', () => { + describe('convertToFormData', () => { + it('should append the file to FormData under the key "file"', () => { + const fileContent = 'Test file contents'; + const fileName = 'test.txt'; + const fileType = 'text/plain'; + const file = new File([fileContent], fileName, { type: fileType }); + + const formData = FileUtils.convertToFormData(file); + + const retrievedFile = formData.get('file'); + expect(retrievedFile).toBe(file); + }); + }); +}); diff --git a/frontend/libs/studio-pure-functions/src/FileUtils/index.ts b/frontend/libs/studio-pure-functions/src/FileUtils/index.ts new file mode 100644 index 00000000000..eaec5c5c5a3 --- /dev/null +++ b/frontend/libs/studio-pure-functions/src/FileUtils/index.ts @@ -0,0 +1 @@ +export { FileUtils } from './FileUtils'; diff --git a/frontend/libs/studio-pure-functions/src/index.ts b/frontend/libs/studio-pure-functions/src/index.ts index 566087a5f25..7fde05af356 100644 --- a/frontend/libs/studio-pure-functions/src/index.ts +++ b/frontend/libs/studio-pure-functions/src/index.ts @@ -2,6 +2,7 @@ export * from './ArrayUtils'; export * from './BlobDownloader'; export * from './DateUtils'; export * from './FileNameUtils'; +export * from './FileUtils'; export * from './NumberUtils'; export * from './ObjectUtils'; export * from './ScopedStorage'; diff --git a/frontend/packages/shared/src/hooks/mutations/useAddOptionListMutation.test.ts b/frontend/packages/shared/src/hooks/mutations/useAddOptionListMutation.test.ts index b0d0d5bd803..34b126c01a2 100644 --- a/frontend/packages/shared/src/hooks/mutations/useAddOptionListMutation.test.ts +++ b/frontend/packages/shared/src/hooks/mutations/useAddOptionListMutation.test.ts @@ -2,7 +2,7 @@ import { queriesMock } from 'app-shared/mocks/queriesMock'; import { app, org } from '@studio/testing/testids'; import { useAddOptionListMutation } from './useAddOptionListMutation'; import { renderHookWithProviders } from 'app-shared/mocks/renderHookWithProviders'; -import { FileUtils } from '@studio/pure-functions/src/FileUtils/FileUtils'; +import { FileUtils } from '@studio/pure-functions'; // Test data: const file = new File(['hello'], 'hello.json', { type: 'text/json' }); diff --git a/frontend/packages/shared/src/hooks/mutations/useAddOptionListMutation.ts b/frontend/packages/shared/src/hooks/mutations/useAddOptionListMutation.ts index f1e81821f57..fd77c4bb432 100644 --- a/frontend/packages/shared/src/hooks/mutations/useAddOptionListMutation.ts +++ b/frontend/packages/shared/src/hooks/mutations/useAddOptionListMutation.ts @@ -2,7 +2,7 @@ import type { MutationMeta } from '@tanstack/react-query'; import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useServicesContext } from 'app-shared/contexts/ServicesContext'; import { QueryKey } from 'app-shared/types/QueryKey'; -import { FileUtils } from '@studio/pure-functions/src/FileUtils/FileUtils'; +import { FileUtils } from '@studio/pure-functions'; export const useAddOptionListMutation = (org: string, app: string, meta?: MutationMeta) => { const queryClient = useQueryClient(); diff --git a/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.test.ts b/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.test.ts index 9d58db70886..b9cb54eb69b 100644 --- a/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.test.ts +++ b/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.test.ts @@ -6,7 +6,7 @@ import { createQueryClientMock } from 'app-shared/mocks/queryClientMock'; import { QueryKey } from 'app-shared/types/QueryKey'; import type { CodeListsResponse } from 'app-shared/types/api/CodeListsResponse'; import type { CodeList } from 'app-shared/types/CodeList'; -import { FileUtils } from '@studio/pure-functions/src/FileUtils/FileUtils'; +import { FileUtils } from '@studio/pure-functions'; // Test data: const fileName = 'fileName'; diff --git a/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.ts b/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.ts index 00860ed0269..171ccabe694 100644 --- a/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.ts +++ b/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.ts @@ -2,7 +2,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useServicesContext } from '../../contexts/ServicesContext'; import { QueryKey } from '../../types/QueryKey'; import type { CodeListsResponse } from 'app-shared/types/api/CodeListsResponse'; -import { FileUtils } from '@studio/pure-functions/src/FileUtils/FileUtils'; +import { FileUtils } from '@studio/pure-functions'; export const useUploadOrgCodeListMutation = (org: string) => { const queryClient = useQueryClient(); From 2e556919ab1c6ad3d05af801aa740432b3ecff3f Mon Sep 17 00:00:00 2001 From: Erling Hauan Date: Tue, 4 Feb 2025 12:57:28 +0100 Subject: [PATCH 12/14] upload mutation test: convert data to string before creating file --- .../hooks/mutations/useUploadOrgCodeListMutation.test.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.test.ts b/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.test.ts index b9cb54eb69b..fb2a7a8bcb4 100644 --- a/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.test.ts +++ b/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.test.ts @@ -10,14 +10,15 @@ import { FileUtils } from '@studio/pure-functions'; // Test data: const fileName = 'fileName'; -const fileData = [ +const fileData: CodeList = [ { value: 'test-value', label: 'test-label', }, ]; -const file = new File(fileData, `${fileName}.json`, { type: 'text/json' }); +const jsonData = JSON.stringify(fileData); +const file = new File([jsonData], `${fileName}.json`, { type: 'text/json' }); const formData = FileUtils.convertToFormData(file); const codeListResponse: CodeListsResponse = [ @@ -39,7 +40,7 @@ describe('useUploadOrgCodeListMutation', () => { it('Replaces cache with api response', async () => { const queryClient = createQueryClientMock(); - const uploadCodeListForOrg = jest.fn(() => Promise.resolve([codeListResponse])); + const uploadCodeListForOrg = jest.fn(() => Promise.resolve(codeListResponse)); const { result } = renderHookWithProviders(() => useUploadOrgCodeListMutation(org), { queryClient, queries: { uploadCodeListForOrg }, @@ -47,7 +48,7 @@ describe('useUploadOrgCodeListMutation', () => { await result.current.mutateAsync(file); - const expectedUpdatedData: CodeListsResponse = [codeListResponse]; + const expectedUpdatedData: CodeListsResponse = codeListResponse; const updatedData = queryClient.getQueryData([QueryKey.OrgCodeLists, org]); expect(updatedData).toEqual(expectedUpdatedData); }); From 3755a90575405014f6f76937a4f1252f853810dd Mon Sep 17 00:00:00 2001 From: Erling Hauan Date: Tue, 4 Feb 2025 13:04:23 +0100 Subject: [PATCH 13/14] fix import paths --- .../mutations/useCreateOrgCodeListMutation.test.ts | 6 +++--- .../hooks/mutations/useCreateOrgCodeListMutation.ts | 4 ++-- .../mutations/useDeleteOrgCodeListMutation.test.ts | 10 +++++----- .../hooks/mutations/useDeleteOrgCodeListMutation.ts | 4 ++-- .../mutations/useUpdateOrgCodeListMutation.test.ts | 8 ++++---- .../hooks/mutations/useUpdateOrgCodeListMutation.ts | 2 +- .../mutations/useUploadOrgCodeListMutation.test.ts | 8 ++++---- .../hooks/mutations/useUploadOrgCodeListMutation.ts | 2 +- .../src/hooks/queries/useOrgCodeListsQuery.test.ts | 6 +++--- 9 files changed, 25 insertions(+), 25 deletions(-) diff --git a/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.test.ts b/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.test.ts index a08f69c7727..40019ba8897 100644 --- a/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.test.ts +++ b/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.test.ts @@ -4,9 +4,9 @@ import { queriesMock } from '../../mocks/queriesMock'; import { useCreateOrgCodeListMutation } from './useCreateOrgCodeListMutation'; import type { CodeList } from '../../types/CodeList'; import type { CodeListData } from '../../types/CodeListData'; -import { createQueryClientMock } from 'app-shared/mocks/queryClientMock'; -import { QueryKey } from 'app-shared/types/QueryKey'; -import type { CodeListsResponse } from 'app-shared/types/api/CodeListsResponse'; +import { createQueryClientMock } from '../../mocks/queryClientMock'; +import { QueryKey } from '../../types/QueryKey'; +import type { CodeListsResponse } from '../../types/api/CodeListsResponse'; // Test data: const codeList: CodeList = [ diff --git a/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.ts b/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.ts index ef23c644c50..0481bd74c31 100644 --- a/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.ts +++ b/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.ts @@ -1,8 +1,8 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useServicesContext } from '../../contexts/ServicesContext'; import { QueryKey } from '../../types/QueryKey'; -import type { CodeListsResponse } from 'app-shared/types/api/CodeListsResponse'; -import type { CodeListData } from 'app-shared/types/CodeListData'; +import type { CodeListsResponse } from '../../types/api/CodeListsResponse'; +import type { CodeListData } from '../../types/CodeListData'; type mutationArgs = Pick; diff --git a/frontend/packages/shared/src/hooks/mutations/useDeleteOrgCodeListMutation.test.ts b/frontend/packages/shared/src/hooks/mutations/useDeleteOrgCodeListMutation.test.ts index e2cf9a1576c..e9235b0d7d1 100644 --- a/frontend/packages/shared/src/hooks/mutations/useDeleteOrgCodeListMutation.test.ts +++ b/frontend/packages/shared/src/hooks/mutations/useDeleteOrgCodeListMutation.test.ts @@ -2,11 +2,11 @@ import { renderHookWithProviders } from '../../mocks/renderHookWithProviders'; import { org } from '@studio/testing/testids'; import { queriesMock } from '../../mocks/queriesMock'; import { useDeleteOrgCodeListMutation } from '../../hooks/mutations/useDeleteOrgCodeListMutation'; -import { createQueryClientMock } from 'app-shared/mocks/queryClientMock'; -import { QueryKey } from 'app-shared/types/QueryKey'; -import { useCreateOrgCodeListMutation } from 'app-shared/hooks/mutations/useCreateOrgCodeListMutation'; -import type { CodeListsResponse } from 'app-shared/types/api/CodeListsResponse'; -import type { CodeListData } from 'app-shared/types/CodeListData'; +import { createQueryClientMock } from '../../mocks/queryClientMock'; +import { QueryKey } from '../../types/QueryKey'; +import { useCreateOrgCodeListMutation } from '../../hooks/mutations/useCreateOrgCodeListMutation'; +import type { CodeListsResponse } from '../../types/api/CodeListsResponse'; +import type { CodeListData } from '../../types/CodeListData'; import type { CodeList } from '../../types/CodeList'; // Test data: diff --git a/frontend/packages/shared/src/hooks/mutations/useDeleteOrgCodeListMutation.ts b/frontend/packages/shared/src/hooks/mutations/useDeleteOrgCodeListMutation.ts index 7d5ce451031..2830c3dcea4 100644 --- a/frontend/packages/shared/src/hooks/mutations/useDeleteOrgCodeListMutation.ts +++ b/frontend/packages/shared/src/hooks/mutations/useDeleteOrgCodeListMutation.ts @@ -1,8 +1,8 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useServicesContext } from '../../contexts/ServicesContext'; import { QueryKey } from '../../types/QueryKey'; -import type { CodeListsResponse } from 'app-shared/types/api/CodeListsResponse'; -import type { CodeListData } from 'app-shared/types/CodeListData'; +import type { CodeListsResponse } from '../../types/api/CodeListsResponse'; +import type { CodeListData } from '../../types/CodeListData'; type mutationArgs = Pick; diff --git a/frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.test.ts b/frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.test.ts index 1de604c8044..03fe3db4109 100644 --- a/frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.test.ts +++ b/frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.test.ts @@ -3,10 +3,10 @@ import { org } from '@studio/testing/testids'; import { queriesMock } from '../../mocks/queriesMock'; import type { CodeList } from '../../types/CodeList'; import { useUpdateOrgCodeListMutation } from './useUpdateOrgCodeListMutation'; -import { createQueryClientMock } from 'app-shared/mocks/queryClientMock'; -import { QueryKey } from 'app-shared/types/QueryKey'; -import type { CodeListsResponse } from 'app-shared/types/api/CodeListsResponse'; -import type { CodeListData } from 'app-shared/types/CodeListData'; +import { createQueryClientMock } from '../../mocks/queryClientMock'; +import { QueryKey } from '../../types/QueryKey'; +import type { CodeListsResponse } from '../../types/api/CodeListsResponse'; +import type { CodeListData } from '../../types/CodeListData'; // Test data: const codeList: CodeList = [ diff --git a/frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.ts b/frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.ts index 602b1ec5381..f96e91849fc 100644 --- a/frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.ts +++ b/frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.ts @@ -2,7 +2,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useServicesContext } from '../../contexts/ServicesContext'; import { QueryKey } from '../../types/QueryKey'; import type { CodeListData } from '../../types/CodeListData'; -import type { CodeListsResponse } from 'app-shared/types/api/CodeListsResponse'; +import type { CodeListsResponse } from '../../types/api/CodeListsResponse'; type mutationArgs = Pick; diff --git a/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.test.ts b/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.test.ts index fb2a7a8bcb4..03badabc8d5 100644 --- a/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.test.ts +++ b/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.test.ts @@ -2,10 +2,10 @@ import { renderHookWithProviders } from '../../mocks/renderHookWithProviders'; import { org } from '@studio/testing/testids'; import { queriesMock } from '../../mocks/queriesMock'; import { useUploadOrgCodeListMutation } from './useUploadOrgCodeListMutation'; -import { createQueryClientMock } from 'app-shared/mocks/queryClientMock'; -import { QueryKey } from 'app-shared/types/QueryKey'; -import type { CodeListsResponse } from 'app-shared/types/api/CodeListsResponse'; -import type { CodeList } from 'app-shared/types/CodeList'; +import { createQueryClientMock } from '../../mocks/queryClientMock'; +import { QueryKey } from '../../types/QueryKey'; +import type { CodeListsResponse } from '../../types/api/CodeListsResponse'; +import type { CodeList } from '../../types/CodeList'; import { FileUtils } from '@studio/pure-functions'; // Test data: diff --git a/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.ts b/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.ts index 171ccabe694..4333880bb50 100644 --- a/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.ts +++ b/frontend/packages/shared/src/hooks/mutations/useUploadOrgCodeListMutation.ts @@ -1,7 +1,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useServicesContext } from '../../contexts/ServicesContext'; import { QueryKey } from '../../types/QueryKey'; -import type { CodeListsResponse } from 'app-shared/types/api/CodeListsResponse'; +import type { CodeListsResponse } from '../../types/api/CodeListsResponse'; import { FileUtils } from '@studio/pure-functions'; export const useUploadOrgCodeListMutation = (org: string) => { diff --git a/frontend/packages/shared/src/hooks/queries/useOrgCodeListsQuery.test.ts b/frontend/packages/shared/src/hooks/queries/useOrgCodeListsQuery.test.ts index fdc6dd29d5a..5e1324de43f 100644 --- a/frontend/packages/shared/src/hooks/queries/useOrgCodeListsQuery.test.ts +++ b/frontend/packages/shared/src/hooks/queries/useOrgCodeListsQuery.test.ts @@ -1,8 +1,8 @@ import { waitFor } from '@testing-library/react'; -import { queriesMock } from 'app-shared/mocks/queriesMock'; -import { renderHookWithProviders } from 'app-shared/mocks/renderHookWithProviders'; +import { queriesMock } from '../../mocks/queriesMock'; +import { renderHookWithProviders } from '../../mocks/renderHookWithProviders'; import { org } from '@studio/testing/testids'; -import { useOrgCodeListsQuery } from 'app-shared/hooks/queries/useOrgCodeListsQuery'; +import { useOrgCodeListsQuery } from '../../hooks/queries/useOrgCodeListsQuery'; describe('useOrgCodeListsQuery', () => { it('calls getCodeListsForOrg with the correct parameters', () => { From f4b656909a21ce322ee7b0f538001a21848960b4 Mon Sep 17 00:00:00 2001 From: Erling Hauan Date: Thu, 6 Feb 2025 09:31:39 +0100 Subject: [PATCH 14/14] fix PR comments --- .../hooks/mutations/useCreateOrgCodeListMutation.test.ts | 4 ++-- .../src/hooks/mutations/useCreateOrgCodeListMutation.ts | 5 +++-- .../hooks/mutations/useDeleteOrgCodeListMutation.test.ts | 6 +++--- .../src/hooks/mutations/useDeleteOrgCodeListMutation.ts | 4 ++-- .../hooks/mutations/useUpdateOrgCodeListMutation.test.ts | 4 ++-- .../src/hooks/mutations/useUpdateOrgCodeListMutation.ts | 5 +++-- 6 files changed, 15 insertions(+), 13 deletions(-) diff --git a/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.test.ts b/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.test.ts index 40019ba8897..42b40bdd9b4 100644 --- a/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.test.ts +++ b/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.test.ts @@ -32,7 +32,7 @@ describe('useCreateOrgCodeListMutation', () => { it('Calls createCodeListForOrg with correct parameters', async () => { const { result } = renderHookWithProviders(() => useCreateOrgCodeListMutation(org)); - await result.current.mutateAsync({ ...newCodeList }); + await result.current.mutateAsync(newCodeList); expect(queriesMock.createCodeListForOrg).toHaveBeenCalledTimes(1); expect(queriesMock.createCodeListForOrg).toHaveBeenCalledWith( @@ -51,7 +51,7 @@ describe('useCreateOrgCodeListMutation', () => { queries: { createCodeListForOrg }, }); - await result.current.mutateAsync({ ...newCodeList }); + await result.current.mutateAsync(newCodeList); const expectedUpdatedData: CodeListsResponse = [existingCodeList, newCodeList]; const updatedData = queryClient.getQueryData([QueryKey.OrgCodeLists, org]); diff --git a/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.ts b/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.ts index 0481bd74c31..58753abcbb5 100644 --- a/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.ts +++ b/frontend/packages/shared/src/hooks/mutations/useCreateOrgCodeListMutation.ts @@ -4,13 +4,14 @@ import { QueryKey } from '../../types/QueryKey'; import type { CodeListsResponse } from '../../types/api/CodeListsResponse'; import type { CodeListData } from '../../types/CodeListData'; -type mutationArgs = Pick; +type CreateOrgCodeListMutationArgs = Pick; export const useCreateOrgCodeListMutation = (org: string) => { const queryClient = useQueryClient(); const { createCodeListForOrg } = useServicesContext(); - const mutationFn = ({ title, data }: mutationArgs) => createCodeListForOrg(org, title, data); + const mutationFn = ({ title, data }: CreateOrgCodeListMutationArgs) => + createCodeListForOrg(org, title, data); return useMutation({ mutationFn, diff --git a/frontend/packages/shared/src/hooks/mutations/useDeleteOrgCodeListMutation.test.ts b/frontend/packages/shared/src/hooks/mutations/useDeleteOrgCodeListMutation.test.ts index e9235b0d7d1..fda7bebd164 100644 --- a/frontend/packages/shared/src/hooks/mutations/useDeleteOrgCodeListMutation.test.ts +++ b/frontend/packages/shared/src/hooks/mutations/useDeleteOrgCodeListMutation.test.ts @@ -19,7 +19,7 @@ const codeList: CodeList = [ }, ]; -const deletedCodeList: CodeListData = { +const codeListToDelete: CodeListData = { title: 'deleted-title', data: codeList, }; @@ -41,14 +41,14 @@ describe('useDeleteOrgCodeListMutation', () => { it('Replaces cache with api response', async () => { const queryClient = createQueryClientMock(); - queryClient.setQueryData([QueryKey.OrgCodeLists, org], [deletedCodeList, otherCodeList]); + queryClient.setQueryData([QueryKey.OrgCodeLists, org], [codeListToDelete, otherCodeList]); const createCodeListForOrg = jest.fn(() => Promise.resolve([otherCodeList])); const { result } = renderHookWithProviders(() => useCreateOrgCodeListMutation(org), { queryClient, queries: { createCodeListForOrg }, }); - await result.current.mutateAsync({ title: deletedCodeList.title }); + await result.current.mutateAsync({ title: codeListToDelete.title }); const expectedUpdatedData: CodeListsResponse = [otherCodeList]; const updatedData = queryClient.getQueryData([QueryKey.OrgCodeLists, org]); diff --git a/frontend/packages/shared/src/hooks/mutations/useDeleteOrgCodeListMutation.ts b/frontend/packages/shared/src/hooks/mutations/useDeleteOrgCodeListMutation.ts index 2830c3dcea4..95e36a13b8e 100644 --- a/frontend/packages/shared/src/hooks/mutations/useDeleteOrgCodeListMutation.ts +++ b/frontend/packages/shared/src/hooks/mutations/useDeleteOrgCodeListMutation.ts @@ -4,13 +4,13 @@ import { QueryKey } from '../../types/QueryKey'; import type { CodeListsResponse } from '../../types/api/CodeListsResponse'; import type { CodeListData } from '../../types/CodeListData'; -type mutationArgs = Pick; +type DeleteOrgCodeListMutationArgs = Pick; export const useDeleteOrgCodeListMutation = (org: string) => { const queryClient = useQueryClient(); const { deleteCodeListForOrg } = useServicesContext(); - const mutationFn = ({ title }: mutationArgs) => deleteCodeListForOrg(org, title); + const mutationFn = ({ title }: DeleteOrgCodeListMutationArgs) => deleteCodeListForOrg(org, title); return useMutation({ mutationFn, diff --git a/frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.test.ts b/frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.test.ts index 03fe3db4109..9e89cd5d4d4 100644 --- a/frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.test.ts +++ b/frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.test.ts @@ -31,7 +31,7 @@ describe('useUpdateOrgCodeListMutation', () => { it('Calls updateCodeListForOrg with correct parameters', async () => { const { result } = renderHookWithProviders(() => useUpdateOrgCodeListMutation(org)); - await result.current.mutateAsync({ ...updatedCodeList }); + await result.current.mutateAsync(updatedCodeList); expect(queriesMock.updateCodeListForOrg).toHaveBeenCalledTimes(1); expect(queriesMock.updateCodeListForOrg).toHaveBeenCalledWith( org, @@ -49,7 +49,7 @@ describe('useUpdateOrgCodeListMutation', () => { queries: { updateCodeListForOrg }, }); - await result.current.mutateAsync({ ...updatedCodeList }); + await result.current.mutateAsync(updatedCodeList); const expectedUpdatedData: CodeListsResponse = [updatedCodeList]; const updatedData = queryClient.getQueryData([QueryKey.OrgCodeLists, org]); diff --git a/frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.ts b/frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.ts index f96e91849fc..5c397a0b356 100644 --- a/frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.ts +++ b/frontend/packages/shared/src/hooks/mutations/useUpdateOrgCodeListMutation.ts @@ -4,13 +4,14 @@ import { QueryKey } from '../../types/QueryKey'; import type { CodeListData } from '../../types/CodeListData'; import type { CodeListsResponse } from '../../types/api/CodeListsResponse'; -type mutationArgs = Pick; +type UpdateOrgCodeListMutationArgs = Pick; export const useUpdateOrgCodeListMutation = (org: string) => { const queryClient = useQueryClient(); const { updateCodeListForOrg } = useServicesContext(); - const mutationFn = ({ title, data }: mutationArgs) => updateCodeListForOrg(org, title, data); + const mutationFn = ({ title, data }: UpdateOrgCodeListMutationArgs) => + updateCodeListForOrg(org, title, data); return useMutation({ mutationFn,