Invoices: validate Saleor version and fail if lower than declared (#220)

* Invoices: validate Saleor version and fail if lower than decalred

* Fix package lock

* Replace tiny-invariant error to custom one to get explicit error message
This commit is contained in:
Lukasz Ostrowski 2023-02-28 12:56:48 +01:00 committed by GitHub
parent 38ea8324e8
commit 55c8f1afcb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 118 additions and 4 deletions

View file

@ -0,0 +1,5 @@
---
"saleor-app-invoices": minor
---
App now validates Saleor version and will fail if lower than 3.10

View file

@ -16,10 +16,11 @@
"schemaVersion": "3.10" "schemaVersion": "3.10"
}, },
"dependencies": { "dependencies": {
"semver": "^7.3.8",
"@material-ui/core": "^4.12.4", "@material-ui/core": "^4.12.4",
"@material-ui/icons": "^4.11.3", "@material-ui/icons": "^4.11.3",
"@material-ui/lab": "4.0.0-alpha.61", "@material-ui/lab": "4.0.0-alpha.61",
"@saleor/app-sdk": "0.29.0", "@saleor/app-sdk": "0.32.7",
"@saleor/macaw-ui": "^0.7.2", "@saleor/macaw-ui": "^0.7.2",
"@sentry/nextjs": "^7.36.0", "@sentry/nextjs": "^7.36.0",
"@tanstack/react-query": "^4.24.4", "@tanstack/react-query": "^4.24.4",

View file

@ -1,9 +1,29 @@
import { createAppRegisterHandler } from "@saleor/app-sdk/handlers/next"; import { createAppRegisterHandler } from "@saleor/app-sdk/handlers/next";
import { saleorApp } from "../../../saleor-app"; import { saleorApp } from "../../../saleor-app";
import { gql } from "urql";
import { createClient } from "../../lib/graphql";
import { SaleorVersionQuery } from "../../../generated/graphql";
import invariant from "tiny-invariant";
import { createLogger } from "../../lib/logger";
const semver = require("semver");
const allowedUrlsPattern = process.env.ALLOWED_DOMAIN_PATTERN; const allowedUrlsPattern = process.env.ALLOWED_DOMAIN_PATTERN;
const SaleorVersion = gql`
query SaleorVersion {
shop {
version
}
}
`;
/**
* 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. * Required endpoint, called by Saleor to install app.
* It will exchange tokens with app, so saleorApp.apl will contain token * It will exchange tokens with app, so saleorApp.apl will contain token
@ -25,4 +45,68 @@ export default createAppRegisterHandler({
return true; return true;
}, },
], ],
/**
* Check Saleor version and reject installation if it doesn't match
*
* TODO: Consider moving to app-sdk and do it under the hood
*
* Also, consume version, if possible, from the request directly
* @see https://github.com/saleor/saleor/issues/12144
*/
async onRequestVerified(req, { authData: { token, saleorApiUrl }, respondWithError }) {
const logger = createLogger({
context: "onRequestVerified",
});
try {
const client = createClient(saleorApiUrl, async () => {
return {
token,
};
});
const saleorVersion = await client
.query<SaleorVersionQuery>(SaleorVersion, {})
.toPromise()
.then((res) => {
return res.data?.shop.version;
});
logger.debug({ saleorVersion }, "Received saleor version from Shop query");
if (!saleorVersion) {
throw new Error("Saleor Version couldnt be fetched from the API");
}
const versionIsValid = semver.satisfies(
semver.coerce(saleorVersion),
APP_SEMVER_REQUIREMENTS
);
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";
logger.debug({ message }, "Failed validating semver, will respond with error and status 400");
throw respondWithError({
message: message,
body: {
success: false,
error: {
code: "INVALID_SALEOR_VERSION",
message: message,
},
},
status: 400,
});
}
},
}); });

View file

