Skip to content

Commit

Permalink
resolveAndMerge $refs and $ands before translating x-type to jsonschema
Browse files Browse the repository at this point in the history
  • Loading branch information
tatomyr committed Jun 6, 2024
1 parent b4569ac commit 1038afb
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 55 deletions.
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,6 @@ Array literals allow defining multiple available options, one of which is applic
["string", "undefined"]
```

The relation between the items is `XOR`.

## Types Combining

It is possible to combine several types into one using the `$and` keyword:
Expand Down
22 changes: 11 additions & 11 deletions applications/__tests__/adapter.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {describe, expect, test} from "vitest"
import {translateXTypeToSchema} from "../x-types-adapter"
import {resolveAndMerge, translateXTypeToSchema} from "../x-types-adapter"

describe("adapter", () => {
test("translates primitive strings", () => {
Expand All @@ -18,11 +18,11 @@ describe("adapter", () => {
})

test("translates a correct $and into an object", () => {
expect(
translateXTypeToSchema({
$and: [{foo: "string"}, {bar: "number"}],
})
).toEqual({
const merged = resolveAndMerge({
$and: [{foo: "string"}, {bar: "number"}],
})
expect(merged).toEqual({foo: "string", bar: "number"})
expect(translateXTypeToSchema(merged)).toEqual({
type: "object",
properties: {foo: {type: "string"}, bar: {type: "number"}},
additionalProperties: false,
Expand All @@ -31,10 +31,10 @@ describe("adapter", () => {
})

test("translates an incorrect $and into `never`", () => {
expect(
translateXTypeToSchema({
$and: ["string", "number"],
})
).toEqual({not: {}})
const merged = resolveAndMerge({
$and: ["string", "number"],
})
expect(merged).toEqual("undefined")
expect(translateXTypeToSchema(merged)).toEqual({not: {}})
})
})
2 changes: 1 addition & 1 deletion applications/resources/openapi-never.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ paths:
/test:
get:
responses:
200:
400:
description: Test
content:
application/json:
Expand Down
83 changes: 45 additions & 38 deletions applications/x-types-adapter.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,45 @@
const {isObject, mergeAll} = require("./x-types-utils")

const translateXTypeToSchema = (xType, ctx) => {
const resolveAndMerge = (xType, ctx) => {
if (typeof xType.$ref !== "undefined") {
const resolved = ctx.resolve(xType).node
if (resolved === undefined) {
console.error()
console.error("ERROR! Cannot resolve $ref:")
console.error(xType.$ref)
console.error()
return "any"
}
return resolveAndMerge(resolved, ctx)
}

if (typeof xType.$and !== "undefined") {
if (!Array.isArray(xType.$and)) {
console.error()
console.error("ERROR! Expected array but got:")
console.error(xType.$and)
console.error()
return "any"
}
return mergeAll(...resolveAndMerge(xType.$and, ctx))
}

if (Array.isArray(xType)) {
return xType.map(type => resolveAndMerge(type, ctx))
}

if (isObject(xType)) {
let obj = {}
for (const key in xType) {
obj[key] = resolveAndMerge(xType[key], ctx)
}
return obj
}

return xType
}

const translateXTypeToSchema = xType => {
if (typeof xType === "undefined") {
throw new Error('Expected "x-type" but got "undefined"')
}
Expand Down Expand Up @@ -46,38 +85,7 @@ const translateXTypeToSchema = (xType, ctx) => {
}

if (typeof xType.array !== "undefined") {
return {type: "array", items: translateXTypeToSchema(xType.array, ctx)}
}

if (typeof xType.$and !== "undefined") {
if (!Array.isArray(xType.$and)) {
console.error()
console.error("ERROR! Expected array but got:")
console.error(xType.$and)
console.error()
return {}
}
return translateXTypeToSchema(mergeAll(...xType.$and), ctx)
/*
// TODO: consider also this:
return {
allOf: xType.$and.map((item) => {
const translated = translateXTypeToSchema(item);
if (isObject(translated) && translated.additionalProperties === undefined) {
translated.additionalProperties = true;
}
return translated;
}),
};
*/
}

if (typeof xType.$ref !== "undefined") {
if (ctx.resolve(xType).node === undefined) {
return translateXTypeToSchema("any")
}

return translateXTypeToSchema(ctx.resolve(xType).node, ctx)
return {type: "array", items: translateXTypeToSchema(xType.array)}
}

if (typeof xType === "string" && xType.startsWith("$literal:")) {
Expand All @@ -96,7 +104,7 @@ const translateXTypeToSchema = (xType, ctx) => {
return {
anyOf: xType
.filter(type => type !== "undefined")
.map(type => translateXTypeToSchema(type, ctx)),
.map(type => translateXTypeToSchema(type)),
}
}

Expand All @@ -105,16 +113,14 @@ const translateXTypeToSchema = (xType, ctx) => {
let required = []
const {string, ...props} = xType
const additionalProperties =
typeof string === "undefined"
? false
: translateXTypeToSchema(string, ctx)
typeof string === "undefined" ? false : translateXTypeToSchema(string)

for (const key in props) {
const realKey = key.startsWith("$literal:")
? key.slice("$literal:".length)
: key

properties[realKey] = translateXTypeToSchema(props[key], ctx)
properties[realKey] = translateXTypeToSchema(props[key])

if (props[key] instanceof Array && props[key].includes("undefined")) {
// skip
Expand All @@ -131,4 +137,5 @@ const translateXTypeToSchema = (xType, ctx) => {

module.exports = {
translateXTypeToSchema,
resolveAndMerge,
}
8 changes: 5 additions & 3 deletions applications/x-types-decorators.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
const {isObject} = require("./x-types-utils")
const {translateXTypeToSchema} = require("./x-types-adapter")
const {translateXTypeToSchema, resolveAndMerge} = require("./x-types-adapter")

const generateSchema = () => {
return {
MediaType: {
leave(mediaType, ctx) {
if (typeof mediaType["x-type"] === "undefined") return
const schema = translateXTypeToSchema(mediaType["x-type"], ctx)
const resolvedXType = resolveAndMerge(mediaType["x-type"], ctx)
const schema = translateXTypeToSchema(resolvedXType)
mediaType.schema = schema
},
},
Parameter: {
leave(parameter, ctx) {
if (typeof parameter["x-type"] === "undefined") return
const schema = translateXTypeToSchema(parameter["x-type"], ctx)
const resolvedXType = resolveAndMerge(parameter["x-type"], ctx)
const schema = translateXTypeToSchema(resolvedXType)
parameter.schema = schema
},
},
Expand Down

0 comments on commit 1038afb

Please sign in to comment.