Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rewriting data model (schema + data) fetching to Tanstack Query #1425

Merged
merged 8 commits into from
Sep 4, 2023
Merged
2 changes: 1 addition & 1 deletion schemas/json/text-resources/text-resources.schema.v1.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
"type": "string",
"title": "Variable lookup location",
"description": "Location of the variable in the data source",
"pattern": "^[\\w\\p{L}.\\-_{}[\\]]+$"
"pattern": "^[\\w\\p{L}.\\-_{}\\[\\]]+$"
},
"dataSource": {
"title": "Variable data source",
Expand Down
13 changes: 9 additions & 4 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import { UnknownError } from 'src/features/instantiate/containers/UnknownError';
import { QueueActions } from 'src/features/queue/queueSlice';
import { useApplicationMetadataQuery } from 'src/hooks/queries/useApplicationMetadataQuery';
import { useApplicationSettingsQuery } from 'src/hooks/queries/useApplicationSettingsQuery';
import { useCurrentDataModelSchemaQuery } from 'src/hooks/queries/useCurrentDataModelSchemaQuery';
import { useFooterLayoutQuery } from 'src/hooks/queries/useFooterLayoutQuery';
import { useFormDataQuery } from 'src/hooks/queries/useFormDataQuery';
import { useCurrentPartyQuery } from 'src/hooks/queries/useGetCurrentPartyQuery';
import { usePartiesQuery } from 'src/hooks/queries/useGetPartiesQuery';
import { useLayoutSetsQuery } from 'src/hooks/queries/useLayoutSetsQuery';
Expand All @@ -31,6 +33,7 @@ export const App = () => {
const { isError: hasLayoutSetError } = useLayoutSetsQuery();
const { isError: hasOrgsError } = useOrgsQuery();
useFooterLayoutQuery(!!applicationMetadata?.features?.footer);
useCurrentDataModelSchemaQuery();

const componentIsReady = applicationSettings && applicationMetadata;
const componentHasError =
Expand Down Expand Up @@ -59,10 +62,10 @@ type AppInternalProps = {

const AppInternal = ({ applicationSettings }: AppInternalProps): JSX.Element | null => {
const allowAnonymousSelector = makeGetAllowAnonymousSelector();
const allowAnonymous: boolean = useAppSelector(allowAnonymousSelector);
const allowAnonymous = useAppSelector(allowAnonymousSelector);

const { isError: hasProfileError } = useProfileQuery(allowAnonymous === false);
const { isError: hasPartiesError } = usePartiesQuery(allowAnonymous === false);
const { isError: hasProfileError, isFetching: isProfileFetching } = useProfileQuery(allowAnonymous === false);
const { isError: hasPartiesError, isFetching: isPartiesFetching } = usePartiesQuery(allowAnonymous === false);

const alwaysPromptForParty = useAlwaysPromptForParty();

Expand All @@ -75,8 +78,10 @@ const AppInternal = ({ applicationSettings }: AppInternalProps): JSX.Element | n

useKeepAlive(applicationSettings.appOidcProvider, allowAnonymous);
useUpdatePdfState(allowAnonymous);
const { isFetching: isFormDataFetching } = useFormDataQuery();

const hasComponentError = hasProfileError || hasCurrentPartyError || hasPartiesError;
const isFetching = isProfileFetching || isPartiesFetching || isFormDataFetching;

// Set the title of the app
React.useEffect(() => {
Expand Down Expand Up @@ -104,7 +109,7 @@ const AppInternal = ({ applicationSettings }: AppInternalProps): JSX.Element | n
/>
<Route
path='/instance/:partyId/:instanceGuid'
element={<ProcessWrapper />}
element={<ProcessWrapper isFetching={isFetching} />}
/>
</Routes>
</>
Expand Down
8 changes: 6 additions & 2 deletions src/components/wrappers/ProcessWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@ import { ProcessTaskType } from 'src/types';
import { behavesLikeDataTask } from 'src/utils/formLayout';
import { checkIfAxiosError, HttpStatusCodes } from 'src/utils/network/networking';

export const ProcessWrapper = () => {
export interface IProcessWrapperProps {
isFetching?: boolean;
}

export const ProcessWrapper = ({ isFetching }: IProcessWrapperProps) => {
const instantiating = useAppSelector((state) => state.instantiation.instantiating);
const isLoading = useAppSelector((state) => state.isLoading.dataTask);
const layoutSets = useAppSelector((state) => state.formLayout.layoutsets);
Expand Down Expand Up @@ -87,7 +91,7 @@ export const ProcessWrapper = () => {
appOwner={appOwner}
type={taskType}
>
{isLoading === false ? (
{isLoading === false && isFetching !== true ? (
<>
{taskType === ProcessTaskType.Data || behavesLikeDataTask(process.taskId, layoutSets) ? (
<Form />
Expand Down
8 changes: 2 additions & 6 deletions src/features/datamodel/datamodelSlice.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { watchFetchJsonSchemaSaga } from 'src/features/datamodel/fetchFormDatamodelSagas';
import { createSagaSlice } from 'src/redux/sagaSlice';
import type {
IDataModelState,
Expand All @@ -18,16 +17,13 @@ export const formDataModelSlice = () => {
name: 'formDataModel',
initialState,
actions: {
fetchJsonSchema: mkAction<void>({
saga: () => watchFetchJsonSchemaSaga,
}),
fetchJsonSchemaFulfilled: mkAction<IFetchJsonSchemaFulfilled>({
fetchFulfilled: mkAction<IFetchJsonSchemaFulfilled>({
reducer: (state, action) => {
const { schema, id } = action.payload;
state.schemas[id] = schema;
},
}),
fetchJsonSchemaRejected: mkAction<IFetchJsonSchemaRejected>({
fetchRejected: mkAction<IFetchJsonSchemaRejected>({
reducer: (state, action) => {
const { error } = action.payload;
state.error = error;
Expand Down
66 changes: 0 additions & 66 deletions src/features/datamodel/fetchFormDatamodelSagas.ts

This file was deleted.

8 changes: 5 additions & 3 deletions src/features/datamodel/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import type { JSONSchema7 } from 'json-schema';

export interface IJsonSchemas {
[id: string]: object;
[id: string]: JSONSchema7;
}

export interface IDataModelState {
Expand All @@ -8,10 +10,10 @@ export interface IDataModelState {
}

export interface IFetchJsonSchemaFulfilled {
schema: object;
schema: JSONSchema7;
id: string;
}

export interface IFetchJsonSchemaRejected {
error: Error;
error: Error | null;
}
72 changes: 42 additions & 30 deletions src/features/entrypoint/Entrypoint.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type { AxiosError } from 'axios';
import { getInitialStateMock } from 'src/__mocks__/initialStateMock';
import { Entrypoint } from 'src/features/entrypoint/Entrypoint';
import { renderWithProviders } from 'src/testUtils';
import type { AppQueriesContext } from 'src/contexts/appQueriesContext';
import type { IApplicationMetadata } from 'src/features/applicationMetadata';
import type { IRuntimeState } from 'src/types';
import type { IApplicationLogic } from 'src/types/shared';
Expand Down Expand Up @@ -35,11 +36,12 @@ describe('Entrypoint', () => {
});

test('should show invalid party error if user has no valid parties', async () => {
const doPartyValidation = () => Promise.resolve({ data: { valid: false, validParties: [], message: '' } });
const queries = {
doPartyValidation,
};
render({ store: mockStore, queries });
render({
store: mockStore,
queries: {
doPartyValidation: () => Promise.resolve({ valid: false, validParties: [], message: '' }),
},
});

await waitForElementToBeRemoved(screen.queryByText('Vent litt, vi henter det du trenger'));

Expand All @@ -60,10 +62,6 @@ describe('Entrypoint', () => {
});

test('should show loader while fetching data then start statelessQueue if stateless app', async () => {
const doPartyValidation = () => Promise.resolve({ data: { valid: true, validParties: [], message: '' } });
const queries = {
doPartyValidation,
};
const statelessApplication: IApplicationMetadata = {
...(mockInitialState.applicationMetadata.applicationMetadata as IApplicationMetadata),
onEntry: {
Expand All @@ -77,7 +75,13 @@ describe('Entrypoint', () => {
mockStore = createStore(mockReducer, mockStateWithStatelessApplication);
mockStore.dispatch = jest.fn();

render({ store: mockStore, queries, allowAnonymous: false });
render({
store: mockStore,
queries: {
doPartyValidation: () => Promise.resolve({ valid: true, validParties: [], message: '' }),
},
allowAnonymous: false,
});
const contentLoader = await screen.findByText('Loading...');
expect(contentLoader).not.toBeNull();

Expand Down Expand Up @@ -131,25 +135,25 @@ describe('Entrypoint', () => {
mockStore = createStore(mockReducer, mockStateWithStatelessApplication);
mockStore.dispatch = jest.fn();

const doPartyValidation = () => Promise.resolve({ data: { valid: true, validParties: [], message: '' } });

const queries = {
doPartyValidation,
fetchActiveInstances: () =>
Promise.resolve([
{
id: 'some-id-1',
lastChanged: '28-01-1992',
lastChangedBy: 'Navn Navnesen',
},
{
id: 'some-id-2',
lastChanged: '06-03-1974',
lastChangedBy: 'Test Testesen',
},
]),
};
render({ store: mockStore, queries });
render({
store: mockStore,
queries: {
doPartyValidation: () => Promise.resolve({ valid: true, validParties: [], message: '' }),
fetchActiveInstances: () =>
Promise.resolve([
{
id: 'some-id-1',
lastChanged: '28-01-1992',
lastChangedBy: 'Navn Navnesen',
},
{
id: 'some-id-2',
lastChanged: '06-03-1974',
lastChangedBy: 'Test Testesen',
},
]),
},
});

await waitFor(async () => {
const selectInstanceText = await screen.findByText('Du har allerede startet å fylle ut dette skjemaet.');
Expand All @@ -172,7 +176,15 @@ describe('Entrypoint', () => {
expect(missingRolesText).not.toBeNull();
});

function render({ store, allowAnonymous = false, queries }: { store: any; allowAnonymous?: boolean; queries?: any }) {
function render({
store,
allowAnonymous = false,
queries,
}: {
store: any;
allowAnonymous?: boolean;
queries?: Partial<AppQueriesContext>;
}) {
return renderWithProviders(
<MemoryRouter>
<Entrypoint allowAnonymous={allowAnonymous} />
Expand Down
Loading