@ -120,7 +120,7 @@ importers:
'@material-ui/core': ^4.12.4 '@material-ui/core': ^4.12.4
'@material-ui/icons': ^4.11.3 '@material-ui/icons': ^4.11.3
'@material-ui/lab': 4.0.0-alpha.61 '@material-ui/lab': 4.0.0-alpha.61
'@saleor/app-sdk': 0.29.0 '@saleor/app-sdk': 0.32.7
'@saleor/apps-shared': workspace:* '@saleor/apps-shared': workspace:*
'@saleor/macaw-ui': ^0.7.2 '@saleor/macaw-ui': ^0.7.2
'@sentry/nextjs': ^7.36.0 '@sentry/nextjs': ^7.36.0
@ -151,6 +151,7 @@ importers:
react-dom: 18.2.0 react-dom: 18.2.0
react-hook-form: ^7.41.0 react-hook-form: ^7.41.0
rimraf: ^3.0.2 rimraf: ^3.0.2
semver: ^7.3.8
tiny-invariant: ^1.3.1 tiny-invariant: ^1.3.1
typescript: 4.9.5 typescript: 4.9.5
urql: ^3.0.3 urql: ^3.0.3
@ -162,7 +163,7 @@ importers:
'@material-ui/core': 4.12.4_5ndqzdd6t4rivxsukjv3i3ak2q '@material-ui/core': 4.12.4_5ndqzdd6t4rivxsukjv3i3ak2q
'@material-ui/icons': 4.11.3_x54wk6dsnsxe7g7vvfmytp77te '@material-ui/icons': 4.11.3_x54wk6dsnsxe7g7vvfmytp77te
'@material-ui/lab': 4.0.0-alpha.61_x54wk6dsnsxe7g7vvfmytp77te '@material-ui/lab': 4.0.0-alpha.61_x54wk6dsnsxe7g7vvfmytp77te
'@saleor/app-sdk': 0.29.0_3vryta7zmbcsw4rrqf4axjqggm '@saleor/app-sdk': 0.32.7_3vryta7zmbcsw4rrqf4axjqggm
'@saleor/apps-shared': link:../../packages/shared '@saleor/apps-shared': link:../../packages/shared
'@saleor/macaw-ui': 0.7.2_pmlnlm755hlzzzocw2qhf3a34e '@saleor/macaw-ui': 0.7.2_pmlnlm755hlzzzocw2qhf3a34e
'@sentry/nextjs': 7.36.0_next@13.1.6+react@18.2.0 '@sentry/nextjs': 7.36.0_next@13.1.6+react@18.2.0
@ -184,6 +185,7 @@ importers:
react: 18.2.0 react: 18.2.0
react-dom: 18.2.0_react@18.2.0 react-dom: 18.2.0_react@18.2.0
react-hook-form: 7.43.1_react@18.2.0 react-hook-form: 7.43.1_react@18.2.0
semver: 7.3.8
tiny-invariant: 1.3.1 tiny-invariant: 1.3.1
urql: 3.0.3_onqnqwb3ubg5opvemcqf7c2qhy urql: 3.0.3_onqnqwb3ubg5opvemcqf7c2qhy
usehooks-ts: 2.9.1_biqbaboplfbrettd7655fr4n2y usehooks-ts: 2.9.1_biqbaboplfbrettd7655fr4n2y
@ -4054,6 +4056,28 @@ packages:
- supports-color - supports-color
dev: false dev: false
/@saleor/app-sdk/0.32.7_3vryta7zmbcsw4rrqf4axjqggm:
resolution: {integrity: sha512-nt4ylAc+pq39LUFzPdlDvmLlvgWDo0K+qMAklvFjiPFGXfFRqTpWs9t9pqDMACiJLM5dvFAkGYJI2T6h/LUD8Q==}
peerDependencies:
next: '>=12'
react: '>=17'
react-dom: '>=17'
dependencies:
'@changesets/cli': 2.26.0
debug: 4.3.4
fast-glob: 3.2.12
graphql: 16.6.0
jose: 4.11.4
next: 13.1.6_biqbaboplfbrettd7655fr4n2y
raw-body: 2.5.1
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
retes: 0.33.0
uuid: 8.3.2
transitivePeerDependencies:
- supports-color
dev: false
/@saleor/macaw-ui/0.7.2_2dwar4pp5qoelfawvjffoi6dne: /@saleor/macaw-ui/0.7.2_2dwar4pp5qoelfawvjffoi6dne:
resolution: {integrity: sha512-Fli7fhTWuHu7q2CzxwTUpB4x9HYaxHSAzCLZLA23VY1ieIWbCxbsXadMiMGWp/nuYitswMr6JXMm+1SDe9K8LQ==} resolution: {integrity: sha512-Fli7fhTWuHu7q2CzxwTUpB4x9HYaxHSAzCLZLA23VY1ieIWbCxbsXadMiMGWp/nuYitswMr6JXMm+1SDe9K8LQ==}
engines: {node: '>=16 <19'} engines: {node: '>=16 <19'}