diff --git a/src/core/plugins/json-schema-2020-12/samples-extensions/fn/core/example.js b/src/core/plugins/json-schema-2020-12/samples-extensions/fn/core/example.js
new file mode 100644
index 00000000000..f9b78e59edb
--- /dev/null
+++ b/src/core/plugins/json-schema-2020-12/samples-extensions/fn/core/example.js
@@ -0,0 +1,57 @@
+/**
+ * @prettier
+ */
+import { isJSONSchemaObject } from "./predicates"
+
+/**
+ * Precedence of keywords that provides author defined values (top of the list = higher priority)
+ *
+ * ### examples
+ * Array containing example values for the item defined by the schema.
+ * Not guaranteed to be valid or invalid against the schema
+ *
+ * ### default
+ * Default value for an item defined by the schema.
+ * Is expected to be a valid instance of the schema.
+ *
+ * ### example
+ * Deprecated. Part of OpenAPI 3.1.0 Schema Object dialect.
+ * Represents single example. Equivalent of `examples` keywords
+ * with single item.
+ */
+
+export const hasExample = (schema) => {
+ if (!isJSONSchemaObject(schema)) return false
+
+ const { examples, example, default: defaultVal } = schema
+
+ if (Array.isArray(examples) && examples.length >= 1) {
+ return true
+ }
+
+ if (typeof defaultVal !== "undefined") {
+ return true
+ }
+
+ return typeof example !== "undefined"
+}
+
+export const extractExample = (schema) => {
+ if (!isJSONSchemaObject(schema)) return null
+
+ const { examples, example, default: defaultVal } = schema
+
+ if (Array.isArray(examples) && examples.length >= 1) {
+ return examples.at(0)
+ }
+
+ if (typeof defaultVal !== "undefined") {
+ return defaultVal
+ }
+
+ if (typeof example !== "undefined") {
+ return example
+ }
+
+ return undefined
+}
diff --git a/src/core/plugins/json-schema-2020-12/samples-extensions/fn/core/fold-type.js b/src/core/plugins/json-schema-2020-12/samples-extensions/fn/core/fold-type.js
index c8a19f75781..2a07d8c84c0 100644
--- a/src/core/plugins/json-schema-2020-12/samples-extensions/fn/core/fold-type.js
+++ b/src/core/plugins/json-schema-2020-12/samples-extensions/fn/core/fold-type.js
@@ -4,7 +4,7 @@
import { ALL_TYPES } from "./constants"
const foldType = (type) => {
- if (Array.isArray(type)) {
+ if (Array.isArray(type) && type.length >= 1) {
if (type.includes("array")) {
return "array"
} else if (type.includes("object")) {
diff --git a/src/core/plugins/json-schema-2020-12/samples-extensions/fn/core/predicates.js b/src/core/plugins/json-schema-2020-12/samples-extensions/fn/core/predicates.js
index ae0bf044fd0..6c780b58d77 100644
--- a/src/core/plugins/json-schema-2020-12/samples-extensions/fn/core/predicates.js
+++ b/src/core/plugins/json-schema-2020-12/samples-extensions/fn/core/predicates.js
@@ -3,14 +3,6 @@
*/
import isPlainObject from "lodash/isPlainObject"
-export const isURI = (uri) => {
- try {
- return new URL(uri) && true
- } catch {
- return false
- }
-}
-
export const isBooleanJSONSchema = (schema) => {
return typeof schema === "boolean"
}
diff --git a/src/core/plugins/json-schema-2020-12/samples-extensions/fn/core/random.js b/src/core/plugins/json-schema-2020-12/samples-extensions/fn/core/random.js
index cd2ec61efad..6a707639cba 100644
--- a/src/core/plugins/json-schema-2020-12/samples-extensions/fn/core/random.js
+++ b/src/core/plugins/json-schema-2020-12/samples-extensions/fn/core/random.js
@@ -3,6 +3,15 @@
*/
import randomBytes from "randombytes"
import RandExp from "randexp"
+
+/**
+ * Some of the functions returns constants. This is due to the nature
+ * of SwaggerUI expectations - provide as stable data as possible.
+ *
+ * In future, we may decide to randomize these function and provide
+ * true random values.
+ */
+
export const bytes = (length) => randomBytes(length)
export const randexp = (pattern) => {
@@ -15,6 +24,10 @@ export const randexp = (pattern) => {
}
}
+export const pick = (list) => {
+ return list.at(0)
+}
+
export const string = () => "string"
export const number = () => 0
diff --git a/src/core/plugins/json-schema-2020-12/samples-extensions/fn/main.js b/src/core/plugins/json-schema-2020-12/samples-extensions/fn/main.js
index 555aac47b2a..6944c3e3dcf 100644
--- a/src/core/plugins/json-schema-2020-12/samples-extensions/fn/main.js
+++ b/src/core/plugins/json-schema-2020-12/samples-extensions/fn/main.js
@@ -4,19 +4,13 @@
import XML from "xml"
import isEmpty from "lodash/isEmpty"
-import { objectify, normalizeArray, deeplyStripKey } from "core/utils"
+import { objectify, normalizeArray } from "core/utils"
import memoizeN from "../../../../../helpers/memoizeN"
import typeMap from "./types/index"
-import { isURI } from "./core/predicates"
import foldType from "./core/fold-type"
import { typeCast } from "./core/utils"
-
-/**
- * Do a couple of quick sanity tests to ensure the value
- * looks like a $$ref that swagger-client generates.
- */
-const sanitizeRef = (value) =>
- deeplyStripKey(value, "$$ref", (val) => typeof val === "string" && isURI(val))
+import { hasExample, extractExample } from "./core/example"
+import { pick as randomPick } from "./core/random"
const objectConstraints = ["maxProperties", "minProperties", "required"]
const arrayConstraints = [
@@ -133,22 +127,21 @@ export const sampleFromSchemaGeneric = (
if (typeof schema?.toJS === "function") schema = schema.toJS()
schema = typeCast(schema)
- let usePlainValue =
- exampleOverride !== undefined ||
- (schema && schema.example !== undefined) ||
- (schema && schema.default !== undefined)
+ let usePlainValue = exampleOverride !== undefined || hasExample(schema)
// first check if there is the need of combining this schema with others required by allOf
const hasOneOf =
!usePlainValue && schema && schema.oneOf && schema.oneOf.length > 0
const hasAnyOf =
!usePlainValue && schema && schema.anyOf && schema.anyOf.length > 0
if (!usePlainValue && (hasOneOf || hasAnyOf)) {
- const schemaToAdd = typeCast(hasOneOf ? schema.oneOf[0] : schema.anyOf[0])
+ const schemaToAdd = typeCast(
+ hasOneOf ? randomPick(schema.oneOf) : randomPick(schema.anyOf)
+ )
liftSampleHelper(schemaToAdd, schema, config)
if (!schema.xml && schemaToAdd.xml) {
schema.xml = schemaToAdd.xml
}
- if (schema.example !== undefined && schemaToAdd.example !== undefined) {
+ if (hasExample(schema) && hasExample(schemaToAdd)) {
usePlainValue = true
} else if (schemaToAdd.properties) {
if (!schema.properties) {
@@ -194,16 +187,8 @@ export const sampleFromSchemaGeneric = (
}
}
const _attr = {}
- let {
- xml,
- type,
- example,
- properties,
- additionalProperties,
- items,
- contains,
- } = schema || {}
- type = foldType(type)
+ let { xml, properties, additionalProperties, items, contains } = schema || {}
+ let type = foldType(schema.type)
let { includeReadOnly, includeWriteOnly } = config
xml = xml || {}
let { name, prefix, namespace } = xml
@@ -320,15 +305,12 @@ export const sampleFromSchemaGeneric = (
if (props[propName].xml.attribute) {
const enumAttrVal = Array.isArray(props[propName].enum)
- ? props[propName].enum[0]
+ ? randomPick(props[propName].enum)
: undefined
- const attrExample = props[propName].example
- const attrDefault = props[propName].default
-
- if (attrExample !== undefined) {
- _attr[props[propName].xml.name || propName] = attrExample
- } else if (attrDefault !== undefined) {
- _attr[props[propName].xml.name || propName] = attrDefault
+ if (hasExample(props[propName])) {
+ _attr[props[propName].xml.name || propName] = extractExample(
+ props[propName]
+ )
} else if (enumAttrVal !== undefined) {
_attr[props[propName].xml.name || propName] = enumAttrVal
} else {
@@ -402,21 +384,19 @@ export const sampleFromSchemaGeneric = (
if (usePlainValue) {
let sample
if (exampleOverride !== undefined) {
- sample = sanitizeRef(exampleOverride)
- } else if (example !== undefined) {
- sample = sanitizeRef(example)
+ sample = exampleOverride
} else {
- sample = sanitizeRef(schema.default)
+ sample = extractExample(schema)
}
// if json just return
if (!respectXML) {
// spacial case yaml parser can not know about
- if (typeof sample === "number" && type?.includes("string")) {
+ if (typeof sample === "number" && type === "string") {
return `${sample}`
}
// return if sample does not need any parsing
- if (typeof sample !== "string" || type?.includes("string")) {
+ if (typeof sample !== "string" || type === "string") {
return sample
}
// check if sample is parsable or just a plain string
@@ -434,7 +414,7 @@ export const sampleFromSchemaGeneric = (
}
// generate xml sample recursively for array case
- if (type?.includes("array")) {
+ if (type === "array") {
if (!Array.isArray(sample)) {
if (typeof sample === "string") {
return sample
@@ -474,7 +454,7 @@ export const sampleFromSchemaGeneric = (
}
// generate xml sample recursively for object case
- if (type?.includes("object")) {
+ if (type === "object") {
// case literal example
if (typeof sample === "string") {
return sample
@@ -522,7 +502,7 @@ export const sampleFromSchemaGeneric = (
}
// use schema to generate sample
- if (type?.includes("array")) {
+ if (type === "array") {
let sampleArray = []
if (contains != null && typeof contains === "object") {
@@ -611,7 +591,7 @@ export const sampleFromSchemaGeneric = (
return sampleArray
}
- if (type?.includes("object")) {
+ if (type === "object") {
for (let propName in props) {
if (!Object.hasOwn(props, propName)) {
continue
@@ -689,7 +669,7 @@ export const sampleFromSchemaGeneric = (
value = schema.const
} else if (schema && Array.isArray(schema.enum)) {
//display enum first value
- value = normalizeArray(schema.enum)[0]
+ value = randomPick(normalizeArray(schema.enum))
} else if (schema) {
// display schema default
const contentSample = Object.hasOwn(schema, "contentSchema")
diff --git a/src/core/plugins/json-schema-2020-12/samples-extensions/fn/types/string.js b/src/core/plugins/json-schema-2020-12/samples-extensions/fn/types/string.js
index 1ba14250137..4c9b33986e5 100644
--- a/src/core/plugins/json-schema-2020-12/samples-extensions/fn/types/string.js
+++ b/src/core/plugins/json-schema-2020-12/samples-extensions/fn/types/string.js
@@ -131,7 +131,7 @@ const stringType = (schema, { sample } = {}) => {
generatedString = generateFormat(schema)
} else if (
isJSONSchema(contentSchema) &&
- typeof contentMediaType !== "undefined" &&
+ typeof contentMediaType === "string" &&
typeof sample !== "undefined"
) {
if (Array.isArray(sample) || typeof sample === "object") {
@@ -139,7 +139,7 @@ const stringType = (schema, { sample } = {}) => {
} else {
generatedString = String(sample)
}
- } else if (typeof contentMediaType !== "undefined") {
+ } else if (typeof contentMediaType === "string") {
const mediaTypeGenerator = mediaTypeAPI(contentMediaType)
if (typeof mediaTypeGenerator === "function") {
generatedString = mediaTypeGenerator(schema)
diff --git a/test/unit/core/plugins/json-schema-2020-12/samples-extensions/fn.js b/test/unit/core/plugins/json-schema-2020-12/samples-extensions/fn.js
index e84fcfe730f..4c2205e79c4 100644
--- a/test/unit/core/plugins/json-schema-2020-12/samples-extensions/fn.js
+++ b/test/unit/core/plugins/json-schema-2020-12/samples-extensions/fn.js
@@ -627,98 +627,6 @@ describe("sampleFromSchema", () => {
)
})
- it("returns object without any $$ref fields at the root schema level", function () {
- const definition = {
- type: "object",
- properties: {
- message: {
- type: "string",
- },
- },
- example: {
- value: {
- message: "Hello, World!",
- },
- $$ref: "https://example.com/#/components/examples/WelcomeExample",
- },
- $$ref: "https://example.com/#/components/schemas/Welcome",
- }
-
- const expected = {
- value: {
- message: "Hello, World!",
- },
- }
-
- expect(sampleFromSchema(definition, { includeWriteOnly: true })).toEqual(
- expected
- )
- })
-
- it("returns object without any $$ref fields at nested schema levels", function () {
- const definition = {
- type: "object",
- properties: {
- message: {
- type: "string",
- },
- },
- example: {
- a: {
- value: {
- message: "Hello, World!",
- },
- $$ref: "https://example.com/#/components/examples/WelcomeExample",
- },
- },
- $$ref: "https://example.com/#/components/schemas/Welcome",
- }
-
- const expected = {
- a: {
- value: {
- message: "Hello, World!",
- },
- },
- }
-
- expect(sampleFromSchema(definition, { includeWriteOnly: true })).toEqual(
- expected
- )
- })
-
- it("returns object with any $$ref fields that appear to be user-created", function () {
- const definition = {
- type: "object",
- properties: {
- message: {
- type: "string",
- },
- },
- example: {
- $$ref: {
- value: {
- message: "Hello, World!",
- },
- $$ref: "https://example.com/#/components/examples/WelcomeExample",
- },
- },
- $$ref: "https://example.com/#/components/schemas/Welcome",
- }
-
- const expected = {
- $$ref: {
- value: {
- message: "Hello, World!",
- },
- },
- }
-
- expect(sampleFromSchema(definition, { includeWriteOnly: true })).toEqual(
- expected
- )
- })
-
it("returns example value for date-time property", () => {
const definition = {
type: "string",
@@ -1836,7 +1744,7 @@ describe("createXMLExample", function () {
it("returns example value when provided", function () {
const expected =
- '\ntwo'
+ '\none'
const definition = {
type: "string",
default: "one",