From 1c02d891473ae8a8cd212fadfbed93b4977a437e Mon Sep 17 00:00:00 2001 From: Bhavjit Chauhan <25716723+bhavjitChauhan@users.noreply.github.com> Date: Tue, 10 Oct 2023 21:11:32 -0700 Subject: [PATCH] Add safelist fallback --- client/package.json | 2 +- client/src/util/graphql.js | 39 +++++++++----- server/lib/api.js | 37 ++++++++++++- server/package-lock.json | 103 +++++++++++++++++++++++++++++++++++++ server/package.json | 1 + 5 files changed, 167 insertions(+), 15 deletions(-) diff --git a/client/package.json b/client/package.json index 39c30ff..182d039 100644 --- a/client/package.json +++ b/client/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "scripts": { - "serve": "npx vue-cli-service serve", + "serve": "set NODE_OPTIONS=--openssl-legacy-provider && npx vue-cli-service serve", "build": "npx vue-cli-service build", "sitemap": "npx vue-cli-service sitemap" }, diff --git a/client/src/util/graphql.js b/client/src/util/graphql.js index 3df9529..d642904 100644 --- a/client/src/util/graphql.js +++ b/client/src/util/graphql.js @@ -7,7 +7,12 @@ function hotlistBody({ } = {}) { return { query: `query hotlist($curationNodeId: String, $onlyOfficialProjectSpinoffs: Boolean!, $sort: ListProgramSortOrder, $pageInfo: ListProgramsPageInfo) { - listTopPrograms(curationNodeId: $curationNodeId, onlyOfficialProjectSpinoffs: $onlyOfficialProjectSpinoffs, sort: $sort, pageInfo: $pageInfo) { + listTopPrograms( + curationNodeId: $curationNodeId + onlyOfficialProjectSpinoffs: $onlyOfficialProjectSpinoffs + sort: $sort + pageInfo: $pageInfo + ) { complete cursor programs { @@ -24,8 +29,7 @@ function hotlistBody({ } __typename } -} -`, +}`, variables: { curationNodeId, onlyOfficialProjectSpinoffs, @@ -67,8 +71,7 @@ function projectsAuthoredByUserBody({ } __typename } -} -`, +}`, variables: { kaid, sort, @@ -89,8 +92,19 @@ function feedbackQueryBody({ } = {}) { return { query: `query feedbackQuery($topicId: String!, $focusKind: String!, $cursor: String, $limit: Int, $feedbackType: FeedbackType!, $currentSort: Int, $qaExpandKey: String) { - feedback(focusId: $topicId, cursor: $cursor, limit: $limit, feedbackType: $feedbackType, focusKind: $focusKind, sort: $currentSort, qaExpandKey: $qaExpandKey, answersLimit: 1) { + feedback( + focusId: $topicId + cursor: $cursor + limit: $limit + feedbackType: $feedbackType + focusKind: $focusKind + sort: $currentSort + qaExpandKey: $qaExpandKey + answersLimit: 1 + ) { feedback { + isLocked + isPinned replyCount appearsAsDeleted author { @@ -145,6 +159,8 @@ function feedbackQueryBody({ ... on QuestionFeedback { hasAnswered answers { + isLocked + isPinned replyCount appearsAsDeleted author { @@ -204,6 +220,8 @@ function feedbackQueryBody({ } ... on AnswerFeedback { question { + isLocked + isPinned replyCount appearsAsDeleted author { @@ -266,8 +284,7 @@ function feedbackQueryBody({ sortedByDate __typename } -} -`, +}`, variables: { topicId, focusKind, @@ -347,8 +364,7 @@ function programQueryBody({ width __typename } -} -`, +}`, variables: { programId } @@ -369,8 +385,7 @@ function avatarDataForProfileBody({ } __typename } -} -`, +}`, variables: { kaid } diff --git a/server/lib/api.js b/server/lib/api.js index 6a5764e..c9cbdac 100644 --- a/server/lib/api.js +++ b/server/lib/api.js @@ -1,4 +1,6 @@ const axios = require('axios').default; +const { getLatestQuery, getLatestMutation } = require('@bhavjit/khan-api'); +const { AxiosError } = require('axios'); const instance = axios.create({ @@ -48,8 +50,39 @@ const get = async (path, params) => { * @returns {Promise} */ const post = async (path, data) => { - const response = await instance.post(path, data) - return response.data; + try { + const response = await instance.post(path, data) + return response.data; + } catch (err) { + if (err instanceof AxiosError && err.response.status === 403) { + const { query } = data; + const isQuery = query.startsWith('query'), + operationName = query.match(/^(?:query|mutation) (\w+)/)?.[1] + + if (!operationName) + throw new Error(`An unknown query is no longer in the safelist`) + + console.warn( + `The query for operation "${operationName}" is no longer in the safelist. Attempting to fetch the latest version from the safelist...` + ) + const latestQuery = isQuery + ? await getLatestQuery(operationName) + : await getLatestMutation(operationName) + + if (!latestQuery) + throw new Error( + `The query for operation "${operationName}" was not found in the safelist` + ) + + const newData = { + ...data, + query: latestQuery, + } + + const response = await instance.post(path, newData) + return response.data + } else throw err; + } }; /** diff --git a/server/package-lock.json b/server/package-lock.json index 86a0f51..e91a18d 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -9,6 +9,7 @@ "version": "1.0.0", "license": "MIT", "dependencies": { + "@bhavjit/khan-api": "^0.6.4", "axios": "^0.27.2", "body-parser": "^1.20.1", "chalk": "^4.1.2", @@ -1079,6 +1080,17 @@ "node": ">=14.0.0" } }, + "node_modules/@bhavjit/khan-api": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@bhavjit/khan-api/-/khan-api-0.6.4.tgz", + "integrity": "sha512-IUDvBUMBANNrP245pE84vmPaQBE0R1xJzFMtIdIdooy90RctLZaOVrSUf7gGoH/NUaS/eH+xqF7a9lUQixSUdg==", + "dependencies": { + "cross-fetch": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/@colors/colors": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", @@ -1499,6 +1511,14 @@ "node": ">= 0.10" } }, + "node_modules/cross-fetch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", + "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", + "dependencies": { + "node-fetch": "^2.6.12" + } + }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -2153,6 +2173,44 @@ "node": ">= 8.0.0" } }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/nodemon": { "version": "2.0.20", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.20.tgz", @@ -3675,6 +3733,14 @@ "tslib": "^2.3.1" } }, + "@bhavjit/khan-api": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@bhavjit/khan-api/-/khan-api-0.6.4.tgz", + "integrity": "sha512-IUDvBUMBANNrP245pE84vmPaQBE0R1xJzFMtIdIdooy90RctLZaOVrSUf7gGoH/NUaS/eH+xqF7a9lUQixSUdg==", + "requires": { + "cross-fetch": "^4.0.0" + } + }, "@colors/colors": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", @@ -3994,6 +4060,14 @@ "vary": "^1" } }, + "cross-fetch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", + "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", + "requires": { + "node-fetch": "^2.6.12" + } + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -4471,6 +4545,35 @@ "clone": "2.x" } }, + "node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "requires": { + "whatwg-url": "^5.0.0" + }, + "dependencies": { + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + } + } + }, "nodemon": { "version": "2.0.20", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.20.tgz", diff --git a/server/package.json b/server/package.json index 8932743..5f6603a 100644 --- a/server/package.json +++ b/server/package.json @@ -10,6 +10,7 @@ "author": "Bhavjit Chauhan", "license": "MIT", "dependencies": { + "@bhavjit/khan-api": "^0.6.4", "axios": "^0.27.2", "body-parser": "^1.20.1", "chalk": "^4.1.2",