Skip to content

Commit 8789f37

Browse files
Merge pull request #341 from saleor/add-schema-version
Parse schema version when processing saleor webhook
2 parents 0477c11 + 9e3d0cc commit 8789f37

File tree

6 files changed

+80
-30
lines changed

6 files changed

+80
-30
lines changed

.changeset/fair-ads-trade.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@saleor/app-sdk": minor
3+
---
4+
5+
Fix wrong logic introduced in [0.49.0](https://github.com/saleor/app-sdk/releases/tag/v0.49.0): there is not header `saleor-schema-version` when app-sdk is processing saleor webhook. This header is only present on install request.
6+
7+
Now app-sdk will try to parse version from `version` field on GraphQL subscription [Event](https://docs.saleor.io/docs/3.x/api-storefront/miscellaneous/interfaces/event#code-style-fontweight-normal-eventbversionbcodestring-). If field is not present `null` will be returned.

src/handlers/next/saleor-webhooks/process-saleor-webhook.test.ts

-23
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ describe("processAsyncSaleorWebhook", () => {
4545
"saleor-event": "product_updated",
4646
"saleor-signature": "mocked_signature",
4747
"content-length": "0", // is ignored by mocked raw-body.
48-
"saleor-schema-version": "3.19",
4948
},
5049
method: "POST",
5150
// body can be skipped because we mock it with raw-body
@@ -173,26 +172,4 @@ describe("processAsyncSaleorWebhook", () => {
173172
schemaVersion: null,
174173
});
175174
});
176-
177-
it("Return schema version if saleor-schema-version header is present", async () => {
178-
await expect(
179-
processSaleorWebhook({
180-
req: mockRequest,
181-
apl: mockAPL,
182-
allowedEvent: "PRODUCT_UPDATED",
183-
})
184-
).resolves.toStrictEqual({
185-
authData: {
186-
appId: "mock-app-id",
187-
domain: "example.com",
188-
jwks: "{}",
189-
saleorApiUrl: "https://example.com/graphql/",
190-
token: "mock-token",
191-
},
192-
baseUrl: "https://some-saleor-host.cloud",
193-
event: "product_updated",
194-
payload: {},
195-
schemaVersion: 3.19,
196-
});
197-
});
198175
});

src/handlers/next/saleor-webhooks/process-saleor-webhook.ts

+12-7
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { createDebug } from "../../../debug";
88
import { fetchRemoteJwks } from "../../../fetch-remote-jwks";
99
import { getBaseUrl, getSaleorHeaders } from "../../../headers";
1010
import { getOtelTracer } from "../../../open-telemetry";
11+
import { parseSchemaVersion } from "../../../util";
1112
import { verifySignatureWithJwks } from "../../../verify-signature";
1213

1314
const debug = createDebug("processSaleorWebhook");
@@ -89,7 +90,7 @@ export const processSaleorWebhook: ProcessSaleorWebhook = async <T>({
8990
throw new WebhookError("Wrong request method, only POST allowed", "WRONG_METHOD");
9091
}
9192

92-
const { event, signature, saleorApiUrl, schemaVersion } = getSaleorHeaders(req.headers);
93+
const { event, signature, saleorApiUrl } = getSaleorHeaders(req.headers);
9394
const baseUrl = getBaseUrl(req.headers);
9495

9596
if (!baseUrl) {
@@ -107,10 +108,6 @@ export const processSaleorWebhook: ProcessSaleorWebhook = async <T>({
107108
throw new WebhookError("Missing saleor-event header", "MISSING_EVENT_HEADER");
108109
}
109110

110-
if (!schemaVersion) {
111-
debug("Missing saleor-schema-version header");
112-
}
113-
114111
const expected = allowedEvent.toLowerCase();
115112

116113
if (event !== expected) {
@@ -140,7 +137,7 @@ export const processSaleorWebhook: ProcessSaleorWebhook = async <T>({
140137
throw new WebhookError("Missing request body", "MISSING_REQUEST_BODY");
141138
}
142139

143-
let parsedBody: unknown;
140+
let parsedBody: unknown & { version?: string | null };
144141

145142
try {
146143
parsedBody = JSON.parse(rawBody);
@@ -150,6 +147,14 @@ export const processSaleorWebhook: ProcessSaleorWebhook = async <T>({
150147
throw new WebhookError("Request body can't be parsed", "CANT_BE_PARSED");
151148
}
152149

150+
let parsedSchemaVersion: number | null = null;
151+
152+
try {
153+
parsedSchemaVersion = parseSchemaVersion(parsedBody.version);
154+
} catch {
155+
debug("Schema version cannot be parsed");
156+
}
157+
153158
/**
154159
* Verify if the app is properly installed for given Saleor API URL
155160
*/
@@ -215,7 +220,7 @@ export const processSaleorWebhook: ProcessSaleorWebhook = async <T>({
215220
event,
216221
payload: parsedBody as T,
217222
authData,
218-
schemaVersion,
223+
schemaVersion: parsedSchemaVersion,
219224
};
220225
} catch (err) {
221226
const message = (err as Error)?.message ?? "Unknown error";

src/util/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export * from "./is-in-iframe";
2+
export * from "./schema-version";
23
export * from "./use-is-mounted";

src/util/schema-version.test.ts

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { describe, expect, test } from "vitest";
2+
3+
import { parseSchemaVersion } from "./schema-version";
4+
5+
describe("parseSchemaVersion", () => {
6+
test.each([
7+
{
8+
rawVersion: "3",
9+
parsedVersion: null,
10+
},
11+
{
12+
rawVersion: "3.19",
13+
parsedVersion: 3.19,
14+
},
15+
{
16+
rawVersion: "3.19.1",
17+
parsedVersion: 3.19,
18+
},
19+
{
20+
rawVersion: "malformed",
21+
parsedVersion: null,
22+
},
23+
{
24+
rawVersion: "malformed.raw",
25+
parsedVersion: null,
26+
},
27+
{
28+
rawVersion: "malformed.raw.version",
29+
parsedVersion: null,
30+
},
31+
{
32+
rawVersion: null,
33+
parsedVersion: null,
34+
},
35+
{
36+
rawVersion: undefined,
37+
parsedVersion: null,
38+
},
39+
])(
40+
"Parses version string from: $rawVersion to: $parsedVersion",
41+
({ rawVersion, parsedVersion }) => {
42+
expect(parseSchemaVersion(rawVersion)).toBe(parsedVersion);
43+
}
44+
);
45+
});

src/util/schema-version.ts

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
export const parseSchemaVersion = (rawVersion: string | undefined | null): number | null => {
2+
if (!rawVersion) {
3+
return null;
4+
}
5+
6+
const [majorString, minorString] = rawVersion.split(".");
7+
const major = parseInt(majorString, 10);
8+
const minor = parseInt(minorString, 10);
9+
10+
if (major && minor) {
11+
return parseFloat(`${major}.${minor}`);
12+
}
13+
14+
return null;
15+
};

0 commit comments

Comments
 (0)