Improve register handler errors API (#201)

This commit is contained in:
Lukasz Ostrowski 2023-02-28 13:54:58 +01:00 committed by GitHub
parent 5a93a166ab
commit a939281e98
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 69 additions and 52 deletions

View file

@ -0,0 +1,5 @@
---
"@saleor/app-sdk": minor
---
Register handler hooks will now respond with errors parsable by the dashboard. "Body" in error was removed, so client code can provide message and status.

View file

@ -67,7 +67,8 @@ export default createAppRegisterHandler({
allowedSaleorUrls: ["https://your-saleor.saleor.cloud/graphql/"], // optional, see options below
async onRequestVerified(req, { authData, respondWithError }) {
await doSomethingAndBlockInstallation(authData.token).catch((err) => {
throw respondWithError({ body: "Error, installation will fail" });
// Return this method to break installation flow and show error in the Dashboard
return respondWithError({ message: "Error, installation will fail" });
});
},
});
@ -93,7 +94,7 @@ export type CreateAppRegisterHandlerOptions = {
authToken?: string;
saleorDomain?: string;
saleorApiUrl?: string;
respondWithError: ({ status, message, body }) => never; // should throw
respondWithError: ({ status, message }) => never; // will throw
}
): Promise<void>;
/**
@ -104,7 +105,7 @@ export type CreateAppRegisterHandlerOptions = {
request: Request,
context: {
authData: AuthData;
respondWithError: ({ status, message, body }) => never; // should throw
respondWithError: ({ status, message }) => never; // will throw
}
): Promise<void>;
/**
@ -115,7 +116,7 @@ export type CreateAppRegisterHandlerOptions = {
request: Request,
context: {
authData: AuthData;
respondWithError: ({ status, message, body }) => never; // should throw
respondWithError: ({ status, message }) => never; // will throw
}
): Promise<void>;
/**
@ -127,7 +128,7 @@ export type CreateAppRegisterHandlerOptions = {
context: {
authData: AuthData;
error: unknown;
respondWithError: ({ status, message, body }) => never; // should throw
respondWithError: ({ status, message }) => never; // will throw
}
): Promise<void>;
};

View file

@ -219,13 +219,12 @@ describe("create-app-register-handler", () => {
context: {
respondWithError(params: { status: number; body: string; message: string }): Error;
}
) => {
throw context.respondWithError({
) =>
context.respondWithError({
status: 401,
body: "test",
message: "test message",
});
}
})
);
const { res, req } = createMocks({
@ -253,7 +252,13 @@ describe("create-app-register-handler", () => {
await handler(req, res);
expect(res._getStatusCode()).toBe(401);
expect(res._getData()).toBe("test");
expect(res._getData()).toEqual({
success: false,
error: {
code: "REGISTER_HANDLER_HOOK_ERROR",
message: "test message",
},
});
});
});
});

View file

@ -16,33 +16,49 @@ const debug = createDebug("createAppRegisterHandler");
type HookCallbackErrorParams = {
status?: number;
body?: object;
message?: string;
};
class RegisterCallbackError extends Error {
public status = 500;
public body: object = {};
constructor(errorParams: HookCallbackErrorParams) {
super(errorParams.message);
if (errorParams.status) {
this.status = errorParams.status;
}
if (errorParams.body) {
this.body = errorParams.body;
}
}
}
const createCallbackError = (params: HookCallbackErrorParams) => new RegisterCallbackError(params);
const createCallbackError = (params: HookCallbackErrorParams) => {
throw new RegisterCallbackError(params);
};
export type RegisterHandlerResponseBody = {
success: boolean;
error?: {
code?: string;
message?: string;
};
};
export const createRegisterHandlerResponseBody = (
success: boolean,
error?: RegisterHandlerResponseBody["error"]
): RegisterHandlerResponseBody => ({
success,
error,
});
const handleHookError = (e: RegisterCallbackError | unknown) => {
if (e instanceof RegisterCallbackError) {
return new Response(e.body, { status: e.status });
return new Response(
createRegisterHandlerResponseBody(false, {
code: "REGISTER_HANDLER_HOOK_ERROR",
message: e.message,
}),
{ status: e.status }
);
}
return Response.InternalServerError("Error during app installation");
};
@ -141,27 +157,24 @@ export const createAppRegisterHandler = ({
if (!validateAllowSaleorUrls(saleorApiUrl, allowedSaleorUrls)) {
debug("Validation of URL %s against allowSaleorUrls param resolves to false, throwing");
return Response.Forbidden({
success: false,
error: {
return Response.Forbidden(
createRegisterHandlerResponseBody(false, {
code: "SALEOR_URL_PROHIBITED",
message: "This app expects to be installed only in allowed saleor instances",
},
});
})
);
}
const { configured: aplConfigured } = await apl.isConfigured();
if (!aplConfigured) {
debug("The APL has not been configured");
return new Response(
{
success: false,
error: {
code: "APL_NOT_CONFIGURED",
message: "APL_NOT_CONFIGURED. App is configured properly. Check APL docs for help.",
},
},
createRegisterHandlerResponseBody(false, {
code: "APL_NOT_CONFIGURED",
message: "APL_NOT_CONFIGURED. App is configured properly. Check APL docs for help.",
}),
{
status: 503,
}
@ -172,14 +185,11 @@ export const createAppRegisterHandler = ({
const appId = await getAppId({ saleorApiUrl, token: authToken });
if (!appId) {
return new Response(
{
success: false,
error: {
code: "UNKNOWN_APP_ID",
message:
"The auth data given during registration request could not be used to fetch app ID.",
},
},
createRegisterHandlerResponseBody(false, {
code: "UNKNOWN_APP_ID",
message:
"The auth data given during registration request could not be used to fetch app ID.",
}),
{
status: 401,
}
@ -190,13 +200,10 @@ export const createAppRegisterHandler = ({
const jwks = await fetchRemoteJwks(saleorApiUrl);
if (!jwks) {
return new Response(
{
success: false,
error: {
code: "JWKS_NOT_AVAILABLE",
message: "Can't fetch the remote JWKS.",
},
},
createRegisterHandlerResponseBody(false, {
code: "JWKS_NOT_AVAILABLE",
message: "Can't fetch the remote JWKS.",
}),
{
status: 401,
}
@ -262,17 +269,16 @@ export const createAppRegisterHandler = ({
}
}
return Response.InternalServerError({
success: false,
error: {
return Response.InternalServerError(
createRegisterHandlerResponseBody(false, {
message: "Registration failed: could not save the auth data.",
},
});
})
);
}
debug("Register complete");
return Response.OK({ success: true });
return Response.OK(createRegisterHandlerResponseBody(true));
};
return toNextHandler([