feat(oas31): render JSON Schema 2020-12 in Operations and Webhooks (#8673)

Includes Callback Objects as well.

Refs #8513
This commit is contained in:
Vladimír Gorej
2023-05-16 10:39:00 +02:00
committed by GitHub
parent 79e0c4e096
commit 2f0282d063
15 changed files with 268 additions and 297 deletions

View File

@@ -11,6 +11,7 @@ import {
useLevel,
useFn,
useIsEmbedded,
useIsExpanded,
useIsExpandedDeeply,
useIsCircular,
useRenderedSchemas,
@@ -24,8 +25,9 @@ import {
const JSONSchema = forwardRef(
({ schema, name, dependentRequired, onExpand }, ref) => {
const fn = useFn()
const isExpanded = useIsExpanded()
const isExpandedDeeply = useIsExpandedDeeply()
const [expanded, setExpanded] = useState(isExpandedDeeply)
const [expanded, setExpanded] = useState(isExpanded || isExpandedDeeply)
const [expandedDeeply, setExpandedDeeply] = useState(isExpandedDeeply)
const [level, nextLevel] = useLevel()
const isEmbedded = useIsEmbedded()

View File

@@ -9,6 +9,9 @@ import { useFn } from "../../../hooks"
const Title = ({ title, schema }) => {
const fn = useFn()
const renderedTitle = title || fn.getTitle(schema)
if (!renderedTitle) return null
return (
<div className="json-schema-2020-12__title">

View File

@@ -3,6 +3,8 @@
@include text_headline($section-models-model-title-font-color);
display: inline-block;
font-weight: bold;
font-size: 12px;
line-height: normal;
& .json-schema-2020-12-keyword__name {
margin: 0;

View File

@@ -38,12 +38,15 @@ export const useIsEmbedded = () => {
return level > 0
}
export const useIsExpandedDeeply = () => {
export const useIsExpanded = () => {
const [level] = useLevel()
const { defaultExpandedLevels } = useConfig()
const isExpandedByDefault = defaultExpandedLevels - level > 0
return isExpandedByDefault || useContext(JSONSchemaDeepExpansionContext)
return defaultExpandedLevels - level > 0
}
export const useIsExpandedDeeply = () => {
return useContext(JSONSchemaDeepExpansionContext)
}
export const useRenderedSchemas = (schema = undefined) => {

View File

@@ -0,0 +1,2 @@
@import './model/model';
@import './models/models';

View File

@@ -0,0 +1,19 @@
.model-box {
// inferred names of Schema Objects
& .json-schema-2020-12:not(.json-schema-2020-12--embedded) > .json-schema-2020-12-head .json-schema-2020-12__title:first-of-type {
font-size: 16px;
}
& > .json-schema-2020-12 {
margin: 0;
}
.json-schema-2020-12 {
padding: 0;
background-color: transparent;
}
.json-schema-2020-12-accordion, .json-schema-2020-12-expand-deep-button {
background-color: transparent;
}
}

View File

@@ -0,0 +1,73 @@
/**
* @prettier
*/
import React, { forwardRef, useCallback } from "react"
import PropTypes from "prop-types"
import ImPropTypes from "react-immutable-proptypes"
const decodeRefName = (uri) => {
const unescaped = uri.replace(/~1/g, "/").replace(/~0/g, "~")
try {
return decodeURIComponent(unescaped)
} catch {
return unescaped
}
}
const getModelName = (uri) => {
if (typeof uri === "string" && uri.includes("#/components/schemas/")) {
return decodeRefName(uri.replace(/^.*#\/components\/schemas\//, ""))
}
return null
}
const Model = forwardRef(({ schema, getComponent, onToggle }, ref) => {
const JSONSchema202012 = getComponent("JSONSchema202012")
const name = getModelName(schema.get("$$ref"))
const handleExpand = useCallback(
(e, expanded) => {
onToggle(name, expanded)
},
[name, onToggle]
)
return (
<JSONSchema202012
name={name}
schema={schema.toJS()}
ref={ref}
onExpand={handleExpand}
/>
)
})
Model.propTypes = {
schema: ImPropTypes.map.isRequired,
getComponent: PropTypes.func.isRequired,
getConfigs: PropTypes.func.isRequired,
specSelectors: PropTypes.object.isRequired,
specPath: ImPropTypes.list.isRequired,
name: PropTypes.string,
displayName: PropTypes.string,
isRef: PropTypes.bool,
required: PropTypes.bool,
expandDepth: PropTypes.number,
depth: PropTypes.number,
includeReadOnly: PropTypes.bool,
includeWriteOnly: PropTypes.bool,
onToggle: PropTypes.func,
}
Model.defaultProps = {
name: "",
displayName: "",
isRef: false,
required: false,
expandDepth: 0,
depth: 1,
includeReadOnly: false,
includeWriteOnly: false,
onToggle: () => {},
}
export default Model

View File

@@ -0,0 +1,3 @@
.models .json-schema-2020-12:not(.json-schema-2020-12--embedded) > .json-schema-2020-12-head .json-schema-2020-12__title:first-of-type {
font-size: 16px;
}

View File

@@ -7,13 +7,13 @@ import Contact from "./components/contact"
import Info from "./components/info"
import JsonSchemaDialect from "./components/json-schema-dialect"
import VersionPragmaFilter from "./components/version-pragma-filter"
import Models from "./components/models"
import Model from "./components/model/model"
import Models from "./components/models/models"
import LicenseWrapper from "./wrap-components/license"
import ContactWrapper from "./wrap-components/contact"
import InfoWrapper from "./wrap-components/info"
import ModelWrapper from "./wrap-components/model"
import ModelsWrapper from "./wrap-components/models"
import OperationsWrapper from "./wrap-components/operations"
import WebhooksWrapper from "./wrap-components/webhooks"
import VersionPragmaFilterWrapper from "./wrap-components/version-pragma-filter"
import VersionStampWrapper from "./wrap-components/version-stamp"
import {
@@ -87,6 +87,7 @@ const OAS31Plugin = ({ getSystem }) => {
OAS31License: License,
OAS31Contact: Contact,
OAS31VersionPragmaFilter: VersionPragmaFilter,
OAS31Model: Model,
OAS31Models: Models,
JSONSchema202012KeywordExample,
JSONSchema202012KeywordXml,
@@ -99,9 +100,8 @@ const OAS31Plugin = ({ getSystem }) => {
Contact: ContactWrapper,
VersionPragmaFilter: VersionPragmaFilterWrapper,
VersionStamp: VersionStampWrapper,
Model: ModelWrapper,
Models: ModelsWrapper,
Operations: OperationsWrapper,
Webhooks: WebhooksWrapper,
JSONSchema202012KeywordDescription:
JSONSchema202012KeywordDescriptionWrapper,
JSONSchema202012KeywordDefault: JSONSchema202012KeywordDefaultWrapper,

View File

@@ -0,0 +1,150 @@
/**
* @prettier
*/
import React from "react"
import { createOnlyOAS31ComponentWrapper } from "../fn"
import { makeIsExpandable } from "../json-schema-2020-12-extensions/fn"
const ModelWrapper = createOnlyOAS31ComponentWrapper(
({ getSystem, ...props }) => {
const system = getSystem()
const { getComponent, fn, getConfigs } = system
const configs = getConfigs()
const Model = getComponent("OAS31Model")
const JSONSchema = getComponent("JSONSchema202012")
const Keyword$schema = getComponent("JSONSchema202012Keyword$schema")
const Keyword$vocabulary = getComponent(
"JSONSchema202012Keyword$vocabulary"
)
const Keyword$id = getComponent("JSONSchema202012Keyword$id")
const Keyword$anchor = getComponent("JSONSchema202012Keyword$anchor")
const Keyword$dynamicAnchor = getComponent(
"JSONSchema202012Keyword$dynamicAnchor"
)
const Keyword$ref = getComponent("JSONSchema202012Keyword$ref")
const Keyword$dynamicRef = getComponent(
"JSONSchema202012Keyword$dynamicRef"
)
const Keyword$defs = getComponent("JSONSchema202012Keyword$defs")
const Keyword$comment = getComponent("JSONSchema202012Keyword$comment")
const KeywordAllOf = getComponent("JSONSchema202012KeywordAllOf")
const KeywordAnyOf = getComponent("JSONSchema202012KeywordAnyOf")
const KeywordOneOf = getComponent("JSONSchema202012KeywordOneOf")
const KeywordNot = getComponent("JSONSchema202012KeywordNot")
const KeywordIf = getComponent("JSONSchema202012KeywordIf")
const KeywordThen = getComponent("JSONSchema202012KeywordThen")
const KeywordElse = getComponent("JSONSchema202012KeywordElse")
const KeywordDependentSchemas = getComponent(
"JSONSchema202012KeywordDependentSchemas"
)
const KeywordPrefixItems = getComponent(
"JSONSchema202012KeywordPrefixItems"
)
const KeywordItems = getComponent("JSONSchema202012KeywordItems")
const KeywordContains = getComponent("JSONSchema202012KeywordContains")
const KeywordProperties = getComponent("JSONSchema202012KeywordProperties")
const KeywordPatternProperties = getComponent(
"JSONSchema202012KeywordPatternProperties"
)
const KeywordAdditionalProperties = getComponent(
"JSONSchema202012KeywordAdditionalProperties"
)
const KeywordPropertyNames = getComponent(
"JSONSchema202012KeywordPropertyNames"
)
const KeywordUnevaluatedItems = getComponent(
"JSONSchema202012KeywordUnevaluatedItems"
)
const KeywordUnevaluatedProperties = getComponent(
"JSONSchema202012KeywordUnevaluatedProperties"
)
const KeywordType = getComponent("JSONSchema202012KeywordType")
const KeywordEnum = getComponent("JSONSchema202012KeywordEnum")
const KeywordConst = getComponent("JSONSchema202012KeywordConst")
const KeywordConstraint = getComponent("JSONSchema202012KeywordConstraint")
const KeywordDependentRequired = getComponent(
"JSONSchema202012KeywordDependentRequired"
)
const KeywordContentSchema = getComponent(
"JSONSchema202012KeywordContentSchema"
)
const KeywordTitle = getComponent("JSONSchema202012KeywordTitle")
const KeywordDescription = getComponent(
"JSONSchema202012KeywordDescription"
)
const KeywordDefault = getComponent("JSONSchema202012KeywordDefault")
const KeywordDeprecated = getComponent("JSONSchema202012KeywordDeprecated")
const KeywordReadOnly = getComponent("JSONSchema202012KeywordReadOnly")
const KeywordWriteOnly = getComponent("JSONSchema202012KeywordWriteOnly")
const Accordion = getComponent("JSONSchema202012Accordion")
const ExpandDeepButton = getComponent("JSONSchema202012ExpandDeepButton")
const ChevronRightIcon = getComponent("JSONSchema202012ChevronRightIcon")
const withSchemaContext = getComponent("withJSONSchema202012Context")
const ModelWithJSONSchemaContext = withSchemaContext(Model, {
config: {
default$schema: "https://spec.openapis.org/oas/3.1/dialect/base",
defaultExpandedLevels: configs.defaultModelExpandDepth,
includeReadOnly: Boolean(props.includeReadOnly),
includeWriteOnly: Boolean(props.includeWriteOnly),
},
components: {
JSONSchema,
Keyword$schema,
Keyword$vocabulary,
Keyword$id,
Keyword$anchor,
Keyword$dynamicAnchor,
Keyword$ref,
Keyword$dynamicRef,
Keyword$defs,
Keyword$comment,
KeywordAllOf,
KeywordAnyOf,
KeywordOneOf,
KeywordNot,
KeywordIf,
KeywordThen,
KeywordElse,
KeywordDependentSchemas,
KeywordPrefixItems,
KeywordItems,
KeywordContains,
KeywordProperties,
KeywordPatternProperties,
KeywordAdditionalProperties,
KeywordPropertyNames,
KeywordUnevaluatedItems,
KeywordUnevaluatedProperties,
KeywordType,
KeywordEnum,
KeywordConst,
KeywordConstraint,
KeywordDependentRequired,
KeywordContentSchema,
KeywordTitle,
KeywordDescription,
KeywordDefault,
KeywordDeprecated,
KeywordReadOnly,
KeywordWriteOnly,
Accordion,
ExpandDeepButton,
ChevronRightIcon,
},
fn: {
upperFirst: fn.upperFirst,
isExpandable: makeIsExpandable(
fn.jsonSchema202012.isExpandable,
system
),
},
})
return <ModelWithJSONSchemaContext {...props} />
}
)
export default ModelWrapper

View File

@@ -78,6 +78,7 @@ const ModelsWrapper = createOnlyOAS31ComponentWrapper(({ getSystem }) => {
const ChevronRightIcon = getComponent("JSONSchema202012ChevronRightIcon")
const withSchemaContext = getComponent("withJSONSchema202012Context")
// we cache the HOC as recreating it with every re-render is quite expensive
ModelsWrapper.ModelsWithJSONSchemaContext = withSchemaContext(Models, {
config: {
default$schema: "https://spec.openapis.org/oas/3.1/dialect/base",

View File

@@ -1,147 +0,0 @@
/**
* @prettier
*/
import React from "react"
import { createOnlyOAS31ComponentWrapper } from "../fn"
import { makeIsExpandable } from "../json-schema-2020-12-extensions/fn"
const OperationsWrapper = createOnlyOAS31ComponentWrapper(({ getSystem }) => {
const system = getSystem()
const { getComponent, fn, getConfigs } = system
const configs = getConfigs()
if (OperationsWrapper.OperationsWithJSONSchemaContext) {
return <OperationsWrapper.OperationsWithJSONSchemaContext />
}
const Operations = getComponent("operations", true)
const JSONSchema = getComponent("JSONSchema202012")
const Keyword$schema = getComponent("JSONSchema202012Keyword$schema")
const Keyword$vocabulary = getComponent("JSONSchema202012Keyword$vocabulary")
const Keyword$id = getComponent("JSONSchema202012Keyword$id")
const Keyword$anchor = getComponent("JSONSchema202012Keyword$anchor")
const Keyword$dynamicAnchor = getComponent(
"JSONSchema202012Keyword$dynamicAnchor"
)
const Keyword$ref = getComponent("JSONSchema202012Keyword$ref")
const Keyword$dynamicRef = getComponent("JSONSchema202012Keyword$dynamicRef")
const Keyword$defs = getComponent("JSONSchema202012Keyword$defs")
const Keyword$comment = getComponent("JSONSchema202012Keyword$comment")
const KeywordAllOf = getComponent("JSONSchema202012KeywordAllOf")
const KeywordAnyOf = getComponent("JSONSchema202012KeywordAnyOf")
const KeywordOneOf = getComponent("JSONSchema202012KeywordOneOf")
const KeywordNot = getComponent("JSONSchema202012KeywordNot")
const KeywordIf = getComponent("JSONSchema202012KeywordIf")
const KeywordThen = getComponent("JSONSchema202012KeywordThen")
const KeywordElse = getComponent("JSONSchema202012KeywordElse")
const KeywordDependentSchemas = getComponent(
"JSONSchema202012KeywordDependentSchemas"
)
const KeywordPrefixItems = getComponent("JSONSchema202012KeywordPrefixItems")
const KeywordItems = getComponent("JSONSchema202012KeywordItems")
const KeywordContains = getComponent("JSONSchema202012KeywordContains")
const KeywordProperties = getComponent("JSONSchema202012KeywordProperties")
const KeywordPatternProperties = getComponent(
"JSONSchema202012KeywordPatternProperties"
)
const KeywordAdditionalProperties = getComponent(
"JSONSchema202012KeywordAdditionalProperties"
)
const KeywordPropertyNames = getComponent(
"JSONSchema202012KeywordPropertyNames"
)
const KeywordUnevaluatedItems = getComponent(
"JSONSchema202012KeywordUnevaluatedItems"
)
const KeywordUnevaluatedProperties = getComponent(
"JSONSchema202012KeywordUnevaluatedProperties"
)
const KeywordType = getComponent("JSONSchema202012KeywordType")
const KeywordEnum = getComponent("JSONSchema202012KeywordEnum")
const KeywordConst = getComponent("JSONSchema202012KeywordConst")
const KeywordConstraint = getComponent("JSONSchema202012KeywordConstraint")
const KeywordDependentRequired = getComponent(
"JSONSchema202012KeywordDependentRequired"
)
const KeywordContentSchema = getComponent(
"JSONSchema202012KeywordContentSchema"
)
const KeywordTitle = getComponent("JSONSchema202012KeywordTitle")
const KeywordDescription = getComponent("JSONSchema202012KeywordDescription")
const KeywordDefault = getComponent("JSONSchema202012KeywordDefault")
const KeywordDeprecated = getComponent("JSONSchema202012KeywordDeprecated")
const KeywordReadOnly = getComponent("JSONSchema202012KeywordReadOnly")
const KeywordWriteOnly = getComponent("JSONSchema202012KeywordWriteOnly")
const Accordion = getComponent("JSONSchema202012Accordion")
const ExpandDeepButton = getComponent("JSONSchema202012ExpandDeepButton")
const ChevronRightIcon = getComponent("JSONSchema202012ChevronRightIcon")
const withSchemaContext = getComponent("withJSONSchema202012Context")
OperationsWrapper.OperationsWithJSONSchemaContext = withSchemaContext(
Operations,
{
config: {
default$schema: "https://spec.openapis.org/oas/3.1/dialect/base",
defaultExpandedLevels: configs.defaultModelExpandDepth - 1,
},
components: {
JSONSchema,
Keyword$schema,
Keyword$vocabulary,
Keyword$id,
Keyword$anchor,
Keyword$dynamicAnchor,
Keyword$ref,
Keyword$dynamicRef,
Keyword$defs,
Keyword$comment,
KeywordAllOf,
KeywordAnyOf,
KeywordOneOf,
KeywordNot,
KeywordIf,
KeywordThen,
KeywordElse,
KeywordDependentSchemas,
KeywordPrefixItems,
KeywordItems,
KeywordContains,
KeywordProperties,
KeywordPatternProperties,
KeywordAdditionalProperties,
KeywordPropertyNames,
KeywordUnevaluatedItems,
KeywordUnevaluatedProperties,
KeywordType,
KeywordEnum,
KeywordConst,
KeywordConstraint,
KeywordDependentRequired,
KeywordContentSchema,
KeywordTitle,
KeywordDescription,
KeywordDefault,
KeywordDeprecated,
KeywordReadOnly,
KeywordWriteOnly,
Accordion,
ExpandDeepButton,
ChevronRightIcon,
},
fn: {
upperFirst: fn.upperFirst,
isExpandable: makeIsExpandable(
fn.jsonSchema202012.isExpandable,
system
),
},
}
)
return <OperationsWrapper.OperationsWithJSONSchemaContext />
})
OperationsWrapper.OperationsWithJSONSchemaContext = null
export default OperationsWrapper

View File

@@ -1,141 +0,0 @@
/**
* @prettier
*/
import React from "react"
import { createOnlyOAS31ComponentWrapper } from "../fn"
import { makeIsExpandable } from "../json-schema-2020-12-extensions/fn"
const WebhooksWrapper = createOnlyOAS31ComponentWrapper(({ getSystem }) => {
const system = getSystem()
const { getComponent, fn, getConfigs } = system
const configs = getConfigs()
if (WebhooksWrapper.WebhooksWithJSONSchemaContext) {
return <WebhooksWrapper.WebhooksWithJSONSchemaContext />
}
const Webhooks = getComponent("Webhooks", true)
const JSONSchema = getComponent("JSONSchema202012")
const Keyword$schema = getComponent("JSONSchema202012Keyword$schema")
const Keyword$vocabulary = getComponent("JSONSchema202012Keyword$vocabulary")
const Keyword$id = getComponent("JSONSchema202012Keyword$id")
const Keyword$anchor = getComponent("JSONSchema202012Keyword$anchor")
const Keyword$dynamicAnchor = getComponent(
"JSONSchema202012Keyword$dynamicAnchor"
)
const Keyword$ref = getComponent("JSONSchema202012Keyword$ref")
const Keyword$dynamicRef = getComponent("JSONSchema202012Keyword$dynamicRef")
const Keyword$defs = getComponent("JSONSchema202012Keyword$defs")
const Keyword$comment = getComponent("JSONSchema202012Keyword$comment")
const KeywordAllOf = getComponent("JSONSchema202012KeywordAllOf")
const KeywordAnyOf = getComponent("JSONSchema202012KeywordAnyOf")
const KeywordOneOf = getComponent("JSONSchema202012KeywordOneOf")
const KeywordNot = getComponent("JSONSchema202012KeywordNot")
const KeywordIf = getComponent("JSONSchema202012KeywordIf")
const KeywordThen = getComponent("JSONSchema202012KeywordThen")
const KeywordElse = getComponent("JSONSchema202012KeywordElse")
const KeywordDependentSchemas = getComponent(
"JSONSchema202012KeywordDependentSchemas"
)
const KeywordPrefixItems = getComponent("JSONSchema202012KeywordPrefixItems")
const KeywordItems = getComponent("JSONSchema202012KeywordItems")
const KeywordContains = getComponent("JSONSchema202012KeywordContains")
const KeywordProperties = getComponent("JSONSchema202012KeywordProperties")
const KeywordPatternProperties = getComponent(
"JSONSchema202012KeywordPatternProperties"
)
const KeywordAdditionalProperties = getComponent(
"JSONSchema202012KeywordAdditionalProperties"
)
const KeywordPropertyNames = getComponent(
"JSONSchema202012KeywordPropertyNames"
)
const KeywordUnevaluatedItems = getComponent(
"JSONSchema202012KeywordUnevaluatedItems"
)
const KeywordUnevaluatedProperties = getComponent(
"JSONSchema202012KeywordUnevaluatedProperties"
)
const KeywordType = getComponent("JSONSchema202012KeywordType")
const KeywordEnum = getComponent("JSONSchema202012KeywordEnum")
const KeywordConst = getComponent("JSONSchema202012KeywordConst")
const KeywordConstraint = getComponent("JSONSchema202012KeywordConstraint")
const KeywordDependentRequired = getComponent(
"JSONSchema202012KeywordDependentRequired"
)
const KeywordContentSchema = getComponent(
"JSONSchema202012KeywordContentSchema"
)
const KeywordTitle = getComponent("JSONSchema202012KeywordTitle")
const KeywordDescription = getComponent("JSONSchema202012KeywordDescription")
const KeywordDefault = getComponent("JSONSchema202012KeywordDefault")
const KeywordDeprecated = getComponent("JSONSchema202012KeywordDeprecated")
const KeywordReadOnly = getComponent("JSONSchema202012KeywordReadOnly")
const KeywordWriteOnly = getComponent("JSONSchema202012KeywordWriteOnly")
const Accordion = getComponent("JSONSchema202012Accordion")
const ExpandDeepButton = getComponent("JSONSchema202012ExpandDeepButton")
const ChevronRightIcon = getComponent("JSONSchema202012ChevronRightIcon")
const withSchemaContext = getComponent("withJSONSchema202012Context")
WebhooksWrapper.WebhooksWithJSONSchemaContext = withSchemaContext(Webhooks, {
config: {
default$schema: "https://spec.openapis.org/oas/3.1/dialect/base",
defaultExpandedLevels: configs.defaultModelExpandDepth - 1,
},
components: {
JSONSchema,
Keyword$schema,
Keyword$vocabulary,
Keyword$id,
Keyword$anchor,
Keyword$dynamicAnchor,
Keyword$ref,
Keyword$dynamicRef,
Keyword$defs,
Keyword$comment,
KeywordAllOf,
KeywordAnyOf,
KeywordOneOf,
KeywordNot,
KeywordIf,
KeywordThen,
KeywordElse,
KeywordDependentSchemas,
KeywordPrefixItems,
KeywordItems,
KeywordContains,
KeywordProperties,
KeywordPatternProperties,
KeywordAdditionalProperties,
KeywordPropertyNames,
KeywordUnevaluatedItems,
KeywordUnevaluatedProperties,
KeywordType,
KeywordEnum,
KeywordConst,
KeywordConstraint,
KeywordDependentRequired,
KeywordContentSchema,
KeywordTitle,
KeywordDescription,
KeywordDefault,
KeywordDeprecated,
KeywordReadOnly,
KeywordWriteOnly,
Accordion,
ExpandDeepButton,
ChevronRightIcon,
},
fn: {
upperFirst: fn.upperFirst,
isExpandable: makeIsExpandable(fn.jsonSchema202012.isExpandable, system),
},
})
return <WebhooksWrapper.WebhooksWithJSONSchemaContext />
})
WebhooksWrapper.WebhooksWithJSONSchemaContext = null
export default WebhooksWrapper

View File

@@ -19,4 +19,5 @@
@import 'split-pane-mode';
@import 'markdown';
@import '../core/plugins/json-schema-2020-12/components/all';
@import '../core/plugins/oas31/components/all';
}