refactor(oas31): introduce selector composition mechanism (#8484)

Refs #8474
This commit is contained in:
Vladimír Gorej
2023-03-20 08:34:11 +01:00
committed by GitHub
parent 47e12f1de3
commit d7099793a4
6 changed files with 196 additions and 175 deletions

View 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
}

View File

@@ -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)
}
}

View File

@@ -9,48 +9,48 @@ import LicenseWrapper from "./wrap-components/license"
import ContactWrapper from "./wrap-components/contact" import ContactWrapper from "./wrap-components/contact"
import InfoWrapper from "./wrap-components/info" import InfoWrapper from "./wrap-components/info"
import { import {
license, license as selectLicense,
contact, contact as selectContact,
webhooks, webhooks as selectWebhooks,
selectLicenseNameField, selectLicenseNameField,
selectLicenseUrlField, selectLicenseUrlField,
selectLicenseIdentifierField, selectLicenseIdentifierField,
selectContactNameField, selectContactNameField,
selectContactEmailField, selectContactEmailField,
selectContactUrlField, selectContactUrlField,
makeSelectContactUrl, selectContactUrl,
makeIsOAS31, isOAS31 as selectIsOAS31,
makeSelectLicenseUrl, selectLicenseUrl,
selectInfoTitleField, selectInfoTitleField,
selectInfoSummaryField, selectInfoSummaryField,
selectInfoDescriptionField, selectInfoDescriptionField,
selectInfoTermsOfServiceField, selectInfoTermsOfServiceField,
makeSelectInfoTermsOfServiceUrl, selectInfoTermsOfServiceUrl,
selectExternalDocsDescriptionField, selectExternalDocsDescriptionField,
selectExternalDocsUrlField, selectExternalDocsUrlField,
makeSelectExternalDocsUrl, selectExternalDocsUrl,
makeSelectWebhooksOperations, selectWebhooksOperations,
} from "./spec-extensions/selectors" } from "./spec-extensions/selectors"
import { import {
isOAS3 as isOAS3Wrapper, isOAS3 as isOAS3Wrapper,
selectLicenseUrl as selectLicenseUrlWrapper, selectLicenseUrl as selectLicenseUrlWrapper,
} from "./spec-extensions/wrap-selectors" } 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 { return {
afterLoad(system) { fn: {
const oas31Selectors = this.statePlugins.oas31.selectors isOAs31: isOAS31Fn,
const specSelectors = this.statePlugins.spec.selectors createSystemSelector: createSystemSelectorFn,
createOnlyOAS31Selector: createOnlyOAS31SelectorFn,
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)
}, },
components: { components: {
Webhooks, Webhooks,
@@ -66,25 +66,32 @@ const OAS31Plugin = () => {
statePlugins: { statePlugins: {
spec: { spec: {
selectors: { selectors: {
license, isOAS31: createSystemSelector(selectIsOAS31),
license: selectLicense,
selectLicenseNameField, selectLicenseNameField,
selectLicenseUrlField, selectLicenseUrlField,
selectLicenseIdentifierField, selectLicenseIdentifierField: createOnlyOAS31Selector(selectLicenseIdentifierField), // prettier-ignore
selectLicenseUrl: createSystemSelector(selectLicenseUrl),
contact, contact: selectContact,
selectContactNameField, selectContactNameField,
selectContactEmailField, selectContactEmailField,
selectContactUrlField, selectContactUrlField,
selectContactUrl: createSystemSelector(selectContactUrl),
selectInfoTitleField, selectInfoTitleField,
selectInfoSummaryField, selectInfoSummaryField: createOnlyOAS31Selector(selectInfoSummaryField), // prettier-ignore
selectInfoDescriptionField, selectInfoDescriptionField,
selectInfoTermsOfServiceField, selectInfoTermsOfServiceField,
selectInfoTermsOfServiceUrl: createSystemSelector(selectInfoTermsOfServiceUrl), // prettier-ignore
selectExternalDocsDescriptionField, selectExternalDocsDescriptionField,
selectExternalDocsUrlField, selectExternalDocsUrlField,
selectExternalDocsUrl: createSystemSelector(selectExternalDocsUrl),
webhooks, webhooks: createOnlyOAS31Selector(selectWebhooks),
selectWebhooksOperations: createOnlyOAS31Selector(createSystemSelector(selectWebhooksOperations)), // prettier-ignore
}, },
wrapSelectors: { wrapSelectors: {
isOAS3: isOAS3Wrapper, isOAS3: isOAS3Wrapper,
@@ -92,7 +99,9 @@ const OAS31Plugin = () => {
}, },
}, },
oas31: { oas31: {
selectors: {}, selectors: {
selectLicenseUrl: createOnlyOAS31Selector(createSystemSelector(selectOAS31LicenseUrl)), // prettier-ignore
},
}, },
}, },
} }

View File

@@ -4,15 +4,12 @@
import { createSelector } from "reselect" import { createSelector } from "reselect"
import { safeBuildUrl } from "core/utils/url" import { safeBuildUrl } from "core/utils/url"
import { onlyOAS31 } from "./helpers"
export const makeSelectLicenseUrl = (system) => export const selectLicenseUrl = createSelector(
onlyOAS31( (state, system) => system.specSelectors.url(),
createSelector( (state, system) => system.oas3Selectors.selectedServer(),
() => system.specSelectors.url(), (state, system) => system.specSelectors.selectLicenseUrlField(),
() => system.oas3Selectors.selectedServer(), (state, system) => system.specSelectors.selectLicenseIdentifierField(),
() => system.specSelectors.selectLicenseUrlField(),
() => system.specSelectors.selectLicenseIdentifierField(),
(specUrl, selectedServer, url, identifier) => { (specUrl, selectedServer, url, identifier) => {
if (url) { if (url) {
return safeBuildUrl(url, specUrl, { selectedServer }) return safeBuildUrl(url, specUrl, { selectedServer })
@@ -24,5 +21,4 @@ export const makeSelectLicenseUrl = (system) =>
return undefined return undefined
} }
) )
)

View File

@@ -5,30 +5,30 @@ import { List, Map } from "immutable"
import { createSelector } from "reselect" import { createSelector } from "reselect"
import { safeBuildUrl } from "core/utils/url" import { safeBuildUrl } from "core/utils/url"
import { isOAS31 as isOAS31Helper, onlyOAS31 } from "../helpers" import { isOAS31 as isOAS31Fn } from "../fn"
const map = Map() const map = Map()
export const makeIsOAS31 = (system) => export const isOAS31 = createSelector(
createSelector(() => system.specSelectors.specJson(), isOAS31Helper) (state, system) => system.specSelectors.specJson(),
isOAS31Fn
)
export const webhooks = onlyOAS31(() => (system) => { export const webhooks = () => (system) => {
return system.specSelectors.specJson().get("webhooks", map) return system.specSelectors.specJson().get("webhooks", map)
}) }
/** /**
* `specResolvedSubtree` selector is needed as input selector, * `specResolvedSubtree` selector is needed as input selector,
* so that we regenerate the selected result whenever the lazy * so that we regenerate the selected result whenever the lazy
* resolution happens. * resolution happens.
*/ */
export const makeSelectWebhooksOperations = (system) => export const selectWebhooksOperations = createSelector(
onlyOAS31( (state, system) => system.specSelectors.webhooks(),
createSelector( (state, system) => system.specSelectors.validOperationMethods(),
() => system.specSelectors.webhooks(), (state, system) => system.specSelectors.specResolvedSubtree(["webhooks"]),
() => system.specSelectors.validOperationMethods(), (webhooks, validOperationMethods) =>
() => system.specSelectors.specResolvedSubtree(["webhooks"]), webhooks
(webhooks, validOperationMethods) => {
return webhooks
.reduce((allOperations, pathItem, pathItemName) => { .reduce((allOperations, pathItem, pathItemName) => {
const pathItemOperations = pathItem const pathItemOperations = pathItem
.entrySeq() .entrySeq()
@@ -45,9 +45,7 @@ export const makeSelectWebhooksOperations = (system) =>
.groupBy((operation) => operation.path) .groupBy((operation) => operation.path)
.map((operations) => operations.toArray()) .map((operations) => operations.toArray())
.toObject() .toObject()
} )
)
)
export const license = () => (system) => { export const license = () => (system) => {
return system.specSelectors.info().get("license", map) return system.specSelectors.info().get("license", map)
@@ -61,11 +59,10 @@ export const selectLicenseUrlField = () => (system) => {
return system.specSelectors.license().get("url") return system.specSelectors.license().get("url")
} }
export const makeSelectLicenseUrl = (system) => export const selectLicenseUrl = createSelector(
createSelector( (state, system) => system.specSelectors.url(),
() => system.specSelectors.url(), (state, system) => system.oas3Selectors.selectedServer(),
() => system.oas3Selectors.selectedServer(), (state, system) => system.specSelectors.selectLicenseUrlField(),
() => system.specSelectors.selectLicenseUrlField(),
(specUrl, selectedServer, url) => { (specUrl, selectedServer, url) => {
if (url) { if (url) {
return safeBuildUrl(url, specUrl, { selectedServer }) return safeBuildUrl(url, specUrl, { selectedServer })
@@ -73,11 +70,11 @@ export const makeSelectLicenseUrl = (system) =>
return undefined return undefined
} }
) )
export const selectLicenseIdentifierField = onlyOAS31(() => (system) => { export const selectLicenseIdentifierField = () => (system) => {
return system.specSelectors.license().get("identifier") return system.specSelectors.license().get("identifier")
}) }
export const contact = () => (system) => { export const contact = () => (system) => {
return system.specSelectors.info().get("contact", map) return system.specSelectors.info().get("contact", map)
@@ -95,11 +92,10 @@ export const selectContactUrlField = () => (system) => {
return system.specSelectors.contact().get("url") return system.specSelectors.contact().get("url")
} }
export const makeSelectContactUrl = (system) => export const selectContactUrl = createSelector(
createSelector( (state, system) => system.specSelectors.url(),
() => system.specSelectors.url(), (state, system) => system.oas3Selectors.selectedServer(),
() => system.oas3Selectors.selectedServer(), (state, system) => system.specSelectors.selectContactUrlField(),
() => system.specSelectors.selectContactUrlField(),
(specUrl, selectedServer, url) => { (specUrl, selectedServer, url) => {
if (url) { if (url) {
return safeBuildUrl(url, specUrl, { selectedServer }) return safeBuildUrl(url, specUrl, { selectedServer })
@@ -107,15 +103,15 @@ export const makeSelectContactUrl = (system) =>
return undefined return undefined
} }
) )
export const selectInfoTitleField = () => (system) => { export const selectInfoTitleField = () => (system) => {
return system.specSelectors.info().get("title") return system.specSelectors.info().get("title")
} }
export const selectInfoSummaryField = onlyOAS31(() => (system) => { export const selectInfoSummaryField = () => (system) => {
return system.specSelectors.info().get("summary") return system.specSelectors.info().get("summary")
}) }
export const selectInfoDescriptionField = () => (system) => { export const selectInfoDescriptionField = () => (system) => {
return system.specSelectors.info().get("description") return system.specSelectors.info().get("description")
@@ -125,11 +121,10 @@ export const selectInfoTermsOfServiceField = () => (system) => {
return system.specSelectors.info().get("termsOfService") return system.specSelectors.info().get("termsOfService")
} }
export const makeSelectInfoTermsOfServiceUrl = (system) => export const selectInfoTermsOfServiceUrl = createSelector(
createSelector( (state, system) => system.specSelectors.url(),
() => system.specSelectors.url(), (state, system) => system.oas3Selectors.selectedServer(),
() => system.oas3Selectors.selectedServer(), (state, system) => system.specSelectors.selectInfoTermsOfServiceField(),
() => system.specSelectors.selectInfoTermsOfServiceField(),
(specUrl, selectedServer, termsOfService) => { (specUrl, selectedServer, termsOfService) => {
if (termsOfService) { if (termsOfService) {
return safeBuildUrl(termsOfService, specUrl, { selectedServer }) return safeBuildUrl(termsOfService, specUrl, { selectedServer })
@@ -137,7 +132,7 @@ export const makeSelectInfoTermsOfServiceUrl = (system) =>
return undefined return undefined
} }
) )
export const selectExternalDocsDescriptionField = () => (system) => { export const selectExternalDocsDescriptionField = () => (system) => {
return system.specSelectors.externalDocs().get("description") return system.specSelectors.externalDocs().get("description")
@@ -147,11 +142,10 @@ export const selectExternalDocsUrlField = () => (system) => {
return system.specSelectors.externalDocs().get("url") return system.specSelectors.externalDocs().get("url")
} }
export const makeSelectExternalDocsUrl = (system) => export const selectExternalDocsUrl = createSelector(
createSelector( (state, system) => system.specSelectors.url(),
() => system.specSelectors.url(), (state, system) => system.oas3Selectors.selectedServer(),
() => system.oas3Selectors.selectedServer(), (state, system) => system.specSelectors.selectExternalDocsUrlField(),
() => system.specSelectors.selectExternalDocsUrlField(),
(specUrl, selectedServer, url) => { (specUrl, selectedServer, url) => {
if (url) { if (url) {
return safeBuildUrl(url, specUrl, { selectedServer }) return safeBuildUrl(url, specUrl, { selectedServer })
@@ -159,4 +153,4 @@ export const makeSelectExternalDocsUrl = (system) =>
return undefined return undefined
} }
) )

View File

@@ -1,7 +1,22 @@
/** /**
* @prettier * @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 = export const isOAS3 =
(oriSelector, system) => (oriSelector, system) =>