subscriptionQueryAst or query argument has to be specified for the event subscription (#107)
This commit is contained in:
parent
33d7666c7d
commit
83ed6719d5
3 changed files with 74 additions and 25 deletions
|
@ -21,7 +21,8 @@ export type SaleorWebhookError =
|
|||
| "NOT_REGISTERED"
|
||||
| "SIGNATURE_VERIFICATION_FAILED"
|
||||
| "WRONG_METHOD"
|
||||
| "CANT_BE_PARSED";
|
||||
| "CANT_BE_PARSED"
|
||||
| "CONFIGURATION_ERROR";
|
||||
|
||||
export class WebhookError extends Error {
|
||||
errorType: SaleorWebhookError = "OTHER";
|
||||
|
@ -116,7 +117,7 @@ export const processAsyncSaleorWebhook: ProcessAsyncSaleorWebhook = async <T>({
|
|||
parsedBody = JSON.parse(rawBody);
|
||||
} catch {
|
||||
debug("Request body cannot be parsed");
|
||||
throw new WebhookError("Request body cant be parsed", "CANT_BE_PARSED");
|
||||
throw new WebhookError("Request body can't be parsed", "CANT_BE_PARSED");
|
||||
}
|
||||
|
||||
// Check if domain is installed in the app
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import { ASTNode } from "graphql";
|
||||
import { createMocks } from "node-mocks-http";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { APL } from "../../APL";
|
||||
import { processAsyncSaleorWebhook } from "./process-async-saleor-webhook";
|
||||
import { NextWebhookApiHandler, SaleorAsyncWebhook } from "./saleor-async-webhook";
|
||||
|
||||
const webhookPath = "api/webhooks/product-updated";
|
||||
|
@ -23,10 +25,40 @@ describe("SaleorAsyncWebhook", () => {
|
|||
isConfigured: vi.fn(),
|
||||
};
|
||||
|
||||
const saleorAsyncWebhook = new SaleorAsyncWebhook({
|
||||
afterEach(async () => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
const validAsyncWebhookConfiguration = {
|
||||
apl: mockAPL,
|
||||
asyncEvent: "PRODUCT_UPDATED",
|
||||
webhookPath,
|
||||
query: "subscription { event { ... on ProductUpdated { product { id }}}}",
|
||||
};
|
||||
|
||||
const saleorAsyncWebhook = new SaleorAsyncWebhook(validAsyncWebhookConfiguration);
|
||||
|
||||
it("throw CONFIGURATION_ERROR if query and subscriptionQueryAst are both absent", async () => {
|
||||
expect(() => {
|
||||
// eslint-disable-next-line no-new
|
||||
new SaleorAsyncWebhook({
|
||||
...validAsyncWebhookConfiguration,
|
||||
// @ts-ignore: We make type error for test purpose
|
||||
query: undefined,
|
||||
subscriptionQueryAst: undefined,
|
||||
});
|
||||
}).toThrowError();
|
||||
});
|
||||
|
||||
it("constructor passes if subscriptionQueryAst is provided", async () => {
|
||||
expect(() => {
|
||||
// eslint-disable-next-line no-new
|
||||
new SaleorAsyncWebhook({
|
||||
...validAsyncWebhookConfiguration,
|
||||
query: undefined,
|
||||
subscriptionQueryAst: {} as ASTNode,
|
||||
});
|
||||
}).not.toThrowError();
|
||||
});
|
||||
|
||||
it("targetUrl should return full path to the webhook route based on given baseUrl", async () => {
|
||||
|
@ -39,18 +71,19 @@ describe("SaleorAsyncWebhook", () => {
|
|||
isActive: true,
|
||||
name: "PRODUCT_UPDATED webhook",
|
||||
targetUrl: "http://example.com/api/webhooks/product-updated",
|
||||
query: "subscription { event { ... on ProductUpdated { product { id }}}}",
|
||||
});
|
||||
});
|
||||
|
||||
it("Test createHandler which return success", async () => {
|
||||
// prepare mocked context returned by mocked process function
|
||||
vi.mock("./process-async-saleor-webhook", () => ({
|
||||
processAsyncSaleorWebhook: vi.fn().mockResolvedValue({
|
||||
vi.mock("./process-async-saleor-webhook");
|
||||
|
||||
vi.mocked(processAsyncSaleorWebhook).mockImplementationOnce(async () => ({
|
||||
baseUrl: "example.com",
|
||||
event: "product_updated",
|
||||
payload: { data: "test_payload" },
|
||||
authData: { domain: "example.com", token: "token" },
|
||||
}),
|
||||
}));
|
||||
|
||||
// Test handler - will throw error if mocked context is not passed to it
|
||||
|
|
|
@ -15,16 +15,26 @@ import {
|
|||
|
||||
const debug = createDebug("SaleorAsyncWebhook");
|
||||
|
||||
export interface WebhookManifestConfiguration {
|
||||
interface WebhookManifestConfigurationBase {
|
||||
name?: string;
|
||||
webhookPath: string;
|
||||
subscriptionQueryAst?: ASTNode;
|
||||
query?: string;
|
||||
asyncEvent: WebhookEvent;
|
||||
isActive?: boolean;
|
||||
apl: APL;
|
||||
}
|
||||
|
||||
interface WebhookManifestConfigurationWithAst extends WebhookManifestConfigurationBase {
|
||||
subscriptionQueryAst: ASTNode;
|
||||
}
|
||||
|
||||
interface WebhookManifestConfigurationWithQuery extends WebhookManifestConfigurationBase {
|
||||
query: string;
|
||||
}
|
||||
|
||||
type WebhookManifestConfiguration =
|
||||
| WebhookManifestConfigurationWithAst
|
||||
| WebhookManifestConfigurationWithQuery;
|
||||
|
||||
export const ErrorCodeMap: Record<SaleorWebhookError, number> = {
|
||||
OTHER: 500,
|
||||
MISSING_HOST_HEADER: 400,
|
||||
|
@ -38,6 +48,7 @@ export const ErrorCodeMap: Record<SaleorWebhookError, number> = {
|
|||
SIGNATURE_VERIFICATION_FAILED: 401,
|
||||
WRONG_METHOD: 405,
|
||||
CANT_BE_PARSED: 400,
|
||||
CONFIGURATION_ERROR: 500,
|
||||
};
|
||||
|
||||
export type NextWebhookApiHandler<TPayload = unknown, TResp = unknown> = (
|
||||
|
@ -61,18 +72,22 @@ export class SaleorAsyncWebhook<TPayload = unknown> {
|
|||
|
||||
apl: APL;
|
||||
|
||||
constructor({
|
||||
name,
|
||||
webhookPath,
|
||||
subscriptionQueryAst,
|
||||
query,
|
||||
asyncEvent,
|
||||
apl,
|
||||
isActive = true,
|
||||
}: WebhookManifestConfiguration) {
|
||||
constructor(configuration: WebhookManifestConfiguration) {
|
||||
const { name, webhookPath, asyncEvent, apl, isActive = true } = configuration;
|
||||
this.name = name || `${asyncEvent} webhook`;
|
||||
this.subscriptionQueryAst = subscriptionQueryAst;
|
||||
this.query = query;
|
||||
if ("query" in configuration) {
|
||||
this.query = configuration.query;
|
||||
}
|
||||
if ("subscriptionQueryAst" in configuration) {
|
||||
this.subscriptionQueryAst = configuration.subscriptionQueryAst;
|
||||
}
|
||||
if (!this.subscriptionQueryAst && !this.query) {
|
||||
throw new WebhookError(
|
||||
"Need to specify `subscriptionQueryAst` or `query` to create webhook subscription",
|
||||
"CONFIGURATION_ERROR"
|
||||
);
|
||||
}
|
||||
|
||||
this.webhookPath = webhookPath;
|
||||
this.asyncEvent = asyncEvent;
|
||||
this.isActive = isActive;
|
||||
|
|
Loading…
Reference in a new issue