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

View File

@@ -4,15 +4,12 @@
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(),
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 })
@@ -25,4 +22,3 @@ export const makeSelectLicenseUrl = (system) =>
return undefined
}
)
)

View File

@@ -5,30 +5,30 @@ 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
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()
@@ -45,8 +45,6 @@ export const makeSelectWebhooksOperations = (system) =>
.groupBy((operation) => operation.path)
.map((operations) => operations.toArray())
.toObject()
}
)
)
export const license = () => (system) => {
@@ -61,11 +59,10 @@ export const selectLicenseUrlField = () => (system) => {
return system.specSelectors.license().get("url")
}
export const makeSelectLicenseUrl = (system) =>
createSelector(
() => system.specSelectors.url(),
() => system.oas3Selectors.selectedServer(),
() => system.specSelectors.selectLicenseUrlField(),
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 })
@@ -75,9 +72,9 @@ export const makeSelectLicenseUrl = (system) =>
}
)
export const selectLicenseIdentifierField = onlyOAS31(() => (system) => {
export const selectLicenseIdentifierField = () => (system) => {
return system.specSelectors.license().get("identifier")
})
}
export const contact = () => (system) => {
return system.specSelectors.info().get("contact", map)
@@ -95,11 +92,10 @@ export const selectContactUrlField = () => (system) => {
return system.specSelectors.contact().get("url")
}
export const makeSelectContactUrl = (system) =>
createSelector(
() => system.specSelectors.url(),
() => system.oas3Selectors.selectedServer(),
() => system.specSelectors.selectContactUrlField(),
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 })
@@ -113,9 +109,9 @@ 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,11 +121,10 @@ export const selectInfoTermsOfServiceField = () => (system) => {
return system.specSelectors.info().get("termsOfService")
}
export const makeSelectInfoTermsOfServiceUrl = (system) =>
createSelector(
() => system.specSelectors.url(),
() => system.oas3Selectors.selectedServer(),
() => system.specSelectors.selectInfoTermsOfServiceField(),
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 })
@@ -147,11 +142,10 @@ export const selectExternalDocsUrlField = () => (system) => {
return system.specSelectors.externalDocs().get("url")
}
export const makeSelectExternalDocsUrl = (system) =>
createSelector(
() => system.specSelectors.url(),
() => system.oas3Selectors.selectedServer(),
() => system.specSelectors.selectExternalDocsUrlField(),
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 })

View File

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