diff --git a/src/core/plugins/oas31/fn.js b/src/core/plugins/oas31/fn.js new file mode 100644 index 00000000..55597e5b --- /dev/null +++ b/src/core/plugins/oas31/fn.js @@ -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 + } diff --git a/src/core/plugins/oas31/helpers.js b/src/core/plugins/oas31/helpers.js deleted file mode 100644 index c9357310..00000000 --- a/src/core/plugins/oas31/helpers.js +++ /dev/null @@ -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) - } - } diff --git a/src/core/plugins/oas31/index.js b/src/core/plugins/oas31/index.js index d20f7116..6c008988 100644 --- a/src/core/plugins/oas31/index.js +++ b/src/core/plugins/oas31/index.js @@ -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 + }, }, }, } diff --git a/src/core/plugins/oas31/selectors.js b/src/core/plugins/oas31/selectors.js index f9d19522..4d96237c 100644 --- a/src/core/plugins/oas31/selectors.js +++ b/src/core/plugins/oas31/selectors.js @@ -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 + } +) diff --git a/src/core/plugins/oas31/spec-extensions/selectors.js b/src/core/plugins/oas31/spec-extensions/selectors.js index acf2092b..fedf1ee0 100644 --- a/src/core/plugins/oas31/spec-extensions/selectors.js +++ b/src/core/plugins/oas31/spec-extensions/selectors.js @@ -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 + } +) diff --git a/src/core/plugins/oas31/spec-extensions/wrap-selectors.js b/src/core/plugins/oas31/spec-extensions/wrap-selectors.js index 0631d49c..1411f291 100644 --- a/src/core/plugins/oas31/spec-extensions/wrap-selectors.js +++ b/src/core/plugins/oas31/spec-extensions/wrap-selectors.js @@ -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) =>