feat(samples): add support for uniqueItems keyword (#8893)
This change is specific to JSON Schema 2020-12 and OpenAPI 3.1.0. Refs #8577
This commit is contained in:
@@ -72,6 +72,31 @@ const isURI = (uri) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const applyArrayConstraints = (array, constraints = {}) => {
|
||||||
|
const { minItems, maxItems, uniqueItems } = constraints
|
||||||
|
let constrainedArray = [...array]
|
||||||
|
|
||||||
|
if (Number.isInteger(maxItems) && maxItems > 0) {
|
||||||
|
constrainedArray = array.slice(0, maxItems)
|
||||||
|
}
|
||||||
|
if (Number.isInteger(minItems) && minItems > 0) {
|
||||||
|
for (let i = 0; constrainedArray.length < minItems; i += 1) {
|
||||||
|
constrainedArray.push(constrainedArray[i % constrainedArray.length])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* If uniqueItems is true, it implies that every item in the array must be unique.
|
||||||
|
* This overrides any minItems constraint that cannot be satisfied with unique items.
|
||||||
|
* So if minItems is greater than the number of unique items,
|
||||||
|
* it should be reduced to the number of unique items.
|
||||||
|
*/
|
||||||
|
if (uniqueItems === true) {
|
||||||
|
constrainedArray = Array.from(new Set(constrainedArray))
|
||||||
|
}
|
||||||
|
|
||||||
|
return constrainedArray
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Do a couple of quick sanity tests to ensure the value
|
* Do a couple of quick sanity tests to ensure the value
|
||||||
* looks like a $$ref that swagger-client generates.
|
* looks like a $$ref that swagger-client generates.
|
||||||
@@ -80,7 +105,7 @@ const sanitizeRef = (value) =>
|
|||||||
deeplyStripKey(value, "$$ref", (val) => typeof val === "string" && isURI(val))
|
deeplyStripKey(value, "$$ref", (val) => typeof val === "string" && isURI(val))
|
||||||
|
|
||||||
const objectContracts = ["maxProperties", "minProperties"]
|
const objectContracts = ["maxProperties", "minProperties"]
|
||||||
const arrayContracts = ["minItems", "maxItems"]
|
const arrayConstraints = ["minItems", "maxItems", "uniqueItems"]
|
||||||
const numberConstraints = [
|
const numberConstraints = [
|
||||||
"minimum",
|
"minimum",
|
||||||
"maximum",
|
"maximum",
|
||||||
@@ -105,7 +130,7 @@ const liftSampleHelper = (oldSchema, target, config = {}) => {
|
|||||||
"type",
|
"type",
|
||||||
"const",
|
"const",
|
||||||
...objectContracts,
|
...objectContracts,
|
||||||
...arrayContracts,
|
...arrayConstraints,
|
||||||
...numberConstraints,
|
...numberConstraints,
|
||||||
...stringConstraints,
|
...stringConstraints,
|
||||||
].forEach((key) => setIfNotDefinedInTarget(key))
|
].forEach((key) => setIfNotDefinedInTarget(key))
|
||||||
@@ -271,7 +296,7 @@ export const sampleFromSchemaGeneric = (
|
|||||||
if (schema && typeof type !== "string" && !Array.isArray(type)) {
|
if (schema && typeof type !== "string" && !Array.isArray(type)) {
|
||||||
if (properties || additionalProperties || schemaHasAny(objectContracts)) {
|
if (properties || additionalProperties || schemaHasAny(objectContracts)) {
|
||||||
type = "object"
|
type = "object"
|
||||||
} else if (items || schemaHasAny(arrayContracts)) {
|
} else if (items || schemaHasAny(arrayConstraints)) {
|
||||||
type = "array"
|
type = "array"
|
||||||
} else if (schemaHasAny(numberConstraints)) {
|
} else if (schemaHasAny(numberConstraints)) {
|
||||||
type = "number"
|
type = "number"
|
||||||
@@ -296,19 +321,6 @@ export const sampleFromSchemaGeneric = (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleMinMaxItems = (sampleArray) => {
|
|
||||||
if (schema?.maxItems !== null && schema?.maxItems !== undefined) {
|
|
||||||
sampleArray = sampleArray.slice(0, schema?.maxItems)
|
|
||||||
}
|
|
||||||
if (schema?.minItems !== null && schema?.minItems !== undefined) {
|
|
||||||
let i = 0
|
|
||||||
while (sampleArray.length < schema?.minItems) {
|
|
||||||
sampleArray.push(sampleArray[i++ % sampleArray.length])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return sampleArray
|
|
||||||
}
|
|
||||||
|
|
||||||
// add to result helper init for xml or json
|
// add to result helper init for xml or json
|
||||||
const props = objectify(properties)
|
const props = objectify(properties)
|
||||||
let addPropertyToResult
|
let addPropertyToResult
|
||||||
@@ -505,7 +517,7 @@ export const sampleFromSchemaGeneric = (
|
|||||||
let itemSamples = sample.map((s) =>
|
let itemSamples = sample.map((s) =>
|
||||||
sampleFromSchemaGeneric(itemSchema, config, s, respectXML)
|
sampleFromSchemaGeneric(itemSchema, config, s, respectXML)
|
||||||
)
|
)
|
||||||
itemSamples = handleMinMaxItems(itemSamples)
|
itemSamples = applyArrayConstraints(itemSamples, schema)
|
||||||
if (xml.wrapped) {
|
if (xml.wrapped) {
|
||||||
res[displayName] = itemSamples
|
res[displayName] = itemSamples
|
||||||
if (!isEmpty(_attr)) {
|
if (!isEmpty(_attr)) {
|
||||||
@@ -602,7 +614,7 @@ export const sampleFromSchemaGeneric = (
|
|||||||
} else {
|
} else {
|
||||||
return sampleFromSchemaGeneric(items, config, undefined, respectXML)
|
return sampleFromSchemaGeneric(items, config, undefined, respectXML)
|
||||||
}
|
}
|
||||||
sampleArray = handleMinMaxItems(sampleArray)
|
sampleArray = applyArrayConstraints(sampleArray, schema)
|
||||||
if (respectXML && xml.wrapped) {
|
if (respectXML && xml.wrapped) {
|
||||||
res[displayName] = sampleArray
|
res[displayName] = sampleArray
|
||||||
if (!isEmpty(_attr)) {
|
if (!isEmpty(_attr)) {
|
||||||
|
|||||||
@@ -1233,6 +1233,37 @@ describe("sampleFromSchema", () => {
|
|||||||
expect(sampleFromSchema(definition)).toEqual(expected)
|
expect(sampleFromSchema(definition)).toEqual(expected)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should handle maxItems", () => {
|
||||||
|
const definition = {
|
||||||
|
type: "array",
|
||||||
|
minItems: 4,
|
||||||
|
maxItems: 7,
|
||||||
|
items: {
|
||||||
|
type: "string",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const expected = sampleFromSchema(definition).length
|
||||||
|
|
||||||
|
expect(expected).toBeGreaterThanOrEqual(4)
|
||||||
|
expect(expected).toBeLessThanOrEqual(7)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should handle uniqueItems", () => {
|
||||||
|
const definition = {
|
||||||
|
type: "array",
|
||||||
|
minItems: 2,
|
||||||
|
uniqueItems: true,
|
||||||
|
items: {
|
||||||
|
type: "string",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const expected = ["string"]
|
||||||
|
|
||||||
|
expect(sampleFromSchema(definition)).toEqual(expected)
|
||||||
|
})
|
||||||
|
|
||||||
it("should handle minItems with example", () => {
|
it("should handle minItems with example", () => {
|
||||||
const definition = {
|
const definition = {
|
||||||
type: "array",
|
type: "array",
|
||||||
|
|||||||
Reference in New Issue
Block a user