Skip to content

Commit 6001e30

Browse files
Add templates to error messages
1 parent 7a57d8b commit 6001e30

File tree

5 files changed

+13
-110
lines changed

5 files changed

+13
-110
lines changed

lambdas/api-handler/src/errors/index.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
import { ApiErrorDetail } from "../contracts/errors";
1+
import util from "util";
22

33
class ApiError extends Error {
4-
detail: ApiErrorDetail;
5-
constructor(detail: ApiErrorDetail, message?: string, cause?: Error) {
6-
super(message ?? detail, { cause });
7-
this.detail = detail;
4+
readonly detail: string;
5+
6+
constructor(detail: string, opts: { args?: unknown[]; cause?: unknown } = {}) {
7+
const formatted = opts.args?.length ? util.format(detail, ...(opts.args)) : detail;
8+
super(formatted, opts.cause ? { cause: opts.cause } : undefined);
9+
this.detail = formatted;
810
}
911
}
1012

lambdas/api-handler/src/handlers/__tests__/get-letters.test.ts

Lines changed: 3 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ describe('API Lambda handler', () => {
124124
const callback = jest.fn();
125125
const result = await getLetters(event, context, callback);
126126

127-
expect(mockedMapErrorToResponse).toHaveBeenCalledWith(new ValidationError(errors.ApiErrorDetail.InvalidRequestLimitNotInRange));
127+
expect(mockedMapErrorToResponse).toHaveBeenCalledWith(new ValidationError(errors.ApiErrorDetail.InvalidRequestLimitNotInRange, { args: [getEnvars().maxLimit] }));
128128
expect(result).toEqual(expectedErrorResponse);
129129
});
130130

@@ -138,7 +138,7 @@ describe('API Lambda handler', () => {
138138
const callback = jest.fn();
139139
const result = await getLetters(event, context, callback);
140140

141-
expect(mockedMapErrorToResponse).toHaveBeenCalledWith(new ValidationError(errors.ApiErrorDetail.InvalidRequestLimitNotInRange));
141+
expect(mockedMapErrorToResponse).toHaveBeenCalledWith(new ValidationError(errors.ApiErrorDetail.InvalidRequestLimitNotInRange, { args: [getEnvars().maxLimit] }));
142142
expect(result).toEqual(expectedErrorResponse);
143143
});
144144

@@ -152,7 +152,7 @@ describe('API Lambda handler', () => {
152152
const callback = jest.fn();
153153
const result = await getLetters(event, context, callback);
154154

155-
expect(mockedMapErrorToResponse).toHaveBeenCalledWith(new ValidationError(errors.ApiErrorDetail.InvalidRequestLimitNotInRange));
155+
expect(mockedMapErrorToResponse).toHaveBeenCalledWith(new ValidationError(errors.ApiErrorDetail.InvalidRequestLimitNotInRange, { args: [getEnvars().maxLimit] }));
156156
expect(result).toEqual(expectedErrorResponse);
157157
});
158158

@@ -189,103 +189,4 @@ describe('API Lambda handler', () => {
189189
expect(mockedMapErrorToResponse).toHaveBeenCalledWith(new ValidationError(errors.ApiErrorDetail.InvalidRequestMissingSupplierId));
190190
expect(result).toEqual(expectedErrorResponse);
191191
});
192-
193-
it("returns 400 if the limit parameter is not a number", async () => {
194-
const event = makeApiGwEvent({
195-
path: "/letters",
196-
queryStringParameters: { limit: "1%" },
197-
});
198-
const context = mockDeep<Context>();
199-
const callback = jest.fn();
200-
const result = await getLetters(event, context, callback);
201-
202-
expect(result).toEqual({
203-
statusCode: 400,
204-
body: "Invalid Request: limit parameter must be a positive number not greater than 2500",
205-
});
206-
});
207-
208-
it("returns 400 if the limit parameter is negative", async () => {
209-
const event = makeApiGwEvent({
210-
path: "/letters",
211-
queryStringParameters: { limit: "-1" },
212-
});
213-
const context = mockDeep<Context>();
214-
const callback = jest.fn();
215-
const result = await getLetters(event, context, callback);
216-
217-
expect(result).toEqual({
218-
statusCode: 400,
219-
body: "Invalid Request: limit parameter must be a positive number not greater than 2500",
220-
});
221-
});
222-
223-
it("returns 400 if the limit parameter is zero", async () => {
224-
const event = makeApiGwEvent({
225-
path: "/letters",
226-
queryStringParameters: { limit: "0" },
227-
});
228-
const context = mockDeep<Context>();
229-
const callback = jest.fn();
230-
const result = await getLetters(event, context, callback);
231-
232-
expect(result).toEqual({
233-
statusCode: 400,
234-
body: "Invalid Request: limit parameter must be a positive number not greater than 2500",
235-
});
236-
});
237-
238-
it("returns 400 if the limit parameter is out of range", async () => {
239-
const event = makeApiGwEvent({
240-
path: "/letters",
241-
queryStringParameters: { limit: "2501" },
242-
});
243-
const context = mockDeep<Context>();
244-
const callback = jest.fn();
245-
const result = await getLetters(event, context, callback);
246-
247-
expect(result).toEqual({
248-
statusCode: 400,
249-
body: "Invalid Request: limit parameter must be a positive number not greater than 2500",
250-
});
251-
});
252-
253-
it("returns 400 if unknown parameters are present", async () => {
254-
const event = makeApiGwEvent({
255-
path: "/letters",
256-
queryStringParameters: { max: "2000" },
257-
});
258-
const context = mockDeep<Context>();
259-
const callback = jest.fn();
260-
const result = await getLetters(event, context, callback);
261-
262-
expect(result).toEqual({
263-
statusCode: 400,
264-
body: "Invalid Request: Only 'limit' query parameter is supported",
265-
});
266-
});
267-
268-
it('returns 400 for missing supplier ID (empty headers)', async () => {
269-
const event = makeApiGwEvent({ path: "/letters", headers: {} });
270-
const context = mockDeep<Context>();
271-
const callback = jest.fn();
272-
const result = await getLetters(event, context, callback);
273-
274-
expect(result).toEqual({
275-
statusCode: 400,
276-
body: 'Invalid Request: Missing supplier ID',
277-
});
278-
});
279-
280-
it('returns 400 for missing supplier ID (undefined headers)', async () => {
281-
const event = makeApiGwEvent({ path: "/letters", headers: undefined });
282-
const context = mockDeep<Context>();
283-
const callback = jest.fn();
284-
const result = await getLetters(event, context, callback);
285-
286-
expect(result).toEqual({
287-
statusCode: 400,
288-
body: 'Invalid Request: Missing supplier ID',
289-
});
290-
});
291192
});

