refactor(oas31): introduce selector composition mechanism (#8484)
Refs #8474
This commit is contained in:
50
src/core/plugins/oas31/fn.js
Normal file
50
src/core/plugins/oas31/fn.js
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
export const isOAS31 = (jsSpec) => {
|
||||
const oasVersion = jsSpec.get("openapi")
|
||||
|
||||
return (
|
||||
typeof oasVersion === "string" && /^3\.1\.(?:[1-9]\d*|0)$/.test(oasVersion)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates selector that returns value of the original
|
||||
* selector when spec is OpenAPI 3.1.0., null otherwise.
|
||||
*
|
||||
* @param selector
|
||||
* @returns {function(*, ...[*]): function(*): (*|null)}
|
||||
*/
|
||||
export const createOnlyOAS31Selector =
|
||||
(selector) =>
|
||||
(state, ...args) =>
|
||||
(system) => {
|
||||
if (system.getSystem().specSelectors.isOAS31()) {
|
||||
const selectedValue = selector(state, ...args)
|
||||
return typeof selectedValue === "function"
|
||||
? selectedValue(system)
|
||||
: selectedValue
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates selector that provides system as the
|
||||
* second argument. This allows to create memoized
|
||||
* composed selectors from different plugins.
|
||||
*
|
||||
* @param selector
|
||||
* @returns {function(*, ...[*]): function(*): *}
|
||||
*/
|
||||
export const createSystemSelector =
|
||||
(selector) =>
|
||||
(state, ...args) =>
|
||||
(system) => {
|
||||
const selectedValue = selector(state, system, ...args)
|
||||
return typeof selectedValue === "function"
|
||||
? selectedValue(system)
|
||||
: selectedValue
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
export const isOAS31 = (jsSpec) => {
|
||||
const oasVersion = jsSpec.get("openapi")
|
||||
|
||||
return (
|
||||
typeof oasVersion === "string" && /^3\.1\.(?:[1-9]\d*|0)$/.test(oasVersion)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Selector maker the only calls the passed selector
|
||||
* when spec is of OpenAPI 3.1.0 version.
|
||||
*/
|
||||
export const onlyOAS31 =
|
||||
(selector) =>
|
||||
(state, ...args) =>
|
||||
(system) => {
|
||||
if (system.getSystem().specSelectors.isOAS31()) {
|
||||
const result = selector(state, ...args)
|
||||
return typeof result === "function" ? result(system) : result
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Selector wrapper maker the only wraps the passed selector
|
||||
* when spec is of OpenAPI 3.1.0 version.
|
||||
*/
|
||||
export const onlyOAS31Wrap =
|
||||
(selector) =>
|
||||
(oriSelector, system) =>
|
||||
(state, ...args) => {
|
||||
if (system.getSystem().specSelectors.isOAS31()) {
|
||||
const result = selector(state, ...args)
|
||||
return typeof result === "function" ? result(system) : result
|
||||
} else {
|
||||
return oriSelector(...args)
|
||||
}
|
||||
}
|
||||
@@ -9,48 +9,48 @@ import LicenseWrapper from "./wrap-components/license"
|
||||
import ContactWrapper from "./wrap-components/contact"
|
||||
import InfoWrapper from "./wrap-components/info"
|
||||
import {
|
||||
license,
|
||||
contact,
|
||||
webhooks,
|
||||
license as selectLicense,
|
||||
contact as selectContact,
|
||||
webhooks as selectWebhooks,
|
||||
selectLicenseNameField,
|
||||
selectLicenseUrlField,
|
||||
selectLicenseIdentifierField,
|
||||
selectContactNameField,
|
||||
selectContactEmailField,
|
||||
selectContactUrlField,
|
||||
makeSelectContactUrl,
|
||||
makeIsOAS31,
|
||||
makeSelectLicenseUrl,
|
||||
selectContactUrl,
|
||||
isOAS31 as selectIsOAS31,
|
||||
selectLicenseUrl,
|
||||
selectInfoTitleField,
|
||||
selectInfoSummaryField,
|
||||
selectInfoDescriptionField,
|
||||
selectInfoTermsOfServiceField,
|
||||
makeSelectInfoTermsOfServiceUrl,
|
||||
selectInfoTermsOfServiceUrl,
|
||||
selectExternalDocsDescriptionField,
|
||||
selectExternalDocsUrlField,
|
||||
makeSelectExternalDocsUrl,
|
||||
makeSelectWebhooksOperations,
|
||||
selectExternalDocsUrl,
|
||||
selectWebhooksOperations,
|
||||
} from "./spec-extensions/selectors"
|
||||
import {
|
||||
isOAS3 as isOAS3Wrapper,
|
||||
selectLicenseUrl as selectLicenseUrlWrapper,
|
||||
} from "./spec-extensions/wrap-selectors"
|
||||
import { makeSelectLicenseUrl as makeOAS31SelectLicenseUrl } from "./selectors"
|
||||
import { selectLicenseUrl as selectOAS31LicenseUrl } from "./selectors"
|
||||
import {
|
||||
isOAS31 as isOAS31Fn,
|
||||
createOnlyOAS31Selector as createOnlyOAS31SelectorFn,
|
||||
createSystemSelector as createSystemSelectorFn,
|
||||
} from "./fn"
|
||||
|
||||
const OAS31Plugin = ({ fn }) => {
|
||||
const createSystemSelector = fn.createSystemSelector || createSystemSelectorFn
|
||||
const createOnlyOAS31Selector = fn.createOnlyOAS31Selector || createOnlyOAS31SelectorFn // prettier-ignore
|
||||
|
||||
const OAS31Plugin = () => {
|
||||
return {
|
||||
afterLoad(system) {
|
||||
const oas31Selectors = this.statePlugins.oas31.selectors
|
||||
const specSelectors = this.statePlugins.spec.selectors
|
||||
|
||||
specSelectors.isOAS31 = makeIsOAS31(system)
|
||||
specSelectors.selectLicenseUrl = makeSelectLicenseUrl(system)
|
||||
specSelectors.selectContactUrl = makeSelectContactUrl(system)
|
||||
specSelectors.selectInfoTermsOfServiceUrl = makeSelectInfoTermsOfServiceUrl(system) // prettier-ignore
|
||||
specSelectors.selectExternalDocsUrl = makeSelectExternalDocsUrl(system)
|
||||
specSelectors.selectWebhooksOperations = makeSelectWebhooksOperations(system) // prettier-ignore
|
||||
|
||||
oas31Selectors.selectLicenseUrl = makeOAS31SelectLicenseUrl(system)
|
||||
fn: {
|
||||
isOAs31: isOAS31Fn,
|
||||
createSystemSelector: createSystemSelectorFn,
|
||||
createOnlyOAS31Selector: createOnlyOAS31SelectorFn,
|
||||
},
|
||||
components: {
|
||||
Webhooks,
|
||||
@@ -66,25 +66,32 @@ const OAS31Plugin = () => {
|
||||
statePlugins: {
|
||||
spec: {
|
||||
selectors: {
|
||||
license,
|
||||
isOAS31: createSystemSelector(selectIsOAS31),
|
||||
|
||||
license: selectLicense,
|
||||
selectLicenseNameField,
|
||||
selectLicenseUrlField,
|
||||
selectLicenseIdentifierField,
|
||||
selectLicenseIdentifierField: createOnlyOAS31Selector(selectLicenseIdentifierField), // prettier-ignore
|
||||
selectLicenseUrl: createSystemSelector(selectLicenseUrl),
|
||||
|
||||
contact,
|
||||
contact: selectContact,
|
||||
selectContactNameField,
|
||||
selectContactEmailField,
|
||||
selectContactUrlField,
|
||||
selectContactUrl: createSystemSelector(selectContactUrl),
|
||||
|
||||
selectInfoTitleField,
|
||||
selectInfoSummaryField,
|
||||
selectInfoSummaryField: createOnlyOAS31Selector(selectInfoSummaryField), // prettier-ignore
|
||||
selectInfoDescriptionField,
|
||||
selectInfoTermsOfServiceField,
|
||||
selectInfoTermsOfServiceUrl: createSystemSelector(selectInfoTermsOfServiceUrl), // prettier-ignore
|
||||
|
||||
selectExternalDocsDescriptionField,
|
||||
selectExternalDocsUrlField,
|
||||
selectExternalDocsUrl: createSystemSelector(selectExternalDocsUrl),
|
||||
|
||||
webhooks,
|
||||
webhooks: createOnlyOAS31Selector(selectWebhooks),
|
||||
selectWebhooksOperations: createOnlyOAS31Selector(createSystemSelector(selectWebhooksOperations)), // prettier-ignore
|
||||
},
|
||||
wrapSelectors: {
|
||||
isOAS3: isOAS3Wrapper,
|
||||
@@ -92,7 +99,9 @@ const OAS31Plugin = () => {
|
||||
},
|
||||
},
|
||||
oas31: {
|
||||
selectors: {},
|
||||
selectors: {
|
||||
selectLicenseUrl: createOnlyOAS31Selector(createSystemSelector(selectOAS31LicenseUrl)), // prettier-ignore
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -4,25 +4,21 @@
|
||||
import { createSelector } from "reselect"
|
||||
|
||||
import { safeBuildUrl } from "core/utils/url"
|
||||
import { onlyOAS31 } from "./helpers"
|
||||
|
||||
export const makeSelectLicenseUrl = (system) =>
|
||||
onlyOAS31(
|
||||
createSelector(
|
||||
() => system.specSelectors.url(),
|
||||
() => system.oas3Selectors.selectedServer(),
|
||||
() => system.specSelectors.selectLicenseUrlField(),
|
||||
() => system.specSelectors.selectLicenseIdentifierField(),
|
||||
(specUrl, selectedServer, url, identifier) => {
|
||||
if (url) {
|
||||
return safeBuildUrl(url, specUrl, { selectedServer })
|
||||
}
|
||||
export const selectLicenseUrl = createSelector(
|
||||
(state, system) => system.specSelectors.url(),
|
||||
(state, system) => system.oas3Selectors.selectedServer(),
|
||||
(state, system) => system.specSelectors.selectLicenseUrlField(),
|
||||
(state, system) => system.specSelectors.selectLicenseIdentifierField(),
|
||||
(specUrl, selectedServer, url, identifier) => {
|
||||
if (url) {
|
||||
return safeBuildUrl(url, specUrl, { selectedServer })
|
||||
}
|
||||
|
||||
if (identifier) {
|
||||
return `https://spdx.org/licenses/${identifier}.html`
|
||||
}
|
||||
if (identifier) {
|
||||
return `https://spdx.org/licenses/${identifier}.html`
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
)
|
||||
)
|
||||
return undefined
|
||||
}
|
||||
)
|
||||
|
||||
@@ -5,49 +5,47 @@ import { List, Map } from "immutable"
|
||||
import { createSelector } from "reselect"
|
||||
|
||||
import { safeBuildUrl } from "core/utils/url"
|
||||
import { isOAS31 as isOAS31Helper, onlyOAS31 } from "../helpers"
|
||||
import { isOAS31 as isOAS31Fn } from "../fn"
|
||||
|
||||
const map = Map()
|
||||
|
||||
export const makeIsOAS31 = (system) =>
|
||||
createSelector(() => system.specSelectors.specJson(), isOAS31Helper)
|
||||
export const isOAS31 = createSelector(
|
||||
(state, system) => system.specSelectors.specJson(),
|
||||
isOAS31Fn
|
||||
)
|
||||
|
||||
export const webhooks = onlyOAS31(() => (system) => {
|
||||
export const webhooks = () => (system) => {
|
||||
return system.specSelectors.specJson().get("webhooks", map)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* `specResolvedSubtree` selector is needed as input selector,
|
||||
* so that we regenerate the selected result whenever the lazy
|
||||
* resolution happens.
|
||||
*/
|
||||
export const makeSelectWebhooksOperations = (system) =>
|
||||
onlyOAS31(
|
||||
createSelector(
|
||||
() => system.specSelectors.webhooks(),
|
||||
() => system.specSelectors.validOperationMethods(),
|
||||
() => system.specSelectors.specResolvedSubtree(["webhooks"]),
|
||||
(webhooks, validOperationMethods) => {
|
||||
return webhooks
|
||||
.reduce((allOperations, pathItem, pathItemName) => {
|
||||
const pathItemOperations = pathItem
|
||||
.entrySeq()
|
||||
.filter(([key]) => validOperationMethods.includes(key))
|
||||
.map(([method, operation]) => ({
|
||||
operation: Map({ operation }),
|
||||
method,
|
||||
path: pathItemName,
|
||||
specPath: List(["webhooks", pathItemName, method]),
|
||||
}))
|
||||
export const selectWebhooksOperations = createSelector(
|
||||
(state, system) => system.specSelectors.webhooks(),
|
||||
(state, system) => system.specSelectors.validOperationMethods(),
|
||||
(state, system) => system.specSelectors.specResolvedSubtree(["webhooks"]),
|
||||
(webhooks, validOperationMethods) =>
|
||||
webhooks
|
||||
.reduce((allOperations, pathItem, pathItemName) => {
|
||||
const pathItemOperations = pathItem
|
||||
.entrySeq()
|
||||
.filter(([key]) => validOperationMethods.includes(key))
|
||||
.map(([method, operation]) => ({
|
||||
operation: Map({ operation }),
|
||||
method,
|
||||
path: pathItemName,
|
||||
specPath: List(["webhooks", pathItemName, method]),
|
||||
}))
|
||||
|
||||
return allOperations.concat(pathItemOperations)
|
||||
}, List())
|
||||
.groupBy((operation) => operation.path)
|
||||
.map((operations) => operations.toArray())
|
||||
.toObject()
|
||||
}
|
||||
)
|
||||
)
|
||||
return allOperations.concat(pathItemOperations)
|
||||
}, List())
|
||||
.groupBy((operation) => operation.path)
|
||||
.map((operations) => operations.toArray())
|
||||
.toObject()
|
||||
)
|
||||
|
||||
export const license = () => (system) => {
|
||||
return system.specSelectors.info().get("license", map)
|
||||
@@ -61,23 +59,22 @@ export const selectLicenseUrlField = () => (system) => {
|
||||
return system.specSelectors.license().get("url")
|
||||
}
|
||||
|
||||
export const makeSelectLicenseUrl = (system) =>
|
||||
createSelector(
|
||||
() => system.specSelectors.url(),
|
||||
() => system.oas3Selectors.selectedServer(),
|
||||
() => system.specSelectors.selectLicenseUrlField(),
|
||||
(specUrl, selectedServer, url) => {
|
||||
if (url) {
|
||||
return safeBuildUrl(url, specUrl, { selectedServer })
|
||||
}
|
||||
|
||||
return undefined
|
||||
export const selectLicenseUrl = createSelector(
|
||||
(state, system) => system.specSelectors.url(),
|
||||
(state, system) => system.oas3Selectors.selectedServer(),
|
||||
(state, system) => system.specSelectors.selectLicenseUrlField(),
|
||||
(specUrl, selectedServer, url) => {
|
||||
if (url) {
|
||||
return safeBuildUrl(url, specUrl, { selectedServer })
|
||||
}
|
||||
)
|
||||
|
||||
export const selectLicenseIdentifierField = onlyOAS31(() => (system) => {
|
||||
return undefined
|
||||
}
|
||||
)
|
||||
|
||||
export const selectLicenseIdentifierField = () => (system) => {
|
||||
return system.specSelectors.license().get("identifier")
|
||||
})
|
||||
}
|
||||
|
||||
export const contact = () => (system) => {
|
||||
return system.specSelectors.info().get("contact", map)
|
||||
@@ -95,27 +92,26 @@ export const selectContactUrlField = () => (system) => {
|
||||
return system.specSelectors.contact().get("url")
|
||||
}
|
||||
|
||||
export const makeSelectContactUrl = (system) =>
|
||||
createSelector(
|
||||
() => system.specSelectors.url(),
|
||||
() => system.oas3Selectors.selectedServer(),
|
||||
() => system.specSelectors.selectContactUrlField(),
|
||||
(specUrl, selectedServer, url) => {
|
||||
if (url) {
|
||||
return safeBuildUrl(url, specUrl, { selectedServer })
|
||||
}
|
||||
|
||||
return undefined
|
||||
export const selectContactUrl = createSelector(
|
||||
(state, system) => system.specSelectors.url(),
|
||||
(state, system) => system.oas3Selectors.selectedServer(),
|
||||
(state, system) => system.specSelectors.selectContactUrlField(),
|
||||
(specUrl, selectedServer, url) => {
|
||||
if (url) {
|
||||
return safeBuildUrl(url, specUrl, { selectedServer })
|
||||
}
|
||||
)
|
||||
|
||||
return undefined
|
||||
}
|
||||
)
|
||||
|
||||
export const selectInfoTitleField = () => (system) => {
|
||||
return system.specSelectors.info().get("title")
|
||||
}
|
||||
|
||||
export const selectInfoSummaryField = onlyOAS31(() => (system) => {
|
||||
export const selectInfoSummaryField = () => (system) => {
|
||||
return system.specSelectors.info().get("summary")
|
||||
})
|
||||
}
|
||||
|
||||
export const selectInfoDescriptionField = () => (system) => {
|
||||
return system.specSelectors.info().get("description")
|
||||
@@ -125,19 +121,18 @@ export const selectInfoTermsOfServiceField = () => (system) => {
|
||||
return system.specSelectors.info().get("termsOfService")
|
||||
}
|
||||
|
||||
export const makeSelectInfoTermsOfServiceUrl = (system) =>
|
||||
createSelector(
|
||||
() => system.specSelectors.url(),
|
||||
() => system.oas3Selectors.selectedServer(),
|
||||
() => system.specSelectors.selectInfoTermsOfServiceField(),
|
||||
(specUrl, selectedServer, termsOfService) => {
|
||||
if (termsOfService) {
|
||||
return safeBuildUrl(termsOfService, specUrl, { selectedServer })
|
||||
}
|
||||
|
||||
return undefined
|
||||
export const selectInfoTermsOfServiceUrl = createSelector(
|
||||
(state, system) => system.specSelectors.url(),
|
||||
(state, system) => system.oas3Selectors.selectedServer(),
|
||||
(state, system) => system.specSelectors.selectInfoTermsOfServiceField(),
|
||||
(specUrl, selectedServer, termsOfService) => {
|
||||
if (termsOfService) {
|
||||
return safeBuildUrl(termsOfService, specUrl, { selectedServer })
|
||||
}
|
||||
)
|
||||
|
||||
return undefined
|
||||
}
|
||||
)
|
||||
|
||||
export const selectExternalDocsDescriptionField = () => (system) => {
|
||||
return system.specSelectors.externalDocs().get("description")
|
||||
@@ -147,16 +142,15 @@ export const selectExternalDocsUrlField = () => (system) => {
|
||||
return system.specSelectors.externalDocs().get("url")
|
||||
}
|
||||
|
||||
export const makeSelectExternalDocsUrl = (system) =>
|
||||
createSelector(
|
||||
() => system.specSelectors.url(),
|
||||
() => system.oas3Selectors.selectedServer(),
|
||||
() => system.specSelectors.selectExternalDocsUrlField(),
|
||||
(specUrl, selectedServer, url) => {
|
||||
if (url) {
|
||||
return safeBuildUrl(url, specUrl, { selectedServer })
|
||||
}
|
||||
|
||||
return undefined
|
||||
export const selectExternalDocsUrl = createSelector(
|
||||
(state, system) => system.specSelectors.url(),
|
||||
(state, system) => system.oas3Selectors.selectedServer(),
|
||||
(state, system) => system.specSelectors.selectExternalDocsUrlField(),
|
||||
(specUrl, selectedServer, url) => {
|
||||
if (url) {
|
||||
return safeBuildUrl(url, specUrl, { selectedServer })
|
||||
}
|
||||
)
|
||||
|
||||
return undefined
|
||||
}
|
||||
)
|
||||
|
||||
@@ -1,7 +1,22 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
import { onlyOAS31Wrap } from "../helpers"
|
||||
|
||||
/**
|
||||
* Selector wrapper maker the only wraps the passed selector
|
||||
* when spec is of OpenAPI 3.1.0 version.
|
||||
*/
|
||||
const onlyOAS31Wrap =
|
||||
(selector) =>
|
||||
(oriSelector, system) =>
|
||||
(state, ...args) => {
|
||||
if (system.getSystem().specSelectors.isOAS31()) {
|
||||
const result = selector(state, ...args)
|
||||
return typeof result === "function" ? result(system) : result
|
||||
} else {
|
||||
return oriSelector(...args)
|
||||
}
|
||||
}
|
||||
|
||||
export const isOAS3 =
|
||||
(oriSelector, system) =>
|
||||
|
||||
Reference in New Issue
Block a user