refactor(oas31): concentrate OpenAPI 3.1.0 code to separate plugin (#8475)
Refs #8474
This commit is contained in:
45
src/core/components/contact.jsx
Normal file
45
src/core/components/contact.jsx
Normal file
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { safeBuildUrl } from "core/utils/url"
|
||||
import { sanitizeUrl } from "core/utils"
|
||||
|
||||
class Contact extends React.Component {
|
||||
static propTypes = {
|
||||
data: PropTypes.object,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
selectedServer: PropTypes.string,
|
||||
url: PropTypes.string.isRequired,
|
||||
}
|
||||
|
||||
render() {
|
||||
const { data, getComponent, selectedServer, url: specUrl } = this.props
|
||||
const name = data.get("name", "the developer")
|
||||
const url = safeBuildUrl(data.get("url"), specUrl, { selectedServer })
|
||||
const email = data.get("email")
|
||||
|
||||
const Link = getComponent("Link")
|
||||
|
||||
return (
|
||||
<div className="info__contact">
|
||||
{url && (
|
||||
<div>
|
||||
<Link href={sanitizeUrl(url)} target="_blank">
|
||||
{name} - Website
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
{email && (
|
||||
<Link href={sanitizeUrl(`mailto:${email}`)}>
|
||||
{url ? `Send email to ${name}` : `Contact ${name}`}
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default Contact
|
||||
@@ -1,102 +1,53 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
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"
|
||||
|
||||
|
||||
export class InfoBasePath extends React.Component {
|
||||
static propTypes = {
|
||||
host: PropTypes.string,
|
||||
basePath: PropTypes.string
|
||||
basePath: PropTypes.string,
|
||||
}
|
||||
|
||||
render() {
|
||||
let { host, basePath } = this.props
|
||||
const { host, basePath } = this.props
|
||||
|
||||
return (
|
||||
<pre className="base-url">
|
||||
[ Base URL: {host}{basePath} ]
|
||||
[ Base URL: {host}
|
||||
{basePath} ]
|
||||
</pre>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class Contact extends React.Component {
|
||||
static propTypes = {
|
||||
data: PropTypes.object,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
selectedServer: PropTypes.string,
|
||||
url: PropTypes.string.isRequired,
|
||||
}
|
||||
|
||||
render(){
|
||||
let { data, getComponent, selectedServer, url: specUrl} = this.props
|
||||
let name = data.get("name") || "the developer"
|
||||
let url = safeBuildUrl(data.get("url"), specUrl, {selectedServer})
|
||||
let email = data.get("email")
|
||||
|
||||
const Link = getComponent("Link")
|
||||
|
||||
return (
|
||||
<div className="info__contact">
|
||||
{ url && <div><Link href={ sanitizeUrl(url) } target="_blank">{ name } - Website</Link></div> }
|
||||
{ email &&
|
||||
<Link href={sanitizeUrl(`mailto:${email}`)}>
|
||||
{ url ? `Send email to ${name}` : `Contact ${name}`}
|
||||
</Link>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export class License extends React.Component {
|
||||
static propTypes = {
|
||||
license: PropTypes.object,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
selectedServer: PropTypes.string,
|
||||
url: PropTypes.string.isRequired,
|
||||
}
|
||||
|
||||
render(){
|
||||
let { license, getComponent, selectedServer, url: specUrl } = this.props
|
||||
const Link = getComponent("Link")
|
||||
let name = license.get("name") || "License"
|
||||
let url = safeBuildUrl(license.get("url"), specUrl, {selectedServer})
|
||||
|
||||
return (
|
||||
<div className="info__license">
|
||||
{
|
||||
url ? <Link target="_blank" href={ sanitizeUrl(url) }>{ name }</Link>
|
||||
: <span>{ name }</span>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export class InfoUrl extends React.PureComponent {
|
||||
static propTypes = {
|
||||
url: PropTypes.string.isRequired,
|
||||
getComponent: PropTypes.func.isRequired
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
const { url, getComponent } = this.props
|
||||
|
||||
const Link = getComponent("Link")
|
||||
|
||||
return <Link target="_blank" href={ sanitizeUrl(url) }><span className="url"> { url }</span></Link>
|
||||
return (
|
||||
<Link target="_blank" href={sanitizeUrl(url)}>
|
||||
<span className="url"> {url}</span>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default class Info extends React.Component {
|
||||
class Info extends React.Component {
|
||||
static propTypes = {
|
||||
title: PropTypes.any,
|
||||
description: PropTypes.any,
|
||||
version: PropTypes.any,
|
||||
info: PropTypes.object,
|
||||
url: PropTypes.string,
|
||||
host: PropTypes.string,
|
||||
@@ -108,16 +59,32 @@ export default class Info extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
let { info, url, host, basePath, getComponent, externalDocs, selectedServer, url: specUrl } = this.props
|
||||
let version = info.get("version")
|
||||
let description = info.get("description")
|
||||
let title = info.get("title")
|
||||
let termsOfServiceUrl = safeBuildUrl(info.get("termsOfService"), specUrl, {selectedServer})
|
||||
let contact = info.get("contact")
|
||||
let license = info.get("license")
|
||||
let rawExternalDocsUrl = externalDocs && externalDocs.get("url")
|
||||
let externalDocsUrl = safeBuildUrl(rawExternalDocsUrl, specUrl, {selectedServer})
|
||||
let externalDocsDescription = externalDocs && externalDocs.get("description")
|
||||
const {
|
||||
info,
|
||||
url,
|
||||
host,
|
||||
basePath,
|
||||
getComponent,
|
||||
externalDocs,
|
||||
selectedServer,
|
||||
url: specUrl,
|
||||
} = this.props
|
||||
const version = info.get("version")
|
||||
const description = info.get("description")
|
||||
const title = info.get("title")
|
||||
const termsOfServiceUrl = safeBuildUrl(
|
||||
info.get("termsOfService"),
|
||||
specUrl,
|
||||
{ selectedServer }
|
||||
)
|
||||
const contactData = info.get("contact")
|
||||
const licenseData = info.get("license")
|
||||
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")
|
||||
@@ -125,14 +92,18 @@ export default class Info extends React.Component {
|
||||
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 }
|
||||
<h2 className="title">
|
||||
{title}
|
||||
{version && <VersionStamp version={version}></VersionStamp>}
|
||||
</h2>
|
||||
{ host || basePath ? <InfoBasePath host={ host } basePath={ basePath } /> : null }
|
||||
{host || basePath ? (
|
||||
<InfoBasePath host={host} basePath={basePath} />
|
||||
) : null}
|
||||
{url && <InfoUrl getComponent={getComponent} url={url} />}
|
||||
</hgroup>
|
||||
|
||||
@@ -140,27 +111,42 @@ export default class Info extends React.Component {
|
||||
<Markdown source={description} />
|
||||
</div>
|
||||
|
||||
{
|
||||
termsOfServiceUrl && <div className="info__tos">
|
||||
<Link target="_blank" href={ sanitizeUrl(termsOfServiceUrl) }>Terms of service</Link>
|
||||
{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 }
|
||||
)}
|
||||
|
||||
{contactData?.size > 0 && (
|
||||
<Contact
|
||||
getComponent={getComponent}
|
||||
data={contactData}
|
||||
selectedServer={selectedServer}
|
||||
url={url}
|
||||
/>
|
||||
)}
|
||||
{licenseData?.size > 0 && (
|
||||
<License
|
||||
getComponent={getComponent}
|
||||
license={licenseData}
|
||||
selectedServer={selectedServer}
|
||||
url={url}
|
||||
/>
|
||||
)}
|
||||
{externalDocsUrl ? (
|
||||
<Link
|
||||
className="info__extdocs"
|
||||
target="_blank"
|
||||
href={sanitizeUrl(externalDocsUrl)}
|
||||
>
|
||||
{externalDocsDescription || externalDocsUrl}
|
||||
</Link>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Info.propTypes = {
|
||||
title: PropTypes.any,
|
||||
description: PropTypes.any,
|
||||
version: PropTypes.any,
|
||||
url: PropTypes.string
|
||||
}
|
||||
export default Info
|
||||
|
||||
@@ -1,37 +1,39 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
|
||||
export default class BaseLayout extends React.Component {
|
||||
|
||||
static propTypes = {
|
||||
errSelectors: PropTypes.object.isRequired,
|
||||
errActions: PropTypes.object.isRequired,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
oas3Selectors: PropTypes.object.isRequired,
|
||||
oas3Actions: PropTypes.object.isRequired,
|
||||
getComponent: PropTypes.func.isRequired
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
render() {
|
||||
let {errSelectors, specSelectors, getComponent} = this.props
|
||||
const { errSelectors, specSelectors, getComponent } = this.props
|
||||
|
||||
let SvgAssets = getComponent("SvgAssets")
|
||||
let InfoContainer = getComponent("InfoContainer", true)
|
||||
let VersionPragmaFilter = getComponent("VersionPragmaFilter")
|
||||
let Operations = getComponent("operations", true)
|
||||
let Models = getComponent("Models", true)
|
||||
let Webhooks = getComponent("Webhooks", true)
|
||||
let Row = getComponent("Row")
|
||||
let Col = getComponent("Col")
|
||||
let Errors = getComponent("errors", true)
|
||||
const SvgAssets = getComponent("SvgAssets")
|
||||
const InfoContainer = getComponent("InfoContainer", true)
|
||||
const VersionPragmaFilter = getComponent("VersionPragmaFilter")
|
||||
const Operations = getComponent("operations", true)
|
||||
const Models = getComponent("Models", true)
|
||||
const Webhooks = getComponent("Webhooks", true)
|
||||
const Row = getComponent("Row")
|
||||
const Col = getComponent("Col")
|
||||
const Errors = getComponent("errors", true)
|
||||
|
||||
const ServersContainer = getComponent("ServersContainer", true)
|
||||
const SchemesContainer = getComponent("SchemesContainer", true)
|
||||
const AuthorizeBtnContainer = getComponent("AuthorizeBtnContainer", true)
|
||||
const FilterContainer = getComponent("FilterContainer", true)
|
||||
let isSwagger2 = specSelectors.isSwagger2()
|
||||
let isOAS3 = specSelectors.isOAS3()
|
||||
const isOpenAPI31 = specSelectors.selectIsOpenAPI31()
|
||||
const isSwagger2 = specSelectors.isSwagger2()
|
||||
const isOAS3 = specSelectors.isOAS3()
|
||||
const isOAS31 = specSelectors.isOAS31()
|
||||
|
||||
const isSpecEmpty = !specSelectors.specStr()
|
||||
|
||||
@@ -40,31 +42,37 @@ export default class BaseLayout extends React.Component {
|
||||
let loadingMessage = null
|
||||
|
||||
if (loadingStatus === "loading") {
|
||||
loadingMessage = <div className="info">
|
||||
loadingMessage = (
|
||||
<div className="info">
|
||||
<div className="loading-container">
|
||||
<div className="loading"></div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (loadingStatus === "failed") {
|
||||
loadingMessage = <div className="info">
|
||||
loadingMessage = (
|
||||
<div className="info">
|
||||
<div className="loading-container">
|
||||
<h4 className="title">Failed to load API definition.</h4>
|
||||
<Errors />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (loadingStatus === "failedConfig") {
|
||||
const lastErr = errSelectors.lastError()
|
||||
const lastErrMsg = lastErr ? lastErr.get("message") : ""
|
||||
loadingMessage = <div className="info failed-config">
|
||||
loadingMessage = (
|
||||
<div className="info failed-config">
|
||||
<div className="loading-container">
|
||||
<h4 className="title">Failed to load remote configuration.</h4>
|
||||
<p>{lastErrMsg}</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (!loadingMessage && isSpecEmpty) {
|
||||
@@ -72,11 +80,11 @@ export default class BaseLayout extends React.Component {
|
||||
}
|
||||
|
||||
if (loadingMessage) {
|
||||
return <div className="swagger-ui">
|
||||
<div className="loading-container">
|
||||
{loadingMessage}
|
||||
</div>
|
||||
return (
|
||||
<div className="swagger-ui">
|
||||
<div className="loading-container">{loadingMessage}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const servers = specSelectors.servers()
|
||||
@@ -87,9 +95,13 @@ export default class BaseLayout extends React.Component {
|
||||
const hasSecurityDefinitions = !!specSelectors.securityDefinitions()
|
||||
|
||||
return (
|
||||
<div className='swagger-ui'>
|
||||
<div className="swagger-ui">
|
||||
<SvgAssets />
|
||||
<VersionPragmaFilter isSwagger2={isSwagger2} isOAS3={isOAS3} alsoShow={<Errors/>}>
|
||||
<VersionPragmaFilter
|
||||
isSwagger2={isSwagger2}
|
||||
isOAS3={isOAS3}
|
||||
alsoShow={<Errors />}
|
||||
>
|
||||
<Errors />
|
||||
<Row className="information-container">
|
||||
<Col mobile={12}>
|
||||
@@ -100,9 +112,9 @@ export default class BaseLayout extends React.Component {
|
||||
{hasServers || hasSchemes || hasSecurityDefinitions ? (
|
||||
<div className="scheme-container">
|
||||
<Col className="schemes wrapper" mobile={12}>
|
||||
{hasServers ? (<ServersContainer />) : null}
|
||||
{hasSchemes ? (<SchemesContainer />) : null}
|
||||
{hasSecurityDefinitions ? (<AuthorizeBtnContainer />) : null}
|
||||
{hasServers ? <ServersContainer /> : null}
|
||||
{hasSchemes ? <SchemesContainer /> : null}
|
||||
{hasSecurityDefinitions ? <AuthorizeBtnContainer /> : null}
|
||||
</Col>
|
||||
</div>
|
||||
) : null}
|
||||
@@ -114,13 +126,13 @@ export default class BaseLayout extends React.Component {
|
||||
<Operations />
|
||||
</Col>
|
||||
</Row>
|
||||
{ isOpenAPI31 &&
|
||||
{isOAS31 && (
|
||||
<Row className="webhooks-container">
|
||||
<Col mobile={12} desktop={12}>
|
||||
<Webhooks />
|
||||
</Col>
|
||||
</Row>
|
||||
}
|
||||
)}
|
||||
<Row>
|
||||
<Col mobile={12} desktop={12}>
|
||||
<Models />
|
||||
|
||||
41
src/core/components/license.jsx
Normal file
41
src/core/components/license.jsx
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { safeBuildUrl } from "core/utils/url"
|
||||
import { sanitizeUrl } from "core/utils"
|
||||
|
||||
class License extends React.Component {
|
||||
static propTypes = {
|
||||
license: PropTypes.object,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
selectedServer: PropTypes.string,
|
||||
url: PropTypes.string.isRequired,
|
||||
}
|
||||
|
||||
render() {
|
||||
const { license, getComponent, selectedServer, url: specUrl } = this.props
|
||||
const name = license.get("name", "License")
|
||||
const url = safeBuildUrl(license.get("url"), specUrl, { selectedServer })
|
||||
|
||||
const Link = getComponent("Link")
|
||||
|
||||
return (
|
||||
<div className="info__license">
|
||||
{url ? (
|
||||
<div className="info__license__url">
|
||||
<Link target="_blank" href={sanitizeUrl(url)}>
|
||||
{name}
|
||||
</Link>
|
||||
</div>
|
||||
) : (
|
||||
<span>{name}</span>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default License
|
||||
@@ -1,6 +1,5 @@
|
||||
import { createSelector } from "reselect"
|
||||
import { List, Map, fromJS } from "immutable"
|
||||
import { isOAS3 as isOAS3Helper } from "../helpers"
|
||||
|
||||
|
||||
// Helpers
|
||||
@@ -9,8 +8,7 @@ const state = state => state
|
||||
|
||||
function onlyOAS3(selector) {
|
||||
return (ori, system) => (...args) => {
|
||||
const spec = system.getSystem().specSelectors.specJson()
|
||||
if(isOAS3Helper(spec)) {
|
||||
if(system.getSystem().specSelectors.isOAS3()) {
|
||||
// Pass the spec plugin state to Reselect to trigger on securityDefinitions update
|
||||
let resolvedSchemes = system.getState().getIn(["spec", "resolvedSubtrees",
|
||||
"components", "securitySchemes"])
|
||||
|
||||
@@ -6,7 +6,6 @@ import ServersContainer from "./servers-container"
|
||||
import RequestBodyEditor from "./request-body-editor"
|
||||
import HttpAuth from "./http-auth"
|
||||
import OperationServers from "./operation-servers"
|
||||
import Webhooks from "./webhooks"
|
||||
|
||||
export default {
|
||||
Callbacks,
|
||||
@@ -17,5 +16,4 @@ export default {
|
||||
RequestBodyEditor,
|
||||
OperationServers,
|
||||
operationLink: OperationLink,
|
||||
Webhooks
|
||||
}
|
||||
|
||||
@@ -1,44 +1,27 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
import React from "react"
|
||||
|
||||
export function isOpenAPI30(jsSpec) {
|
||||
export function isOAS30(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) {
|
||||
const oasVersion = jsSpec.get("openapi")
|
||||
if(typeof oasVersion !== "string") {
|
||||
return false
|
||||
}
|
||||
return isOpenAPI30(jsSpec) || isOpenAPI31(jsSpec)
|
||||
return (
|
||||
typeof oasVersion === "string" &&
|
||||
/^3\.0\.([0123])(?:-rc[012])?$/.test(oasVersion)
|
||||
)
|
||||
}
|
||||
|
||||
export function isSwagger2(jsSpec) {
|
||||
const swaggerVersion = jsSpec.get("swagger")
|
||||
if(typeof swaggerVersion !== "string") {
|
||||
return false
|
||||
}
|
||||
|
||||
return swaggerVersion.startsWith("2.0")
|
||||
return typeof swaggerVersion === "string" && swaggerVersion === "2.0"
|
||||
}
|
||||
|
||||
export function OAS3ComponentWrapFactory(Component) {
|
||||
return (Ori, system) => (props) => {
|
||||
if(system && system.specSelectors && system.specSelectors.specJson) {
|
||||
const spec = system.specSelectors.specJson()
|
||||
|
||||
if(isOAS3(spec)) {
|
||||
if (typeof system.specSelectors?.isOAS3 === "function") {
|
||||
if (system.specSelectors.isOAS3()) {
|
||||
return <Component {...props} {...system} Ori={Ori}></Component>
|
||||
} else {
|
||||
return <Ori {...props}></Ori>
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
// import reducers from "./reducers"
|
||||
// import * as actions from "./actions"
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
import * as specWrapSelectors from "./spec-extensions/wrap-selectors"
|
||||
import * as authWrapSelectors from "./auth-extensions/wrap-selectors"
|
||||
import * as specSelectors from "./spec-extensions/selectors"
|
||||
import components from "./components"
|
||||
import wrapComponents from "./wrap-components"
|
||||
import * as oas3Actions from "./actions"
|
||||
import * as oas3Selectors from "./selectors"
|
||||
import oas3Reducers from "./reducers"
|
||||
import * as actions from "./actions"
|
||||
import * as selectors from "./selectors"
|
||||
import reducers from "./reducers"
|
||||
|
||||
export default function () {
|
||||
return {
|
||||
@@ -16,16 +17,16 @@ export default function() {
|
||||
statePlugins: {
|
||||
spec: {
|
||||
wrapSelectors: specWrapSelectors,
|
||||
selectors: specSelectors
|
||||
selectors: specSelectors,
|
||||
},
|
||||
auth: {
|
||||
wrapSelectors: authWrapSelectors
|
||||
wrapSelectors: authWrapSelectors,
|
||||
},
|
||||
oas3: {
|
||||
actions: oas3Actions,
|
||||
reducers: oas3Reducers,
|
||||
selectors: oas3Selectors,
|
||||
}
|
||||
}
|
||||
actions,
|
||||
reducers,
|
||||
selectors,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
import { OrderedMap, Map, List } from "immutable"
|
||||
import { isOAS3 as isOAS3Helper } from "./helpers"
|
||||
import { getDefaultRequestBodyValue } from "./components/request-body"
|
||||
import { stringify } from "../../utils"
|
||||
|
||||
// Helpers
|
||||
|
||||
function onlyOAS3(selector) {
|
||||
return (...args) => (system) => {
|
||||
const spec = system.getSystem().specSelectors.specJson()
|
||||
if(isOAS3Helper(spec)) {
|
||||
return (...args) =>
|
||||
(system) => {
|
||||
if (system.getSystem().specSelectors.isOAS3()) {
|
||||
return selector(...args)
|
||||
} else {
|
||||
return null
|
||||
@@ -17,12 +19,18 @@ function onlyOAS3(selector) {
|
||||
}
|
||||
|
||||
function validateRequestBodyIsRequired(selector) {
|
||||
return (...args) => (system) => {
|
||||
return (...args) =>
|
||||
(system) => {
|
||||
const specJson = system.getSystem().specSelectors.specJson()
|
||||
const argsList = [...args]
|
||||
// expect argsList[0] = state
|
||||
let pathMethod = argsList[1] || []
|
||||
let isOas3RequestBodyRequired = specJson.getIn(["paths", ...pathMethod, "requestBody", "required"])
|
||||
let isOas3RequestBodyRequired = specJson.getIn([
|
||||
"paths",
|
||||
...pathMethod,
|
||||
"requestBody",
|
||||
"required",
|
||||
])
|
||||
|
||||
if (isOas3RequestBodyRequired) {
|
||||
return selector(...args)
|
||||
@@ -35,7 +43,11 @@ function validateRequestBodyIsRequired(selector) {
|
||||
|
||||
const validateRequestBodyValueExists = (state, pathMethod) => {
|
||||
pathMethod = pathMethod || []
|
||||
let oas3RequestBodyValue = state.getIn(["requestData", ...pathMethod, "bodyValue"])
|
||||
let oas3RequestBodyValue = state.getIn([
|
||||
"requestData",
|
||||
...pathMethod,
|
||||
"bodyValue",
|
||||
])
|
||||
// context: bodyValue can be a String, or a Map
|
||||
if (!oas3RequestBodyValue) {
|
||||
return false
|
||||
@@ -44,36 +56,39 @@ const validateRequestBodyValueExists = (state, pathMethod) => {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
export const selectedServer = onlyOAS3((state, namespace) => {
|
||||
const path = namespace ? [namespace, "selectedServer"] : ["selectedServer"]
|
||||
return state.getIn(path) || ""
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
export const requestBodyValue = onlyOAS3((state, path, method) => {
|
||||
return state.getIn(["requestData", path, method, "bodyValue"]) || null
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
export const shouldRetainRequestBodyValue = onlyOAS3((state, path, method) => {
|
||||
return state.getIn(["requestData", path, method, "retainBodyValue"]) || false
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
export const selectDefaultRequestBodyValue = (state, path, method) => (system) => {
|
||||
export const selectDefaultRequestBodyValue =
|
||||
(state, path, method) => (system) => {
|
||||
const { oas3Selectors, specSelectors } = system.getSystem()
|
||||
const spec = specSelectors.specJson()
|
||||
if(isOAS3Helper(spec)) {
|
||||
|
||||
if (specSelectors.isOAS3()) {
|
||||
const currentMediaType = oas3Selectors.requestContentType(path, method)
|
||||
if (currentMediaType) {
|
||||
return getDefaultRequestBodyValue(
|
||||
specSelectors.specResolvedSubtree(["paths", path, method, "requestBody"]),
|
||||
specSelectors.specResolvedSubtree([
|
||||
"paths",
|
||||
path,
|
||||
method,
|
||||
"requestBody",
|
||||
]),
|
||||
currentMediaType,
|
||||
oas3Selectors.activeExamplesMember(
|
||||
path, method,
|
||||
"requestBody",
|
||||
path,
|
||||
method,
|
||||
"requestBody",
|
||||
"requestBody"
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -83,29 +98,43 @@ export const selectDefaultRequestBodyValue = (state, path, method) => (system) =
|
||||
|
||||
export const hasUserEditedBody = (state, path, method) => (system) => {
|
||||
const { oas3Selectors, specSelectors } = system.getSystem()
|
||||
const spec = specSelectors.specJson()
|
||||
if(isOAS3Helper(spec)) {
|
||||
|
||||
if (specSelectors.isOAS3()) {
|
||||
let userHasEditedBody = false
|
||||
const currentMediaType = oas3Selectors.requestContentType(path, method)
|
||||
let userEditedRequestBody = oas3Selectors.requestBodyValue(path, method)
|
||||
if (Map.isMap(userEditedRequestBody)) {
|
||||
// context is not application/json media-type
|
||||
userEditedRequestBody = stringify(userEditedRequestBody.mapEntries((kv) => Map.isMap(kv[1]) ? [kv[0], kv[1].get("value")] : kv).toJS())
|
||||
userEditedRequestBody = stringify(
|
||||
userEditedRequestBody
|
||||
.mapEntries((kv) =>
|
||||
Map.isMap(kv[1]) ? [kv[0], kv[1].get("value")] : kv
|
||||
)
|
||||
.toJS()
|
||||
)
|
||||
}
|
||||
if (List.isList(userEditedRequestBody)) {
|
||||
userEditedRequestBody = stringify(userEditedRequestBody)
|
||||
}
|
||||
if (currentMediaType) {
|
||||
const currentMediaTypeDefaultBodyValue = getDefaultRequestBodyValue(
|
||||
specSelectors.specResolvedSubtree(["paths", path, method, "requestBody"]),
|
||||
specSelectors.specResolvedSubtree([
|
||||
"paths",
|
||||
path,
|
||||
method,
|
||||
"requestBody",
|
||||
]),
|
||||
currentMediaType,
|
||||
oas3Selectors.activeExamplesMember(
|
||||
path, method,
|
||||
"requestBody",
|
||||
path,
|
||||
method,
|
||||
"requestBody",
|
||||
"requestBody"
|
||||
)
|
||||
)
|
||||
userHasEditedBody = !!userEditedRequestBody && userEditedRequestBody !== currentMediaTypeDefaultBodyValue
|
||||
userHasEditedBody =
|
||||
!!userEditedRequestBody &&
|
||||
userEditedRequestBody !== currentMediaTypeDefaultBodyValue
|
||||
}
|
||||
return userHasEditedBody
|
||||
} else {
|
||||
@@ -115,28 +144,32 @@ export const hasUserEditedBody = (state, path, method) => (system) => {
|
||||
|
||||
export const requestBodyInclusionSetting = onlyOAS3((state, path, method) => {
|
||||
return state.getIn(["requestData", path, method, "bodyInclusion"]) || Map()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
export const requestBodyErrors = onlyOAS3((state, path, method) => {
|
||||
return state.getIn(["requestData", path, method, "errors"]) || null
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
export const activeExamplesMember = onlyOAS3((state, path, method, type, name) => {
|
||||
return state.getIn(["examples", path, method, type, name, "activeExample"]) || null
|
||||
export const activeExamplesMember = onlyOAS3(
|
||||
(state, path, method, type, name) => {
|
||||
return (
|
||||
state.getIn(["examples", path, method, type, name, "activeExample"]) ||
|
||||
null
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
export const requestContentType = onlyOAS3((state, path, method) => {
|
||||
return state.getIn(["requestData", path, method, "requestContentType"]) || null
|
||||
}
|
||||
return (
|
||||
state.getIn(["requestData", path, method, "requestContentType"]) || null
|
||||
)
|
||||
})
|
||||
|
||||
export const responseContentType = onlyOAS3((state, path, method) => {
|
||||
return state.getIn(["requestData", path, method, "responseContentType"]) || null
|
||||
}
|
||||
return (
|
||||
state.getIn(["requestData", path, method, "responseContentType"]) || null
|
||||
)
|
||||
})
|
||||
|
||||
export const serverVariableValue = onlyOAS3((state, locationData, key) => {
|
||||
let path
|
||||
@@ -156,8 +189,7 @@ export const serverVariableValue = onlyOAS3((state, locationData, key) => {
|
||||
}
|
||||
|
||||
return state.getIn(path) || null
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
export const serverVariables = onlyOAS3((state, locationData) => {
|
||||
let path
|
||||
@@ -177,8 +209,7 @@ export const serverVariables = onlyOAS3((state, locationData) => {
|
||||
}
|
||||
|
||||
return state.getIn(path) || OrderedMap()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
export const serverEffectiveValue = onlyOAS3((state, locationData) => {
|
||||
var varValues, serverValue
|
||||
@@ -206,14 +237,20 @@ export const serverEffectiveValue = onlyOAS3((state, locationData) => {
|
||||
})
|
||||
|
||||
return str
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
export const validateBeforeExecute = validateRequestBodyIsRequired(
|
||||
(state, pathMethod) => validateRequestBodyValueExists(state, pathMethod)
|
||||
)
|
||||
|
||||
export const validateShallowRequired = (state, { oas3RequiredRequestBodyContentType, oas3RequestContentType, oas3RequestBodyValue} ) => {
|
||||
export const validateShallowRequired = (
|
||||
state,
|
||||
{
|
||||
oas3RequiredRequestBodyContentType,
|
||||
oas3RequestContentType,
|
||||
oas3RequestBodyValue,
|
||||
}
|
||||
) => {
|
||||
let missingRequiredKeys = []
|
||||
// context: json => String; urlencoded, form-data => Map
|
||||
if (!Map.isMap(oas3RequestBodyValue)) {
|
||||
@@ -221,16 +258,19 @@ export const validateShallowRequired = (state, { oas3RequiredRequestBodyContentT
|
||||
}
|
||||
let requiredKeys = []
|
||||
// Cycle through list of possible contentTypes for matching contentType and defined requiredKeys
|
||||
Object.keys(oas3RequiredRequestBodyContentType.requestContentType).forEach((contentType) => {
|
||||
Object.keys(oas3RequiredRequestBodyContentType.requestContentType).forEach(
|
||||
(contentType) => {
|
||||
if (contentType === oas3RequestContentType) {
|
||||
let contentTypeVal = oas3RequiredRequestBodyContentType.requestContentType[contentType]
|
||||
let contentTypeVal =
|
||||
oas3RequiredRequestBodyContentType.requestContentType[contentType]
|
||||
contentTypeVal.forEach((requiredKey) => {
|
||||
if (requiredKeys.indexOf(requiredKey) < 0) {
|
||||
requiredKeys.push(requiredKey)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
requiredKeys.forEach((key) => {
|
||||
let requiredKeyValue = oas3RequestBodyValue.getIn([key, "value"])
|
||||
if (!requiredKeyValue) {
|
||||
|
||||
@@ -1,72 +1,39 @@
|
||||
import { createSelector } from "reselect"
|
||||
import { Map } from "immutable"
|
||||
import { isOAS3 as isOAS3Helper, isOpenAPI31 as isOpenAPI31Helper, isSwagger2 as isSwagger2Helper } from "../helpers"
|
||||
import { isSwagger2 as isSwagger2Helper, isOAS30 as isOAS30Helper } from "../helpers"
|
||||
|
||||
|
||||
// Helpers
|
||||
/**
|
||||
* Helpers
|
||||
*/
|
||||
|
||||
// 1/2023: as of now, more accurately, isAnyOAS3
|
||||
function onlyOAS3(selector) {
|
||||
return () => (system, ...args) => {
|
||||
const spec = system.getSystem().specSelectors.specJson()
|
||||
if(isOAS3Helper(spec)) {
|
||||
return selector(...args)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isOpenAPI31(selector) {
|
||||
return () => (system, ...args) => {
|
||||
const spec = system.getSystem().specSelectors.specJson()
|
||||
if (isOpenAPI31Helper(spec)) {
|
||||
return selector(...args)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const state = state => {
|
||||
return state || Map()
|
||||
}
|
||||
|
||||
const specJson = createSelector(
|
||||
state,
|
||||
spec => spec.get("json", Map())
|
||||
)
|
||||
|
||||
const specResolved = createSelector(
|
||||
state,
|
||||
spec => spec.get("resolved", Map())
|
||||
)
|
||||
|
||||
const spec = state => {
|
||||
let res = specResolved(state)
|
||||
if(res.count() < 1)
|
||||
res = specJson(state)
|
||||
return res
|
||||
}
|
||||
|
||||
// New selectors
|
||||
|
||||
export const servers = onlyOAS3(createSelector(
|
||||
spec,
|
||||
spec => spec.getIn(["servers"]) || Map()
|
||||
))
|
||||
|
||||
export const isSwagger2 = (ori, system) => () => {
|
||||
export const isSwagger2 = () => (system) => {
|
||||
const spec = system.getSystem().specSelectors.specJson()
|
||||
return isSwagger2Helper(spec)
|
||||
}
|
||||
|
||||
export const selectIsOpenAPI31 = (ori, system) => () => {
|
||||
export const isOAS30 = () => (system) => {
|
||||
const spec = system.getSystem().specSelectors.specJson()
|
||||
return isOpenAPI31Helper(spec)
|
||||
return isOAS30Helper(spec)
|
||||
}
|
||||
|
||||
export const selectWebhooks = isOpenAPI31(createSelector(
|
||||
spec,
|
||||
spec => spec.getIn(["webhooks"]) || Map()
|
||||
))
|
||||
export const isOAS3 = () => (system) => {
|
||||
return system.getSystem().specSelectors.isOAS30()
|
||||
}
|
||||
|
||||
function onlyOAS3(selector) {
|
||||
return () => (system, ...args) => {
|
||||
const spec = system.getSystem().specSelectors.specJson()
|
||||
if(system.specSelectors.isOAS3(spec)) {
|
||||
const result = selector(...args)
|
||||
return typeof result === "function" ? result(system, ...args) : result
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const servers = onlyOAS3(() => (system) => {
|
||||
const spec = system.specSelectors.specJson()
|
||||
return spec.get("servers", servers.mapConst)
|
||||
})
|
||||
servers.mapConst = Map()
|
||||
|
||||
@@ -1,107 +1,54 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
import { createSelector } from "reselect"
|
||||
import { specJsonWithResolvedSubtrees } from "../../spec/selectors"
|
||||
import { Map } from "immutable"
|
||||
import { isOAS3 as isOAS3Helper, isOpenAPI31 as isOpenAPI31Helper, isSwagger2 as isSwagger2Helper } from "../helpers"
|
||||
|
||||
|
||||
// Helpers
|
||||
// 1/2023: as of now, more accurately, isAnyOAS3
|
||||
/**
|
||||
* Helpers
|
||||
*/
|
||||
function onlyOAS3(selector) {
|
||||
return (ori, system) => (...args) => {
|
||||
const spec = system.getSystem().specSelectors.specJson()
|
||||
if(isOAS3Helper(spec)) {
|
||||
return selector(...args)
|
||||
return (ori, system) =>
|
||||
(...args) => {
|
||||
if (system.getSystem().specSelectors.isOAS3()) {
|
||||
const result = selector(...args)
|
||||
return typeof result === "function" ? result(system, ...args) : result
|
||||
} else {
|
||||
return ori(...args)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 => {
|
||||
return state || Map()
|
||||
}
|
||||
|
||||
const nullSelector = createSelector(() => null)
|
||||
|
||||
const OAS3NullSelector = onlyOAS3(nullSelector)
|
||||
|
||||
const specJson = createSelector(
|
||||
state,
|
||||
spec => spec.get("json", Map())
|
||||
)
|
||||
/**
|
||||
* Wrappers
|
||||
*/
|
||||
|
||||
const specResolved = createSelector(
|
||||
state,
|
||||
spec => spec.get("resolved", Map())
|
||||
)
|
||||
export const definitions = onlyOAS3(() => (system) => {
|
||||
const spec = system.getSystem().specSelectors.specJson()
|
||||
const schemas = spec.getIn(["components", "schemas"])
|
||||
return Map.isMap(schemas) ? schemas : definitions.mapConst
|
||||
})
|
||||
definitions.mapConst = Map()
|
||||
|
||||
const spec = state => {
|
||||
let res = specResolved(state)
|
||||
if(res.count() < 1)
|
||||
res = specJson(state)
|
||||
return res
|
||||
}
|
||||
|
||||
// Wrappers
|
||||
|
||||
export const definitions = onlyOAS3(createSelector(
|
||||
spec,
|
||||
spec => {
|
||||
const res = spec.getIn(["components", "schemas"])
|
||||
return Map.isMap(res) ? res : Map()
|
||||
}
|
||||
))
|
||||
|
||||
export const hasHost = onlyOAS3((state) => {
|
||||
return spec(state).hasIn(["servers", 0])
|
||||
export const hasHost = onlyOAS3(() => (system) => {
|
||||
const spec = system.getSystem().specSelectors.specJson()
|
||||
return spec.hasIn(["servers", 0])
|
||||
})
|
||||
|
||||
export const securityDefinitions = onlyOAS3(createSelector(
|
||||
export const securityDefinitions = onlyOAS3(
|
||||
createSelector(
|
||||
specJsonWithResolvedSubtrees,
|
||||
spec => spec.getIn(["components", "securitySchemes"]) || null
|
||||
))
|
||||
(spec) => spec.getIn(["components", "securitySchemes"]) || null
|
||||
)
|
||||
)
|
||||
|
||||
export const host = OAS3NullSelector
|
||||
export const basePath = OAS3NullSelector
|
||||
export const consumes = OAS3NullSelector
|
||||
export const produces = OAS3NullSelector
|
||||
export const schemes = OAS3NullSelector
|
||||
|
||||
// New selectors
|
||||
|
||||
export const servers = onlyOAS3(createSelector(
|
||||
spec,
|
||||
spec => spec.getIn(["servers"]) || Map()
|
||||
))
|
||||
|
||||
export const isOAS3 = (ori, system) => () => {
|
||||
const spec = system.getSystem().specSelectors.specJson()
|
||||
return isOAS3Helper(Map.isMap(spec) ? spec : Map())
|
||||
}
|
||||
|
||||
export const isSwagger2 = (ori, system) => () => {
|
||||
const spec = system.getSystem().specSelectors.specJson()
|
||||
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,8 +4,6 @@ import VersionStamp from "./version-stamp"
|
||||
import OnlineValidatorBadge from "./online-validator-badge"
|
||||
import Model from "./model"
|
||||
import JsonSchema_string from "./json-schema-string"
|
||||
import License from "./license"
|
||||
import info from "./info"
|
||||
|
||||
export default {
|
||||
Markdown,
|
||||
@@ -14,6 +12,4 @@ export default {
|
||||
VersionStamp,
|
||||
model: Model,
|
||||
onlineValidatorBadge: OnlineValidatorBadge,
|
||||
License,
|
||||
info,
|
||||
}
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
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)
|
||||
@@ -1,49 +0,0 @@
|
||||
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)
|
||||
155
src/core/plugins/oas31/components/info.jsx
Normal file
155
src/core/plugins/oas31/components/info.jsx
Normal file
@@ -0,0 +1,155 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
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"
|
||||
|
||||
export class InfoBasePath extends React.Component {
|
||||
static propTypes = {
|
||||
host: PropTypes.string,
|
||||
basePath: PropTypes.string,
|
||||
}
|
||||
|
||||
render() {
|
||||
const { host, basePath } = this.props
|
||||
|
||||
return (
|
||||
<pre className="base-url">
|
||||
[ Base URL: {host}
|
||||
{basePath} ]
|
||||
</pre>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export class InfoUrl extends React.PureComponent {
|
||||
static propTypes = {
|
||||
url: PropTypes.string.isRequired,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
render() {
|
||||
const { url, getComponent } = this.props
|
||||
const Link = getComponent("Link")
|
||||
|
||||
return (
|
||||
<Link target="_blank" href={sanitizeUrl(url)}>
|
||||
<span className="url"> {url}</span>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class Info extends React.Component {
|
||||
static propTypes = {
|
||||
title: PropTypes.any,
|
||||
description: PropTypes.any,
|
||||
version: PropTypes.any,
|
||||
info: PropTypes.object,
|
||||
url: PropTypes.string,
|
||||
host: PropTypes.string,
|
||||
basePath: PropTypes.string,
|
||||
externalDocs: ImPropTypes.map,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
oas3selectors: PropTypes.func,
|
||||
selectedServer: PropTypes.string,
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
info,
|
||||
url,
|
||||
host,
|
||||
basePath,
|
||||
getComponent,
|
||||
externalDocs,
|
||||
selectedServer,
|
||||
url: specUrl,
|
||||
} = this.props
|
||||
const version = info.get("version")
|
||||
const summary = info.get("summary")
|
||||
const description = info.get("description")
|
||||
const title = info.get("title")
|
||||
const termsOfServiceUrl = safeBuildUrl(
|
||||
info.get("termsOfService"),
|
||||
specUrl,
|
||||
{ selectedServer }
|
||||
)
|
||||
const contactData = info.get("contact")
|
||||
const licenseData = info.get("license")
|
||||
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>
|
||||
{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>
|
||||
)}
|
||||
{contactData?.size > 0 && (
|
||||
<Contact
|
||||
getComponent={getComponent}
|
||||
data={contactData}
|
||||
selectedServer={selectedServer}
|
||||
url={url}
|
||||
/>
|
||||
)}
|
||||
{licenseData?.size > 0 && (
|
||||
<License
|
||||
getComponent={getComponent}
|
||||
license={licenseData}
|
||||
selectedServer={selectedServer}
|
||||
url={url}
|
||||
/>
|
||||
)}
|
||||
{externalDocsUrl ? (
|
||||
<Link
|
||||
className="info__extdocs"
|
||||
target="_blank"
|
||||
href={sanitizeUrl(externalDocsUrl)}
|
||||
>
|
||||
{externalDocsDescription || externalDocsUrl}
|
||||
</Link>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default Info
|
||||
52
src/core/plugins/oas31/components/license.jsx
Normal file
52
src/core/plugins/oas31/components/license.jsx
Normal file
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { safeBuildUrl } from "core/utils/url"
|
||||
import { sanitizeUrl } from "core/utils"
|
||||
|
||||
class License extends React.Component {
|
||||
static propTypes = {
|
||||
license: PropTypes.object,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
selectedServer: PropTypes.string,
|
||||
url: PropTypes.string.isRequired,
|
||||
}
|
||||
|
||||
render() {
|
||||
const { license, getComponent, selectedServer, url: specUrl } = this.props
|
||||
const name = license.get("name", "License")
|
||||
const url = sanitizeUrl(
|
||||
safeBuildUrl(license.get("url"), specUrl, { selectedServer })
|
||||
)
|
||||
const identifier = license.get("identifier", "")
|
||||
const spdxURL = sanitizeUrl(`https://spdx.org/licenses/${identifier}.html`)
|
||||
|
||||
const Link = getComponent("Link")
|
||||
|
||||
return (
|
||||
<div className="info__license">
|
||||
{identifier && (
|
||||
<div className="info__license__url">
|
||||
<Link target="_blank" href={spdxURL}>
|
||||
{name}
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{url && !identifier && (
|
||||
<div className="info__license__url">
|
||||
<Link target="_blank" href={url}>
|
||||
{name}
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!url && !identifier && <span>{name}</span>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default License
|
||||
@@ -1,4 +1,3 @@
|
||||
// OpenAPI 3.1 feature
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { fromJS } from "immutable"
|
||||
@@ -9,7 +8,7 @@ import ImPropTypes from "react-immutable-proptypes"
|
||||
const Webhooks = (props) => {
|
||||
const { specSelectors, getComponent, specPath } = props
|
||||
|
||||
const webhooksPathItems = specSelectors.selectWebhooks() // OrderedMap
|
||||
const webhooksPathItems = specSelectors.webhooks()
|
||||
if (!webhooksPathItems || webhooksPathItems?.size < 1) {
|
||||
return null
|
||||
}
|
||||
10
src/core/plugins/oas31/helpers.js
Normal file
10
src/core/plugins/oas31/helpers.js
Normal file
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
export const isOAS31 = (jsSpec) => {
|
||||
const oasVersion = jsSpec.get("openapi")
|
||||
|
||||
return (
|
||||
typeof oasVersion === "string" && /^3\.1\.(?:[1-9]\d*|0)$/.test(oasVersion)
|
||||
)
|
||||
}
|
||||
37
src/core/plugins/oas31/index.js
Normal file
37
src/core/plugins/oas31/index.js
Normal file
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
import Webhooks from "./components/webhooks"
|
||||
import License from "./components/license"
|
||||
import Info from "./components/info"
|
||||
import LicenseWrapper from "./wrap-components/license"
|
||||
import InfoWrapper from "./wrap-components/info"
|
||||
import { isOAS31, webhooks } from "./spec-extensions/selectors"
|
||||
import { isOAS3 } from "./spec-extensions/wrap-selectors"
|
||||
|
||||
const OAS31Plugin = () => {
|
||||
return {
|
||||
components: {
|
||||
Webhooks,
|
||||
OAS31Info: Info,
|
||||
OAS31License: License,
|
||||
},
|
||||
wrapComponents: {
|
||||
License: LicenseWrapper,
|
||||
info: InfoWrapper,
|
||||
},
|
||||
statePlugins: {
|
||||
spec: {
|
||||
selectors: {
|
||||
isOAS31,
|
||||
webhooks,
|
||||
},
|
||||
wrapSelectors: {
|
||||
isOAS3,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export default OAS31Plugin
|
||||
29
src/core/plugins/oas31/spec-extensions/selectors.js
Normal file
29
src/core/plugins/oas31/spec-extensions/selectors.js
Normal file
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
import { Map } from "immutable"
|
||||
|
||||
import { isOAS31 as isOAS31Helper } from "../helpers"
|
||||
|
||||
export const isOAS31 = () => (system) => {
|
||||
const spec = system.specSelectors.specJson()
|
||||
return isOAS31Helper(spec)
|
||||
}
|
||||
|
||||
const onlyOAS31 =
|
||||
(selector) =>
|
||||
() =>
|
||||
(system, ...args) => {
|
||||
if (system.getSystem().specSelectors.isOAS31()) {
|
||||
const result = selector(...args)
|
||||
return typeof result === "function" ? result(system, ...args) : result
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export const webhooks = onlyOAS31(() => (system) => {
|
||||
const spec = system.specSelectors.specJson()
|
||||
return spec.get("webhooks", webhooks.mapConst)
|
||||
})
|
||||
webhooks.mapConst = Map()
|
||||
4
src/core/plugins/oas31/spec-extensions/wrap-selectors.js
Normal file
4
src/core/plugins/oas31/spec-extensions/wrap-selectors.js
Normal file
@@ -0,0 +1,4 @@
|
||||
export const isOAS3 = (oriSelector, system) => (state, ...args) => {
|
||||
const isOAS31 = system.specSelectors.isOAS31()
|
||||
return isOAS31 || oriSelector(...args)
|
||||
}
|
||||
16
src/core/plugins/oas31/wrap-components/info.jsx
Normal file
16
src/core/plugins/oas31/wrap-components/info.jsx
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
import React from "react"
|
||||
|
||||
const InfoWrapper = (Original, system) => (props) => {
|
||||
if (system.specSelectors.isOAS31()) {
|
||||
const OAS31Info = system.getComponent("OAS31Info")
|
||||
|
||||
return <OAS31Info {...props} />
|
||||
}
|
||||
|
||||
return <Original {...props} />
|
||||
}
|
||||
|
||||
export default InfoWrapper
|
||||
16
src/core/plugins/oas31/wrap-components/license.jsx
Normal file
16
src/core/plugins/oas31/wrap-components/license.jsx
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
import React from "react"
|
||||
|
||||
const LicenseWrapper = (Original, system) => (props) => {
|
||||
if (system.specSelectors.isOAS31()) {
|
||||
const OAS31License = system.getComponent("OAS31License")
|
||||
|
||||
return <OAS31License {...props} />
|
||||
}
|
||||
|
||||
return <Original {...props} />
|
||||
}
|
||||
|
||||
export default LicenseWrapper
|
||||
@@ -1,12 +1,10 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
import BasePreset from "./base"
|
||||
import OAS3Plugin from "../plugins/oas3"
|
||||
|
||||
// Just the base, for now.
|
||||
import OAS31Plugin from "../plugins/oas31"
|
||||
|
||||
export default function PresetApis() {
|
||||
|
||||
return [
|
||||
BasePreset,
|
||||
OAS3Plugin
|
||||
]
|
||||
return [BasePreset, OAS3Plugin, OAS31Plugin]
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
import err from "core/plugins/err"
|
||||
import layout from "core/plugins/layout"
|
||||
import spec from "core/plugins/spec"
|
||||
@@ -57,13 +60,10 @@ import Errors from "core/components/errors"
|
||||
import ContentType from "core/components/content-type"
|
||||
import Overview from "core/components/overview"
|
||||
import InitializedInput from "core/components/initialized-input"
|
||||
import Info, {
|
||||
InfoUrl,
|
||||
InfoBasePath,
|
||||
License,
|
||||
Contact,
|
||||
} from "core/components/info"
|
||||
import Info, { InfoUrl, InfoBasePath } from "core/components/info"
|
||||
import InfoContainer from "core/containers/info"
|
||||
import Contact from "core/components/contact"
|
||||
import License from "core/components/license"
|
||||
import JumpToPath from "core/components/jump-to-path"
|
||||
import CopyToClipboardBtn from "core/components/copy-to-clipboard-btn"
|
||||
import Footer from "core/components/footer"
|
||||
@@ -87,16 +87,12 @@ import VersionPragmaFilter from "core/components/version-pragma-filter"
|
||||
import VersionStamp from "core/components/version-stamp"
|
||||
import DeepLink from "core/components/deep-link"
|
||||
import SvgAssets from "core/components/svg-assets"
|
||||
|
||||
import Markdown from "core/components/providers/markdown"
|
||||
|
||||
import BaseLayout from "core/components/layouts/base"
|
||||
|
||||
import * as LayoutUtils from "core/components/layout-utils"
|
||||
import * as JsonSchemaComponents from "core/json-schema-components"
|
||||
|
||||
export default function () {
|
||||
|
||||
let coreComponents = {
|
||||
components: {
|
||||
App,
|
||||
@@ -115,6 +111,10 @@ export default function() {
|
||||
InitializedInput,
|
||||
info: Info,
|
||||
InfoContainer,
|
||||
InfoUrl,
|
||||
InfoBasePath,
|
||||
Contact,
|
||||
License,
|
||||
JumpToPath,
|
||||
CopyToClipboardBtn,
|
||||
onlineValidatorBadge: OnlineValidatorBadge,
|
||||
@@ -163,23 +163,19 @@ export default function() {
|
||||
OperationTag,
|
||||
OperationContainer,
|
||||
DeepLink,
|
||||
InfoUrl,
|
||||
InfoBasePath,
|
||||
License,
|
||||
Contact,
|
||||
SvgAssets,
|
||||
Example,
|
||||
ExamplesSelect,
|
||||
ExamplesSelectValueRetainer,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
let formComponents = {
|
||||
components: LayoutUtils
|
||||
components: LayoutUtils,
|
||||
}
|
||||
|
||||
let jsonSchemaComponents = {
|
||||
components: JsonSchemaComponents
|
||||
components: JsonSchemaComponents,
|
||||
}
|
||||
|
||||
return [
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
swagger: 2.0.0
|
||||
swagger: "2.0"
|
||||
|
||||
info:
|
||||
title: OpenAPI 2.0 Info Object
|
||||
version: 1.0.0
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
swagger: 2.0.0
|
||||
swagger: "2.0"
|
||||
info:
|
||||
title: OpenAPI 2.0 License with only url present
|
||||
version: 1.0.0
|
||||
|
||||
@@ -5,5 +5,4 @@ info:
|
||||
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)
|
||||
identifier: Apache-2.0
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
describe("Render Info Component", () => {
|
||||
describe("OpenAPI 2.x", () => {
|
||||
describe("OpenAPI 2.0", () => {
|
||||
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")
|
||||
@@ -16,19 +18,23 @@ describe("Render Info Component", () => {
|
||||
.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")
|
||||
@@ -39,19 +45,23 @@ describe("Render Info Component", () => {
|
||||
.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")
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
describe("Render License Component", () => {
|
||||
describe("OpenAPI 2", () =>{
|
||||
describe("OpenAPI 2.0", () => {
|
||||
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")
|
||||
.get(".info__license__url > .link")
|
||||
.should("have.attr", "rel")
|
||||
.and("include", "noopener")
|
||||
.and("include", "noreferrer")
|
||||
@@ -21,17 +23,17 @@ describe("Render License Component", () => {
|
||||
.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")
|
||||
@@ -43,18 +45,21 @@ describe("Render License Component", () => {
|
||||
.and("equal", "_blank")
|
||||
})
|
||||
})
|
||||
|
||||
describe("OpenAPI 3.1.x", () => {
|
||||
describe("only URL", () => {
|
||||
describe("given URL field", () => {
|
||||
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")
|
||||
.get(".info__license__url > a")
|
||||
.should("have.attr", "href")
|
||||
.and("equal", "https://www.apache.org/licenses/LICENSE-2.0.html")
|
||||
})
|
||||
|
||||
it("should render URL anchor target xss link with safe `rel` attributes", () => {
|
||||
cy.visit(baseUrl)
|
||||
.get(".info__license__url > a")
|
||||
@@ -66,34 +71,37 @@ describe("Render License Component", () => {
|
||||
.and("equal", "_blank")
|
||||
})
|
||||
})
|
||||
describe("only SPDX Identifier", () => {
|
||||
const baseUrl = "/?url=/documents/features/license-openAPI31-identifier.yaml"
|
||||
it("should render SPDX Identifier", () => {
|
||||
|
||||
describe("given identifier field", () => {
|
||||
const baseUrl =
|
||||
"/?url=/documents/features/license-openAPI31-identifier.yaml"
|
||||
|
||||
it("should render URL using 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")
|
||||
.should("exist")
|
||||
.should("contains.text", "Apache 2.0")
|
||||
.get(".info__license__url > a")
|
||||
.should("have.attr", "href")
|
||||
.and("equal", "https://spdx.org/licenses/Apache-2.0.html")
|
||||
})
|
||||
it("should render SPDX and Identifier anchor target xss links with safe `rel` attributes ", () => {
|
||||
|
||||
it("should render URL anchor target xss links with safe `rel` attributes", () => {
|
||||
cy.visit(baseUrl)
|
||||
.get(".info__license__identifier > a")
|
||||
.each(($el) => {
|
||||
cy.get($el)
|
||||
.get(".info__license__url > a")
|
||||
.should("have.attr", "rel")
|
||||
.and("include", "noopener")
|
||||
.and("include", "noreferrer")
|
||||
cy.get($el)
|
||||
.get(".info .main > a")
|
||||
.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"
|
||||
const baseUrl =
|
||||
"/?url=/documents/features/license-openAPI31-error-both-identifier-and-url.yaml"
|
||||
cy.visit(baseUrl)
|
||||
.get(".info__license__identifier")
|
||||
.should("not.exist")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { fromJS } from "immutable"
|
||||
import { isOAS3, isSwagger2 } from "corePlugins/oas3/helpers"
|
||||
import { isOAS30, isSwagger2 } from "corePlugins/oas3/helpers"
|
||||
|
||||
const isOAS3Shorthand = (version) => isOAS3(fromJS({
|
||||
const isOAS3Shorthand = (version) => isOAS30(fromJS({
|
||||
openapi: version
|
||||
}))
|
||||
|
||||
@@ -13,7 +13,7 @@ describe("isOAS3", function () {
|
||||
it("should recognize valid OAS3 version values", function () {
|
||||
expect(isOAS3Shorthand("3.0.0")).toEqual(true)
|
||||
expect(isOAS3Shorthand("3.0.1")).toEqual(true)
|
||||
expect(isOAS3Shorthand("3.0.11111")).toEqual(true)
|
||||
expect(isOAS3Shorthand("3.0.11111")).toEqual(false)
|
||||
expect(isOAS3Shorthand("3.0.0-rc0")).toEqual(true)
|
||||
})
|
||||
|
||||
@@ -31,7 +31,7 @@ describe("isOAS3", function () {
|
||||
})
|
||||
|
||||
it("should gracefully fail when `openapi` field is missing", function () {
|
||||
expect(isOAS3(fromJS({
|
||||
expect(isOAS30(fromJS({
|
||||
openApi: "3.0.0"
|
||||
}))).toEqual(false)
|
||||
expect(isOAS3Shorthand(null)).toEqual(false)
|
||||
@@ -41,7 +41,7 @@ describe("isOAS3", function () {
|
||||
describe("isSwagger2", function () {
|
||||
it("should recognize valid Swagger 2.0 version values", function () {
|
||||
expect(isSwagger2Shorthand("2.0")).toEqual(true)
|
||||
expect(isSwagger2Shorthand("2.0-rc0")).toEqual(true)
|
||||
expect(isSwagger2Shorthand("2.0-rc0")).toEqual(false)
|
||||
})
|
||||
|
||||
it("should fail for invalid Swagger 2.0 version values", function () {
|
||||
|
||||
@@ -26,7 +26,8 @@ describe("OAS3 plugin - state", function() {
|
||||
specSelectors: {
|
||||
specJson: () => {
|
||||
return fromJS({ openapi: "3.0.0" })
|
||||
}
|
||||
},
|
||||
isOAS3: () => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -55,7 +56,8 @@ describe("OAS3 plugin - state", function() {
|
||||
specSelectors: {
|
||||
specJson: () => {
|
||||
return fromJS({ openapi: "3.0.0" })
|
||||
}
|
||||
},
|
||||
isOAS3: () => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -88,7 +90,8 @@ describe("OAS3 plugin - state", function() {
|
||||
specSelectors: {
|
||||
specJson: () => {
|
||||
return fromJS({ openapi: "3.0.0" })
|
||||
}
|
||||
},
|
||||
isOAS3: () => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -124,7 +127,8 @@ describe("OAS3 plugin - state", function() {
|
||||
specSelectors: {
|
||||
specJson: () => {
|
||||
return fromJS({ openapi: "3.0.0" })
|
||||
}
|
||||
},
|
||||
isOAS3: () => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -167,7 +171,8 @@ describe("OAS3 plugin - state", function() {
|
||||
specSelectors: {
|
||||
specJson: () => {
|
||||
return fromJS({ openapi: "3.0.0" })
|
||||
}
|
||||
},
|
||||
isOAS3: () => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -207,7 +212,8 @@ describe("OAS3 plugin - state", function() {
|
||||
specSelectors: {
|
||||
specJson: () => {
|
||||
return fromJS({ openapi: "3.0.0" })
|
||||
}
|
||||
},
|
||||
isOAS3: () => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -261,7 +267,8 @@ describe("OAS3 plugin - state", function() {
|
||||
specSelectors: {
|
||||
specJson: () => {
|
||||
return fromJS({ openapi: "3.0.0" })
|
||||
}
|
||||
},
|
||||
isOAS3: () => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -314,7 +321,8 @@ describe("OAS3 plugin - state", function() {
|
||||
specSelectors: {
|
||||
specJson: () => {
|
||||
return fromJS({ openapi: "3.0.0" })
|
||||
}
|
||||
},
|
||||
isOAS3: () => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -342,7 +350,8 @@ describe("OAS3 plugin - state", function() {
|
||||
specSelectors: {
|
||||
specJson: () => {
|
||||
return fromJS({ openapi: "3.0.0" })
|
||||
}
|
||||
},
|
||||
isOAS3: () => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ describe("oas3 plugin - auth extensions - wrapSelectors", function(){
|
||||
specJson: () => fromJS({
|
||||
openapi: "3.0.0"
|
||||
}),
|
||||
isOAS3: () => true,
|
||||
securityDefinitions: () => {
|
||||
return fromJS({
|
||||
"oauth2AuthorizationCode": {
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
|
||||
import { fromJS } from "immutable"
|
||||
import {
|
||||
definitions
|
||||
} from "corePlugins/oas3/spec-extensions/wrap-selectors"
|
||||
import { definitions } from "corePlugins/oas3/spec-extensions/wrap-selectors"
|
||||
|
||||
describe("oas3 plugin - spec extensions - wrapSelectors", function(){
|
||||
|
||||
@@ -28,6 +25,7 @@ describe("oas3 plugin - spec extensions - wrapSelectors", function(){
|
||||
getSystem: () => system,
|
||||
specSelectors: {
|
||||
specJson: () => spec,
|
||||
isOAS3: () => true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +55,7 @@ describe("oas3 plugin - spec extensions - wrapSelectors", function(){
|
||||
getSystem: () => system,
|
||||
specSelectors: {
|
||||
specJson: () => spec,
|
||||
isOAS3: () => true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +81,8 @@ describe("oas3 plugin - spec extensions - wrapSelectors", function(){
|
||||
getSystem: () => system,
|
||||
specSelectors: {
|
||||
specJson: () => spec,
|
||||
}
|
||||
isOAS3: () => true,
|
||||
},
|
||||
}
|
||||
|
||||
// When
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import React from "react"
|
||||
import { render } from "enzyme"
|
||||
import { fromJS } from "immutable"
|
||||
import Info, { InfoUrl, License } from "components/info"
|
||||
import Info, { InfoUrl } from "components/info"
|
||||
import Contact from "components/contact"
|
||||
import License from "components/license"
|
||||
import { Link } from "components/layout-utils"
|
||||
import Markdown from "components/providers/markdown"
|
||||
|
||||
@@ -11,7 +13,8 @@ describe("<Info/> Anchor Target Safety", function(){
|
||||
Markdown,
|
||||
InfoUrl,
|
||||
License,
|
||||
Link
|
||||
Contact,
|
||||
Link,
|
||||
}
|
||||
const baseProps = {
|
||||
getComponent: c => components[c] || dummyComponent,
|
||||
|
||||
Reference in New Issue
Block a user