Skip to content

Commit

Permalink
Fixing race condition preventing language from loading (#480)
Browse files Browse the repository at this point in the history
Co-authored-by: Ole Martin Handeland <[email protected]>
  • Loading branch information
olemartinorg and Ole Martin Handeland authored Sep 23, 2022
1 parent b7ceea6 commit 0bdb869
Show file tree
Hide file tree
Showing 6 changed files with 44 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,10 @@ import {
} from 'src/utils/appUrlHelper';
import { convertModelToDataBinding } from 'src/utils/databindings';
import { putWithoutConfig } from 'src/utils/networking';
import { waitFor } from 'src/utils/sagas';
import type { IApplicationMetadata } from 'src/shared/resources/applicationMetadata';
import type { IProcessState } from 'src/shared/resources/process';
import type { ILayoutSets, IRuntimeState } from 'src/types';
import type { ILayoutSets } from 'src/types';

import { get } from 'altinn-shared/utils';
import type { IInstance } from 'altinn-shared/types';
Expand Down Expand Up @@ -135,18 +136,6 @@ function* fetchFormDataStateless(applicationMetadata: IApplicationMetadata) {
}
}

function* waitFor(selector) {
if (yield select(selector)) {
return;
}
while (true) {
yield take('*');
if (yield select(selector)) {
return;
}
}
}

export function* watchFetchFormDataInitialSaga(): SagaIterator {
while (true) {
yield take(FormDataActions.fetchInitial);
Expand All @@ -157,10 +146,8 @@ export function* watchFetchFormDataInitialSaga(): SagaIterator {
yield take(DataModelActions.fetchJsonSchemaFulfilled);
const allowAnonymous = yield select(makeGetAllowAnonymousSelector());
if (!allowAnonymous) {
call(
waitFor,
(state: IRuntimeState) =>
currentSelectedPartyIdSelector(state) !== undefined,
yield waitFor(
(state) => currentSelectedPartyIdSelector(state) !== undefined,
);
}
} else if (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
watchFetchLanguageSaga,
} from 'src/shared/resources/language/fetch/fetchLanguageSagas';
import { LanguageActions } from 'src/shared/resources/language/languageSlice';
import { ProfileActions } from 'src/shared/resources/profile/profileSlice';
import { waitForFunc } from 'src/utils/sagas';

import { getLanguageFromCode } from 'altinn-shared/language';
import * as language from 'altinn-shared/language';
Expand Down Expand Up @@ -60,7 +60,9 @@ describe('fetchLanguageSagas', () => {
expect(generator.next().value).toEqual(
select(makeGetAllowAnonymousSelector()),
);
expect(generator.next().value).toEqual(take(ProfileActions.fetchFulfilled));
expect(generator.next().value).toEqual(
call(waitForFunc, expect.anything()),
);
expect(generator.next().value).toEqual(call(fetchLanguageSaga));
expect(generator.next().value).toEqual(
takeLatest(LanguageActions.updateSelectedAppLanguage, fetchLanguageSaga),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { appLanguageStateSelector } from 'src/selectors/appLanguageStateSelector
import { makeGetAllowAnonymousSelector } from 'src/selectors/getAllowAnonymous';
import { ApplicationMetadataActions } from 'src/shared/resources/applicationMetadata/applicationMetadataSlice';
import { LanguageActions } from 'src/shared/resources/language/languageSlice';
import { ProfileActions } from 'src/shared/resources/profile/profileSlice';
import { QueueActions } from 'src/shared/resources/queue/queueSlice';
import { waitFor } from 'src/utils/sagas';

import { getLanguageFromCode } from 'altinn-shared/language';

Expand All @@ -32,7 +32,7 @@ export function* watchFetchLanguageSaga(): SagaIterator {

const allowAnonymous = yield select(makeGetAllowAnonymousSelector());
if (!allowAnonymous) {
yield take(ProfileActions.fetchFulfilled);
yield waitFor((state) => !!state.profile.profile);
}

yield call(fetchLanguageSaga);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ import { makeGetAllowAnonymousSelector } from 'src/selectors/getAllowAnonymous';
import { profileStateSelector } from 'src/selectors/simpleSelectors';
import { ApplicationMetadataActions } from 'src/shared/resources/applicationMetadata/applicationMetadataSlice';
import { LanguageActions } from 'src/shared/resources/language/languageSlice';
import { ProfileActions } from 'src/shared/resources/profile/profileSlice';
import {
fetchTextResources,
watchFetchTextResourcesSaga,
} from 'src/shared/resources/textResources/fetch/fetchTextResourcesSagas';
import { TextResourcesActions } from 'src/shared/resources/textResources/textResourcesSlice';
import { textResourcesUrl } from 'src/utils/appUrlHelper';
import { get } from 'src/utils/networking';
import { waitForFunc } from 'src/utils/sagas';

import type { IProfile } from 'altinn-shared/types';

Expand All @@ -31,7 +31,9 @@ describe('fetchTextResourcesSagas', () => {
expect(generator.next().value).toEqual(
select(makeGetAllowAnonymousSelector()),
);
expect(generator.next().value).toEqual(take(ProfileActions.fetchFulfilled));
expect(generator.next().value).toEqual(
call(waitForFunc, expect.anything()),
);
expect(generator.next().value).toEqual(call(fetchTextResources));
expect(generator.next().value).toEqual(
takeLatest(TextResourcesActions.fetch, fetchTextResources),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import { appLanguageStateSelector } from 'src/selectors/appLanguageStateSelector
import { makeGetAllowAnonymousSelector } from 'src/selectors/getAllowAnonymous';
import { ApplicationMetadataActions } from 'src/shared/resources/applicationMetadata/applicationMetadataSlice';
import { LanguageActions } from 'src/shared/resources/language/languageSlice';
import { ProfileActions } from 'src/shared/resources/profile/profileSlice';
import { QueueActions } from 'src/shared/resources/queue/queueSlice';
import { TextResourcesActions } from 'src/shared/resources/textResources/textResourcesSlice';
import { oldTextResourcesUrl, textResourcesUrl } from 'src/utils/appUrlHelper';
import { get } from 'src/utils/networking';
import { waitFor } from 'src/utils/sagas';

export function* fetchTextResources(): SagaIterator {
try {
Expand Down Expand Up @@ -49,10 +49,10 @@ export function* watchFetchTextResourcesSaga(): SagaIterator {
]);

const allowAnonymous = yield select(makeGetAllowAnonymousSelector());

if (!allowAnonymous) {
yield take(ProfileActions.fetchFulfilled);
yield waitFor((state) => !!state.profile.profile);
}

yield call(fetchTextResources);
yield takeLatest(TextResourcesActions.fetch, fetchTextResources);
yield takeLatest(
Expand Down
27 changes: 27 additions & 0 deletions src/altinn-app-frontend/src/utils/sagas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { call, select, take } from 'redux-saga/effects';
import type { SagaIterator } from 'redux-saga';

import type { IRuntimeState } from 'src/types';

export function* waitForFunc(
selector: (state: IRuntimeState) => boolean,
): SagaIterator {
if (yield select(selector)) {
return;
}
while (true) {
yield take('*');
if (yield select(selector)) {
return;
}
}
}

/**
* This saga effect allows you to wait for a specific state to change. It will wait for new actions to be dispatched
* until your selector returns true, and it might be a safer way to wait than running yield take(...) on the action
* you wanted to wait for (as this will return immediately if the state already is as expected, instead of waiting
* for the event in question).
*/
export const waitFor = (selector: (state: IRuntimeState) => boolean) =>
call(waitForFunc, selector);

0 comments on commit 0bdb869

Please sign in to comment.