Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
7238cd8
docs: add claude skill for adding support for new oas version
robert-hebel-sb Jan 22, 2026
0b463f6
feat: add OAS 3.2 support
robert-hebel-sb Jan 23, 2026
a3d985d
feat(oas32): add QUERY operation support
robert-hebel-sb Jan 26, 2026
a423f25
style: auto-format oas32-contact-and-license test
robert-hebel-sb Jan 26, 2026
d07a986
fix(oas32): pass isOAS31 and isOAS32 props to VersionPragmaFilter
robert-hebel-sb Jan 26, 2026
4ef6e84
fix(oas32): wrap isOAS3 selector to enable OAS3 features for OAS 3.2
robert-hebel-sb Jan 26, 2026
3a6a86e
fix(oas32): wrap operations selector to include QUERY method operations
robert-hebel-sb Jan 26, 2026
b7e105a
fix(oas32): correct operations selector wrapper pattern for QUERY ope…
robert-hebel-sb Jan 26, 2026
1d1df30
fix(oas32): correct selector implementations to fix runtime errors
robert-hebel-sb Jan 26, 2026
b78b0ee
test(oas32): add comprehensive QUERY operation rendering verification
robert-hebel-sb Jan 26, 2026
0b44351
fix(oas32): enable QUERY operation support in core selectors
robert-hebel-sb Jan 26, 2026
7afbf18
build: regenerate dist files for basic OAS 3.2 implementation
robert-hebel-sb Feb 10, 2026
b2bc93f
refactor(oas32): remove $self field from basic implementation
robert-hebel-sb Feb 10, 2026
ba458c6
refactor(oas32): remove enhancement selectors from basic implementation
robert-hebel-sb Feb 10, 2026
8fa2a35
fix(base): add missing isOAS32 variable declaration
robert-hebel-sb Feb 10, 2026
4ed0e80
test(oas32): fix unit tests for basic implementation
robert-hebel-sb Feb 10, 2026
64083c5
test(oas32): update version-detection E2E test expectations
robert-hebel-sb Feb 10, 2026
5c582df
fix(e2e): correct URL paths for OAS32 test specs
robert-hebel-sb Feb 10, 2026
76555e2
fix(oas32): register selectInfoSummaryField selector
robert-hebel-sb Feb 10, 2026
045cf1b
style(oas32): update QUERY operation color to purple/magenta
robert-hebel-sb Feb 10, 2026
cd8c982
chore: remove temporary OAS32 documentation files
robert-hebel-sb Feb 10, 2026
08f5eb8
chore: reset dev helper to default Petstore API URL
robert-hebel-sb Feb 10, 2026
a2e2d99
Merge branch 'master' into feat/oas32-basic-implementation
robert-hebel-sb Feb 10, 2026
97db134
build: remove generated LICENSE.txt files from dist
robert-hebel-sb Feb 10, 2026
ce405a3
Merge remote-tracking branch 'origin/feat/oas32-basic-implementation'…
robert-hebel-sb Feb 10, 2026
44279ec
refactor(test): simplify OAS32 E2E tests to be more concise
robert-hebel-sb Feb 10, 2026
1f20e89
docs(oas32): add TODO for OAS 3.2 JSON Schema version update
robert-hebel-sb Feb 10, 2026
e045d62
fix: correct supported OpenAPI version text in error messages
robert-hebel-sb Feb 10, 2026
90663b4
fix(test): correct operation IDs in QUERY E2E test
robert-hebel-sb Feb 10, 2026
7974cf5
style: apply Prettier formatting to SCSS and JS files
robert-hebel-sb Feb 10, 2026
2846900
build: remove LICENSE.txt files from dist
robert-hebel-sb Feb 10, 2026
989edab
chore: move .claude content to separate branch
robert-hebel-sb Feb 10, 2026
6a1e3aa
fix(test): simplify QUERY operation E2E test assertions
robert-hebel-sb Feb 10, 2026
ff48093
Merge branch 'master' into feat/oas32-basic-implementation
robert-hebel-sb Feb 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ Swagger UI uses a **sophisticated plugin system** powered by Redux. The core sys
- `logs` - Logging
- `oas3` - OpenAPI 3.0.x support
- `oas31` - OpenAPI 3.1.x support
- `oas32` - OpenAPI 3.2.x support
- `on-complete` - Completion callbacks
- `request-snippets` - Code snippet generation
- `safe-render` - Safe component rendering
Expand Down Expand Up @@ -813,6 +814,7 @@ dist/ # Build output (generated)
- OAS 2.0: Use `src/core/plugins/swagger-client/`
- OAS 3.0.x: Use `src/core/plugins/oas3/`
- OAS 3.1.x: Use `src/core/plugins/oas31/`
- OAS 3.2.x: Use `src/core/plugins/oas32/`

