Skip to content
Merged
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
@@ -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
}
Original file line number Diff line number Diff line change
Expand Up @@ -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")) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand All @@ -15,6 +24,10 @@ export const randexp = (pattern) => {
}
}

export const pick = (list) => {
return list.at(0)
}

export const string = () => "string"

export const number = () => 0
Expand Down
69 changes: 25 additions & 44 deletions src/core/plugins/json-schema-2020-12/samples-extensions/fn/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
Expand Down Expand Up @@ -49,6 +43,7 @@ const liftSampleHelper = (oldSchema, target, config = {}) => {
}

;[
"examples",
"example",
"default",
"enum",
Expand Down Expand Up @@ -133,22 +128,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) {
Expand Down Expand Up @@ -194,16 +188,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
Expand Down Expand Up @@ -320,15 +306,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 {
Expand Down Expand Up @@ -402,21 +385,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
Expand All @@ -434,7 +415,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
Expand Down Expand Up @@ -474,7 +455,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
Expand Down Expand Up @@ -522,7 +503,7 @@ export const sampleFromSchemaGeneric = (
}

// use schema to generate sample
if (type?.includes("array")) {
if (type === "array") {
let sampleArray = []

if (contains != null && typeof contains === "object") {
Expand Down Expand Up @@ -611,7 +592,7 @@ export const sampleFromSchemaGeneric = (
return sampleArray
}

if (type?.includes("object")) {
if (type === "object") {
for (let propName in props) {
if (!Object.hasOwn(props, propName)) {
continue
Expand Down Expand Up @@ -689,7 +670,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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,15 +131,15 @@ 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") {
generatedString = JSON.stringify(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)
Expand Down
Loading