refactor(oas31): simplify Webhooks component by utilizing selectors (#8481)
Refs #8474
This commit is contained in:
@@ -126,6 +126,7 @@ export default class BaseLayout extends React.Component {
|
|||||||
<Operations />
|
<Operations />
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
{isOAS31 && (
|
{isOAS31 && (
|
||||||
<Row className="webhooks-container">
|
<Row className="webhooks-container">
|
||||||
<Col mobile={12} desktop={12}>
|
<Col mobile={12} desktop={12}>
|
||||||
@@ -133,6 +134,7 @@ export default class BaseLayout extends React.Component {
|
|||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Row>
|
<Row>
|
||||||
<Col mobile={12} desktop={12}>
|
<Col mobile={12} desktop={12}>
|
||||||
<Models />
|
<Models />
|
||||||
|
|||||||
@@ -2,13 +2,6 @@ import React from "react"
|
|||||||
import PropTypes from "prop-types"
|
import PropTypes from "prop-types"
|
||||||
import Im from "immutable"
|
import Im from "immutable"
|
||||||
|
|
||||||
const SWAGGER2_OPERATION_METHODS = [
|
|
||||||
"get", "put", "post", "delete", "options", "head", "patch"
|
|
||||||
]
|
|
||||||
|
|
||||||
const OAS3_OPERATION_METHODS = SWAGGER2_OPERATION_METHODS.concat(["trace"])
|
|
||||||
|
|
||||||
|
|
||||||
export default class Operations extends React.Component {
|
export default class Operations extends React.Component {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
@@ -53,6 +46,7 @@ export default class Operations extends React.Component {
|
|||||||
layoutActions,
|
layoutActions,
|
||||||
getConfigs,
|
getConfigs,
|
||||||
} = this.props
|
} = this.props
|
||||||
|
const validOperationMethods = specSelectors.validOperationMethods()
|
||||||
const OperationContainer = getComponent("OperationContainer", true)
|
const OperationContainer = getComponent("OperationContainer", true)
|
||||||
const OperationTag = getComponent("OperationTag")
|
const OperationTag = getComponent("OperationTag")
|
||||||
const operations = tagObj.get("operations")
|
const operations = tagObj.get("operations")
|
||||||
@@ -74,16 +68,7 @@ export default class Operations extends React.Component {
|
|||||||
const method = op.get("method")
|
const method = op.get("method")
|
||||||
const specPath = Im.List(["paths", path, method])
|
const specPath = Im.List(["paths", path, method])
|
||||||
|
|
||||||
|
if (validOperationMethods.indexOf(method) === -1) {
|
||||||
// FIXME: (someday) this logic should probably be in a selector,
|
|
||||||
// but doing so would require further opening up
|
|
||||||
// selectors to the plugin system, to allow for dynamic
|
|
||||||
// overriding of low-level selectors that other selectors
|
|
||||||
// rely on. --KS, 12/17
|
|
||||||
const validMethods = specSelectors.isOAS3() ?
|
|
||||||
OAS3_OPERATION_METHODS : SWAGGER2_OPERATION_METHODS
|
|
||||||
|
|
||||||
if (validMethods.indexOf(method) === -1) {
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
* @prettier
|
* @prettier
|
||||||
*/
|
*/
|
||||||
import { OrderedMap, Map, List } from "immutable"
|
import { OrderedMap, Map, List } from "immutable"
|
||||||
|
import { createSelector } from "reselect"
|
||||||
|
|
||||||
import { getDefaultRequestBodyValue } from "./components/request-body"
|
import { getDefaultRequestBodyValue } from "./components/request-body"
|
||||||
import { stringify } from "../../utils"
|
import { stringify } from "../../utils"
|
||||||
|
|
||||||
@@ -279,3 +281,14 @@ export const validateShallowRequired = (
|
|||||||
})
|
})
|
||||||
return missingRequiredKeys
|
return missingRequiredKeys
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const validOperationMethods = createSelector(() => [
|
||||||
|
"get",
|
||||||
|
"put",
|
||||||
|
"post",
|
||||||
|
"delete",
|
||||||
|
"options",
|
||||||
|
"head",
|
||||||
|
"patch",
|
||||||
|
"trace",
|
||||||
|
])
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ function onlyOAS3(selector) {
|
|||||||
(...args) => {
|
(...args) => {
|
||||||
if (system.getSystem().specSelectors.isOAS3()) {
|
if (system.getSystem().specSelectors.isOAS3()) {
|
||||||
const result = selector(...args)
|
const result = selector(...args)
|
||||||
return typeof result === "function" ? result(system, ...args) : result
|
return typeof result === "function" ? result(system) : result
|
||||||
} else {
|
} else {
|
||||||
return ori(...args)
|
return ori(...args)
|
||||||
}
|
}
|
||||||
@@ -49,6 +49,16 @@ export const securityDefinitions = onlyOAS3(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
export const validOperationMethods =
|
||||||
|
(oriSelector, system) =>
|
||||||
|
(state, ...args) => {
|
||||||
|
if (system.specSelectors.isOAS3()) {
|
||||||
|
return system.oas3Selectors.validOperationMethods()
|
||||||
|
}
|
||||||
|
|
||||||
|
return oriSelector(...args)
|
||||||
|
}
|
||||||
|
|
||||||
export const host = OAS3NullSelector
|
export const host = OAS3NullSelector
|
||||||
export const basePath = OAS3NullSelector
|
export const basePath = OAS3NullSelector
|
||||||
export const consumes = OAS3NullSelector
|
export const consumes = OAS3NullSelector
|
||||||
|
|||||||
@@ -1,59 +1,45 @@
|
|||||||
|
/**
|
||||||
|
* @prettier
|
||||||
|
*/
|
||||||
import React from "react"
|
import React from "react"
|
||||||
import PropTypes from "prop-types"
|
import PropTypes from "prop-types"
|
||||||
import { fromJS } from "immutable"
|
|
||||||
import ImPropTypes from "react-immutable-proptypes"
|
|
||||||
|
|
||||||
// Todo: nice to have: similar to operation-tags, could have an expand/collapse button
|
const Webhooks = ({ specSelectors, getComponent }) => {
|
||||||
// to show/hide all webhook items
|
const operationDTOs = specSelectors.selectWebhooksOperations()
|
||||||
const Webhooks = (props) => {
|
const pathItemNames = Object.keys(operationDTOs)
|
||||||
const { specSelectors, getComponent, specPath } = props
|
|
||||||
|
|
||||||
const webhooksPathItems = specSelectors.webhooks()
|
|
||||||
if (!webhooksPathItems || webhooksPathItems?.size < 1) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
const OperationContainer = getComponent("OperationContainer", true)
|
const OperationContainer = getComponent("OperationContainer", true)
|
||||||
|
|
||||||
const pathItemsElements = webhooksPathItems.entrySeq().map(([pathItemName, pathItem], i) => {
|
if (pathItemNames.length === 0) return null
|
||||||
const operationsElements = pathItem.entrySeq().map(([operationMethod, operation], j) => {
|
|
||||||
const op = fromJS({
|
|
||||||
operation
|
|
||||||
})
|
|
||||||
// using defaultProps for `specPath`; may want to remove from props
|
|
||||||
// and/or if extract to separate PathItem component, allow for use
|
|
||||||
// with both OAS3.1 "webhooks" and "components.pathItems" features
|
|
||||||
return <OperationContainer
|
|
||||||
{...props}
|
|
||||||
op={op}
|
|
||||||
key={`${pathItemName}--${operationMethod}--${j}`}
|
|
||||||
tag={""}
|
|
||||||
method={operationMethod}
|
|
||||||
path={pathItemName}
|
|
||||||
specPath={specPath.push("webhooks", pathItemName, operationMethod)}
|
|
||||||
allowTryItOut={false}
|
|
||||||
/>
|
|
||||||
})
|
|
||||||
return <div key={`${pathItemName}-${i}`}>
|
|
||||||
{operationsElements}
|
|
||||||
</div>
|
|
||||||
})
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="webhooks">
|
<div className="webhooks">
|
||||||
<h2>Webhooks</h2>
|
<h2>Webhooks</h2>
|
||||||
{pathItemsElements}
|
|
||||||
|
{pathItemNames.map((pathItemName) => (
|
||||||
|
<div key={`${pathItemName}-webhook`}>
|
||||||
|
{operationDTOs[pathItemName].map((operationDTO) => (
|
||||||
|
<OperationContainer
|
||||||
|
key={`${pathItemName}-${operationDTO.method}-webhook`}
|
||||||
|
op={operationDTO.operation}
|
||||||
|
tag=""
|
||||||
|
method={operationDTO.method}
|
||||||
|
path={pathItemName}
|
||||||
|
specPath={operationDTO.specPath}
|
||||||
|
allowTryItOut={false}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
Webhooks.propTypes = {
|
Webhooks.propTypes = {
|
||||||
specSelectors: PropTypes.object.isRequired,
|
specSelectors: PropTypes.shape({
|
||||||
|
selectWebhooksOperations: PropTypes.func.isRequired,
|
||||||
|
}).isRequired,
|
||||||
getComponent: PropTypes.func.isRequired,
|
getComponent: PropTypes.func.isRequired,
|
||||||
specPath: ImPropTypes.list,
|
|
||||||
}
|
|
||||||
|
|
||||||
Webhooks.defaultProps = {
|
|
||||||
specPath: fromJS([])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Webhooks
|
export default Webhooks
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* @prettier
|
* @prettier
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export const isOAS31 = (jsSpec) => {
|
export const isOAS31 = (jsSpec) => {
|
||||||
const oasVersion = jsSpec.get("openapi")
|
const oasVersion = jsSpec.get("openapi")
|
||||||
|
|
||||||
@@ -9,14 +10,34 @@ export const isOAS31 = (jsSpec) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selector maker the only calls the passed selector
|
||||||
|
* when spec is of OpenAPI 3.1.0 version.
|
||||||
|
*/
|
||||||
export const onlyOAS31 =
|
export const onlyOAS31 =
|
||||||
(selector) =>
|
(selector) =>
|
||||||
() =>
|
(state, ...args) =>
|
||||||
(system, ...args) => {
|
(system) => {
|
||||||
if (system.getSystem().specSelectors.isOAS31()) {
|
if (system.getSystem().specSelectors.isOAS31()) {
|
||||||
const result = selector(...args)
|
const result = selector(state, ...args)
|
||||||
return typeof result === "function" ? result(system, ...args) : result
|
return typeof result === "function" ? result(system) : result
|
||||||
} else {
|
} else {
|
||||||
return null
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -25,10 +25,11 @@ import {
|
|||||||
selectInfoSummaryField,
|
selectInfoSummaryField,
|
||||||
selectInfoDescriptionField,
|
selectInfoDescriptionField,
|
||||||
selectInfoTermsOfServiceField,
|
selectInfoTermsOfServiceField,
|
||||||
makeSelectInfoTermsOfServiceUrl as makeSelectTosUrl,
|
makeSelectInfoTermsOfServiceUrl,
|
||||||
selectExternalDocsDescriptionField,
|
selectExternalDocsDescriptionField,
|
||||||
selectExternalDocsUrlField,
|
selectExternalDocsUrlField,
|
||||||
makeSelectExternalDocsUrl,
|
makeSelectExternalDocsUrl,
|
||||||
|
makeSelectWebhooksOperations,
|
||||||
} from "./spec-extensions/selectors"
|
} from "./spec-extensions/selectors"
|
||||||
import {
|
import {
|
||||||
isOAS3 as isOAS3Wrapper,
|
isOAS3 as isOAS3Wrapper,
|
||||||
@@ -45,8 +46,9 @@ const OAS31Plugin = () => {
|
|||||||
specSelectors.isOAS31 = makeIsOAS31(system)
|
specSelectors.isOAS31 = makeIsOAS31(system)
|
||||||
specSelectors.selectLicenseUrl = makeSelectLicenseUrl(system)
|
specSelectors.selectLicenseUrl = makeSelectLicenseUrl(system)
|
||||||
specSelectors.selectContactUrl = makeSelectContactUrl(system)
|
specSelectors.selectContactUrl = makeSelectContactUrl(system)
|
||||||
specSelectors.selectInfoTermsOfServiceUrl = makeSelectTosUrl(system)
|
specSelectors.selectInfoTermsOfServiceUrl = makeSelectInfoTermsOfServiceUrl(system) // prettier-ignore
|
||||||
specSelectors.selectExternalDocsUrl = makeSelectExternalDocsUrl(system)
|
specSelectors.selectExternalDocsUrl = makeSelectExternalDocsUrl(system)
|
||||||
|
specSelectors.selectWebhooksOperations = makeSelectWebhooksOperations(system) // prettier-ignore
|
||||||
|
|
||||||
oas31Selectors.selectLicenseUrl = makeOAS31SelectLicenseUrl(system)
|
oas31Selectors.selectLicenseUrl = makeOAS31SelectLicenseUrl(system)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* @prettier
|
* @prettier
|
||||||
*/
|
*/
|
||||||
import { Map } from "immutable"
|
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"
|
||||||
@@ -16,6 +16,39 @@ export const webhooks = onlyOAS31(() => (system) => {
|
|||||||
return system.specSelectors.specJson().get("webhooks", map)
|
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]),
|
||||||
|
}))
|
||||||
|
|
||||||
|
return allOperations.concat(pathItemOperations)
|
||||||
|
}, List())
|
||||||
|
.groupBy((operation) => operation.path)
|
||||||
|
.map((operations) => operations.toArray())
|
||||||
|
.toObject()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
export const license = () => (system) => {
|
export const license = () => (system) => {
|
||||||
return system.specSelectors.info().get("license", map)
|
return system.specSelectors.info().get("license", map)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
/**
|
/**
|
||||||
* @prettier
|
* @prettier
|
||||||
*/
|
*/
|
||||||
|
import { onlyOAS31Wrap } from "../helpers"
|
||||||
|
|
||||||
export const isOAS3 =
|
export const isOAS3 =
|
||||||
(oriSelector, system) =>
|
(oriSelector, system) =>
|
||||||
(state, ...args) => {
|
(state, ...args) => {
|
||||||
@@ -8,12 +10,6 @@ export const isOAS3 =
|
|||||||
return isOAS31 || oriSelector(...args)
|
return isOAS31 || oriSelector(...args)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const selectLicenseUrl =
|
export const selectLicenseUrl = onlyOAS31Wrap(() => (system) => {
|
||||||
(oriSelector, system) =>
|
|
||||||
(state, ...args) => {
|
|
||||||
if (system.specSelectors.isOAS31()) {
|
|
||||||
return system.oas31Selectors.selectLicenseUrl()
|
return system.oas31Selectors.selectLicenseUrl()
|
||||||
}
|
})
|
||||||
|
|
||||||
return oriSelector(...args)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -114,6 +114,8 @@ export const paths = createSelector(
|
|||||||
spec => spec.get("paths")
|
spec => spec.get("paths")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
export const validOperationMethods = createSelector(() => ["get", "put", "post", "delete", "options", "head", "patch"])
|
||||||
|
|
||||||
export const operations = createSelector(
|
export const operations = createSelector(
|
||||||
paths,
|
paths,
|
||||||
paths => {
|
paths => {
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ describe("<Operations/>", function(){
|
|||||||
specSelectors: {
|
specSelectors: {
|
||||||
isOAS3() { return false },
|
isOAS3() { return false },
|
||||||
url() { return "https://petstore.swagger.io/v2/swagger.json" },
|
url() { return "https://petstore.swagger.io/v2/swagger.json" },
|
||||||
|
validOperationMethods() { return ["get", "put", "post", "delete", "options", "head", "patch"] },
|
||||||
taggedOperations() {
|
taggedOperations() {
|
||||||
return fromJS({
|
return fromJS({
|
||||||
"default": {
|
"default": {
|
||||||
@@ -83,6 +84,7 @@ describe("<Operations/>", function(){
|
|||||||
specSelectors: {
|
specSelectors: {
|
||||||
isOAS3() { return true },
|
isOAS3() { return true },
|
||||||
url() { return "https://petstore.swagger.io/v2/swagger.json" },
|
url() { return "https://petstore.swagger.io/v2/swagger.json" },
|
||||||
|
validOperationMethods() { return ["get", "put", "post", "delete", "options", "head", "patch", "trace"] },
|
||||||
taggedOperations() {
|
taggedOperations() {
|
||||||
return fromJS({
|
return fromJS({
|
||||||
"default": {
|
"default": {
|
||||||
|
|||||||
Reference in New Issue
Block a user