**Adding Test Specs:**
- Add to `test/e2e-cypress/static/documents/`
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ The OpenAPI Specification has undergone 5 revisions since initial creation in 20

| Swagger UI Version | Release Date | OpenAPI Spec compatibility | Notes |
|--------------------|--------------|-------------------------------------------------------------|-----------------------------------------------------------------------|
| 5.20.0 | TBD | 2.0, 3.0.0, 3.0.1, 3.0.2, 3.0.3, 3.0.4, 3.1.0, 3.1.1, 3.1.2, 3.2.0 | [next release](https://github.com/swagger-api/swagger-ui/tree/master) |
| 5.19.0 | 2025-02-17 | 2.0, 3.0.0, 3.0.1, 3.0.2, 3.0.3, 3.0.4, 3.1.0, 3.1.1, 3.1.2 | [tag v5.19.0](https://github.com/swagger-api/swagger-ui/tree/v5.19.0) |
| 5.0.0 | 2023-06-12 | 2.0, 3.0.0, 3.0.1, 3.0.2, 3.0.3, 3.1.0 | [tag v5.0.0](https://github.com/swagger-api/swagger-ui/tree/v5.0.0) |
| 4.0.0 | 2021-11-03 | 2.0, 3.0.0, 3.0.1, 3.0.2, 3.0.3 | [tag v4.0.0](https://github.com/swagger-api/swagger-ui/tree/v4.0.0) |
Expand Down
2 changes: 1 addition & 1 deletion dist/swagger-ui-bundle.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/swagger-ui-es-bundle-core.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/swagger-ui-es-bundle-core.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/swagger-ui-es-bundle.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/swagger-ui.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/swagger-ui.css.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/swagger-ui.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/swagger-ui.js.map

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions src/core/components/layouts/base.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export default class BaseLayout extends React.Component {
const isSwagger2 = specSelectors.isSwagger2()
const isOAS3 = specSelectors.isOAS3()
const isOAS31 = specSelectors.isOAS31()
const isOAS32 = specSelectors.isOAS32()

const isSpecEmpty = !specSelectors.specStr()

Expand Down Expand Up @@ -100,6 +101,8 @@ export default class BaseLayout extends React.Component {
<VersionPragmaFilter
isSwagger2={isSwagger2}
isOAS3={isOAS3}
isOAS31={isOAS31}
isOAS32={isOAS32}
alsoShow={<Errors />}
>
<Errors />
Expand Down
6 changes: 3 additions & 3 deletions src/core/containers/OperationContainer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,11 @@ export default class OperationContainer extends PureComponent {
componentDidUpdate(prevProps) {
const { response, isShown } = this.props
const resolvedSubtree = this.getResolvedSubtree()

if (response !== prevProps.response) {
this.setState({ executeInProgress: false })
}

if (isShown && resolvedSubtree === undefined && !prevProps.isShown) {
this.requestResolvedSubtree()
}
Expand Down Expand Up @@ -132,7 +132,7 @@ export default class OperationContainer extends PureComponent {
jsonRequestBodyValue[key] = jsonRequestBodyValue[key].map((val) => {
if (typeof val === "object") {
return JSON.stringify(val, null, 2)
}
}
return val
})
} else if (typeof value === "object") {
Expand Down
12 changes: 6 additions & 6 deletions src/core/plugins/oas31/components/version-pragma-filter.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ const VersionPragmaFilter = ({
one of the fields.
</p>
<p>
Supported version fields are <code>swagger: &quot;2.0&quot;</code> and
those that match <code>openapi: 3.x.y</code> (for example,{" "}
<code>openapi: 3.1.0</code>).
Supported version fields are <code>swagger: &quot;2.0&quot;</code>,{" "}
<code>openapi: 3.0.x</code>, or <code>openapi: 3.1.x</code> (for
example, <code>openapi: 3.1.0</code>).
</p>
</div>
</div>
Expand All @@ -48,9 +48,9 @@ const VersionPragmaFilter = ({
</p>
<p>
Please indicate a valid Swagger or OpenAPI version field.
Supported version fields are <code>swagger: &quot;2.0&quot;</code> and
those that match <code>openapi: 3.x.y</code> (for example,{" "}
<code>openapi: 3.1.0</code>).
Supported version fields are <code>swagger: &quot;2.0&quot;</code>,{" "}
<code>openapi: 3.0.x</code>, or <code>openapi: 3.1.x</code> (for
example, <code>openapi: 3.1.0</code>).
</p>
</div>
</div>
Expand Down
16 changes: 10 additions & 6 deletions src/core/plugins/oas31/wrap-components/contact.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@
*/
import React from "react"

import { createOnlyOAS31ComponentWrapper } from "../fn"
const ContactWrapper = (Original, system) => (props) => {
const isOAS31 = system.specSelectors.isOAS31?.() || false
const isOAS32 = system.specSelectors.isOAS32?.() || false

const ContactWrapper = createOnlyOAS31ComponentWrapper(({ getSystem }) => {
const system = getSystem()
const OAS31Contact = system.getComponent("OAS31Contact", true)
// Use OAS31Contact for both OAS 3.1 and OAS 3.2 specs
if (isOAS31 || isOAS32) {
const OAS31Contact = system.getComponent("OAS31Contact", true)
return <OAS31Contact {...props} />
}

return <OAS31Contact />
})
return <Original {...props} />
}

export default ContactWrapper
16 changes: 10 additions & 6 deletions src/core/plugins/oas31/wrap-components/license.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@
*/
import React from "react"

import { createOnlyOAS31ComponentWrapper } from "../fn"
const LicenseWrapper = (Original, system) => (props) => {
const isOAS31 = system.specSelectors.isOAS31?.() || false
const isOAS32 = system.specSelectors.isOAS32?.() || false

const LicenseWrapper = createOnlyOAS31ComponentWrapper(({ getSystem }) => {
const system = getSystem()
const OAS31License = system.getComponent("OAS31License", true)
// Use OAS31License for both OAS 3.1 and OAS 3.2 specs
if (isOAS31 || isOAS32) {
const OAS31License = system.getComponent("OAS31License", true)
return <OAS31License {...props} />
}

return <OAS31License />
})
return <Original {...props} />
}

export default LicenseWrapper
23 changes: 23 additions & 0 deletions src/core/plugins/oas32/after-load.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* @prettier
*/

/**
* afterLoad hook for OAS 3.2 plugin
*
* This hook runs after all plugins are loaded and allows
* modification of the system's functions and behaviors.
*
* OAS 3.2 is a minor version update with backward-compatible additions,
* so minimal modifications are needed.
*/
function afterLoad() {
// TODO: OAS 3.2 should use the new JSON Schema version from
// https://spec.openapis.org/oas/3.2/schema/2025-09-17.html
// Currently using JSON Schema 2020-12 from OAS 3.1 for basic implementation.
// This needs to be updated to properly support OAS 3.2 schema validation.
// Future: If any function wrapping is needed for OAS 3.2 specific behavior,
// it can be added here using wrapOAS32Fn
}

export default afterLoad
99 changes: 99 additions & 0 deletions src/core/plugins/oas32/components/info.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/**
* @prettier
*/
import React from "react"
import PropTypes from "prop-types"

import { sanitizeUrl } from "core/utils/url"

const Info = ({ getComponent, specSelectors }) => {
const version = specSelectors.version()
const url = specSelectors.url()
const basePath = specSelectors.basePath()
const host = specSelectors.host()
const summary = specSelectors.selectInfoSummaryField()
const description = specSelectors.selectInfoDescriptionField()
const title = specSelectors.selectInfoTitleField()
const termsOfServiceUrl = specSelectors.selectInfoTermsOfServiceUrl()
const externalDocsUrl = specSelectors.selectExternalDocsUrl()
const externalDocsDesc = specSelectors.selectExternalDocsDescriptionField()
const contact = specSelectors.contact()
const license = specSelectors.license()

const Markdown = getComponent("Markdown", true)
const Link = getComponent("Link")
const VersionStamp = getComponent("VersionStamp")
const OpenAPIVersion = getComponent("OpenAPIVersion")
const InfoUrl = getComponent("InfoUrl")
const InfoBasePath = getComponent("InfoBasePath")
const License = getComponent("License", true)
const Contact = getComponent("Contact", true)
const JsonSchemaDialect = getComponent("JsonSchemaDialect", true)

return (
<div className="info">
<hgroup className="main">
<h1 className="title">
{title}
<span>
{version && <VersionStamp version={version} />}
<OpenAPIVersion oasVersion="3.2" />
</span>
</h1>

{(host || basePath) && <InfoBasePath host={host} basePath={basePath} />}
{url && <InfoUrl getComponent={getComponent} url={url} />}
</hgroup>

{summary && <p className="info__summary">{summary}</p>}

<div className="info__description description">
<Markdown source={description} />
</div>

{termsOfServiceUrl && (
<div className="info__tos">
<Link target="_blank" href={sanitizeUrl(termsOfServiceUrl)}>
Terms of service
</Link>
</div>
)}

{contact.size > 0 && <Contact />}

{license.size > 0 && <License />}

{externalDocsUrl && (
<Link
className="info__extdocs"
target="_blank"
href={sanitizeUrl(externalDocsUrl)}
>
{externalDocsDesc || externalDocsUrl}
</Link>
)}

<JsonSchemaDialect />
</div>
)
}

Info.propTypes = {
getComponent: PropTypes.func.isRequired,
specSelectors: PropTypes.shape({
version: PropTypes.func.isRequired,
url: PropTypes.func.isRequired,
basePath: PropTypes.func.isRequired,
host: PropTypes.func.isRequired,
selectInfoSummaryField: PropTypes.func.isRequired,
selectInfoDescriptionField: PropTypes.func.isRequired,
selectInfoTitleField: PropTypes.func.isRequired,
selectInfoTermsOfServiceUrl: PropTypes.func.isRequired,
selectExternalDocsUrl: PropTypes.func.isRequired,
selectExternalDocsDescriptionField: PropTypes.func.isRequired,
contact: PropTypes.func.isRequired,
license: PropTypes.func.isRequired,
}).isRequired,
}

export default Info
80 changes: 80 additions & 0 deletions src/core/plugins/oas32/components/version-pragma-filter.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/**
* @prettier
*/
import React from "react"
import PropTypes from "prop-types"

const VersionPragmaFilter = ({
bypass,
isSwagger2,
isOAS3,
isOAS31,
isOAS32,
alsoShow,
children,
}) => {
if (bypass) {
return <div>{children}</div>
}

if (isSwagger2 && (isOAS3 || isOAS31 || isOAS32)) {
return (
<div className="version-pragma">
{alsoShow}
<div className="version-pragma__message version-pragma__message--ambiguous">
<div>
<h3>Unable to render this definition</h3>
<p>
<code>swagger</code> and <code>openapi</code> fields cannot be
present in the same Swagger or OpenAPI definition. Please remove
one of the fields.
</p>
<p>
Supported version fields are <code>swagger: &quot;2.0&quot;</code>{" "}
and <code>openapi: 3.0.x</code>, <code>openapi: 3.1.x</code>, or{" "}
<code>openapi: 3.2.x</code> (for example,{" "}
<code>openapi: 3.2.0</code>).
</p>
</div>
</div>
</div>
)
}

if (!isSwagger2 && !isOAS3 && !isOAS31 && !isOAS32) {
return (
<div className="version-pragma">
{alsoShow}
<div className="version-pragma__message version-pragma__message--missing">
<div>
<h3>Unable to render this definition</h3>
<p>
The provided definition does not specify a valid version field.
</p>
<p>
Please indicate a valid Swagger or OpenAPI version field.
Supported version fields are <code>swagger: &quot;2.0&quot;</code>{" "}
and <code>openapi: 3.0.x</code>, <code>openapi: 3.1.x</code>, or{" "}
<code>openapi: 3.2.x</code> (for example,{" "}
<code>openapi: 3.2.0</code>).
</p>
</div>
</div>
</div>
)
}

return <div>{children}</div>
}

VersionPragmaFilter.propTypes = {
isSwagger2: PropTypes.bool.isRequired,
isOAS3: PropTypes.bool.isRequired,
isOAS31: PropTypes.bool.isRequired,
isOAS32: PropTypes.bool.isRequired,
bypass: PropTypes.bool,
alsoShow: PropTypes.element,
children: PropTypes.any,
}

export default VersionPragmaFilter
Loading