@tsed/schema
Version:
JsonSchema module for Ts.ED Framework
696 lines (637 loc) • 13.8 kB
text/typescript
import {ancestorsOf, nameOf, Type} from "@tsed/core";
import {Post} from "../../test/helpers/Post.js";
import {CollectionOf, Email, Format, getJsonSchema, JsonEntityStore, MinLength, Name, Property, Required} from "../index.js";
describe("getJsonSchema", () => {
it("should declare all schema correctly (basic)", () => {
// WHEN
class Model {
@Property()
@Required()
prop1: string;
@CollectionOf(String)
prop2: string[];
@CollectionOf(String)
prop3: Set<string>;
@CollectionOf(String)
prop4: Map<string, string>;
}
// THEN
expect(getJsonSchema(Model)).toEqual({
type: "object",
required: ["prop1"],
properties: {
prop1: {
minLength: 1,
type: "string"
},
prop2: {
type: "array",
items: {
type: "string"
}
},
prop3: {
type: "array",
uniqueItems: true,
items: {
type: "string"
}
},
prop4: {
type: "object",
additionalProperties: {
type: "string"
}
}
}
});
});
it("should declare all schema correctly (alias)", () => {
// WHEN
class Model {
@Name("prop_1")
@Required()
prop1: string;
}
// THEN
const classSchema = JsonEntityStore.from(Model);
expect(classSchema.schema.toJSON()).toEqual({
type: "object",
required: ["prop_1"],
properties: {
prop_1: {
minLength: 1,
type: "string"
}
}
});
});
it("should declare prop with a nested model", () => {
// WHEN
class NestedModel {
@Property()
id: string;
@Property()
prop1: string;
}
class Model {
@Property()
id: string;
@Property()
nested: NestedModel;
}
// THEN
expect(getJsonSchema(NestedModel)).toEqual({
type: "object",
properties: {
id: {
type: "string"
},
prop1: {
type: "string"
}
}
});
expect(getJsonSchema(Model)).toEqual({
type: "object",
properties: {
id: {
type: "string"
},
nested: {
$ref: "#/definitions/NestedModel"
}
},
definitions: {
NestedModel: {
type: "object",
properties: {
id: {
type: "string"
},
prop1: {
type: "string"
}
}
}
}
});
});
it("should declare prop with a nested model with named model", () => {
// WHEN
@Name("Nested")
class NestedModel {
@Property()
id: string;
@Property()
prop1: string;
}
class Model {
@Property()
id: string;
@Property()
nested: NestedModel;
}
// THEN
expect(getJsonSchema(NestedModel)).toEqual({
type: "object",
properties: {
id: {
type: "string"
},
prop1: {
type: "string"
}
}
});
const options = {components: {schemas: {}}};
expect(JsonEntityStore.from(Model).schema.clone().toJSON(options)).toEqual({
type: "object",
properties: {
id: {
type: "string"
},
nested: {
$ref: "#/definitions/Nested"
}
}
});
expect(options).toEqual({
components: {
schemas: {
Nested: {
properties: {
id: {
type: "string"
},
prop1: {
type: "string"
}
},
type: "object"
}
}
}
});
expect(getJsonSchema(Model)).toEqual({
type: "object",
properties: {
id: {
type: "string"
},
nested: {
$ref: "#/definitions/Nested"
}
},
definitions: {
Nested: {
type: "object",
properties: {
id: {
type: "string"
},
prop1: {
type: "string"
}
}
}
}
});
});
it("should declare prop with a nested of nested model (Array)", () => {
// WHEN
class ChildModel {
@Property()
id: string;
@Property()
prop1: string;
}
class NestedModel {
@Property()
id: string;
@CollectionOf(ChildModel)
children: ChildModel[];
}
class Model {
@Property()
id: string;
@Property()
nested: NestedModel;
}
// THEN
expect(getJsonSchema(ChildModel)).toEqual({
type: "object",
properties: {
id: {
type: "string"
},
prop1: {
type: "string"
}
}
});
expect(getJsonSchema(NestedModel)).toEqual({
type: "object",
properties: {
id: {
type: "string"
},
children: {
type: "array",
items: {
$ref: "#/definitions/ChildModel"
}
}
},
definitions: {
ChildModel: {
type: "object",
properties: {
id: {
type: "string"
},
prop1: {
type: "string"
}
}
}
}
});
expect(getJsonSchema(Model)).toEqual({
type: "object",
properties: {
id: {
type: "string"
},
nested: {
$ref: "#/definitions/NestedModel"
}
},
definitions: {
NestedModel: {
type: "object",
properties: {
id: {
type: "string"
},
children: {
type: "array",
items: {
$ref: "#/definitions/ChildModel"
}
}
}
},
ChildModel: {
type: "object",
properties: {
id: {
type: "string"
},
prop1: {
type: "string"
}
}
}
}
});
});
it("should declare prop with a nested of nested model (Set)", () => {
// WHEN
class ChildModel {
@Property()
id: string;
@Property()
prop1: string;
}
class NestedModel {
@Property()
id: string;
@CollectionOf(ChildModel)
children: Set<ChildModel>;
}
class Model {
@Property()
id: string;
@Property()
nested: NestedModel;
}
// THEN
expect(getJsonSchema(ChildModel)).toEqual({
type: "object",
properties: {
id: {
type: "string"
},
prop1: {
type: "string"
}
}
});
expect(getJsonSchema(NestedModel)).toEqual({
type: "object",
properties: {
id: {
type: "string"
},
children: {
type: "array",
uniqueItems: true,
items: {
$ref: "#/definitions/ChildModel"
}
}
},
definitions: {
ChildModel: {
type: "object",
properties: {
id: {
type: "string"
},
prop1: {
type: "string"
}
}
}
}
});
expect(getJsonSchema(Model)).toEqual({
type: "object",
properties: {
id: {
type: "string"
},
nested: {
$ref: "#/definitions/NestedModel"
}
},
definitions: {
NestedModel: {
type: "object",
properties: {
id: {
type: "string"
},
children: {
type: "array",
uniqueItems: true,
items: {
$ref: "#/definitions/ChildModel"
}
}
}
},
ChildModel: {
type: "object",
properties: {
id: {
type: "string"
},
prop1: {
type: "string"
}
}
}
}
});
});
it("should declare prop with a nested of nested model (Map)", () => {
// WHEN
class ChildModel {
@Property()
id: string;
@Property()
prop1: string;
}
class NestedModel {
@Property()
id: string;
@CollectionOf(ChildModel)
children: Map<string, ChildModel>;
}
class Model {
@Property()
id: string;
@Property()
nested: NestedModel;
}
// THEN
expect(getJsonSchema(ChildModel)).toEqual({
type: "object",
properties: {
id: {
type: "string"
},
prop1: {
type: "string"
}
}
});
expect(getJsonSchema(NestedModel)).toEqual({
type: "object",
properties: {
id: {
type: "string"
},
children: {
type: "object",
additionalProperties: {
$ref: "#/definitions/ChildModel"
}
}
},
definitions: {
ChildModel: {
type: "object",
properties: {
id: {
type: "string"
},
prop1: {
type: "string"
}
}
}
}
});
expect(getJsonSchema(Model)).toEqual({
type: "object",
properties: {
id: {
type: "string"
},
nested: {
$ref: "#/definitions/NestedModel"
}
},
definitions: {
NestedModel: {
type: "object",
properties: {
id: {
type: "string"
},
children: {
type: "object",
additionalProperties: {
$ref: "#/definitions/ChildModel"
}
}
}
},
ChildModel: {
type: "object",
properties: {
id: {
type: "string"
},
prop1: {
type: "string"
}
}
}
}
});
});
it("should accept circular ref", () => {
// WHEN
// THEN
const classSchema = getJsonSchema(Post);
expect(classSchema).toEqual({
definitions: {
Post: {
properties: {
id: {
type: "string"
},
owner: {
$ref: "#/definitions/User"
}
},
type: "object"
},
User: {
properties: {
name: {
type: "string"
},
posts: {
items: {
$ref: "#/definitions/Post"
},
type: "array"
}
},
type: "object"
}
},
properties: {
id: {
type: "string"
},
owner: {
$ref: "#/definitions/User"
}
},
type: "object"
});
});
it("should return the json schema for an inherited model", () => {
class Base {
@Property()
id: string;
@Required()
@Email()
email: string;
}
class Model extends Base {
@MinLength(0)
declare email: string;
@Property()
name: string;
}
expect(getJsonSchema(Model)).toEqual({
type: "object",
properties: {
id: {
type: "string"
},
email: {
type: "string",
minLength: 0,
format: "email"
},
name: {
type: "string"
}
},
required: ["email"]
});
});
it("should keep required meta from inherited class", () => {
// WHEN
const AutoUUID = <T extends Type<any>>(Base: T) => {
class AutoUUIDMixin extends Base {
@Property() //showing up in swagger as a property
@Format("uuid") //showing up as a uuid in swagger
@Required(true)
id: string;
}
return AutoUUIDMixin;
};
const WithDates = <T extends Type<any>>(Base: T) => {
class WithDates extends Base {
@Property() //showing up in swagger as a property
@Format("datetime") //showing up as a uuid in swagger
@Required(true)
created_at: string;
@Property() //showing up in swagger as a property
@Format("datetime") //showing up as a uuid in swagger
@Required(true)
updated_at: string;
}
return WithDates;
};
class Model {
@Required()
name: string;
}
const DefaultMixins = <T extends Type<any>>(Base: T) => AutoUUID(WithDates(Base));
class EmailTemplate extends DefaultMixins(Model) {
@Required()
engine: string;
}
// THEN
expect(ancestorsOf(EmailTemplate).map(nameOf)).toEqual(["Model", "WithDates", "AutoUUIDMixin", "EmailTemplate"]);
expect(getJsonSchema(EmailTemplate)).toEqual({
properties: {
created_at: {
format: "datetime",
minLength: 1,
type: "string"
},
engine: {
minLength: 1,
type: "string"
},
id: {
format: "uuid",
minLength: 1,
type: "string"
},
name: {
minLength: 1,
type: "string"
},
updated_at: {
format: "datetime",
minLength: 1,
type: "string"
}
},
required: ["name", "created_at", "updated_at", "id", "engine"],
type: "object"
});
});
});