Improve register handler errors API (#201)
This commit is contained in:
parent
5a93a166ab
commit
a939281e98
4 changed files with 69 additions and 52 deletions
5
.changeset/big-glasses-hang.md
Normal file
5
.changeset/big-glasses-hang.md
Normal 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.
|
|
@ -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>;
|
||||
};
|
||||
|
|
|
@ -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",
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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: {
|
||||
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: {
|
||||
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: {
|
||||
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([
|
||||
|
|
Loading…
Reference in a new issue