diff --git a/.changeset/loud-taxis-end.md b/.changeset/loud-taxis-end.md new file mode 100644 index 0000000..bc529dd --- /dev/null +++ b/.changeset/loud-taxis-end.md @@ -0,0 +1,5 @@ +--- +"saleor-app-invoices": patch +--- + +Changed how Saleor version is validated during installation, to use dedicated SaleorVersionCompatibilityValidator. It also doesnt "coerce" version anymore, but uses "includePrelease" flag instead. This should match actual Saleor versioning better diff --git a/.changeset/rare-tigers-agree.md b/.changeset/rare-tigers-agree.md new file mode 100644 index 0000000..b61c9a4 --- /dev/null +++ b/.changeset/rare-tigers-agree.md @@ -0,0 +1,5 @@ +--- +"saleor-app-invoices": patch +--- + +Use REQUIRED_SALEOR_VERSION from manifest in app's own Saleor version validation diff --git a/apps/invoices/package.json b/apps/invoices/package.json index aed7724..27222d9 100644 --- a/apps/invoices/package.json +++ b/apps/invoices/package.json @@ -69,7 +69,8 @@ "rimraf": "^3.0.2", "typescript": "4.9.5", "vite": "^4.1.1", - "vitest": "^0.28.4" + "vitest": "^0.28.4", + "@types/semver": "^7.3.13" }, "lint-staged": { "*.{js,ts,tsx}": "eslint --cache --fix", diff --git a/apps/invoices/src/lib/saleor-version-compatibility-validator.test.ts b/apps/invoices/src/lib/saleor-version-compatibility-validator.test.ts new file mode 100644 index 0000000..b8ef9cf --- /dev/null +++ b/apps/invoices/src/lib/saleor-version-compatibility-validator.test.ts @@ -0,0 +1,32 @@ +import { describe, expect, it } from "vitest"; +import { SaleorVersionCompatibilityValidator } from "./saleor-version-compatibility-validator"; + +describe("SaleorVersionCompatibilityValidator", () => { + it.each([ + [">=3.10 <4", "3.12.0"], + [">=3.10 <4", "3.999.0"], + [">=3.10", "4.0.0"], + [">=3.10", "4.1.0"], + [">3.10", "3.11.0"], + /** + * -a suffix is Saleor staging version indicator + */ + [">=3.10", "3.10.0-a"], + [">3.10", "3.11.0-a"], + ])('Passes for app requirement "%s" and saleor version "%s"', (appVersionReq, saleorVersion) => { + expect(() => + new SaleorVersionCompatibilityValidator(appVersionReq).validateOrThrow(saleorVersion) + ).not.to.throw(); + }); + + it.each([ + [">=3.10 <4", "4.0.0"], + [">3.10 <4", "3.10.0"], + [">3.10", "3.10.0"], + [">=3.10", "2.0.0"], + ])('Throws for app requirement "%s" and saleor version "%s"', (appVersionReq, saleorVersion) => { + expect(() => + new SaleorVersionCompatibilityValidator(appVersionReq).validateOrThrow(saleorVersion) + ).to.throw(); + }); +}); diff --git a/apps/invoices/src/lib/saleor-version-compatibility-validator.ts b/apps/invoices/src/lib/saleor-version-compatibility-validator.ts new file mode 100644 index 0000000..af23456 --- /dev/null +++ b/apps/invoices/src/lib/saleor-version-compatibility-validator.ts @@ -0,0 +1,18 @@ +const semver = require("semver"); + +/** + * TODO Extract to shared or even SDK + */ +export class SaleorVersionCompatibilityValidator { + constructor(private appRequiredVersion: string) {} + + validateOrThrow(saleorVersion: string) { + const versionIsValid = semver.satisfies(saleorVersion, this.appRequiredVersion, { + includePrerelease: true, + }); + + if (!versionIsValid) { + throw new Error(`App requires Saleor matching semver: ${this.appRequiredVersion}`); + } + } +} diff --git a/apps/invoices/src/modules/trpc/protected-client-procedure.ts b/apps/invoices/src/modules/trpc/protected-client-procedure.ts index ec273fe..4bb3b5d 100644 --- a/apps/invoices/src/modules/trpc/protected-client-procedure.ts +++ b/apps/invoices/src/modules/trpc/protected-client-procedure.ts @@ -1,7 +1,7 @@ import { createClient } from "../../lib/graphql"; import { verifyJWT } from "@saleor/app-sdk/verify-jwt"; import { middleware, procedure } from "./trpc-server"; -import { saleorApp } from "../../../saleor-app"; +import { saleorApp } from "../../saleor-app"; import { TRPCError } from "@trpc/server"; import { ProtectedHandlerError } from "@saleor/app-sdk/handlers/next"; import { logger } from "../../lib/logger"; diff --git a/apps/invoices/src/pages/api/manifest.ts b/apps/invoices/src/pages/api/manifest.ts index bedf02e..93fd771 100644 --- a/apps/invoices/src/pages/api/manifest.ts +++ b/apps/invoices/src/pages/api/manifest.ts @@ -3,6 +3,7 @@ import { AppManifest } from "@saleor/app-sdk/types"; import packageJson from "../../../package.json"; import { invoiceRequestedWebhook } from "./webhooks/invoice-requested"; +import { REQUIRED_SALEOR_VERSION } from "../../saleor-app"; export default createManifestHandler({ async manifestFactory(context) { @@ -21,7 +22,7 @@ export default createManifestHandler({ /** * Requires 3.10 due to invoices event payload - in previous versions, order reference was missing */ - requiredSaleorVersion: ">=3.10 <4", + requiredSaleorVersion: REQUIRED_SALEOR_VERSION, }; return manifest; diff --git a/apps/invoices/src/pages/api/register.ts b/apps/invoices/src/pages/api/register.ts index be70546..32bad24 100644 --- a/apps/invoices/src/pages/api/register.ts +++ b/apps/invoices/src/pages/api/register.ts @@ -1,12 +1,11 @@ import { createAppRegisterHandler } from "@saleor/app-sdk/handlers/next"; -import { saleorApp } from "../../../saleor-app"; +import { REQUIRED_SALEOR_VERSION, saleorApp } from "../../saleor-app"; import { gql } from "urql"; import { createClient } from "../../lib/graphql"; import { SaleorVersionQuery } from "../../../generated/graphql"; import { createLogger } from "../../lib/logger"; - -const semver = require("semver"); +import { SaleorVersionCompatibilityValidator } from "../../lib/saleor-version-compatibility-validator"; const allowedUrlsPattern = process.env.ALLOWED_DOMAIN_PATTERN; @@ -18,12 +17,6 @@ const SaleorVersion = gql` } `; -/** - * TODO: Move to Manifest, when implemented - * @see https://github.com/saleor/saleor-app-sdk/pull/186 - */ -const APP_SEMVER_REQUIREMENTS = ">=3.10"; - /** * Required endpoint, called by Saleor to install app. * It will exchange tokens with app, so saleorApp.apl will contain token @@ -78,19 +71,9 @@ export default createAppRegisterHandler({ throw new Error("Saleor Version couldnt be fetched from the API"); } - const versionIsValid = semver.satisfies( - semver.coerce(saleorVersion), - APP_SEMVER_REQUIREMENTS + new SaleorVersionCompatibilityValidator(REQUIRED_SALEOR_VERSION).validateOrThrow( + saleorVersion ); - - logger.debug( - { saleorVersion, APP_SEMVER_REQUIREMENTS, coerced: semver.coerce(saleorVersion) }, - "Semver validation failed" - ); - - if (!versionIsValid) { - throw new Error(`App requires Saleor matching semver: ${APP_SEMVER_REQUIREMENTS}`); - } } catch (e: unknown) { const message = (e as Error)?.message ?? "Unknown error"; diff --git a/apps/invoices/src/pages/api/webhooks/invoice-requested.ts b/apps/invoices/src/pages/api/webhooks/invoice-requested.ts index be47bdd..ae83940 100644 --- a/apps/invoices/src/pages/api/webhooks/invoice-requested.ts +++ b/apps/invoices/src/pages/api/webhooks/invoice-requested.ts @@ -1,6 +1,6 @@ import { NextWebhookApiHandler, SaleorAsyncWebhook } from "@saleor/app-sdk/handlers/next"; import { gql } from "urql"; -import { saleorApp } from "../../../../saleor-app"; +import { saleorApp } from "../../../saleor-app"; import { InvoiceRequestedPayloadFragment, OrderPayloadFragment, diff --git a/apps/invoices/saleor-app.ts b/apps/invoices/src/saleor-app.ts similarity index 93% rename from apps/invoices/saleor-app.ts rename to apps/invoices/src/saleor-app.ts index 256a3ad..ee9f4e8 100644 --- a/apps/invoices/saleor-app.ts +++ b/apps/invoices/src/saleor-app.ts @@ -33,3 +33,5 @@ switch (aplType) { export const saleorApp = new SaleorApp({ apl, }); + +export const REQUIRED_SALEOR_VERSION = ">=3.10 <4"; diff --git a/apps/invoices/turbo.json b/apps/invoices/turbo.json index 6ef6261..d2681ce 100644 --- a/apps/invoices/turbo.json +++ b/apps/invoices/turbo.json @@ -12,7 +12,9 @@ "PORT", "VERCEL_URL", "ALLOWED_DOMAIN_PATTERN", - "NEXT_PUBLIC_VERCEL_ENV" + "NEXT_PUBLIC_VERCEL_ENV", + "REST_APL_ENDPOINT", + "REST_APL_TOKEN" ] } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3f05474..a9629ea 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -771,6 +771,9 @@ importers: '@types/rimraf': specifier: ^3.0.2 version: 3.0.2 + '@types/semver': + specifier: ^7.3.13 + version: 7.3.13 '@vitejs/plugin-react': specifier: ^3.0.0 version: 3.1.0(vite@4.1.1)