Skip to content

Commit

Permalink
feat(handler): custom error handling (#2943)
Browse files Browse the repository at this point in the history
  • Loading branch information
brunozoric committed Jan 15, 2023
1 parent 505f90b commit de01bfc
Show file tree
Hide file tree
Showing 12 changed files with 193 additions and 14 deletions.
4 changes: 2 additions & 2 deletions apps/api/graphql/tsconfig.json
Expand Up @@ -110,8 +110,8 @@
"~/*": ["./src/*"],
"@webiny/api-aco/*": ["../../../packages/api-aco/src/*"],
"@webiny/api-aco": ["../../../packages/api-aco/src"],
"@webiny/api-aco-so-ddb/*": ["../../../packages/api-aco/src/*"],
"@webiny/api-aco-so-ddb": ["../../../packages/api-aco/src"],
"@webiny/api-aco-so-ddb/*": ["../../../packages/api-aco-so-ddb/src/*"],
"@webiny/api-aco-so-ddb": ["../../../packages/api-aco-so-ddb/src"],
"@webiny/api-admin-users-cognito/*": ["../../../packages/api-admin-users-cognito/src/*"],
"@webiny/api-admin-users-cognito": ["../../../packages/api-admin-users-cognito/src"],
"@webiny/api-admin-users-cognito-so-ddb/*": [
Expand Down
2 changes: 1 addition & 1 deletion packages/handler-aws/src/dynamodb/index.ts
Expand Up @@ -25,7 +25,7 @@ export interface CreateHandlerParams extends BaseCreateHandlerParams {
export const createHandler = (params: CreateHandlerParams): HandlerCallable => {
return (payload, context) => {
const app = createBaseHandler({
plugins: params.plugins,
...params,
options: {
logger: params.debug === true,
...(params.options || {})
Expand Down
2 changes: 1 addition & 1 deletion packages/handler-aws/src/eventBridge/index.ts
Expand Up @@ -30,7 +30,7 @@ export const createHandler = <DetailType extends string, Detail>(
): HandlerCallable<DetailType, Detail> => {
return (payload, context) => {
const app = createBaseHandler({
plugins: params.plugins,
...params,
options: {
logger: params.debug === true,
...(params.options || {})
Expand Down
2 changes: 1 addition & 1 deletion packages/handler-aws/src/gateway/index.ts
Expand Up @@ -64,7 +64,7 @@ const attachRequiredProperties = (event: APIGatewayEvent): void => {
export const createHandler = (params: CreateHandlerParams): HandlerCallable => {
return (event, context) => {
const app = createBaseHandler({
plugins: params.plugins,
...params,
options: {
logger: params.http?.debug === true,
...(params.options || {})
Expand Down
2 changes: 1 addition & 1 deletion packages/handler-aws/src/raw/index.ts
Expand Up @@ -32,7 +32,7 @@ export const createHandler = <Payload = any, Response = APIGatewayProxyResult>(
): HandlerCallable<Payload, Response> => {
return (payload, context) => {
const app = createBaseHandler({
plugins: params.plugins,
...params,
options: {
logger: params.http?.debug === true,
...(params.options || {})
Expand Down
2 changes: 1 addition & 1 deletion packages/handler-aws/src/s3/index.ts
Expand Up @@ -22,7 +22,7 @@ export interface CreateHandlerParams extends BaseCreateHandlerParams {
export const createHandler = (params: CreateHandlerParams): HandlerCallable => {
return (payload, context) => {
const app = createBaseHandler({
plugins: params.plugins,
...params,
options: {
logger: params.debug === true,
...(params.options || {})
Expand Down
2 changes: 1 addition & 1 deletion packages/handler-aws/src/sqs/index.ts
Expand Up @@ -22,7 +22,7 @@ export interface CreateHandlerParams extends BaseCreateHandlerParams {
export const createHandler = (params: CreateHandlerParams): HandlerCallable => {
return (payload, context) => {
const app = createBaseHandler({
plugins: params.plugins,
...params,
options: {
logger: params.debug === true,
...(params.options || {})
Expand Down
107 changes: 107 additions & 0 deletions packages/handler/__tests__/customErrorHandler.test.ts
@@ -0,0 +1,107 @@
import { createHandler } from "~/fastify";
import { createRoute } from "~/plugins/RoutePlugin";
import WebinyError from "@webiny/error";
import { createModifyFastifyPlugin } from "~/plugins/ModifyFastifyPlugin";

describe("custom error handler", () => {
const data = {
stringValue: "123",
numberValue: 123,
booleanValue: false,
arrayValue: ["123", 123, false],
objectValue: {
testing: true,
errorMessage: "not ok"
}
};

it("should properly output error via built-in error handler", async () => {
const app = createHandler({
plugins: [
createRoute(({ onAll }) => {
onAll("/webiny-test", async () => {
throw new WebinyError(
"Testing custom error handler output",
"CUSTOM_ERROR_HANDLER_CODE",
data
);
});
})
]
});

const result = await app.inject({
path: "/webiny-test",
headers: {
"content-type": "application/json"
},
method: "POST",
payload: "{}"
});

const expected = JSON.stringify({
message: "Testing custom error handler output",
code: "CUSTOM_ERROR_HANDLER_CODE",
data: {
stringValue: "123",
numberValue: 123,
booleanValue: false,
arrayValue: ["123", 123, false],
objectValue: {
testing: true,
errorMessage: "not ok"
}
}
});

expect(result).toMatchObject({
payload: expected,
body: expected,
statusCode: 500
});
});

it("should properly output error via user defined error handler", async () => {
const app = createHandler({
plugins: [
createModifyFastifyPlugin(instance => {
instance.setErrorHandler((error, request, reply) => {
return reply
.send({
justSimpleOutput: true
})
.code(404);
});
}),
createRoute(({ onAll }) => {
onAll("/webiny-test", async () => {
throw new WebinyError(
"Testing custom error handler output",
"CUSTOM_ERROR_HANDLER_CODE",
data
);
});
})
]
});

const result = await app.inject({
path: "/webiny-test",
headers: {
"content-type": "application/json"
},
method: "GET",
payload: "{}"
});

const expected = JSON.stringify({
justSimpleOutput: true
});

expect(result).toMatchObject({
payload: expected,
body: expected,
statusCode: 404
});
});
});
16 changes: 16 additions & 0 deletions packages/handler/__tests__/modifyFastify.test.ts
@@ -0,0 +1,16 @@
import { createHandler } from "~/fastify";
import { createModifyFastifyPlugin } from "~/plugins/ModifyFastifyPlugin";

describe("modify fastify with plugin", () => {
it("should modify existing Fastify instance via the plugin", async () => {
const app = createHandler({
plugins: [
createModifyFastifyPlugin(instance => {
(instance as any).modifiedValue = true;
})
]
});

expect((app as any).modifiedValue).toEqual(true);
});
});
40 changes: 35 additions & 5 deletions packages/handler/src/fastify.ts
Expand Up @@ -16,6 +16,7 @@ import { ContextPlugin } from "@webiny/api";
import { BeforeHandlerPlugin } from "./plugins/BeforeHandlerPlugin";
import { HandlerResultPlugin } from "./plugins/HandlerResultPlugin";
import { HandlerErrorPlugin } from "./plugins/HandlerErrorPlugin";
import { ModifyFastifyPlugin } from "~/plugins/ModifyFastifyPlugin";

const DEFAULT_HEADERS: Record<string, string> = {
"Cache-Control": "no-store",
Expand Down Expand Up @@ -300,15 +301,31 @@ export const createHandler = (params: CreateHandlerParams) => {

app.addHook("preSerialization", preSerialization);

app.addHook("onError", async (_, reply, error) => {
app.setErrorHandler<WebinyError>(async (error, request, reply) => {
return reply
.status(500)
.headers({
"Cache-Control": "no-store"
})
.send({
message: error.message,
code: error.code,
data: error.data
});
});

app.addHook("onError", async (_, reply, error: any) => {
const plugins = app.webiny.plugins.byType<HandlerErrorPlugin>(HandlerErrorPlugin.type);
// Log error to cloud, as these can be extremely annoying to debug!
/**
* Log error to cloud, as these can be extremely annoying to debug!
*/
console.log("@webiny/handler");
console.log(
JSON.stringify({
...(error || {}),
message: error?.message,
code: error?.code
...error,
message: error.message,
code: error.code,
data: error.data
})
);
const handler = middleware(
Expand All @@ -321,11 +338,24 @@ export const createHandler = (params: CreateHandlerParams) => {
await handler(app.webiny, error);

return reply
.send({
message: error.message,
code: error.code,
data: error.data
})
.headers({
"Cache-Control": "no-store"
})
.status(500);
});

/**
* With these plugins we give users possibility to do anything they want on our fastify instance.
*/
const modifyPlugins = app.webiny.plugins.byType<ModifyFastifyPlugin>(ModifyFastifyPlugin.type);
for (const plugin of modifyPlugins) {
plugin.modify(app);
}

return app;
};
3 changes: 2 additions & 1 deletion packages/handler/src/index.ts
@@ -1,8 +1,9 @@
export * from "~/fastify";
export * from "@webiny/api/plugins/ContextPlugin";
export * from "~/fastify";
export * from "~/Context";
export * from "~/plugins/EventPlugin";
export * from "~/plugins/RoutePlugin";
export * from "~/plugins/BeforeHandlerPlugin";
export * from "~/plugins/HandlerErrorPlugin";
export * from "~/plugins/HandlerResultPlugin";
export * from "~/plugins/ModifyFastifyPlugin";
25 changes: 25 additions & 0 deletions packages/handler/src/plugins/ModifyFastifyPlugin.ts
@@ -0,0 +1,25 @@
import { Plugin } from "@webiny/plugins/Plugin";
import { FastifyInstance } from "fastify";

interface ModifyFastifyPluginCallable {
(app: FastifyInstance): void;
}

export class ModifyFastifyPlugin extends Plugin {
public static override type = "handler.fastify.modify";

private readonly cb: ModifyFastifyPluginCallable;

public constructor(cb: ModifyFastifyPluginCallable) {
super();
this.cb = cb;
}

public modify(app: FastifyInstance): void {
this.cb(app);
}
}

export const createModifyFastifyPlugin = (cb: ModifyFastifyPluginCallable) => {
return new ModifyFastifyPlugin(cb);
};

0 comments on commit de01bfc

Please sign in to comment.