-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathaccount-deletion-processor-handler.ts
More file actions
124 lines (110 loc) · 4.43 KB
/
Copy pathaccount-deletion-processor-handler.ts
File metadata and controls
124 lines (110 loc) · 4.43 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import logger from '../commons/logger';
import Ajv2019 from 'ajv/dist/2019';
import { DynamoDatabaseService } from '../services/dynamo-database-service';
import { LOGS_PREFIX_SENSITIVE_INFO, MetricNames } from '../data-types/constants';
import { AppConfigService } from '../services/app-config-service';
import type { Context, SQSEvent, SQSRecord } from 'aws-lambda';
import { addMetric, metric } from '../commons/metrics';
import { accountDeleteMessageSchema } from '../contracts/account-delete-message';
import { prettifyError } from 'zod';
import jsonSafeParse from '../commons/json-safe-parse';
import { AUTH_DELETE_ACCOUNTSchema } from '@govuk-one-login/event-catalogue-schemas';
const ajv2019 = new Ajv2019({ allErrors: true });
const validateEventCatalogue = ajv2019.compile(AUTH_DELETE_ACCOUNTSchema);
enum ParserErrorType {
BODY_JSON_PARSER_ERROR = 'BODY_JSON_PARSER_ERROR',
BODY_FORMAT_PARSER_ERROR = 'BODY_FORMAT_PARSER_ERROR',
MISSING_USER_ID = 'MISSING_USER_ID',
EMPTY_USER_ID = 'EMPTY_USER_ID',
}
class ParserError extends Error {
constructor(
public readonly errorType: ParserErrorType,
message?: string,
) {
super(message ?? 'The SQS message can not be parsed.');
this.name = 'ParserError';
}
}
const appConfig = AppConfigService.getInstance();
const ddbService = new DynamoDatabaseService(appConfig.tableName);
/**
* Account deletion processor handler. Updates accounts and marks them for deletion.
* @param event - SQS event. Consumes these events from a queue and takes the User ID from the record to match the account.
* @param context - This object provides methods and properties that provide information about the invocation, function, and execution environment.
* @returns - Void. Sends off response to DynamoDB.
*/
export async function handler(event: SQSEvent, context: Context): Promise<void> {
logger.addContext(context);
if (!event.Records[0]) {
logger.error('The event does not contain any records.');
return;
}
const updateRecordsByIdPromises = event.Records.map((record) => {
const userId = getUserId(record);
return userId ? updateDeleteStatusId(userId) : undefined;
}).filter((prom) => prom !== undefined);
await Promise.all(updateRecordsByIdPromises);
metric.publishStoredMetrics();
}
/**
* Function to parse the JSON message from the SQS Record.
* @param record - The record passed in from the event.
* @returns - User ID or undefined
*/
function parseMessage(record: SQSRecord): string {
// JSON parse record.body
const recordBodyResult = jsonSafeParse(record.body);
if (!recordBodyResult.success) throw new ParserError(ParserErrorType.BODY_JSON_PARSER_ERROR);
// Validate against zod schema
const result = accountDeleteMessageSchema.safeParse(recordBodyResult.data);
if (!result.success)
throw new ParserError(
ParserErrorType.BODY_FORMAT_PARSER_ERROR,
`The SQS message can not be parsed. ${prettifyError(result.error)}`,
);
if (!validateEventCatalogue(recordBodyResult.data)) {
logger.debug(`${LOGS_PREFIX_SENSITIVE_INFO} Event has failed event catalogue schema validation.`, {
validationErrors: validateEventCatalogue.errors,
});
addMetric(MetricNames.INVALID_EVENT_RECEIVED_EVENT_CATALOGUE, undefined, undefined, {
EVENT_NAME: result.data.event_name,
});
}
return result.data.user.user_id;
}
/**
* Function to take the User ID from the SQS Record.
* @param record - The record passed in from the event.
* @returns - User ID as a string, with whitespace removed.
*/
function getUserId(record: SQSRecord) {
try {
return parseMessage(record);
} catch (error) {
if (error instanceof ParserError) {
logger.error(error.message);
addMetric(MetricNames.DELETE_EVENT_PARSER_ERROR, undefined, undefined, {
ERROR: error.errorType,
});
metric.publishStoredMetrics();
return;
}
throw error;
}
}
/**
* Function to call DynamoDB and mark the account as deleted.
* @param userId - User ID taken from the record of the SQS Event.
* @throws - Error if there is a problem updating the account.
*/
async function updateDeleteStatusId(userId: string) {
try {
await ddbService.updateDeleteStatus(userId);
} catch (error) {
logger.error(`${LOGS_PREFIX_SENSITIVE_INFO} Error updating account ${userId}`, { error });
addMetric(MetricNames.MARK_AS_DELETED_FAILED);
metric.publishStoredMetrics();
throw new Error('Failed to update the account status.', { cause: error });
}
}