Skip to content

Commit

Permalink
fix(schema): add discriminator.mappings information on generated schema
Browse files Browse the repository at this point in the history
  • Loading branch information
Romakita committed Nov 16, 2024
1 parent 0158fb6 commit b616308
Show file tree
Hide file tree
Showing 21 changed files with 513 additions and 248 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ lib-cov

# Coverage directory used by tools like istanbul
coverage

coverage-*
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

Expand Down
18 changes: 13 additions & 5 deletions docs/docs/model.md
Original file line number Diff line number Diff line change
Expand Up @@ -1457,24 +1457,32 @@ By declaring a discriminatorKey, `@tsed/json-mapper` will be able to determine t
Here is an example:

```typescript
import {Discriminator, DiscriminatorKey, DiscriminatorValue, Property, Required, OneOf} from "@tsed/schema";
import {DiscriminatorKey, DiscriminatorValue, OneOf, Property, Required} from "@tsed/schema";

export enum EventType {
PAGE_VIEW = "page_view",
ACTION = "action",
CLICK_ACTION = "click_action"
}

export class Event {
@DiscriminatorKey() // declare this property as discriminator key
type: string;

type: string; // Note: Do not set EventType enum here. The @DiscriminatorKey decorator will automatically generate the correct values based on @DiscriminatorValue decorators in derived classes.
@Property()
value: string;
}

@DiscriminatorValue("page_view")
@DiscriminatorValue(EventType.PAGE_VIEW)
// or @DiscriminatorValue() value can be inferred by the class name (ex: "page_view")
export class PageView extends Event {
override type = EventType.PAGE_VIEW; // optional

@Required()
url: string;
}

@DiscriminatorValue("action", "click_action")
@DiscriminatorValue(EventType.ACTION, EventType.CLICK_ACTION)
export class Action extends Event {
@Required()
event: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ exports[`Discriminator > os3 > should generate the spec 1`] = `
"type": "string",
},
"type": {
"enum": [
"custom_action",
],
"example": "custom_action",
"type": "string",
},
Expand All @@ -79,6 +82,9 @@ exports[`Discriminator > os3 > should generate the spec 1`] = `
"type": "string",
},
"type": {
"enum": [
"custom_action",
],
"example": "custom_action",
"type": "string",
},
Expand All @@ -91,6 +97,9 @@ exports[`Discriminator > os3 > should generate the spec 1`] = `
"PageView": {
"properties": {
"type": {
"enum": [
"page_view",
],
"example": "page_view",
"type": "string",
},
Expand All @@ -110,6 +119,9 @@ exports[`Discriminator > os3 > should generate the spec 1`] = `
"PageViewPartial": {
"properties": {
"type": {
"enum": [
"page_view",
],
"example": "page_view",
"type": "string",
},
Expand Down Expand Up @@ -140,6 +152,12 @@ exports[`Discriminator > os3 > should generate the spec 1`] = `
"application/json": {
"schema": {
"discriminator": {
"mapping": {
"action": "#/components/schemas/Action",
"click_action": "#/components/schemas/Action",
"custom_action": "#/components/schemas/CustomAction",
"page_view": "#/components/schemas/PageView",
},
"propertyName": "type",
},
"oneOf": [
Expand Down Expand Up @@ -167,6 +185,12 @@ exports[`Discriminator > os3 > should generate the spec 1`] = `
"application/json": {
"schema": {
"discriminator": {
"mapping": {
"action": "#/components/schemas/Action",
"click_action": "#/components/schemas/Action",
"custom_action": "#/components/schemas/CustomAction",
"page_view": "#/components/schemas/PageView",
},
"propertyName": "type",
},
"oneOf": [
Expand Down Expand Up @@ -204,6 +228,12 @@ exports[`Discriminator > os3 > should generate the spec 1`] = `
"schema": {
"items": {
"discriminator": {
"mapping": {
"action": "#/components/schemas/Action",
"click_action": "#/components/schemas/Action",
"custom_action": "#/components/schemas/CustomAction",
"page_view": "#/components/schemas/PageView",
},
"propertyName": "type",
},
"oneOf": [
Expand Down Expand Up @@ -234,6 +264,12 @@ exports[`Discriminator > os3 > should generate the spec 1`] = `
"schema": {
"items": {
"discriminator": {
"mapping": {
"action": "#/components/schemas/Action",
"click_action": "#/components/schemas/Action",
"custom_action": "#/components/schemas/CustomAction",
"page_view": "#/components/schemas/PageView",
},
"propertyName": "type",
},
"oneOf": [
Expand Down Expand Up @@ -272,6 +308,12 @@ exports[`Discriminator > os3 > should generate the spec 1`] = `
"application/json": {
"schema": {
"discriminator": {
"mapping": {
"action": "#/components/schemas/ActionPartial",
"click_action": "#/components/schemas/ActionPartial",
"custom_action": "#/components/schemas/CustomActionPartial",
"page_view": "#/components/schemas/PageViewPartial",
},
"propertyName": "type",
},
"oneOf": [
Expand Down Expand Up @@ -299,6 +341,12 @@ exports[`Discriminator > os3 > should generate the spec 1`] = `
"application/json": {
"schema": {
"discriminator": {
"mapping": {
"action": "#/components/schemas/Action",
"click_action": "#/components/schemas/Action",
"custom_action": "#/components/schemas/CustomAction",
"page_view": "#/components/schemas/PageView",
},
"propertyName": "type",
},
"oneOf": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ describe("Discriminator", () => {
value: "value",
url: "https://url.com"
})
.expect(200);
//.expect(200);

expect(body).toEqual({
type: "page_view",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import {isString} from "@tsed/core";
import {JsonSchema} from "../../domain/JsonSchema.js";
import {SpecTypes} from "../../domain/SpecTypes.js";
import type {JsonSchemaOptions} from "../../interfaces/JsonSchemaOptions.js";
import {registerJsonSchemaMapper} from "../../registries/JsonSchemaMapperContainer.js";
import {toRef} from "../../utils/ref.js";

interface SchemaWithDiscriminator {
discriminator?: {mapping?: Record<string, JsonSchema | string>};
}

export function discriminatorMappingMapper(obj: SchemaWithDiscriminator, _: JsonSchema, options: JsonSchemaOptions) {
if (obj.discriminator?.mapping) {
const entries = Object.entries(obj.discriminator.mapping);
const newMapping: Record<string, string> = {};

for (const [key, value] of entries) {
newMapping[key] = isString(value) ? value : toRef(value, null, options).$ref;
}

obj.discriminator.mapping = newMapping;
}

return obj;
}

function defaultDiscriminatorMappingMapper(obj: SchemaWithDiscriminator) {
if (obj.discriminator?.mapping) {
delete obj.discriminator.mapping;
}

return obj;
}


registerJsonSchemaMapper("discriminatorMapping", defaultDiscriminatorMappingMapper);

Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {inlineEnumsMapper} from "./inlineEnumsMapper";
import {enumsMapper} from "./enumsMapper.js";

describe("inlineEnumsMapper()", () => {
describe("enumsMapper()", () => {
it("should inline enums", () => {
const result = inlineEnumsMapper(
const result = enumsMapper(
{
enum: {
$isJsonDocument: true,
Expand All @@ -22,7 +22,7 @@ describe("inlineEnumsMapper()", () => {
});

it("should inline enums and set type (object to string)", () => {
const result = inlineEnumsMapper(
const result = enumsMapper(
{
type: "object",
enum: {
Expand All @@ -42,7 +42,7 @@ describe("inlineEnumsMapper()", () => {
});
});
it("should inline enums and keep the type", () => {
const result = inlineEnumsMapper(
const result = enumsMapper(
{
type: "string",
enum: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {JsonSchema} from "../../domain/JsonSchema.js";
import {JsonSchemaOptions} from "../../interfaces/JsonSchemaOptions.js";
import {registerJsonSchemaMapper} from "../../registries/JsonSchemaMapperContainer.js";

export function inlineEnumsMapper(obj: any, schema: JsonSchema, options: JsonSchemaOptions) {
export function enumsMapper(obj: any, schema: JsonSchema, options: JsonSchemaOptions) {
if (options.inlineEnums && obj.enum?.$isJsonDocument) {
obj.enum = obj.enum.toJSON().enum;
}
Expand All @@ -14,4 +14,4 @@ export function inlineEnumsMapper(obj: any, schema: JsonSchema, options: JsonSch
return obj;
}

registerJsonSchemaMapper("inlineEnums", inlineEnumsMapper);
registerJsonSchemaMapper("enums", enumsMapper);
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type {JsonSchema} from "../../domain/JsonSchema.js";
import {alterRequiredGroups} from "../../hooks/alterRequiredGroups.js";
import type {JsonSchemaOptions} from "../../interfaces/JsonSchemaOptions.js";
import {registerJsonSchemaMapper} from "../../registries/JsonSchemaMapperContainer.js";
import {createRef, createRefName, toRef} from "../../utils/ref.js";

function mapRequiredProps(obj: any, schema: JsonSchema, options: JsonSchemaOptions = {}) {
const {useAlias} = options;
Expand Down Expand Up @@ -58,10 +59,7 @@ export function requiredMapper(obj: any, schema: JsonSchema, options: JsonSchema
}

if (required.length) {
return {
...obj,
required
};
obj.required = required;
}

return obj;
Expand Down
18 changes: 8 additions & 10 deletions packages/specs/schema/src/components/default/schemaMapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const IGNORES = ["name", "$required", "$hooks", "_nestedGenerics", SpecTypes.OPE
/**
* @ignore
*/
const IGNORES_OPENSPEC = ["const"];
const IGNORES_OPENSPEC: string[] = [];

/**
* @ignore
Expand Down Expand Up @@ -76,10 +76,8 @@ function mapKeys(schema: JsonSchema, options: JsonSchemaOptions) {
key = key.replace(/^#/, "");

if (key === "type") {
return {
...item,
[key]: schema.getJsonType()
};
item[key] = schema.getJsonType();
return item;
}

if (isExample(key, value, options)) {
Expand All @@ -99,10 +97,9 @@ function mapKeys(schema: JsonSchema, options: JsonSchemaOptions) {
}
}

return {
...item,
[key]: value
};
item[key] = value;

return item;
}, {});
}

Expand Down Expand Up @@ -132,7 +129,8 @@ function serializeSchema(schema: JsonSchema, options: JsonSchemaOptions) {
obj = execMapper("required", [obj, schema], options);
obj = execMapper("nullable", [obj, schema], options);
obj = alterOneOf(obj, schema, options);
obj = execMapper("inlineEnums", [obj, schema], options);
obj = execMapper("enums", [obj, schema], options);
obj = execMapper("discriminatorMapping", [obj, schema], options);

return obj;
}
Expand Down
5 changes: 4 additions & 1 deletion packages/specs/schema/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ export * from "./async-api/payloadMapper.js";
export * from "./async-api/responseMapper.js";
export * from "./default/anyMapper.js";
export * from "./default/classMapper.js";
export * from "./default/discriminatorMappingMapper.js";
export * from "./default/enumsMapper.js";
export * from "./default/genericsMapper.js";
export * from "./default/inheritedClassMapper.js";
export * from "./default/inlineEnumsMapper.js";
export * from "./default/itemMapper.js";
export * from "./default/lazyRefMapper.js";
export * from "./default/mapMapper.js";
Expand All @@ -20,6 +21,8 @@ export * from "./default/ofMapper.js";
export * from "./default/propertiesMapper.js";
export * from "./default/requiredMapper.js";
export * from "./default/schemaMapper.js";
export * from "./open-spec/discriminatorMappingMapper.js";
export * from "./open-spec/enumsMapper.js";
export * from "./open-spec/generate.js";
export * from "./open-spec/nullableMapper.js";
export * from "./open-spec/operationInFilesMapper.js";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import {SpecTypes} from "../../domain/SpecTypes.js";
import {registerJsonSchemaMapper} from "../../registries/JsonSchemaMapperContainer.js";
import {discriminatorMappingMapper} from "../default/discriminatorMappingMapper.js";

registerJsonSchemaMapper("discriminatorMapping", discriminatorMappingMapper, SpecTypes.OPENAPI);
registerJsonSchemaMapper("discriminatorMapping", discriminatorMappingMapper, SpecTypes.SWAGGER);
registerJsonSchemaMapper("discriminatorMapping", discriminatorMappingMapper, SpecTypes.ASYNCAPI);
20 changes: 20 additions & 0 deletions packages/specs/schema/src/components/open-spec/enumsMapper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import {JsonSchema} from "../../domain/JsonSchema.js";
import {SpecTypes} from "../../domain/SpecTypes.js";
import {JsonSchemaOptions} from "../../interfaces/JsonSchemaOptions.js";
import {registerJsonSchemaMapper} from "../../registries/JsonSchemaMapperContainer.js";
import {enumsMapper} from "../default/enumsMapper.js";

export function wrapEnumsMapper(obj: any, schema: JsonSchema, options: JsonSchemaOptions) {
obj = enumsMapper(obj, schema, options);

if (obj.const) {
obj.enum = [obj.const];
delete obj.const;
}

return obj
}

registerJsonSchemaMapper("enums", wrapEnumsMapper, SpecTypes.OPENAPI);
registerJsonSchemaMapper("enums", wrapEnumsMapper, SpecTypes.SWAGGER);
registerJsonSchemaMapper("enums", wrapEnumsMapper, SpecTypes.ASYNCAPI);
Loading

0 comments on commit b616308

Please sign in to comment.