From 2f1eb1d56a69f25743e31926390fdd018598bd26 Mon Sep 17 00:00:00 2001 From: Oliwia Rogala Date: Thu, 21 Mar 2024 10:51:15 +0100 Subject: [PATCH 1/3] fix: parse boolean values in URL query parameters Refs #9674 --- src/core/utils/index.js | 11 ++++- .../features/syntax-highlighting-json.cy.js | 44 ++++++++++++++----- test/unit/core/utils.js | 5 +++ 3 files changed, 49 insertions(+), 11 deletions(-) diff --git a/src/core/utils/index.js b/src/core/utils/index.js index 9ff5770ee2e..e27b74f682a 100644 --- a/src/core/utils/index.js +++ b/src/core/utils/index.js @@ -619,7 +619,16 @@ export const parseSearch = () => { continue } i = params[i].split("=") - map[decodeURIComponent(i[0])] = (i[1] && decodeURIComponent(i[1])) || "" + + let value = i[1] ? decodeURIComponent(i[1]) : "" + + if (value === "true") { + value = true + } else if (value === "false") { + value = false + } + + map[decodeURIComponent(i[0])] = value } } diff --git a/test/e2e-cypress/e2e/features/syntax-highlighting-json.cy.js b/test/e2e-cypress/e2e/features/syntax-highlighting-json.cy.js index 236aabaa761..be1dbd473ec 100644 --- a/test/e2e-cypress/e2e/features/syntax-highlighting-json.cy.js +++ b/test/e2e-cypress/e2e/features/syntax-highlighting-json.cy.js @@ -8,17 +8,29 @@ describe("Syntax Highlighting for JSON value cases", () => { describe("OAS 2", () => { it("should render full syntax highlighted string in Request (param body) example", () => { cy.visit("/?url=/documents/features/syntax-highlighting-json-oas2.yaml") - .get("#operations-default-post_setServices") - .click() - .get(".body-param__example > .language-json > :nth-child(10)") - .should("have.text", "\"79daf5b4-aa4b-1452-eae5-42c231477ba7\"") + .get("#operations-default-post_setServices") + .click() + .get(".body-param__example > .language-json > :nth-child(10)") + .should("have.text", '"79daf5b4-aa4b-1452-eae5-42c231477ba7"') }) it("should render full syntax highlighted string in Response example", () => { cy.visit("/?url=/documents/features/syntax-highlighting-json-oas2.yaml") - .get("#operations-default-post_setServices") - .click() - .get(".example > .language-json > :nth-child(28)") - .should("have.text", "\"5ff06f632bb165394501b05d3a833355\"") + .get("#operations-default-post_setServices") + .click() + .get(".example > .language-json > :nth-child(28)") + .should("have.text", '"5ff06f632bb165394501b05d3a833355"') + }) + it("should not render syntax highlighted string when syntaxHighlight.activated is set to false", () => { + cy.visit( + "/?syntaxHighlight.activated=false&url=/documents/features/syntax-highlighting-json-oas2.yaml" + ) + .get("#operations-default-post_setServices") + .click() + .get(".example > .language-json") + .should("not.exist") + .get(".example") + .contains('"5ff06f632bb165394501b05d3a833355"') + .should("exist") }) }) describe("OAS 3", () => { @@ -27,14 +39,26 @@ describe("Syntax Highlighting for JSON value cases", () => { .get("#operations-default-post_setServices") .click() .get(".body-param__example > .language-json > :nth-child(15)") - .should("have.text", "\"22a124b4-594b-4452-bdf5-fc3ef1477ba7\"") + .should("have.text", '"22a124b4-594b-4452-bdf5-fc3ef1477ba7"') }) it("should render full syntax highlighted string in Response example", () => { cy.visit("/?url=/documents/features/syntax-highlighting-json-oas3.yaml") .get("#operations-default-post_setServices") .click() .get(".example > .language-json > :nth-child(33)") - .should("have.text", "\"f0009babde9dbe204540d79cf754408e\"") + .should("have.text", '"f0009babde9dbe204540d79cf754408e"') + }) + it("should not render syntax highlighted string when syntaxHighlight is set to false", () => { + cy.visit( + "/?syntaxHighlight=false&url=/documents/features/syntax-highlighting-json-oas3.yaml" + ) + .get("#operations-default-post_setServices") + .click() + .get(".example > .language-json") + .should("not.exist") + .get(".example") + .contains('"f0009babde9dbe204540d79cf754408e"') + .should("exist") }) }) }) diff --git a/test/unit/core/utils.js b/test/unit/core/utils.js index a733543e63e..e69b33a1e88 100644 --- a/test/unit/core/utils.js +++ b/test/unit/core/utils.js @@ -1326,6 +1326,11 @@ describe("utils", () => { win.location.search = "?foo=foo%20bar" expect(parseSearch()).toEqual({foo: "foo bar"}) }) + + it("parses boolean values", () => { + win.location.search = "?foo=true&bar=false" + expect(parseSearch()).toEqual({foo: true, bar: false}) + }) }) describe("serializing", () => { From 981dfe0ae8200331f41668498ea768def234d324 Mon Sep 17 00:00:00 2001 From: Oliwia Rogala Date: Thu, 11 Apr 2024 14:06:59 +0200 Subject: [PATCH 2/3] fix: convert URL config values to proper types --- src/core/index.js | 3 +- src/core/utils/convertConfigValues.js | 77 +++++++++++++++++++++++++++ src/core/utils/index.js | 11 +--- test/unit/core/utils.js | 59 ++++++++++++++++++-- 4 files changed, 134 insertions(+), 16 deletions(-) create mode 100644 src/core/utils/convertConfigValues.js diff --git a/src/core/index.js b/src/core/index.js index c3725f6644b..499c81c6539 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -29,6 +29,7 @@ import DownloadUrlPlugin from "./plugins/download-url" import SafeRenderPlugin from "./plugins/safe-render" import { parseSearch } from "./utils" +import { convertConfigValues } from "./utils/convertConfigValues" import win from "./window" // eslint-disable-next-line no-undef @@ -134,7 +135,7 @@ export default function SwaggerUI(opts) { } } - let queryConfig = opts.queryConfigEnabled ? parseSearch() : {} + let queryConfig = opts.queryConfigEnabled ? convertConfigValues(parseSearch()) : {} const domNode = opts.domNode delete opts.domNode diff --git a/src/core/utils/convertConfigValues.js b/src/core/utils/convertConfigValues.js new file mode 100644 index 00000000000..19dfbad2d6a --- /dev/null +++ b/src/core/utils/convertConfigValues.js @@ -0,0 +1,77 @@ +/** + * @prettier + */ +const booleanConfigs = [ + "deepLinking", + "displayOperationId", + "displayRequestDuration", + "persistAuthorization", + "requestSnippetsEnabled", + "showCommonExtensions", + "showExtensions", + "showMutatedRequest", + "syntaxHighlight.activated", + "tryItOutEnabled", + "withCredentials", +] +const numberConfigs = [ + "defaultModelExpandDepth", + "defaultModelsExpandDepth", + "maxDisplayedTags", +] +const objectConfigs = ["syntaxHighlight", "requestSnippets"] +const arrayConfigs = ["request.curlOptions", "supportedSubmitMethods"] + +const convertValue = (key, value) => { + const isBoolean = booleanConfigs.includes(key) + const isNumber = numberConfigs.includes(key) + const isObject = objectConfigs.includes(key) + const isArray = arrayConfigs.includes(key) + + if (key === "validatorUrl") { + return value === "null" ? null : value + } + + if (key === "filter") { + return value === "false" ? false : value + } + + if (isBoolean) { + return value === "true" ? true : value === "false" ? false : value + } + + if (isNumber) { + const parsedValue = parseInt(value) + return isNaN(parsedValue) ? value : parsedValue + } + + if (isObject) { + if (key === "syntaxHighlight" && value === "false") return false + try { + const parsedValue = JSON.parse(value) + return typeof parsedValue === "object" && !Array.isArray(parsedValue) + ? parsedValue + : value + } catch (e) { + return value + } + } + + if (isArray) { + try { + const parsedValue = JSON.parse(value) + return Array.isArray(parsedValue) ? parsedValue : value + } catch (e) { + return value + } + } + + return value +} + +export const convertConfigValues = (config) => { + Object.entries(config).forEach(([key, value]) => { + config[key] = convertValue(key, value) + }) + return config +} diff --git a/src/core/utils/index.js b/src/core/utils/index.js index e27b74f682a..9ff5770ee2e 100644 --- a/src/core/utils/index.js +++ b/src/core/utils/index.js @@ -619,16 +619,7 @@ export const parseSearch = () => { continue } i = params[i].split("=") - - let value = i[1] ? decodeURIComponent(i[1]) : "" - - if (value === "true") { - value = true - } else if (value === "false") { - value = false - } - - map[decodeURIComponent(i[0])] = value + map[decodeURIComponent(i[0])] = (i[1] && decodeURIComponent(i[1])) || "" } } diff --git a/test/unit/core/utils.js b/test/unit/core/utils.js index e69b33a1e88..fcbe977d2d5 100644 --- a/test/unit/core/utils.js +++ b/test/unit/core/utils.js @@ -37,6 +37,10 @@ import { safeBuildUrl, } from "core/utils/url" +import { + convertConfigValues +} from "core/utils/convertConfigValues" + import win from "core/window" import { afterAll, beforeAll, expect, jest } from "@jest/globals" @@ -1326,11 +1330,6 @@ describe("utils", () => { win.location.search = "?foo=foo%20bar" expect(parseSearch()).toEqual({foo: "foo bar"}) }) - - it("parses boolean values", () => { - win.location.search = "?foo=true&bar=false" - expect(parseSearch()).toEqual({foo: true, bar: false}) - }) }) describe("serializing", () => { @@ -1781,4 +1780,54 @@ describe("utils", () => { expect(createCodeChallenge(codeVerifier)).toBe(expectedCodeChallenge) }) }) + + describe("convertConfigValues", () => { + it("should convert stringified `true` and `false` values to boolean" , () => { + const config = { deepLinking: "true", tryItOutEnabled: "false" } + + const expectedConfig = { deepLinking: true, tryItOutEnabled: false } + + expect(convertConfigValues(config)).toStrictEqual(expectedConfig) + }) + + it("should convert stringified number values to number" , () => { + const config = { defaultModelExpandDepth: "5", defaultModelsExpandDepth: "-1" } + + const expectedConfig = { defaultModelExpandDepth: 5, defaultModelsExpandDepth: -1 } + + expect(convertConfigValues(config)).toStrictEqual(expectedConfig) + }) + + it("should convert stringified number values to number" , () => { + const config = { defaultModelExpandDepth: "5", defaultModelsExpandDepth: "-1" } + + const expectedConfig = { defaultModelExpandDepth: 5, defaultModelsExpandDepth: -1 } + + expect(convertConfigValues(config)).toStrictEqual(expectedConfig) + }) + + it("should convert stringified array values to arrays" , () => { + const config = { supportedSubmitMethods: '["get", "post"]' } + + const expectedConfig = { supportedSubmitMethods: ["get", "post"] } + + expect(convertConfigValues(config)).toStrictEqual(expectedConfig) + }) + + it("should convert stringified object values to objects" , () => { + const config = { syntaxHighlight: '{"theme":"monokai"}' } + + const expectedConfig = { syntaxHighlight: { theme: "monokai" } } + + expect(convertConfigValues(config)).toStrictEqual(expectedConfig) + }) + + it("should not convert string values" , () => { + const config = { defaultModelRendering: "model" } + + const expectedConfig = { defaultModelRendering: "model" } + + expect(convertConfigValues(config)).toStrictEqual(expectedConfig) + }) + }) }) From 1ca3fd66da5c6f8b2d09323353658d9d8e613e41 Mon Sep 17 00:00:00 2001 From: Oliwia Rogala Date: Thu, 11 Apr 2024 14:44:45 +0200 Subject: [PATCH 3/3] move filter to boolean configs --- src/core/utils/convertConfigValues.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/core/utils/convertConfigValues.js b/src/core/utils/convertConfigValues.js index 19dfbad2d6a..54ad62d7674 100644 --- a/src/core/utils/convertConfigValues.js +++ b/src/core/utils/convertConfigValues.js @@ -5,6 +5,7 @@ const booleanConfigs = [ "deepLinking", "displayOperationId", "displayRequestDuration", + "filter", "persistAuthorization", "requestSnippetsEnabled", "showCommonExtensions", @@ -32,10 +33,6 @@ const convertValue = (key, value) => { return value === "null" ? null : value } - if (key === "filter") { - return value === "false" ? false : value - } - if (isBoolean) { return value === "true" ? true : value === "false" ? false : value }