Skip to content

Commit

Permalink
feat(samples): add support for uniqueItems keyword (#8893)
Browse files Browse the repository at this point in the history
This change is specific to JSON Schema 2020-12
and OpenAPI 3.1.0.

Refs #8577
  • Loading branch information
char0n authored Jun 7, 2023
1 parent 8a91492 commit 1114965
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 18 deletions.
48 changes: 30 additions & 18 deletions src/core/plugins/json-schema-2020-12/samples-extensions/fn.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
* looks like a $$ref that swagger-client generates.
Expand All @@ -80,7 +105,7 @@ const sanitizeRef = (value) =>
deeplyStripKey(value, "$$ref", (val) => typeof val === "string" && isURI(val))

const objectContracts = ["maxProperties", "minProperties"]
const arrayContracts = ["minItems", "maxItems"]
const arrayConstraints = ["minItems", "maxItems", "uniqueItems"]
const numberConstraints = [
"minimum",
"maximum",
Expand All @@ -105,7 +130,7 @@ const liftSampleHelper = (oldSchema, target, config = {}) => {
"type",
"const",
...objectContracts,
...arrayContracts,
...arrayConstraints,
...numberConstraints,
...stringConstraints,
].forEach((key) => setIfNotDefinedInTarget(key))
Expand Down Expand Up @@ -271,7 +296,7 @@ export const sampleFromSchemaGeneric = (
if (schema && typeof type !== "string" && !Array.isArray(type)) {
if (properties || additionalProperties || schemaHasAny(objectContracts)) {
type = "object"
} else if (items || schemaHasAny(arrayContracts)) {
} else if (items || schemaHasAny(arrayConstraints)) {
type = "array"
} else if (schemaHasAny(numberConstraints)) {
type = "number"
Expand All @@ -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
const props = objectify(properties)
let addPropertyToResult
Expand Down Expand Up @@ -505,7 +517,7 @@ export const sampleFromSchemaGeneric = (
let itemSamples = sample.map((s) =>
sampleFromSchemaGeneric(itemSchema, config, s, respectXML)
)
itemSamples = handleMinMaxItems(itemSamples)
itemSamples = applyArrayConstraints(itemSamples, schema)
if (xml.wrapped) {
res[displayName] = itemSamples
if (!isEmpty(_attr)) {
Expand Down Expand Up @@ -602,7 +614,7 @@ export const sampleFromSchemaGeneric = (
} else {
return sampleFromSchemaGeneric(items, config, undefined, respectXML)
}
sampleArray = handleMinMaxItems(sampleArray)
sampleArray = applyArrayConstraints(sampleArray, schema)
if (respectXML && xml.wrapped) {
res[displayName] = sampleArray
if (!isEmpty(_attr)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1233,6 +1233,37 @@ describe("sampleFromSchema", () => {
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", () => {
const definition = {
type: "array",
Expand Down

0 comments on commit 1114965

Please sign in to comment.