lambdas/api-handler/src/handlers/get-letters.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ function assertLimitInRange(limitNumber: number, maxLimit: number) {
8181
description: "Limit value is invalid",
8282
limitNumber,
8383
});
84-
throw new ValidationError(errors.ApiErrorDetail.InvalidRequestLimitNotInRange);
84+
throw new ValidationError(errors.ApiErrorDetail.InvalidRequestLimitNotInRange, { args: [maxLimit]});
8585
}
8686
}
8787

lambdas/api-handler/src/handlers/patch-letters.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export const patchLetters: APIGatewayProxyHandler = async (event) => {
2222
patchLetterRequest = LetterApiDocumentSchema.parse(JSON.parse(body));
2323
} catch (error) {
2424
if (error instanceof Error) {
25-
throw new ValidationError(errors.ApiErrorDetail.InvalidRequestBody, error.message, error);
25+
throw new ValidationError(errors.ApiErrorDetail.InvalidRequestBody, { cause: error});
2626
}
2727
else throw error;
2828
}

lambdas/api-handler/src/mappers/error-mapper.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export function mapErrorToResponse(error: unknown): APIGatewayProxyResult {
2222
}
2323
}
2424

25-
function buildResponseFromErrorCode(code: ApiErrorCode, detail: ApiErrorDetail | string): APIGatewayProxyResult {
25+
function buildResponseFromErrorCode(code: ApiErrorCode, detail: string): APIGatewayProxyResult {
2626
const responseError = buildApiError({
2727
code,
2828
status: codeToStatus(code),

0 commit comments

Comments
 (0)