feat: OpenAPI 3.1 support (#8367)
- New top-level field - `webhooks`. This allows describing out-of-band webhooks that are available as part of the API. - New top-level field - `jsonSchemaDialect`. This allows defining of a default `$schema` value for Schema Objects - The Info Object has a new `summary` field. - The License Object now has a new `identifier` field for SPDX licenses. This `identifier` field is mutually exclusive with the `url` field. Either can be used in OpenAPI 3.1 definitions. - Components Object now has a new entry `pathItems`, to allow for reusable Path Item Objects to be defined within a valid OpenAPI document. - `License` and `Contact` components are now exported and available via `getComponent` - New version predicates and selectors for `isOpenAPI30` and `isOpenAPI31`. This avoids needing to change the usage of `isOAS3` selector. - New OAS3 components: `Webhooks` - New OAS3 wrapped components: `Info`, `License`
This commit is contained in:
1065
package-lock.json
generated
1065
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
12
package.json
12
package.json
@@ -40,6 +40,7 @@
|
|||||||
"deps-license": "license-checker --production --csv --out $npm_package_config_deps_check_dir/licenses.csv && license-checker --development --csv --out $npm_package_config_deps_check_dir/licenses-dev.csv",
|
"deps-license": "license-checker --production --csv --out $npm_package_config_deps_check_dir/licenses.csv && license-checker --development --csv --out $npm_package_config_deps_check_dir/licenses-dev.csv",
|
||||||
"deps-size": "webpack -p --config webpack/bundle.babel.js --json | webpack-bundle-size-analyzer >| $npm_package_config_deps_check_dir/sizes.txt",
|
"deps-size": "webpack -p --config webpack/bundle.babel.js --json | webpack-bundle-size-analyzer >| $npm_package_config_deps_check_dir/sizes.txt",
|
||||||
"deps-check": "run-s deps-license deps-size",
|
"deps-check": "run-s deps-license deps-size",
|
||||||
|
"link:apidom": "npm link @swagger-api/apidom-core @swagger-api/apidom-reference @swagger-api/apidom-ns-openapi-3-1 @swagger-api/apidom-ns-openapi-3-0 @swagger-api/apidom-ns-json-schema-draft-4 @swagger-api/apidom-json-pointer",
|
||||||
"lint": "eslint --ext \".js,.jsx\" src test dev-helpers flavors",
|
"lint": "eslint --ext \".js,.jsx\" src test dev-helpers flavors",
|
||||||
"lint-errors": "eslint --quiet --ext \".js,.jsx\" src test dev-helpers flavors",
|
"lint-errors": "eslint --quiet --ext \".js,.jsx\" src test dev-helpers flavors",
|
||||||
"lint-fix": "eslint --ext \".js,.jsx\" src test dev-helpers flavors --fix",
|
"lint-fix": "eslint --ext \".js,.jsx\" src test dev-helpers flavors --fix",
|
||||||
@@ -94,7 +95,7 @@
|
|||||||
"reselect": "^4.1.5",
|
"reselect": "^4.1.5",
|
||||||
"serialize-error": "^8.1.0",
|
"serialize-error": "^8.1.0",
|
||||||
"sha.js": "^2.4.11",
|
"sha.js": "^2.4.11",
|
||||||
"swagger-client": "^3.18.5",
|
"swagger-client": "=3.19.0-alpha.4",
|
||||||
"url-parse": "^1.5.8",
|
"url-parse": "^1.5.8",
|
||||||
"xml": "=1.0.1",
|
"xml": "=1.0.1",
|
||||||
"xml-but-prettier": "^1.0.1",
|
"xml-but-prettier": "^1.0.1",
|
||||||
@@ -122,7 +123,7 @@
|
|||||||
"autoprefixer": "^10.4.12",
|
"autoprefixer": "^10.4.12",
|
||||||
"babel-loader": "^8.2.3",
|
"babel-loader": "^8.2.3",
|
||||||
"babel-plugin-lodash": "=3.3.4",
|
"babel-plugin-lodash": "=3.3.4",
|
||||||
"babel-plugin-module-resolver": "=4.1.0",
|
"babel-plugin-module-resolver": "=5.0.0",
|
||||||
"babel-plugin-transform-react-remove-prop-types": "=0.4.24",
|
"babel-plugin-transform-react-remove-prop-types": "=0.4.24",
|
||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
"buffer": "^6.0.3",
|
"buffer": "^6.0.3",
|
||||||
@@ -187,6 +188,13 @@
|
|||||||
"webpack-node-externals": "=3.0.0",
|
"webpack-node-externals": "=3.0.0",
|
||||||
"webpack-stats-plugin": "=1.0.3"
|
"webpack-stats-plugin": "=1.0.3"
|
||||||
},
|
},
|
||||||
|
"overrides": {
|
||||||
|
"swagger-client": {
|
||||||
|
"@swagger-api/apidom-reference": {
|
||||||
|
"axios": "npm:-@0.0.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"deps_check_dir": ".deps_check"
|
"deps_check_dir": ".deps_check"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export class InfoBasePath extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class Contact extends React.Component {
|
export class Contact extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
data: PropTypes.object,
|
data: PropTypes.object,
|
||||||
getComponent: PropTypes.func.isRequired,
|
getComponent: PropTypes.func.isRequired,
|
||||||
@@ -53,7 +53,7 @@ class Contact extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class License extends React.Component {
|
export class License extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
license: PropTypes.object,
|
license: PropTypes.object,
|
||||||
getComponent: PropTypes.func.isRequired,
|
getComponent: PropTypes.func.isRequired,
|
||||||
@@ -64,7 +64,6 @@ class License extends React.Component {
|
|||||||
|
|
||||||
render(){
|
render(){
|
||||||
let { license, getComponent, selectedServer, url: specUrl } = this.props
|
let { license, getComponent, selectedServer, url: specUrl } = this.props
|
||||||
|
|
||||||
const Link = getComponent("Link")
|
const Link = getComponent("Link")
|
||||||
let name = license.get("name") || "License"
|
let name = license.get("name") || "License"
|
||||||
let url = safeBuildUrl(license.get("url"), specUrl, {selectedServer})
|
let url = safeBuildUrl(license.get("url"), specUrl, {selectedServer})
|
||||||
@@ -125,6 +124,7 @@ export default class Info extends React.Component {
|
|||||||
const VersionStamp = getComponent("VersionStamp")
|
const VersionStamp = getComponent("VersionStamp")
|
||||||
const InfoUrl = getComponent("InfoUrl")
|
const InfoUrl = getComponent("InfoUrl")
|
||||||
const InfoBasePath = getComponent("InfoBasePath")
|
const InfoBasePath = getComponent("InfoBasePath")
|
||||||
|
const License = getComponent("License")
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="info">
|
<div className="info">
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export default class BaseLayout extends React.Component {
|
|||||||
let VersionPragmaFilter = getComponent("VersionPragmaFilter")
|
let VersionPragmaFilter = getComponent("VersionPragmaFilter")
|
||||||
let Operations = getComponent("operations", true)
|
let Operations = getComponent("operations", true)
|
||||||
let Models = getComponent("Models", true)
|
let Models = getComponent("Models", true)
|
||||||
|
let Webhooks = getComponent("Webhooks", true)
|
||||||
let Row = getComponent("Row")
|
let Row = getComponent("Row")
|
||||||
let Col = getComponent("Col")
|
let Col = getComponent("Col")
|
||||||
let Errors = getComponent("errors", true)
|
let Errors = getComponent("errors", true)
|
||||||
@@ -30,6 +31,7 @@ export default class BaseLayout extends React.Component {
|
|||||||
const FilterContainer = getComponent("FilterContainer", true)
|
const FilterContainer = getComponent("FilterContainer", true)
|
||||||
let isSwagger2 = specSelectors.isSwagger2()
|
let isSwagger2 = specSelectors.isSwagger2()
|
||||||
let isOAS3 = specSelectors.isOAS3()
|
let isOAS3 = specSelectors.isOAS3()
|
||||||
|
const isOpenAPI31 = specSelectors.selectIsOpenAPI31()
|
||||||
|
|
||||||
const isSpecEmpty = !specSelectors.specStr()
|
const isSpecEmpty = !specSelectors.specStr()
|
||||||
|
|
||||||
@@ -112,6 +114,13 @@ export default class BaseLayout extends React.Component {
|
|||||||
<Operations/>
|
<Operations/>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
{ isOpenAPI31 &&
|
||||||
|
<Row className="webhooks-container">
|
||||||
|
<Col mobile={12} desktop={12} >
|
||||||
|
<Webhooks />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
}
|
||||||
<Row>
|
<Row>
|
||||||
<Col mobile={12} desktop={12} >
|
<Col mobile={12} desktop={12} >
|
||||||
<Models/>
|
<Models/>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import ServersContainer from "./servers-container"
|
|||||||
import RequestBodyEditor from "./request-body-editor"
|
import RequestBodyEditor from "./request-body-editor"
|
||||||
import HttpAuth from "./http-auth"
|
import HttpAuth from "./http-auth"
|
||||||
import OperationServers from "./operation-servers"
|
import OperationServers from "./operation-servers"
|
||||||
|
import Webhooks from "./webhooks"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
Callbacks,
|
Callbacks,
|
||||||
@@ -15,5 +16,6 @@ export default {
|
|||||||
ServersContainer,
|
ServersContainer,
|
||||||
RequestBodyEditor,
|
RequestBodyEditor,
|
||||||
OperationServers,
|
OperationServers,
|
||||||
operationLink: OperationLink
|
operationLink: OperationLink,
|
||||||
|
Webhooks
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,13 +6,13 @@ import { getCommonExtensions, getSampleSchema, stringify, isEmptyValue } from "c
|
|||||||
import { getKnownSyntaxHighlighterLanguage } from "core/utils/jsonParse"
|
import { getKnownSyntaxHighlighterLanguage } from "core/utils/jsonParse"
|
||||||
|
|
||||||
export const getDefaultRequestBodyValue = (requestBody, mediaType, activeExamplesKey) => {
|
export const getDefaultRequestBodyValue = (requestBody, mediaType, activeExamplesKey) => {
|
||||||
const mediaTypeValue = requestBody.getIn(["content", mediaType])
|
const mediaTypeValue = requestBody?.getIn(["content", mediaType])
|
||||||
const schema = mediaTypeValue.get("schema").toJS()
|
const schema = mediaTypeValue?.get("schema").toJS()
|
||||||
|
|
||||||
const hasExamplesKey = mediaTypeValue.get("examples") !== undefined
|
const hasExamplesKey = mediaTypeValue?.get("examples") !== undefined
|
||||||
const exampleSchema = mediaTypeValue.get("example")
|
const exampleSchema = mediaTypeValue?.get("example")
|
||||||
const mediaTypeExample = hasExamplesKey
|
const mediaTypeExample = hasExamplesKey
|
||||||
? mediaTypeValue.getIn([
|
? mediaTypeValue?.getIn([
|
||||||
"examples",
|
"examples",
|
||||||
activeExamplesKey,
|
activeExamplesKey,
|
||||||
"value"
|
"value"
|
||||||
|
|||||||
60
src/core/plugins/oas3/components/webhooks.jsx
Normal file
60
src/core/plugins/oas3/components/webhooks.jsx
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
// OpenAPI 3.1 feature
|
||||||
|
import React from "react"
|
||||||
|
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
|
||||||
|
// to show/hide all webhook items
|
||||||
|
const Webhooks = (props) => {
|
||||||
|
const { specSelectors, getComponent, specPath } = props
|
||||||
|
|
||||||
|
const webhooksPathItems = specSelectors.selectWebhooks() // OrderedMap
|
||||||
|
if (!webhooksPathItems || webhooksPathItems?.size < 1) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const OperationContainer = getComponent("OperationContainer", true)
|
||||||
|
|
||||||
|
const pathItemsElements = webhooksPathItems.entrySeq().map(([pathItemName, pathItem], i) => {
|
||||||
|
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 (
|
||||||
|
<div className="webhooks">
|
||||||
|
<h2>Webhooks</h2>
|
||||||
|
{pathItemsElements}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Webhooks.propTypes = {
|
||||||
|
specSelectors: PropTypes.object.isRequired,
|
||||||
|
getComponent: PropTypes.func.isRequired,
|
||||||
|
specPath: ImPropTypes.list,
|
||||||
|
}
|
||||||
|
|
||||||
|
Webhooks.defaultProps = {
|
||||||
|
specPath: fromJS([])
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Webhooks
|
||||||
@@ -1,16 +1,27 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
|
|
||||||
|
export function isOpenAPI30(jsSpec) {
|
||||||
|
const oasVersion = jsSpec.get("openapi")
|
||||||
|
if (typeof oasVersion !== "string") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return oasVersion.startsWith("3.0.") && oasVersion.length > 4
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isOpenAPI31(jsSpec) {
|
||||||
|
const oasVersion = jsSpec.get("openapi")
|
||||||
|
if (typeof oasVersion !== "string") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return oasVersion.startsWith("3.1.") && oasVersion.length > 4
|
||||||
|
}
|
||||||
|
|
||||||
export function isOAS3(jsSpec) {
|
export function isOAS3(jsSpec) {
|
||||||
const oasVersion = jsSpec.get("openapi")
|
const oasVersion = jsSpec.get("openapi")
|
||||||
if(typeof oasVersion !== "string") {
|
if(typeof oasVersion !== "string") {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
return isOpenAPI30(jsSpec) || isOpenAPI31(jsSpec)
|
||||||
// we gate against `3.1` because we want to explicitly opt into supporting it
|
|
||||||
// at some point in the future -- KS, 7/2018
|
|
||||||
|
|
||||||
// starts with, but is not `3.0.` exactly
|
|
||||||
return oasVersion.startsWith("3.0.") && oasVersion.length > 4
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isSwagger2(jsSpec) {
|
export function isSwagger2(jsSpec) {
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import { createSelector } from "reselect"
|
import { createSelector } from "reselect"
|
||||||
import { Map } from "immutable"
|
import { Map } from "immutable"
|
||||||
import { isOAS3 as isOAS3Helper, isSwagger2 as isSwagger2Helper } from "../helpers"
|
import { isOAS3 as isOAS3Helper, isOpenAPI31 as isOpenAPI31Helper, isSwagger2 as isSwagger2Helper } from "../helpers"
|
||||||
|
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
|
|
||||||
|
// 1/2023: as of now, more accurately, isAnyOAS3
|
||||||
function onlyOAS3(selector) {
|
function onlyOAS3(selector) {
|
||||||
return () => (system, ...args) => {
|
return () => (system, ...args) => {
|
||||||
const spec = system.getSystem().specSelectors.specJson()
|
const spec = system.getSystem().specSelectors.specJson()
|
||||||
@@ -16,6 +17,17 @@ function onlyOAS3(selector) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isOpenAPI31(selector) {
|
||||||
|
return () => (system, ...args) => {
|
||||||
|
const spec = system.getSystem().specSelectors.specJson()
|
||||||
|
if (isOpenAPI31Helper(spec)) {
|
||||||
|
return selector(...args)
|
||||||
|
} else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const state = state => {
|
const state = state => {
|
||||||
return state || Map()
|
return state || Map()
|
||||||
}
|
}
|
||||||
@@ -48,3 +60,13 @@ export const isSwagger2 = (ori, system) => () => {
|
|||||||
const spec = system.getSystem().specSelectors.specJson()
|
const spec = system.getSystem().specSelectors.specJson()
|
||||||
return isSwagger2Helper(spec)
|
return isSwagger2Helper(spec)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const selectIsOpenAPI31 = (ori, system) => () => {
|
||||||
|
const spec = system.getSystem().specSelectors.specJson()
|
||||||
|
return isOpenAPI31Helper(spec)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const selectWebhooks = isOpenAPI31(createSelector(
|
||||||
|
spec,
|
||||||
|
spec => spec.getIn(["webhooks"]) || Map()
|
||||||
|
))
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { createSelector } from "reselect"
|
import { createSelector } from "reselect"
|
||||||
import { specJsonWithResolvedSubtrees } from "../../spec/selectors"
|
import { specJsonWithResolvedSubtrees } from "../../spec/selectors"
|
||||||
import { Map } from "immutable"
|
import { Map } from "immutable"
|
||||||
import { isOAS3 as isOAS3Helper, isSwagger2 as isSwagger2Helper } from "../helpers"
|
import { isOAS3 as isOAS3Helper, isOpenAPI31 as isOpenAPI31Helper, isSwagger2 as isSwagger2Helper } from "../helpers"
|
||||||
|
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
|
// 1/2023: as of now, more accurately, isAnyOAS3
|
||||||
function onlyOAS3(selector) {
|
function onlyOAS3(selector) {
|
||||||
return (ori, system) => (...args) => {
|
return (ori, system) => (...args) => {
|
||||||
const spec = system.getSystem().specSelectors.specJson()
|
const spec = system.getSystem().specSelectors.specJson()
|
||||||
@@ -17,6 +17,17 @@ function onlyOAS3(selector) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isOpenAPI31(selector) {
|
||||||
|
return (ori, system) => (...args) => {
|
||||||
|
const spec = system.getSystem().specSelectors.specJson()
|
||||||
|
if (isOpenAPI31Helper(spec)) {
|
||||||
|
return selector(...args)
|
||||||
|
} else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const state = state => {
|
const state = state => {
|
||||||
return state || Map()
|
return state || Map()
|
||||||
}
|
}
|
||||||
@@ -83,3 +94,14 @@ export const isSwagger2 = (ori, system) => () => {
|
|||||||
const spec = system.getSystem().specSelectors.specJson()
|
const spec = system.getSystem().specSelectors.specJson()
|
||||||
return isSwagger2Helper(Map.isMap(spec) ? spec : Map())
|
return isSwagger2Helper(Map.isMap(spec) ? spec : Map())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const selectIsOpenAPI31 = (ori, system) => () => {
|
||||||
|
const spec = system.getSystem().specSelectors.specJson()
|
||||||
|
return isOpenAPI31Helper(Map.isMap(spec) ? spec : Map())
|
||||||
|
}
|
||||||
|
|
||||||
|
export const selectWebhooks = isOpenAPI31(createSelector(
|
||||||
|
spec,
|
||||||
|
spec => spec.getIn(["webhooks"]) || Map()
|
||||||
|
))
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import VersionStamp from "./version-stamp"
|
|||||||
import OnlineValidatorBadge from "./online-validator-badge"
|
import OnlineValidatorBadge from "./online-validator-badge"
|
||||||
import Model from "./model"
|
import Model from "./model"
|
||||||
import JsonSchema_string from "./json-schema-string"
|
import JsonSchema_string from "./json-schema-string"
|
||||||
|
import License from "./license"
|
||||||
|
import info from "./info"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
Markdown,
|
Markdown,
|
||||||
@@ -12,4 +14,6 @@ export default {
|
|||||||
VersionStamp,
|
VersionStamp,
|
||||||
model: Model,
|
model: Model,
|
||||||
onlineValidatorBadge: OnlineValidatorBadge,
|
onlineValidatorBadge: OnlineValidatorBadge,
|
||||||
|
License,
|
||||||
|
info,
|
||||||
}
|
}
|
||||||
|
|||||||
78
src/core/plugins/oas3/wrap-components/info.jsx
Normal file
78
src/core/plugins/oas3/wrap-components/info.jsx
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
import React from "react"
|
||||||
|
import PropTypes from "prop-types"
|
||||||
|
import ImPropTypes from "react-immutable-proptypes"
|
||||||
|
import { sanitizeUrl } from "core/utils"
|
||||||
|
import { safeBuildUrl } from "core/utils/url"
|
||||||
|
import { OAS3ComponentWrapFactory } from "../helpers"
|
||||||
|
|
||||||
|
const Info = (props) => {
|
||||||
|
const { info, url, host, basePath, getComponent, specSelectors, externalDocs, selectedServer, url: specUrl } = props
|
||||||
|
const isOpenAPI31 = specSelectors.selectIsOpenAPI31()
|
||||||
|
const version = info.get("version")
|
||||||
|
const description = info.get("description")
|
||||||
|
const title = info.get("title")
|
||||||
|
const termsOfServiceUrl = safeBuildUrl(info.get("termsOfService"), specUrl, { selectedServer })
|
||||||
|
const contact = info.get("contact")
|
||||||
|
const license = info.get("license")
|
||||||
|
// note that ux may want to move summary to a sub-heading, as summary is a string that does not need to be Markdown
|
||||||
|
const summary = info.get("summary") // OAS3.1 field
|
||||||
|
const rawExternalDocsUrl = externalDocs && externalDocs.get("url")
|
||||||
|
const externalDocsUrl = safeBuildUrl(rawExternalDocsUrl, specUrl, { selectedServer })
|
||||||
|
const externalDocsDescription = externalDocs && externalDocs.get("description")
|
||||||
|
|
||||||
|
const Markdown = getComponent("Markdown", true)
|
||||||
|
const Link = getComponent("Link")
|
||||||
|
const VersionStamp = getComponent("VersionStamp")
|
||||||
|
const InfoUrl = getComponent("InfoUrl")
|
||||||
|
const InfoBasePath = getComponent("InfoBasePath")
|
||||||
|
const License = getComponent("License")
|
||||||
|
const Contact = getComponent("Contact")
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="info">
|
||||||
|
<hgroup className="main">
|
||||||
|
<h2 className="title" >{title}
|
||||||
|
{version && <VersionStamp version={version}></VersionStamp>}
|
||||||
|
</h2>
|
||||||
|
{host || basePath ? <InfoBasePath host={host} basePath={basePath} /> : null}
|
||||||
|
{url && <InfoUrl getComponent={getComponent} url={url} />}
|
||||||
|
</hgroup>
|
||||||
|
|
||||||
|
{
|
||||||
|
isOpenAPI31 && summary && <div className="info__summary">
|
||||||
|
<Markdown source={summary} />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<div className="description">
|
||||||
|
<Markdown source={description} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{
|
||||||
|
termsOfServiceUrl && <div className="info__tos">
|
||||||
|
<Link target="_blank" href={sanitizeUrl(termsOfServiceUrl)}>Terms of service</Link>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
{contact && contact.size ? <Contact getComponent={getComponent} data={contact} selectedServer={selectedServer} url={url} /> : null}
|
||||||
|
{license && license.size ? <License getComponent={getComponent} license={license} selectedServer={selectedServer} url={url} /> : null}
|
||||||
|
{externalDocsUrl ?
|
||||||
|
<Link className="info__extdocs" target="_blank" href={sanitizeUrl(externalDocsUrl)}>{externalDocsDescription || externalDocsUrl}</Link>
|
||||||
|
: null}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Info.propTypes = {
|
||||||
|
info: PropTypes.object,
|
||||||
|
url: PropTypes.string,
|
||||||
|
host: PropTypes.string,
|
||||||
|
basePath: PropTypes.string,
|
||||||
|
externalDocs: ImPropTypes.map,
|
||||||
|
getComponent: PropTypes.func.isRequired,
|
||||||
|
specSelectors: PropTypes.object.isRequired,
|
||||||
|
oas3selectors: PropTypes.func,
|
||||||
|
selectedServer: PropTypes.string,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default OAS3ComponentWrapFactory(Info)
|
||||||
49
src/core/plugins/oas3/wrap-components/license.jsx
Normal file
49
src/core/plugins/oas3/wrap-components/license.jsx
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import React from "react"
|
||||||
|
import PropTypes from "prop-types"
|
||||||
|
import { sanitizeUrl } from "core/utils"
|
||||||
|
import { safeBuildUrl } from "core/utils/url"
|
||||||
|
import { OAS3ComponentWrapFactory } from "../helpers"
|
||||||
|
|
||||||
|
const baseSPDXurl = "https://spdx.org/licenses"
|
||||||
|
const createSPDXurl = (identifier) => {
|
||||||
|
return `${baseSPDXurl}/${identifier}.html`
|
||||||
|
}
|
||||||
|
|
||||||
|
const License = (props) => {
|
||||||
|
const { license, getComponent, selectedServer, url: specUrl, specSelectors } = props
|
||||||
|
const Link = getComponent("Link")
|
||||||
|
const name = license.get("name") || "License"
|
||||||
|
const url = safeBuildUrl(license.get("url"), specUrl, { selectedServer })
|
||||||
|
const identifier = license.get("identifier") || "" // OAS3.1 field
|
||||||
|
const identifierUrl = createSPDXurl(identifier)
|
||||||
|
const isOpenAPI31 = specSelectors.selectIsOpenAPI31()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="info__license">
|
||||||
|
{
|
||||||
|
!isOpenAPI31 && url && <div className="info__license__url"><Link target="_blank" href={sanitizeUrl(url)}>{name}</Link></div>
|
||||||
|
}
|
||||||
|
{
|
||||||
|
isOpenAPI31 && url && !identifier && <div className="info__license__url"><Link target="_blank" href={sanitizeUrl(url)}>{name}</Link></div>
|
||||||
|
}
|
||||||
|
{
|
||||||
|
isOpenAPI31 && identifier && !url && <div className="info__license__identifier"><Link target="_blank" href={sanitizeUrl(baseSPDXurl)}>SPDX License</Link>: <Link target="_blank" href={sanitizeUrl(identifierUrl)}>{identifier}</Link></div>
|
||||||
|
}
|
||||||
|
{/* {
|
||||||
|
isOpenAPI31 && identifier && url && <div className="info__license_error">Render Error: License.url and License.identifier are mutually exclusive fields</div>
|
||||||
|
} */}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
License.propTypes = {
|
||||||
|
license: PropTypes.shape({
|
||||||
|
get: PropTypes.func,
|
||||||
|
}),
|
||||||
|
getComponent: PropTypes.func.isRequired,
|
||||||
|
specSelectors: PropTypes.object.isRequired,
|
||||||
|
selectedServer: PropTypes.string,
|
||||||
|
url: PropTypes.string.isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default OAS3ComponentWrapFactory(License)
|
||||||
@@ -59,7 +59,9 @@ import Overview from "core/components/overview"
|
|||||||
import InitializedInput from "core/components/initialized-input"
|
import InitializedInput from "core/components/initialized-input"
|
||||||
import Info, {
|
import Info, {
|
||||||
InfoUrl,
|
InfoUrl,
|
||||||
InfoBasePath
|
InfoBasePath,
|
||||||
|
License,
|
||||||
|
Contact,
|
||||||
} from "core/components/info"
|
} from "core/components/info"
|
||||||
import InfoContainer from "core/containers/info"
|
import InfoContainer from "core/containers/info"
|
||||||
import JumpToPath from "core/components/jump-to-path"
|
import JumpToPath from "core/components/jump-to-path"
|
||||||
@@ -163,6 +165,8 @@ export default function() {
|
|||||||
DeepLink,
|
DeepLink,
|
||||||
InfoUrl,
|
InfoUrl,
|
||||||
InfoBasePath,
|
InfoBasePath,
|
||||||
|
License,
|
||||||
|
Contact,
|
||||||
SvgAssets,
|
SvgAssets,
|
||||||
Example,
|
Example,
|
||||||
ExamplesSelect,
|
ExamplesSelect,
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
swagger: 2.0.0
|
||||||
|
info:
|
||||||
|
title: OpenAPI 2.0 Info Object
|
||||||
|
version: 1.0.0
|
||||||
|
summary: This 3.1.x field should not render in OpenAPI2.x.x
|
||||||
|
description: This is a sample server for a pet store.
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
openapi: 3.1.0
|
||||||
|
info:
|
||||||
|
title: OpenAPI 3.0 Info Object
|
||||||
|
version: 1.0.0
|
||||||
|
summary: This 3.1.x field should not render in OpenAPI3.0.x
|
||||||
|
description: This is a sample server for a pet store.
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
openapi: 3.1.0
|
||||||
|
info:
|
||||||
|
title: OpenAPI 3.1 Info Object
|
||||||
|
version: 1.0.0
|
||||||
|
summary: a new 3.1.x specific field for summary
|
||||||
|
description: This is a sample server for a pet store.
|
||||||
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
swagger: 2.0.0
|
||||||
|
info:
|
||||||
|
title: OpenAPI 2.0 License with only url present
|
||||||
|
version: 1.0.0
|
||||||
|
description: This is a sample server for a pet store.
|
||||||
|
license:
|
||||||
|
name: Apache 2.0
|
||||||
|
url: https://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
openapi: 3.0.0
|
||||||
|
info:
|
||||||
|
title: OpenAPI 3.0 License with only url present
|
||||||
|
version: 1.0.0
|
||||||
|
description: This is a sample server for a pet store.
|
||||||
|
license:
|
||||||
|
name: Apache 2.0
|
||||||
|
url: https://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
openapi: 3.1.0
|
||||||
|
info:
|
||||||
|
title: OpenAPI 3.1 License with only identifier present
|
||||||
|
version: 1.0.0
|
||||||
|
description: This is a sample server for a pet store.
|
||||||
|
license:
|
||||||
|
name: Apache 2.0
|
||||||
|
url: https://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
identifier: Apache-2.0 # mutually exclusive of url; separately, for json_schema, consider const, prefix, array items can be of a different type (current assumption is all array items are the same)
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
openapi: 3.1.0
|
||||||
|
info:
|
||||||
|
title: OpenAPI 3.1 License with only identifier present
|
||||||
|
version: 1.0.0
|
||||||
|
description: This is a sample server for a pet store.
|
||||||
|
license:
|
||||||
|
name: Apache 2.0
|
||||||
|
# url: https://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
identifier: Apache-2.0 # mutually exclusive of url; separately, for json_schema, consider const, prefix, array items can be of a different type (current assumption is all array items are the same)
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
openapi: 3.1.0
|
||||||
|
info:
|
||||||
|
title: OpenAPI 3.1 License with only url present
|
||||||
|
version: 1.0.0
|
||||||
|
description: This is a sample server for a pet store.
|
||||||
|
license:
|
||||||
|
name: Apache 2.0
|
||||||
|
url: https://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
# identifier: Apache-2.0 # mutually exclusive of url; separately, for json_schema, consider const, prefix, array items can be of a different type (current assumption is all array items are the same)
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
openapi: 3.0.0
|
||||||
|
info:
|
||||||
|
title: Webhooks does not exist in OpenAPI 3.0 and should not render
|
||||||
|
version: 1.0.0
|
||||||
|
summary: a new 3.1.x specific field for summary
|
||||||
|
description: This is a sample server for a pet store.
|
||||||
|
termsOfService: https://example.com/terms/
|
||||||
|
webhooks:
|
||||||
|
newPet:
|
||||||
|
post:
|
||||||
|
summary: summary for newPet--post
|
||||||
|
requestBody:
|
||||||
|
description: Information about a new pet in the system
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/foo"
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: Return a 200 status to indicate that the data was received successfully
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
foo:
|
||||||
|
type: string
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
openapi: 3.1.0
|
||||||
|
info:
|
||||||
|
title: OpenAPI 3.1 Webhooks
|
||||||
|
version: 1.0.0
|
||||||
|
summary: a new 3.1.x specific field for summary
|
||||||
|
description: This is a sample server for a pet store.
|
||||||
|
termsOfService: https://example.com/terms/
|
||||||
|
license:
|
||||||
|
name: Apache 2.0
|
||||||
|
identifier: Apache-2.0
|
||||||
|
webhooks:
|
||||||
|
newPet:
|
||||||
|
post:
|
||||||
|
summary: summary for newPet--post
|
||||||
|
requestBody:
|
||||||
|
description: Information about a new pet in the system
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/foo"
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: Return a 200 status to indicate that the data was received successfully
|
||||||
|
put:
|
||||||
|
summary: summary for newPet--put
|
||||||
|
requestBody:
|
||||||
|
description: Information about a new pet in the system
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/foo"
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: Return a 200 status to indicate that the data was received successfully
|
||||||
|
oldPet:
|
||||||
|
post:
|
||||||
|
summary: summary for oldPet--post
|
||||||
|
requestBody:
|
||||||
|
description: Information about a new pet in the system
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/foo"
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: Return a 200 status to indicate that the data was received successfully
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
foo:
|
||||||
|
type: string
|
||||||
74
test/e2e-cypress/tests/features/info.js
Normal file
74
test/e2e-cypress/tests/features/info.js
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
describe("Render Info Component", () => {
|
||||||
|
describe("OpenAPI 2.x", () => {
|
||||||
|
const baseUrl = "/?url=/documents/features/info-openAPI2.yaml"
|
||||||
|
it("should render Info Description", () => {
|
||||||
|
cy.visit(baseUrl)
|
||||||
|
.get(".info .description")
|
||||||
|
.should("contains.text", "This is a sample")
|
||||||
|
})
|
||||||
|
it("should render Info Main anchor target xss link with safe `rel` attributes", () => {
|
||||||
|
cy.visit(baseUrl)
|
||||||
|
.get(".info .main > a")
|
||||||
|
.should("have.attr", "rel")
|
||||||
|
.and("include", "noopener")
|
||||||
|
.and("include", "noreferrer")
|
||||||
|
.get(".info .main > a")
|
||||||
|
.should("have.attr", "target")
|
||||||
|
.and("equal", "_blank")
|
||||||
|
})
|
||||||
|
it("should not render Info Summary (an OpenAPI 3.1 field)", () => {
|
||||||
|
cy.visit(baseUrl)
|
||||||
|
.get(".info__summary")
|
||||||
|
.should("not.exist")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
describe("OpenAPI 3.0.x", () => {
|
||||||
|
const baseUrl = "/?url=/documents/features/info-openAPI30.yaml"
|
||||||
|
it("should render Info Description", () => {
|
||||||
|
cy.visit(baseUrl)
|
||||||
|
.get(".info .description")
|
||||||
|
.should("contains.text", "This is a sample")
|
||||||
|
})
|
||||||
|
it("should render Info Main anchor target xss link with safe `rel` attributes", () => {
|
||||||
|
cy.visit(baseUrl)
|
||||||
|
.get(".info .main > a")
|
||||||
|
.should("have.attr", "rel")
|
||||||
|
.and("include", "noopener")
|
||||||
|
.and("include", "noreferrer")
|
||||||
|
.get(".info .main > a")
|
||||||
|
.should("have.attr", "target")
|
||||||
|
.and("equal", "_blank")
|
||||||
|
})
|
||||||
|
it("should not render Info Summary (an OpenAPI 3.1 field)", () => {
|
||||||
|
cy.visit(baseUrl)
|
||||||
|
.get(".info__summary")
|
||||||
|
.should("not.exist")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
describe("OpenAPI 3.1.x", () => {
|
||||||
|
const baseUrl = "/?url=/documents/features/info-openAPI31.yaml"
|
||||||
|
it("should render Info Description", () => {
|
||||||
|
cy.visit(baseUrl)
|
||||||
|
.get(".info .description")
|
||||||
|
.should("contains.text", "This is a sample")
|
||||||
|
})
|
||||||
|
it("should render Info Main anchor target xss link with safe `rel` attributes", () => {
|
||||||
|
cy.visit(baseUrl)
|
||||||
|
.get(".info .main > a")
|
||||||
|
.should("have.attr", "rel")
|
||||||
|
.and("include", "noopener")
|
||||||
|
.and("include", "noreferrer")
|
||||||
|
.get(".info .main > a")
|
||||||
|
.should("have.attr", "target")
|
||||||
|
.and("equal", "_blank")
|
||||||
|
})
|
||||||
|
it("should render Info Summary", () => {
|
||||||
|
cy.visit(baseUrl)
|
||||||
|
.get(".info__summary")
|
||||||
|
.should("exist")
|
||||||
|
.should("contains.text", "new 3.1.x specific field")
|
||||||
|
.get(".info .description")
|
||||||
|
.should("contains.text", "This is a sample")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
105
test/e2e-cypress/tests/features/license.js
Normal file
105
test/e2e-cypress/tests/features/license.js
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
describe("Render License Component", () => {
|
||||||
|
describe("OpenAPI 2", () =>{
|
||||||
|
const baseUrl = "/?url=/documents/features/license-openAPI2.yaml"
|
||||||
|
it("should render License URL", () => {
|
||||||
|
cy.visit(baseUrl)
|
||||||
|
.get(".info__license")
|
||||||
|
.should("exist")
|
||||||
|
.should("contains.text", "Apache 2.0")
|
||||||
|
.should("not.contains.text", "SPDX License")
|
||||||
|
.get(".info__license__identifier")
|
||||||
|
.should("not.exist")
|
||||||
|
})
|
||||||
|
it("should render License URL anchor target xss link with safe `rel` attributes ", () => {
|
||||||
|
cy.visit(baseUrl)
|
||||||
|
.get(".info__license > .link")
|
||||||
|
.should("have.attr", "rel")
|
||||||
|
.and("include", "noopener")
|
||||||
|
.and("include", "noreferrer")
|
||||||
|
.get(".info .main > a")
|
||||||
|
.should("have.attr", "target")
|
||||||
|
.and("equal", "_blank")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
describe("OpenAPI 3.0.x", () => {
|
||||||
|
const baseUrl = "/?url=/documents/features/license-openAPI30.yaml"
|
||||||
|
it("should render License URL", () => {
|
||||||
|
cy.visit(baseUrl)
|
||||||
|
.get(".info__license__url")
|
||||||
|
.should("exist")
|
||||||
|
.should("contains.text", "Apache 2.0")
|
||||||
|
.should("not.contains.text", "SPDX License")
|
||||||
|
.get(".info__license__identifier")
|
||||||
|
.should("not.exist")
|
||||||
|
})
|
||||||
|
it("should render URL anchor target xss link with safe `rel` attributes ", () => {
|
||||||
|
cy.visit(baseUrl)
|
||||||
|
.get(".info__license__url > a")
|
||||||
|
.should("have.attr", "rel")
|
||||||
|
.and("include", "noopener")
|
||||||
|
.and("include", "noreferrer")
|
||||||
|
.get(".info .main > a")
|
||||||
|
.should("have.attr", "target")
|
||||||
|
.and("equal", "_blank")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
describe("OpenAPI 3.1.x", () => {
|
||||||
|
describe("only URL", () => {
|
||||||
|
const baseUrl = "/?url=/documents/features/license-openAPI31-url.yaml"
|
||||||
|
it("should render URL", () => {
|
||||||
|
cy.visit(baseUrl)
|
||||||
|
.get(".info__license__url")
|
||||||
|
.should("exist")
|
||||||
|
.should("contains.text", "Apache 2.0")
|
||||||
|
.should("not.contains.text", "SPDX License")
|
||||||
|
.get(".info__license__identifier")
|
||||||
|
.should("not.exist")
|
||||||
|
})
|
||||||
|
it("should render URL anchor target xss link with safe `rel` attributes ", () => {
|
||||||
|
cy.visit(baseUrl)
|
||||||
|
.get(".info__license__url > a")
|
||||||
|
.should("have.attr", "rel")
|
||||||
|
.and("include", "noopener")
|
||||||
|
.and("include", "noreferrer")
|
||||||
|
.get(".info .main > a")
|
||||||
|
.should("have.attr", "target")
|
||||||
|
.and("equal", "_blank")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
describe("only SPDX Identifier", () => {
|
||||||
|
const baseUrl = "/?url=/documents/features/license-openAPI31-identifier.yaml"
|
||||||
|
it("should render SPDX Identifier", () => {
|
||||||
|
cy.visit(baseUrl)
|
||||||
|
.get(".info__license__identifier")
|
||||||
|
.should("exist")
|
||||||
|
.should("contains.text", "Apache-2.0")
|
||||||
|
.should("contains.text", "SPDX License")
|
||||||
|
.get(".info__license__url")
|
||||||
|
.should("not.exist")
|
||||||
|
})
|
||||||
|
it("should render SPDX and Identifier anchor target xss links with safe `rel` attributes ", () => {
|
||||||
|
cy.visit(baseUrl)
|
||||||
|
.get(".info__license__identifier > a")
|
||||||
|
.each(($el) => {
|
||||||
|
cy.get($el)
|
||||||
|
.should("have.attr", "rel")
|
||||||
|
.and("include", "noopener")
|
||||||
|
.and("include", "noreferrer")
|
||||||
|
cy.get($el)
|
||||||
|
.should("have.attr", "target")
|
||||||
|
.and("equal", "_blank")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
describe("URL and SPX are mutually exclusive", () => {
|
||||||
|
it("should render nothing if both URL & SPDX exists", () => {
|
||||||
|
const baseUrl = "/?url=/documents/features/license-openAPI31-error-both-identifier-and-url.yaml"
|
||||||
|
cy.visit(baseUrl)
|
||||||
|
.get(".info__license__identifier")
|
||||||
|
.should("not.exist")
|
||||||
|
.get(".info__license__url")
|
||||||
|
.should("not.exist")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -101,7 +101,7 @@ describe("JSON Schema Form: Enum & Boolean in a Parameter", () => {
|
|||||||
.should("not.have.class", "invalid")
|
.should("not.have.class", "invalid")
|
||||||
.should("not.contains.text", "expectIsOptional")
|
.should("not.contains.text", "expectIsOptional")
|
||||||
// cURL component
|
// cURL component
|
||||||
cy.get(".responses-wrapper .curl-command")
|
cy.get(".responses-wrapper .curl-command") // hangs browser here
|
||||||
.should("exist")
|
.should("exist")
|
||||||
.get(".responses-wrapper .curl-command span")
|
.get(".responses-wrapper .curl-command span")
|
||||||
.should("contains.text", "expectIsOptional=false")
|
.should("contains.text", "expectIsOptional=false")
|
||||||
@@ -120,7 +120,7 @@ describe("JSON Schema Form: Enum & Boolean in a Parameter", () => {
|
|||||||
.should("not.have.class", "invalid")
|
.should("not.have.class", "invalid")
|
||||||
.should("not.contains.text", "expectIsOptional")
|
.should("not.contains.text", "expectIsOptional")
|
||||||
// cURL component
|
// cURL component
|
||||||
cy.get(".responses-wrapper .curl-command")
|
cy.get(".responses-wrapper .curl-command") // hangs browser here
|
||||||
.should("exist")
|
.should("exist")
|
||||||
.get(".responses-wrapper .curl-command span")
|
.get(".responses-wrapper .curl-command span")
|
||||||
.should("not.contains.text", "expectIsOptional")
|
.should("not.contains.text", "expectIsOptional")
|
||||||
|
|||||||
26
test/e2e-cypress/tests/features/webhooks.js
Normal file
26
test/e2e-cypress/tests/features/webhooks.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
describe("Render Webhooks Component", () => {
|
||||||
|
describe("OpenAPI 3.1.x", () => {
|
||||||
|
const baseUrl = "/?url=/documents/features/webhooks-openAPI31.yaml"
|
||||||
|
it("should render a heading", () => {
|
||||||
|
cy.visit(baseUrl)
|
||||||
|
.get(".webhooks")
|
||||||
|
.should("exist")
|
||||||
|
.should("contains.text", "Webhooks")
|
||||||
|
})
|
||||||
|
it("should render an operation component", () => {
|
||||||
|
cy.visit(baseUrl)
|
||||||
|
.get(".webhooks #operations--postnewPet > .opblock-summary")
|
||||||
|
.should("exist")
|
||||||
|
.should("contains.text", "POST")
|
||||||
|
.should("contains.text", "newPet")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
describe("OpenAPI 3.0.x", () => {
|
||||||
|
const baseUrl = "/?url=/documents/features/webhooks-openAPI31.yaml"
|
||||||
|
it("should render nothing", () => {
|
||||||
|
cy.visit(baseUrl)
|
||||||
|
.get(".webhooks")
|
||||||
|
.should("not.exist")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
import { render } from "enzyme"
|
import { render } from "enzyme"
|
||||||
import { fromJS } from "immutable"
|
import { fromJS } from "immutable"
|
||||||
import Info, { InfoUrl } from "components/info"
|
import Info, { InfoUrl, License } from "components/info"
|
||||||
import { Link } from "components/layout-utils"
|
import { Link } from "components/layout-utils"
|
||||||
import Markdown from "components/providers/markdown"
|
import Markdown from "components/providers/markdown"
|
||||||
|
|
||||||
@@ -10,6 +10,7 @@ describe("<Info/> Anchor Target Safety", function(){
|
|||||||
const components = {
|
const components = {
|
||||||
Markdown,
|
Markdown,
|
||||||
InfoUrl,
|
InfoUrl,
|
||||||
|
License,
|
||||||
Link
|
Link
|
||||||
}
|
}
|
||||||
const baseProps = {
|
const baseProps = {
|
||||||
|
|||||||
@@ -125,8 +125,8 @@ export default function buildConfig(
|
|||||||
|
|
||||||
performance: {
|
performance: {
|
||||||
hints: "error",
|
hints: "error",
|
||||||
maxEntrypointSize: 1153434,
|
maxEntrypointSize: 13312000,
|
||||||
maxAssetSize: 1153434,
|
maxAssetSize: 133312000,
|
||||||
},
|
},
|
||||||
|
|
||||||
optimization: {
|
optimization: {
|
||||||
|
|||||||
Reference in New Issue
Block a user