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
|
allowedSaleorUrls: ["https://your-saleor.saleor.cloud/graphql/"], // optional, see options below
|
||||||
async onRequestVerified(req, { authData, respondWithError }) {
|
async onRequestVerified(req, { authData, respondWithError }) {
|
||||||
await doSomethingAndBlockInstallation(authData.token).catch((err) => {
|
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;
|
authToken?: string;
|
||||||
saleorDomain?: string;
|
saleorDomain?: string;
|
||||||
saleorApiUrl?: string;
|
saleorApiUrl?: string;
|
||||||
respondWithError: ({ status, message, body }) => never; // should throw
|
respondWithError: ({ status, message }) => never; // will throw
|
||||||
}
|
}
|
||||||
): Promise<void>;
|
): Promise<void>;
|
||||||
/**
|
/**
|
||||||
|
@ -104,7 +105,7 @@ export type CreateAppRegisterHandlerOptions = {
|
||||||
request: Request,
|
request: Request,
|
||||||
context: {
|
context: {
|
||||||
authData: AuthData;
|
authData: AuthData;
|
||||||
respondWithError: ({ status, message, body }) => never; // should throw
|
respondWithError: ({ status, message }) => never; // will throw
|
||||||
}
|
}
|
||||||
): Promise<void>;
|
): Promise<void>;
|
||||||
/**
|
/**
|
||||||
|
@ -115,7 +116,7 @@ export type CreateAppRegisterHandlerOptions = {
|
||||||
request: Request,
|
request: Request,
|
||||||
context: {
|
context: {
|
||||||
authData: AuthData;
|
authData: AuthData;
|
||||||
respondWithError: ({ status, message, body }) => never; // should throw
|
respondWithError: ({ status, message }) => never; // will throw
|
||||||
}
|
}
|
||||||
): Promise<void>;
|
): Promise<void>;
|
||||||
/**
|
/**
|
||||||
|
@ -127,7 +128,7 @@ export type CreateAppRegisterHandlerOptions = {
|
||||||
context: {
|
context: {
|
||||||
authData: AuthData;
|
authData: AuthData;
|
||||||
error: unknown;
|
error: unknown;
|
||||||
respondWithError: ({ status, message, body }) => never; // should throw
|
respondWithError: ({ status, message }) => never; // will throw
|
||||||
}
|
}
|
||||||
): Promise<void>;
|
): Promise<void>;
|
||||||
};
|
};
|
||||||
|
|
|
@ -219,13 +219,12 @@ describe("create-app-register-handler", () => {
|
||||||
context: {
|
context: {
|
||||||
respondWithError(params: { status: number; body: string; message: string }): Error;
|
respondWithError(params: { status: number; body: string; message: string }): Error;
|
||||||
}
|
}
|
||||||
) => {
|
) =>
|
||||||
throw context.respondWithError({
|
context.respondWithError({
|
||||||
status: 401,
|
status: 401,
|
||||||
body: "test",
|
body: "test",
|
||||||
message: "test message",
|
message: "test message",
|
||||||
});
|
})
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const { res, req } = createMocks({
|
const { res, req } = createMocks({
|
||||||
|
@ -253,7 +252,13 @@ describe("create-app-register-handler", () => {
|
||||||
await handler(req, res);
|
await handler(req, res);
|
||||||
|
|
||||||
expect(res._getStatusCode()).toBe(401);
|
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 = {
|
type HookCallbackErrorParams = {
|
||||||
status?: number;
|
status?: number;
|
||||||
body?: object;
|
|
||||||
message?: string;
|
message?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
class RegisterCallbackError extends Error {
|
class RegisterCallbackError extends Error {
|
||||||
public status = 500;
|
public status = 500;
|
||||||
|
|
||||||
public body: object = {};
|
|
||||||
|
|
||||||
constructor(errorParams: HookCallbackErrorParams) {
|
constructor(errorParams: HookCallbackErrorParams) {
|
||||||
super(errorParams.message);
|
super(errorParams.message);
|
||||||
|
|
||||||
if (errorParams.status) {
|
if (errorParams.status) {
|
||||||
this.status = 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) => {
|
const handleHookError = (e: RegisterCallbackError | unknown) => {
|
||||||
if (e instanceof RegisterCallbackError) {
|
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");
|
return Response.InternalServerError("Error during app installation");
|
||||||
};
|
};
|
||||||
|
@ -141,27 +157,24 @@ export const createAppRegisterHandler = ({
|
||||||
if (!validateAllowSaleorUrls(saleorApiUrl, allowedSaleorUrls)) {
|
if (!validateAllowSaleorUrls(saleorApiUrl, allowedSaleorUrls)) {
|
||||||
debug("Validation of URL %s against allowSaleorUrls param resolves to false, throwing");
|
debug("Validation of URL %s against allowSaleorUrls param resolves to false, throwing");
|
||||||
|
|
||||||
return Response.Forbidden({
|
return Response.Forbidden(
|
||||||
success: false,
|
createRegisterHandlerResponseBody(false, {
|
||||||
error: {
|
|
||||||
code: "SALEOR_URL_PROHIBITED",
|
code: "SALEOR_URL_PROHIBITED",
|
||||||
message: "This app expects to be installed only in allowed saleor instances",
|
message: "This app expects to be installed only in allowed saleor instances",
|
||||||
},
|
})
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { configured: aplConfigured } = await apl.isConfigured();
|
const { configured: aplConfigured } = await apl.isConfigured();
|
||||||
|
|
||||||
if (!aplConfigured) {
|
if (!aplConfigured) {
|
||||||
debug("The APL has not been configured");
|
debug("The APL has not been configured");
|
||||||
|
|
||||||
return new Response(
|
return new Response(
|
||||||
{
|
createRegisterHandlerResponseBody(false, {
|
||||||
success: false,
|
|
||||||
error: {
|
|
||||||
code: "APL_NOT_CONFIGURED",
|
code: "APL_NOT_CONFIGURED",
|
||||||
message: "APL_NOT_CONFIGURED. App is configured properly. Check APL docs for help.",
|
message: "APL_NOT_CONFIGURED. App is configured properly. Check APL docs for help.",
|
||||||
},
|
}),
|
||||||
},
|
|
||||||
{
|
{
|
||||||
status: 503,
|
status: 503,
|
||||||
}
|
}
|
||||||
|
@ -172,14 +185,11 @@ export const createAppRegisterHandler = ({
|
||||||
const appId = await getAppId({ saleorApiUrl, token: authToken });
|
const appId = await getAppId({ saleorApiUrl, token: authToken });
|
||||||
if (!appId) {
|
if (!appId) {
|
||||||
return new Response(
|
return new Response(
|
||||||
{
|
createRegisterHandlerResponseBody(false, {
|
||||||
success: false,
|
|
||||||
error: {
|
|
||||||
code: "UNKNOWN_APP_ID",
|
code: "UNKNOWN_APP_ID",
|
||||||
message:
|
message:
|
||||||
"The auth data given during registration request could not be used to fetch app ID.",
|
"The auth data given during registration request could not be used to fetch app ID.",
|
||||||
},
|
}),
|
||||||
},
|
|
||||||
{
|
{
|
||||||
status: 401,
|
status: 401,
|
||||||
}
|
}
|
||||||
|
@ -190,13 +200,10 @@ export const createAppRegisterHandler = ({
|
||||||
const jwks = await fetchRemoteJwks(saleorApiUrl);
|
const jwks = await fetchRemoteJwks(saleorApiUrl);
|
||||||
if (!jwks) {
|
if (!jwks) {
|
||||||
return new Response(
|
return new Response(
|
||||||
{
|
createRegisterHandlerResponseBody(false, {
|
||||||
success: false,
|
|
||||||
error: {
|
|
||||||
code: "JWKS_NOT_AVAILABLE",
|
code: "JWKS_NOT_AVAILABLE",
|
||||||
message: "Can't fetch the remote JWKS.",
|
message: "Can't fetch the remote JWKS.",
|
||||||
},
|
}),
|
||||||
},
|
|
||||||
{
|
{
|
||||||
status: 401,
|
status: 401,
|
||||||
}
|
}
|
||||||
|
@ -262,17 +269,16 @@ export const createAppRegisterHandler = ({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Response.InternalServerError({
|
return Response.InternalServerError(
|
||||||
success: false,
|
createRegisterHandlerResponseBody(false, {
|
||||||
error: {
|
|
||||||
message: "Registration failed: could not save the auth data.",
|
message: "Registration failed: could not save the auth data.",
|
||||||
},
|
})
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
debug("Register complete");
|
debug("Register complete");
|
||||||
|
|
||||||
return Response.OK({ success: true });
|
return Response.OK(createRegisterHandlerResponseBody(true));
|
||||||
};
|
};
|
||||||
|
|
||||||
return toNextHandler([
|
return toNextHandler([
|
||||||
|
|
Loading…
Reference in a new issue