Skip to content

Commit

Permalink
feat: show insufficient loa error on download data HP-2268
Browse files Browse the repository at this point in the history
  • Loading branch information
mikkojamG committed Apr 18, 2024
1 parent 928628a commit afb4452
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 20 deletions.
33 changes: 31 additions & 2 deletions src/gdprApi/actions/__tests__/getDownloadData.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { waitFor } from '@testing-library/react';

import {
getDownloadDataAction,
getDownloadDataResult,
getDownloadDataResultOrError,
isInsufficientLoaResult,
} from '../getDownloadData';
import { createActionQueueRunner } from '../../../common/actionQueue/actionQueueRunner';
import { Action, getOption } from '../../../common/actionQueue/actionQueue';
Expand All @@ -13,6 +14,8 @@ import {
} from '../authCodeParser';
import { getMockCalls } from '../../../common/test/mockHelper';

type ActionResults = ReturnType<typeof getDownloadDataResultOrError>;

describe('getDownloadData.ts', () => {
const queryTracker = vi.fn();
const successfulResponse = { variable1: 'variable1' };
Expand All @@ -22,11 +25,13 @@ describe('getDownloadData.ts', () => {
noKeycloadAuthCode,
returnNoData,
returnError,
returnInsufficientLoa,
}: {
noKeycloadAuthCode?: boolean;
noTunnistamoAuthCode?: boolean;
returnNoData?: boolean;
returnError?: boolean;
returnInsufficientLoa?: boolean;
} = {}) => {
fetchMock.mockIf(/.*\/graphql\/.*$/, async (req: Request) => {
const payload = await req.json();
Expand All @@ -44,6 +49,13 @@ describe('getDownloadData.ts', () => {
}),
});
}
if (returnInsufficientLoa === true) {
return Promise.reject({
body: JSON.stringify({
message: 'insufficientLoa',
}),
});
}
return Promise.resolve({ body: JSON.stringify(response) });
});
const queue = [
Expand Down Expand Up @@ -116,6 +128,21 @@ describe('getDownloadData.ts', () => {
const [error] = await to(getAction().executor(getAction(), runner));
expect(error).toBeDefined();
});
it('Insufficient loa returns error', async () => {
const { runner, getAction } = initTests({
returnInsufficientLoa: true,
returnNoData: true,
});
const [errorMessage] = await to(
getAction().executor(getAction(), runner)
);

expect(
isInsufficientLoaResult(({
errorMessage,
} as unknown) as ActionResults)
).toBeTruthy();
});
it('Result should not be stored to sessionStorage', async () => {
const { getAction } = initTests();
expect(getOption(getAction(), 'noStorage')).toBeTruthy();
Expand All @@ -126,7 +153,9 @@ describe('getDownloadData.ts', () => {
await waitFor(() => {
expect(runner.isFinished()).toBeTruthy();
});
expect(getDownloadDataResult(runner)).toMatchObject(successfulResponse);
expect(getDownloadDataResultOrError(runner).result).toMatchObject(
successfulResponse
);
});
});
});
4 changes: 2 additions & 2 deletions src/gdprApi/actions/downloadAsFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ import {
ActionExecutor,
ActionProps,
} from '../../common/actionQueue/actionQueue';
import { getDownloadDataResult } from './getDownloadData';
import { getDownloadDataResultOrError } from './getDownloadData';

const downloadAsFile = 'downloadAsFile';

