diff --git a/src/gdprApi/actions/__tests__/getDownloadData.test.ts b/src/gdprApi/actions/__tests__/getDownloadData.test.ts index f59d3e1fc..10c43c35a 100644 --- a/src/gdprApi/actions/__tests__/getDownloadData.test.ts +++ b/src/gdprApi/actions/__tests__/getDownloadData.test.ts @@ -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'; @@ -13,6 +14,8 @@ import { } from '../authCodeParser'; import { getMockCalls } from '../../../common/test/mockHelper'; +type ActionResults = ReturnType; + describe('getDownloadData.ts', () => { const queryTracker = vi.fn(); const successfulResponse = { variable1: 'variable1' }; @@ -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(); @@ -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 = [ @@ -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(); @@ -126,7 +153,9 @@ describe('getDownloadData.ts', () => { await waitFor(() => { expect(runner.isFinished()).toBeTruthy(); }); - expect(getDownloadDataResult(runner)).toMatchObject(successfulResponse); + expect(getDownloadDataResultOrError(runner).result).toMatchObject( + successfulResponse + ); }); }); }); diff --git a/src/gdprApi/actions/downloadAsFile.ts b/src/gdprApi/actions/downloadAsFile.ts index e67fb55b3..53b1e260c 100644 --- a/src/gdprApi/actions/downloadAsFile.ts +++ b/src/gdprApi/actions/downloadAsFile.ts @@ -4,7 +4,7 @@ import { ActionExecutor, ActionProps, } from '../../common/actionQueue/actionQueue'; -import { getDownloadDataResult } from './getDownloadData'; +import { getDownloadDataResultOrError } from './getDownloadData'; const downloadAsFile = 'downloadAsFile'; @@ -12,7 +12,7 @@ const downloadAsFileExecutor: ActionExecutor = async ( action, queueController ) => { - const data = getDownloadDataResult(queueController); + const data = getDownloadDataResultOrError(queueController).result; if (!data) { return Promise.reject('No profile data'); } else { diff --git a/src/gdprApi/actions/getDownloadData.ts b/src/gdprApi/actions/getDownloadData.ts index 1cf5e2bea..22b19b426 100644 --- a/src/gdprApi/actions/getDownloadData.ts +++ b/src/gdprApi/actions/getDownloadData.ts @@ -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(downloadDataType, queueController) - .result; +type DownloadDataResult = keyof typeof resultTypes; + +const resultTypes = { + insufficientLoa: 'insufficientLoa', +} as const; + +export const getDownloadDataResultOrError = ( + queueController: QueueController +) => + getActionResultAndErrorMessage( + downloadDataType, + queueController + ); + +export const isInsufficientLoaResult = ( + resultOrError: ReturnType +) => resultOrError.errorMessage === resultTypes.insufficientLoa; const getDownloadDataExecutor: ActionExecutor = async ( action, @@ -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) { diff --git a/src/i18n/en.json b/src/i18n/en.json index 98f33a004..5831db1c0 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -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 the service 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": { @@ -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." } -} \ No newline at end of file +} diff --git a/src/i18n/fi.json b/src/i18n/fi.json index f76f75eda..ed5e0d1b6 100644 --- a/src/i18n/fi.json +++ b/src/i18n/fi.json @@ -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ä kyseiseen palveluun 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": { @@ -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." } -} \ No newline at end of file +} diff --git a/src/profile/components/downloadData/DownloadData.tsx b/src/profile/components/downloadData/DownloadData.tsx index a846d432e..8e541fd32 100644 --- a/src/profile/components/downloadData/DownloadData.tsx +++ b/src/profile/components/downloadData/DownloadData.tsx @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Button, Notification } from 'hds-react'; @@ -6,10 +6,22 @@ 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(); + + const onError: AuthCodeQueuesProps['onError'] = useCallback(controller => { + const failed = controller.getFailed(); + const message = (failed && failed.errorMessage) || 'unknown'; + + setErrorMessage(message); + }, []); + const { startOrRestart, canStart, @@ -21,6 +33,7 @@ function DownloadData(): React.ReactElement { } = useAuthCodeQueues({ startPagePath: config.downloadPath, queueName: 'downloadProfile', + onError, }); const canUserDoSomething = canStart() || shouldRestart(); const { t } = useTranslation(); @@ -41,13 +54,26 @@ function DownloadData(): React.ReactElement {
- {hasError && ( - - )} + {hasError && + (isInsufficientLoaResult({ errorMessage, result: undefined }) ? ( + + {t('downloadData.extrapanelTextforLightAuthentication')} + + ) : ( + + {t('notification.defaultErrorText')} + + ))}