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 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
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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) =>
|
||||||
|
|||||||
Reference in New Issue
Block a user