Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ const Items = ({ schema }) => {
*/
if (!fn.hasKeyword(schema, "items")) return null

const itemsTitle = fn.getTitle(schema.items)
const name = (
<span className="json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary">
Items
{itemsTitle || "Items"}
</span>
)

Expand Down
24 changes: 3 additions & 21 deletions src/core/plugins/json-schema-5/components/model.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,7 @@ import PropTypes from "prop-types"
import { Map } from "immutable"

import RollingLoadSVG from "core/assets/rolling-load.svg"

const decodeRefName = uri => {
const unescaped = uri.replace(/~1/g, "/").replace(/~0/g, "~")

try {
return decodeURIComponent(unescaped)
} catch {
return unescaped
}
}
import { getModelName } from "core/utils/get-model-name"

export default class Model extends ImmutablePureComponent {
static propTypes = {
Expand All @@ -33,15 +24,6 @@ export default class Model extends ImmutablePureComponent {
includeWriteOnly: PropTypes.bool,
}

getModelName =( ref )=> {
if ( ref.indexOf("#/definitions/") !== -1 ) {
return decodeRefName(ref.replace(/^.*#\/definitions\//, ""))
}
if ( ref.indexOf("#/components/schemas/") !== -1 ) {
return decodeRefName(ref.replace(/^.*#\/components\/schemas\//, ""))
}
}

getRefSchema =( model )=> {
let { specSelectors } = this.props

Expand All @@ -60,7 +42,7 @@ export default class Model extends ImmutablePureComponent {

// If we weren't passed a `name` but have a resolved ref, grab the name from the ref
if (!name && $$ref) {
name = this.getModelName($$ref)
name = getModelName($$ref)
}

/*
Expand All @@ -71,7 +53,7 @@ export default class Model extends ImmutablePureComponent {
* - we had a circular ref inside the allOf keyword
*/
if ($ref) {
const refName = this.getModelName($ref)
const refName = getModelName($ref)
const refSchema = this.getRefSchema(refName)
if (Map.isMap(refSchema)) {
// NOTE(immutable-v4): mergeDeep now concatenates Lists instead of replacing them.
Expand Down
8 changes: 7 additions & 1 deletion src/core/plugins/oas31/after-load.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*/
import {
makeIsExpandable,
makeGetTitle,
getProperties,
} from "./json-schema-2020-12-extensions/fn"
import { wrapOAS31Fn } from "./fn"
Expand All @@ -16,8 +17,13 @@ function afterLoad({ fn, getSystem }) {
fn.jsonSchema202012.isExpandable,
getSystem
)
const getTitle = makeGetTitle(fn.jsonSchema202012.getTitle)

Object.assign(this.fn.jsonSchema202012, { isExpandable, getProperties })
Object.assign(this.fn.jsonSchema202012, {
isExpandable,
getProperties,
...(getTitle && { getTitle }),
})
}

// wraps schema generators from samples plugin and make them specific to OpenAPI 3.1 version
Expand Down
15 changes: 1 addition & 14 deletions src/core/plugins/oas31/components/model/model.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,7 @@ 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
}
import { getModelName } from "../../../../utils/get-model-name"

const Model = forwardRef(
({ schema, getComponent, onToggle = () => {}, specPath }, ref) => {
Expand Down
14 changes: 14 additions & 0 deletions src/core/plugins/oas31/json-schema-2020-12-extensions/fn.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
/**
* @prettier
*/
import { getModelName } from "../../../utils/get-model-name"

export const makeGetTitle = (original) => {
Comment thread
adrianodpdiaz marked this conversation as resolved.
if (typeof original !== "function") {
return null
}

return (schema, options) => {
const title = original(schema, options)
if (title) return title
return getModelName(schema?.$$ref) || ""
}
}

export const makeIsExpandable = (original, getSystem) => {
const { fn } = getSystem()

Expand Down
1 change: 1 addition & 0 deletions src/core/plugins/oas31/wrap-components/model.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ const ModelWrapper = createOnlyOAS31ComponentWrapper(
fn: {
getProperties: fn.jsonSchema202012.getProperties,
isExpandable: fn.jsonSchema202012.isExpandable,
getTitle: fn.jsonSchema202012.getTitle,
getSchemaKeywords: makeGetSchemaKeywords(
fn.jsonSchema202012.getSchemaKeywords
),
Expand Down
1 change: 1 addition & 0 deletions src/core/plugins/oas31/wrap-components/models.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const ModelsWrapper = createOnlyOAS31ComponentWrapper(({ getSystem }) => {
fn: {
getProperties: fn.jsonSchema202012.getProperties,
isExpandable: fn.jsonSchema202012.isExpandable,
getTitle: fn.jsonSchema202012.getTitle,
getSchemaKeywords: makeGetSchemaKeywords(
fn.jsonSchema202012.getSchemaKeywords
),
Expand Down
24 changes: 24 additions & 0 deletions src/core/utils/get-model-name.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* @prettier
*/
export const decodeRefName = (uri) => {
const unescaped = uri.replace(/~1/g, "/").replace(/~0/g, "~")

try {
return decodeURIComponent(unescaped)
} catch {
return unescaped
}
}

export const getModelName = (ref) => {
if (typeof ref !== "string") return null

if (ref.indexOf("#/definitions/") !== -1) {
return decodeRefName(ref.replace(/^.*#\/definitions\//, ""))
}
if (ref.indexOf("#/components/schemas/") !== -1) {
return decodeRefName(ref.replace(/^.*#\/components\/schemas\//, ""))
}
return null
}
23 changes: 23 additions & 0 deletions test/e2e-cypress/e2e/features/oas31-array-items-label.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* @prettier
*/
describe("OpenAPI 3.1 array items label", () => {
beforeEach(() => {
cy.visit("/?url=/documents/features/oas31-array-items-label.yaml")
cy.get(".models").click()
cy.get(".json-schema-2020-12").contains("UserList").click()
cy.get(".json-schema-2020-12-accordion").contains("users").click()
})

it("should display the referenced schema name instead of 'Items' for array items", () => {
cy.get(".json-schema-2020-12-keyword--items")
.find(".json-schema-2020-12-keyword__name--primary")
.should("have.text", "User")
})

it("should not display the generic 'Items' label when items is a $ref", () => {
cy.get(".json-schema-2020-12-keyword--items")
.find(".json-schema-2020-12-keyword__name--primary")
.should("not.have.text", "Items")
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
openapi: 3.1.0
info:
title: Test
version: "1.0"
paths:
/users:
get:
summary: Get users
responses:
"200":
description: OK
content:
application/json:
schema:
$ref: "#/components/schemas/UserList"
components:
schemas:
User:
type: object
description: A user of the system.
properties:
id:
type: string
name:
type: string
UserList:
type: object
description: A list of users.
properties:
results:
type: integer
users:
type: array
items:
$ref: "#/components/schemas/User"
12 changes: 5 additions & 7 deletions test/unit/core/helpers/get-model-name.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,18 @@
* @prettier
*/

import Model from "../../../../src/core/plugins/json-schema-5/components/model"
import { getModelName } from "../../../../src/core/utils/get-model-name"

describe("getModelName", () => {
const model = new Model()

it("should decode JSON Pointer and URI encoding for OpenAPI v3 refs", () => {
const actual = model.getModelName("#/components/schemas/a~1b%2Bc")
const actual = getModelName("#/components/schemas/a~1b%2Bc")
const expected = "a/b+c"

expect(actual).toStrictEqual(expected)
})

it("should decode JSON Pointer and URI encoding for Swagger v2 refs", () => {
const actual = model.getModelName(
const actual = getModelName(
"#/definitions/custom%3A%3Anamespace%3A%3APerson"
)
const expected = "custom::namespace::Person"
Expand All @@ -24,14 +22,14 @@ describe("getModelName", () => {
})

it("should decode multiple json-pointer values", () => {
const actual = model.getModelName("#/components/schemas/~1~1~0~0")
const actual = getModelName("#/components/schemas/~1~1~0~0")
const expected = "//~~"

expect(actual).toStrictEqual(expected)
})

it("should support invalid URI encoding", () => {
const actual = model.getModelName("#/components/schemas/%25%d")
const actual = getModelName("#/components/schemas/%25%d")
const expected = "%25%d"

expect(actual).toStrictEqual(expected)
Expand Down
67 changes: 67 additions & 0 deletions test/unit/core/plugins/oas31/json-schema-2020-12-extensions/fn.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
* @prettier
*/
import { makeGetTitle } from "core/plugins/oas31/json-schema-2020-12-extensions/fn"

const baseGetTitle = (schema) => schema?.title ?? ""

describe("OAS31 - json-schema-2020-12-extensions - makeGetTitle", () => {
const getTitle = makeGetTitle(baseGetTitle)

it("should return null when original is not a function", () => {
expect(makeGetTitle(null)).toBeNull()
expect(makeGetTitle(undefined)).toBeNull()
expect(makeGetTitle("string")).toBeNull()
})

describe("when schema has a title", () => {
it("should return the title from the original getTitle", () => {
expect(getTitle({ title: "MyTitle" })).toBe("MyTitle")
})

it("should prefer title over $$ref", () => {
expect(
getTitle({
title: "MyTitle",
$$ref: "#/components/schemas/Foo",
})
).toBe("MyTitle")
})
})

describe("when schema has no title but has $$ref", () => {
it("should extract the schema name from a full URL $$ref", () => {
expect(
getTitle({
$$ref: "http://localhost:3200/swagger.json#/components/schemas/Foo",
})
).toBe("Foo")
})

it("should extract the schema name from a relative $$ref", () => {
expect(getTitle({ $$ref: "swagger.json#/components/schemas/Bar" })).toBe(
"Bar"
)
})

it("should extract the schema name from a fragment-only $$ref", () => {
expect(getTitle({ $$ref: "#/components/schemas/Foo" })).toBe("Foo")
})
})

describe("when schema has no title but has $$ref pointing to #/definitions/", () => {
it("should extract the schema name from a definitions $$ref", () => {
expect(getTitle({ $$ref: "#/definitions/Bar" })).toBe("Bar")
})
})

describe("when schema has no title and $$ref does not match known paths", () => {
it("should return empty string for an unrecognized $$ref", () => {
expect(getTitle({ $$ref: "#/other/path/Baz" })).toBe("")
})

it("should return empty string for null schema", () => {
expect(getTitle(null)).toBe("")
})
})
})