const downloadAsFileExecutor: ActionExecutor = async (
action,
queueController
) => {
const data = getDownloadDataResult(queueController);
const data = getDownloadDataResultOrError(queueController).result;
if (!data) {
return Promise.reject('No profile data');
} else {
Expand Down
26 changes: 23 additions & 3 deletions src/gdprApi/actions/getDownloadData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,27 @@ import {
} from './authCodeParser';
import reportErrorsToSentry from '../../common/sentry/reportErrorsToSentry';
import DOWNLOAD_MY_PROFILE from '../../profile/graphql/DownloadMyProfileQuery.graphql';
import parseGraphQLError from '../../profile/helpers/parseGraphQLError';

const downloadDataType = 'downloadData';

export const getDownloadDataResult = (queueController: QueueController) =>
getActionResultAndErrorMessage<unknown>(downloadDataType, queueController)
.result;
type DownloadDataResult = keyof typeof resultTypes;

const resultTypes = {
insufficientLoa: 'insufficientLoa',
} as const;

export const getDownloadDataResultOrError = (
queueController: QueueController
) =>
getActionResultAndErrorMessage<DownloadDataResult | unknown>(
downloadDataType,
queueController
);

export const isInsufficientLoaResult = (
resultOrError: ReturnType<typeof getDownloadDataResultOrError>
) => resultOrError.errorMessage === resultTypes.insufficientLoa;

const getDownloadDataExecutor: ActionExecutor = async (
action,
Expand Down Expand Up @@ -51,6 +66,11 @@ const getDownloadDataExecutor: ActionExecutor = async (
);
if (error) {
reportErrorsToSentry(error);

if (parseGraphQLError(error).isInsufficientLoaError) {
return Promise.reject(resultTypes.insufficientLoa);
}

return Promise.reject(error);
}
if (!result || !result.data) {
Expand Down
4 changes: 2 additions & 2 deletions src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@
"connectionRemovalVerificationButtonText": "Delete data",
"connectionRemovalError": "For some reason, the deletion was not successful. Please, try again later!",
"contactServiceToDelete": "If you still want to delete the data, please contact <linkToExternalServiceList >the service</linkToExternalServiceList> directly and then retry deleting the data.",
"explanationforLightAuthentication": "You can only delete data from this service if you are strongly identified. Please log out and log in again using Suomi.fi-identification to delete your data. "
"explanationforLightAuthentication": "You can only delete data from this service if you are strongly identified. Please log out and log in again using Suomi.fi-identification to delete your data."
},
"skipToContent": "Skip to content",
"validation": {
Expand Down Expand Up @@ -282,4 +282,4 @@
"changeProfilePassword ": {
"explanationForStrongAuthentication": "You can only change your password if you are strongly identified. Log out and log in again using Suomi.fi-authentication to change your password."
}
}
}
4 changes: 2 additions & 2 deletions src/i18n/fi.json
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@
"connectionRemovalVerificationButtonText": "Poista tiedot",
"connectionRemovalError": "Jostain syystä poisto ei onnistunut. Yritä hetken kuluttua uudelleen!",
"contactServiceToDelete": "Jos haluat silti poistaa tiedot, ole suoraan yhteydessä <linkToExternalServiceList>kyseiseen palveluun</linkToExternalServiceList> ja yritä tietojen poistoa sen jälkeen uudestaan.",
"explanationforLightAuthentication": "Voit poistaa tietoja tästä palvelusta vain, jos olet vahvasti tunnistautunut. Kirjaudu ulos ja takaisin sisään käyttäen Suomi.fi-tunnistautumista tietojen poistamiseksi. "
"explanationforLightAuthentication": "Voit poistaa tietoja tästä palvelusta vain, jos olet vahvasti tunnistautunut. Kirjaudu ulos ja takaisin sisään käyttäen Suomi.fi-tunnistautumista tietojen poistamiseksi."
},
"skipToContent": "Siirry suoraan sisältöön",
"validation": {
Expand Down Expand Up @@ -282,4 +282,4 @@
"changeProfilePassword ": {
"explanationForStrongAuthentication": "Voit vaihtaa salasanan vain, jos olet vahvasti tunnistautunut. Kirjaudu ulos ja takaisin sisään käyttäen Suomi.fi-tunnistautumista vaihtaaksesi salasanasi."
}
}
}
44 changes: 35 additions & 9 deletions src/profile/components/downloadData/DownloadData.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,27 @@
import React, { useEffect } from 'react';
import React, { useCallback, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Button, Notification } from 'hds-react';

import commonFormStyles from '../../../common/cssHelpers/form.module.css';
import contentStyles from '../../../common/cssHelpers/content.module.css';
import ProfileSection from '../../../common/profileSection/ProfileSection';
import { useScrollIntoView } from '../../hooks/useScrollIntoView';
import useAuthCodeQueues from '../../../gdprApi/useAuthCodeQueues';
import useAuthCodeQueues, {
AuthCodeQueuesProps,
} from '../../../gdprApi/useAuthCodeQueues';
import config from '../../../config';
import { isInsufficientLoaResult } from '../../../gdprApi/actions/getDownloadData';

function DownloadData(): React.ReactElement {
const [errorMessage, setErrorMessage] = useState<string>();

const onError: AuthCodeQueuesProps['onError'] = useCallback(controller => {
const failed = controller.getFailed();
const message = (failed && failed.errorMessage) || 'unknown';

setErrorMessage(message);
}, []);

const {
startOrRestart,
canStart,
Expand All @@ -21,6 +33,7 @@ function DownloadData(): React.ReactElement {
} = useAuthCodeQueues({
startPagePath: config.downloadPath,
queueName: 'downloadProfile',
onError,
});
const canUserDoSomething = canStart() || shouldRestart();
const { t } = useTranslation();
Expand All @@ -41,13 +54,26 @@ function DownloadData(): React.ReactElement {
</div>
<div className={commonFormStyles['uneditable-box-content']}>
<div className={contentStyles['common-child-vertical-spacing']}>
{hasError && (
<Notification
label={t('notification.defaultErrorText')}
type={'error'}
dataTestId="download-profile-error"
></Notification>
)}
{hasError &&
(isInsufficientLoaResult({ errorMessage, result: undefined }) ? (
<Notification
size="small"
label={t('downloadData.extrapanelTextforLightAuthentication')}
type={'error'}
dataTestId="download-profile-insufficient-loa-error"
>
{t('downloadData.extrapanelTextforLightAuthentication')}
</Notification>
) : (
<Notification
size="small"
label=" "
type={'error'}
dataTestId="download-profile-error"
>
{t('notification.defaultErrorText')}
</Notification>
))}
<Button
onClick={onDownloadClick}
disabled={!canUserDoSomething}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ describe('<DownloadData /> ', () => {
testId: 'download-profile-error',
};

const insufficientLoaErrorNotification: ElementSelector = {
testId: 'download-profile-insufficient-loa-error',
};

afterEach(() => {
mockedWindowControls.reset();
cleanComponentMocks();
Expand Down Expand Up @@ -111,4 +115,24 @@ describe('<DownloadData /> ', () => {
await waitForElement(errorNotification);
});
});

it(`When insufficient loa, an error is shown.`, async () => {
initTestQueue(
getScenarioForScopes({
autoTrigger: true,
overrides: [
{
type: getGdprQueryScopesAction.type,
resolveValue: undefined,
rejectValue: new Error('insufficientLoa'),
},
],
})
);
await act(async () => {
const { clickElement, waitForElement } = await initTests();
await clickElement(submitButton);
await waitForElement(insufficientLoaErrorNotification);
});
});
});

0 comments on commit afb4452

Please sign in to comment.