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

HP-2268 GDPR error #337

Merged
merged 4 commits into from
Apr 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 28 additions & 2 deletions src/gdprApi/actions/__tests__/deleteProfile.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@ import { getMockCalls } from '../../../common/test/mockHelper';
import {
createDeleteProfileAction,
deleteProfileType,
getDeleteProfileResult,
getDeleteProfileResultOrError,
isInsufficientLoaResult,
} from '../deleteProfile';
import { getDeleteMyProfileMutationResult } from '../../../common/test/getDeleteMyProfileMutationResult';
import { DeleteResultLists } from '../../../profile/helpers/parseDeleteProfileResult';

type ActionResults = ReturnType<typeof getDeleteProfileResultOrError>;

describe('deleteProfile.ts', () => {
const queryTracker = vi.fn();
const keycloakAuthCode = 'keycloak-auth-code';
Expand All @@ -26,12 +29,14 @@ describe('deleteProfile.ts', () => {
returnFailed,
returnError,
returnNoData,
returnInsufficientLoa,
}: {
noKeycloadAuthCode?: boolean;
noTunnistamoAuthCode?: boolean;
returnFailed?: boolean;
returnError?: boolean;
returnNoData?: boolean;
returnInsufficientLoa?: boolean;
} = {}) => {
fetchMock.mockIf(/.*\/graphql\/.*$/, async (req: Request) => {
const payload = await req.json();
Expand All @@ -49,6 +54,13 @@ describe('deleteProfile.ts', () => {
}),
});
}
if (returnInsufficientLoa === true) {
return Promise.reject({
body: JSON.stringify({
message: 'insufficientLoa',
}),
});
}
return Promise.resolve({ body: JSON.stringify(response) });
});

Expand Down Expand Up @@ -152,6 +164,19 @@ describe('deleteProfile.ts', () => {
expect(result).toBeUndefined();
expect(!!error).toBeTruthy();
});
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 @@ -162,7 +187,8 @@ describe('deleteProfile.ts', () => {
await waitFor(() => {
expect(runner.isFinished()).toBeTruthy();
});
const resultArray = getDeleteProfileResult(runner) as DeleteResultLists;
const resultArray = getDeleteProfileResultOrError(runner)
.result as DeleteResultLists;
expect(resultArray.successful).toHaveLength(2);
expect(resultArray.failures).toHaveLength(0);
});
Expand Down
23 changes: 23 additions & 0 deletions src/gdprApi/actions/__tests__/deleteServiceConnection.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
deleteServiceConnectionType,
getDeleteServiceConnectionResultOrError,
isForbiddenResult,
isInsufficientLoaResult,
isSuccessResult,
} from '../deleteServiceConnection';

Expand All @@ -29,12 +30,14 @@ describe('deleteServiceConnection.ts', () => {
returnForbidden,
returnError,
returnNoData,
returnInsufficientLoa,
}: {
noKeycloadAuthCode?: boolean;
noTunnistamoAuthCode?: boolean;
returnForbidden?: boolean;
returnError?: boolean;
returnNoData?: boolean;
returnInsufficientLoa?: boolean;
} = {}) => {
fetchMock.mockIf(/.*\/graphql\/.*$/, async (req: Request) => {
const payload = await req.json();
Expand All @@ -56,6 +59,13 @@ describe('deleteServiceConnection.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 @@ -163,6 +173,19 @@ describe('deleteServiceConnection.ts', () => {
).toBeFalsy();
expect(isSuccessResult({ result } as ActionResults)).toBeFalsy();
});
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 Down
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
);
});
});
});
24 changes: 21 additions & 3 deletions src/gdprApi/actions/deleteProfile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,27 @@ import parseDeleteProfileResult, {
import { convertStringToTranslationLanguage } from '../../profile/helpers/createServiceConnectionsQueryVariables';
import reportErrorsToSentry from '../../common/sentry/reportErrorsToSentry';
import DELETE_PROFILE from '../graphql/GdprDeleteMyProfileMutation.graphql';
import parseGraphQLError from '../../profile/helpers/parseGraphQLError';

export const deleteProfileType = 'deleteProfile';

export const getDeleteProfileResult = (queueController: QueueController) =>
getActionResultAndErrorMessage<DeleteResultLists>(
type DeleteProfileResult = keyof typeof resultTypes;

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

export const getDeleteProfileResultOrError = (
queueController: QueueController
) =>
getActionResultAndErrorMessage<DeleteResultLists | DeleteProfileResult>(
deleteProfileType,
queueController
).result;
);

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

const deleteProfileExecutor: ActionExecutor = async (
action,
Expand Down Expand Up @@ -66,6 +79,11 @@ const deleteProfileExecutor: 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
12 changes: 12 additions & 0 deletions src/gdprApi/actions/deleteServiceConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
} from './authCodeParser';
import reportErrorsToSentry from '../../common/sentry/reportErrorsToSentry';
import DELETE_SERVICE_DATA from '../graphql/GdprDeleteServiceDataMutation.graphql';
import parseGraphQLError from '../../profile/helpers/parseGraphQLError';

export const deleteServiceConnectionType = 'deleteServiceConnection';

Expand All @@ -30,6 +31,7 @@ const resultTypes = {
forbidden: 'forbidden',
queryError: 'queryError',
noAuthCodes: 'noAuthCodes',
insufficientLoa: 'insufficientLoa',
} as const;

export const getDeleteServiceConnectionResultOrError = (
Expand All @@ -44,6 +46,10 @@ export const isForbiddenResult = (
resultOrError: ReturnType<typeof getDeleteServiceConnectionResultOrError>
) => resultOrError.errorMessage === resultTypes.forbidden;

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

export const isSuccessResult = (
resultOrError: ReturnType<typeof getDeleteServiceConnectionResultOrError>
) => resultOrError.result === resultTypes.success;
Expand Down Expand Up @@ -78,8 +84,14 @@ const deleteServiceConnectionExecutor: ActionExecutor = async (
},
})
);

if (error) {
reportErrorsToSentry(error);

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

return Promise.reject(resultTypes.queryError);
}

Expand Down
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
8 changes: 4 additions & 4 deletions src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,7 @@
"deleteServiceFromPage": "You can delete the data from these services via the <linkToServices>{{linkToServicesText}}</linkToServices> page.",
"unableToDeleteServices": "We cannot delete data from these services, for example due to the transaction status or storage time.",
"contactServiceToDelete": "If you still want to delete the data, please contact <linkToExternalServiceList >the service</linkToExternalServiceList> directly and then retry deleting the profile.",
"urlToServiceList": "https://www.hel.fi/search/services",
"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. "
"urlToServiceList": "https://www.hel.fi/search/services"
},
"downloadData": {
"button": "Download my information",
Expand Down Expand Up @@ -234,7 +233,8 @@
"connectionRemovalVerificationTitle": "Are you sure you want to delete your data?",
"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."
"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."
},
"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."
}
}
}
Loading
Loading