Skip to content

Commit

Permalink
fix(config): cast configuration values into proper types (#9829)
Browse files Browse the repository at this point in the history
Refs #9808
  • Loading branch information
glowcloud committed Apr 18, 2024
1 parent c2b63ab commit 7378821
Show file tree
Hide file tree
Showing 28 changed files with 359 additions and 83 deletions.
4 changes: 2 additions & 2 deletions flavors/swagger-ui-react/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ SwaggerUI.defaultProps = {
deepLinking: false,
showExtensions: false,
showCommonExtensions: false,
filter: null,
filter: false,
requestSnippetsEnabled: false,
requestSnippets: {
generators: {
Expand All @@ -164,7 +164,7 @@ SwaggerUI.defaultProps = {
},
tryItOutEnabled: false,
displayRequestDuration: false,
withCredentials: undefined,
withCredentials: false,
persistAuthorization: false,
oauth2RedirectUrl: undefined,
}
Expand Down
4 changes: 2 additions & 2 deletions src/core/components/live-response.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,10 @@ export default class LiveResponse extends React.Component {

return (
<div>
{ curlRequest && (requestSnippetsEnabled === true || requestSnippetsEnabled === "true"
{ curlRequest && requestSnippetsEnabled
? <RequestSnippets request={ curlRequest }/>
: <Curl request={ curlRequest } />
)}
}
{ url && <div>
<div className="request-url">
<h4>Request URL</h4>
Expand Down
4 changes: 1 addition & 3 deletions src/core/components/operation-tag.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,6 @@ export default class OperationTag extends React.Component {
deepLinking,
} = getConfigs()

const isDeepLinkingEnabled = deepLinking && deepLinking !== "false"

const Collapse = getComponent("Collapse")
const Markdown = getComponent("Markdown", true)
const DeepLink = getComponent("DeepLink")
Expand Down Expand Up @@ -80,7 +78,7 @@ export default class OperationTag extends React.Component {
data-is-open={showTag}
>
<DeepLink
enabled={isDeepLinkingEnabled}
enabled={deepLinking}
isShown={showTag}
path={createDeepLinkPath(tag)}
text={tag} />
Expand Down
6 changes: 3 additions & 3 deletions src/core/config/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ const defaultOptions = Object.freeze({
urls: null,
layout: "BaseLayout",
docExpansion: "list",
maxDisplayedTags: null,
filter: null,
maxDisplayedTags: -1,
filter: false,
validatorUrl: "https://validator.swagger.io/validator",
oauth2RedirectUrl: `${window.location.protocol}//${window.location.host}${window.location.pathname.substring(0, window.location.pathname.lastIndexOf("/"))}/oauth2-redirect.html`,
persistAuthorization: false,
Expand All @@ -30,7 +30,7 @@ const defaultOptions = Object.freeze({
defaultModelsExpandDepth: 1,
showExtensions: false,
showCommonExtensions: false,
withCredentials: undefined,
withCredentials: false,
requestSnippetsEnabled: false,
requestSnippets: {
generators: {
Expand Down
4 changes: 2 additions & 2 deletions src/core/config/factorization/store.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
/**
* @prettier
*/
import merge from "../merge"
import deepExtend from "deep-extend"

const storeFactorization = (options) => {
const state = merge(
const state = deepExtend(
{
layout: {
layout: options.layout,
Expand Down
5 changes: 3 additions & 2 deletions src/core/config/merge.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@
*
* NOTE1: lodash.merge & lodash.mergeWith prefers to ignore undefined values
* NOTE2: special handling of `domNode` option is now required as `deep-extend` will corrupt it (lodash.merge handles it correctly)
* NOTE3: oauth2RedirectUrl and withCredentials options can be set to undefined. By expecting null instead of undefined, we can't use lodash.merge.
* NOTE3: oauth2RedirectUrl option can be set to undefined. By expecting null instead of undefined, we can't use lodash.merge.
* NOTE4: urls.primaryName needs to handled in special way, because it's an arbitrary property on Array instance
*
* TODO(vladimir.gorej@gmail.com): remove deep-extend in favor of lodash.merge
*/
import deepExtend from "deep-extend"
import typeCast from "./type-cast"

const merge = (target, ...sources) => {
let domNode = Symbol.for("domNode")
Expand Down Expand Up @@ -51,7 +52,7 @@ const merge = (target, ...sources) => {
merged.urls.primaryName = primaryName
}

return merged
return typeCast(merged)
}

export default merge
24 changes: 24 additions & 0 deletions src/core/config/type-cast/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* @prettier
*/
import has from "lodash/has"
import get from "lodash/get"
import set from "lodash/fp/set"

import typeCasters from "./mappings"

const typeCast = (options) => {
return Object.entries(typeCasters).reduce(
(acc, [optionPath, { typeCaster, defaultValue }]) => {
if (has(acc, optionPath)) {
const uncasted = get(acc, optionPath)
const casted = typeCaster(uncasted, defaultValue)
acc = set(optionPath, casted, acc)
}
return acc
},
{ ...options }
)
}

export default typeCast
115 changes: 115 additions & 0 deletions src/core/config/type-cast/mappings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/**
* @prettier
*/
import arrayTypeCaster from "./type-casters/array"
import booleanTypeCaster from "./type-casters/boolean"
import domNodeTypeCaster from "./type-casters/dom-node"
import filterTypeCaster from "./type-casters/filter"
import nullableArrayTypeCaster from "./type-casters/nullable-array"
import nullableStringTypeCaster from "./type-casters/nullable-string"
import numberTypeCaster from "./type-casters/number"
import objectTypeCaster from "./type-casters/object"
import stringTypeCaster from "./type-casters/string"
import syntaxHighlightTypeCaster from "./type-casters/syntax-highlight"
import undefinedStringTypeCaster from "./type-casters/undefined-string"
import defaultOptions from "../defaults"

const typeCasters = {
configUrl: { typeCaster: stringTypeCaster },
deepLinking: {
typeCaster: booleanTypeCaster,
defaultValue: defaultOptions.deepLinking,
},
defaultModelExpandDepth: {
typeCaster: numberTypeCaster,
defaultValue: defaultOptions.defaultModelExpandDepth,
},
defaultModelRendering: { typeCaster: stringTypeCaster },
defaultModelsExpandDepth: {
typeCaster: numberTypeCaster,
defaultValue: defaultOptions.defaultModelsExpandDepth,
},
displayOperationId: {
typeCaster: booleanTypeCaster,
defaultValue: defaultOptions.displayOperationId,
},
displayRequestDuration: {
typeCaster: booleanTypeCaster,
defaultValue: defaultOptions.displayRequestDuration,
},
docExpansion: { typeCaster: stringTypeCaster },
dom_id: { typeCaster: nullableStringTypeCaster },
domNode: { typeCaster: domNodeTypeCaster },
filter: { typeCaster: filterTypeCaster },
layout: { typeCaster: stringTypeCaster },
maxDisplayedTags: {
typeCaster: numberTypeCaster,
defaultValue: defaultOptions.maxDisplayedTags,
},
oauth2RedirectUrl: { typeCaster: undefinedStringTypeCaster },
persistAuthorization: {
typeCaster: booleanTypeCaster,
defaultValue: defaultOptions.persistAuthorization,
},
plugins: {
typeCaster: arrayTypeCaster,
defaultValue: defaultOptions.plugins,
},
pluginsOptions: {
typeCaster: objectTypeCaster,
pluginsOptions: defaultOptions.pluginsOptions,
},
"pluginsOptions.pluginsLoadType": { typeCaster: stringTypeCaster },
presets: {
typeCaster: arrayTypeCaster,
defaultValue: defaultOptions.presets,
},
requestSnippets: {
typeCaster: objectTypeCaster,
defaultValue: defaultOptions.requestSnippets,
},
requestSnippetsEnabled: {
typeCaster: booleanTypeCaster,
defaultValue: defaultOptions.requestSnippetsEnabled,
},
showCommonExtensions: {
typeCaster: booleanTypeCaster,
defaultValue: defaultOptions.showCommonExtensions,
},
showExtensions: {
typeCaster: booleanTypeCaster,
defaultValue: defaultOptions.showExtensions,
},
showMutatedRequest: {
typeCaster: booleanTypeCaster,
defaultValue: defaultOptions.showMutatedRequest,
},
spec: { typeCaster: objectTypeCaster, defaultValue: defaultOptions.spec },
supportedSubmitMethods: {
typeCaster: arrayTypeCaster,
defaultValue: defaultOptions.supportedSubmitMethods,
},
syntaxHighlight: {
typeCaster: syntaxHighlightTypeCaster,
defaultValue: defaultOptions.syntaxHighlight,
},
"syntaxHighlight.activated": {
typeCaster: booleanTypeCaster,
defaultValue: defaultOptions.syntaxHighlight.activated,
},
"syntaxHighlight.theme": { typeCaster: stringTypeCaster },
tryItOutEnabled: {
typeCaster: booleanTypeCaster,
defaultValue: defaultOptions.tryItOutEnabled,
},
url: { typeCaster: stringTypeCaster },
urls: { typeCaster: nullableArrayTypeCaster },
"urls.primaryName": { typeCaster: stringTypeCaster },
validatorUrl: { typeCaster: nullableStringTypeCaster },
withCredentials: {
typeCaster: booleanTypeCaster,
defaultValue: defaultOptions.withCredentials,
},
}

export default typeCasters
7 changes: 7 additions & 0 deletions src/core/config/type-cast/type-casters/array.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* @prettier
*/
const arrayTypeCaster = (value, defaultValue = []) =>
Array.isArray(value) ? value : defaultValue

export default arrayTypeCaster
11 changes: 11 additions & 0 deletions src/core/config/type-cast/type-casters/boolean.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* @prettier
*/
const booleanTypeCaster = (value, defaultValue = false) =>
value === true || value === "true" || value === 1 || value === "1"
? true
: value === false || value === "false" || value === 0 || value === "0"
? false
: defaultValue

export default booleanTypeCaster
7 changes: 7 additions & 0 deletions src/core/config/type-cast/type-casters/dom-node.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* @prettier
*/
const domNodeTypeCaster = (value) =>
value === null || value === "null" ? null : value

export default domNodeTypeCaster
11 changes: 11 additions & 0 deletions src/core/config/type-cast/type-casters/filter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* @prettier
*/
import booleanTypeCaster from "./boolean"

const filterTypeCaster = (value) => {
const defaultValue = String(value)
return booleanTypeCaster(value, defaultValue)
}

export default filterTypeCaster
6 changes: 6 additions & 0 deletions src/core/config/type-cast/type-casters/nullable-array.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* @prettier
*/
const nullableArrayTypeCaster = (value) => (Array.isArray(value) ? value : null)

export default nullableArrayTypeCaster
7 changes: 7 additions & 0 deletions src/core/config/type-cast/type-casters/nullable-string.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* @prettier
*/
const nullableStringTypeCaster = (value) =>
value === null || value === "null" ? null : String(value)

export default nullableStringTypeCaster
9 changes: 9 additions & 0 deletions src/core/config/type-cast/type-casters/number.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* @prettier
*/
const numberTypeCaster = (value, defaultValue = -1) => {
const parsedValue = parseInt(value, 10)
return Number.isNaN(parsedValue) ? defaultValue : parsedValue
}

export default numberTypeCaster
9 changes: 9 additions & 0 deletions src/core/config/type-cast/type-casters/object.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* @prettier
*/
import isPlainObject from "lodash/isPlainObject"

const objectTypeCaster = (value, defaultValue = {}) =>
isPlainObject(value) ? value : defaultValue

export default objectTypeCaster
6 changes: 6 additions & 0 deletions src/core/config/type-cast/type-casters/string.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* @prettier
*/
const stringTypeCaster = (value) => String(value)

export default stringTypeCaster
14 changes: 14 additions & 0 deletions src/core/config/type-cast/type-casters/syntax-highlight.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* @prettier
*/
import isPlainObject from "lodash/isPlainObject"

const syntaxHighlightTypeCaster = (value, defaultValue) => {
return isPlainObject(value)
? value
: value === false || value === "false" || value === 0 || value === "0"
? { activated: false }
: defaultValue
}

export default syntaxHighlightTypeCaster
7 changes: 7 additions & 0 deletions src/core/config/type-cast/type-casters/undefined-string.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* @prettier
*/
const undefinedStringTypeCaster = (value) =>
value === undefined || value === "undefined" ? undefined : String(value)

export default undefinedStringTypeCaster
5 changes: 2 additions & 3 deletions src/core/containers/OperationContainer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default class OperationContainer extends PureComponent {
const { tryItOutEnabled } = props.getConfigs()

this.state = {
tryItOutEnabled: tryItOutEnabled === true || tryItOutEnabled === "true",
tryItOutEnabled,
executeInProgress: false
}
}
Expand Down Expand Up @@ -61,14 +61,13 @@ export default class OperationContainer extends PureComponent {
const showSummary = layoutSelectors.showSummary()
const operationId = op.getIn(["operation", "__originalOperationId"]) || op.getIn(["operation", "operationId"]) || opId(op.get("operation"), props.path, props.method) || op.get("id")
const isShownKey = ["operations", props.tag, operationId]
const isDeepLinkingEnabled = deepLinking && deepLinking !== "false"
const allowTryItOut = supportedSubmitMethods.indexOf(props.method) >= 0 && (typeof props.allowTryItOut === "undefined" ?
props.specSelectors.allowTryItOutFor(props.path, props.method) : props.allowTryItOut)
const security = op.getIn(["operation", "security"]) || props.specSelectors.security()

return {
operationId,
isDeepLinkingEnabled,
isDeepLinkingEnabled: deepLinking,
showSummary,
displayOperationId,
displayRequestDuration,
Expand Down
4 changes: 2 additions & 2 deletions src/core/containers/filter.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ export default class FilterContainer extends React.Component {

return (
<div>
{filter === null || filter === false || filter === "false" ? null :
{filter === false ? null :
<div className="filter-container">
<Col className="filter wrapper" mobile={12}>
<input className={classNames.join(" ")} placeholder="Filter by tag" type="text"
onChange={this.onFilterChange} value={filter === true || filter === "true" ? "" : filter}
onChange={this.onFilterChange} value={typeof filter === "string" ? filter : ""}
disabled={isLoading}/>
</Col>
</div>
Expand Down

0 comments on commit 7378821

Please sign in to comment.