Skip to content

Commit d5ee693

Browse files
committed
feat: discriminator mapping draft
1 parent d185c22 commit d5ee693

File tree

9 files changed

+461
-3
lines changed

9 files changed

+461
-3
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@
6060
"test:another-query-params": "node tests/spec/another-query-params/test.js",
6161
"test:on-insert-path-param": "node tests/spec/on-insert-path-param/test.js",
6262
"test:extra-templates": "node tests/spec/extra-templates/test.js",
63-
"test:extract-enums": "node tests/spec/extract-enums/test.js"
63+
"test:extract-enums": "node tests/spec/extract-enums/test.js",
64+
"test:discriminator": "node tests/spec/discriminator/test.js"
6465
},
6566
"author": "acacode",
6667
"license": "MIT",

src/constants.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const SCHEMA_TYPES = {
1212
REF: "$ref",
1313
PRIMITIVE: "primitive",
1414
COMPLEX: "complex",
15+
DISCRIMINATOR: "discriminator",
1516
COMPLEX_ONE_OF: "oneOf",
1617
COMPLEX_ANY_OF: "anyOf",
1718
COMPLEX_ALL_OF: "allOf",

src/schema-components-map.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,12 @@ class SchemaComponentsMap {
2323
);
2424
}
2525

26+
createRef = (componentName, typeName) => {
27+
return `#/components/${componentName}/${typeName}`;
28+
};
29+
2630
createComponent(componentName, typeName, rawTypeData) {
27-
const $ref = `#/components/${componentName}/${typeName}`;
31+
const $ref = this.createRef(componentName, typeName);
2832

2933
const componentSchema = {
3034
$ref,

src/schema-parser/schema-parser.js

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,10 +256,88 @@ class SchemaParser {
256256
content: type === this.config.Ts.Keyword.Null ? type : contentType || this.getSchemaType(schema),
257257
};
258258
},
259+
[SCHEMA_TYPES.DISCRIMINATOR]: (schema, typeName) => {
260+
const { discriminator, ...noDiscriminatorSchema } = schema;
261+
262+
if (typeName == null || !discriminator.mapping) return this.parseSchema(noDiscriminatorSchema, typeName);
263+
264+
const refPath = this.schemaComponentsMap.createRef("schemas", typeName);
265+
const complexSchemaKeys = _.keys(this.complexSchemaParsers);
266+
const abstractSchema = _.omit(_.clone(noDiscriminatorSchema), complexSchemaKeys);
267+
const discTypeName = this.config.componentTypeNameResolver.resolve([
268+
pascalCase(`Abstract ${typeName}`),
269+
pascalCase(`Discriminator ${typeName}`),
270+
pascalCase(`Internal ${typeName}`),
271+
pascalCase(`Polymorph ${typeName}`),
272+
]);
273+
const abstractSchemaIsEmpty = !_.keys(abstractSchema).length;
274+
const abstractComponent =
275+
!abstractSchemaIsEmpty &&
276+
this.schemaComponentsMap.createComponent("schemas", discTypeName, {
277+
...abstractSchema,
278+
internal: true,
279+
});
280+
const complexType = this.getComplexType(schema);
281+
282+
const abstractSchemaContent = !abstractSchemaIsEmpty && this.getInlineParseContent(abstractComponent);
283+
const complexSchemaContent =
284+
complexType !== SCHEMA_TYPES.COMPLEX_UNKNOWN
285+
? this.config.Ts.ExpressionGroup(this.complexSchemaParsers[complexType](schema))
286+
: null;
287+
const discriminatorSchemaContent =
288+
discriminator.mapping &&
289+
this.config.Ts.ExpressionGroup(
290+
this.config.Ts.UnionType(
291+
_.map(discriminator.mapping, (schema, key) => {
292+
const mappingSchema = typeof schema === "string" ? { $ref: schema } : schema;
293+
if (mappingSchema.$ref) {
294+
const mappingRefSchema = this.schemaUtils.getSchemaRefType(mappingSchema)?.rawTypeData;
295+
if (mappingRefSchema) {
296+
complexSchemaKeys.forEach((schemaKey) => {
297+
if (_.isArray(mappingRefSchema[schemaKey])) {
298+
mappingRefSchema[schemaKey] = mappingRefSchema[schemaKey].map((schema) => {
299+
if (schema.$ref === refPath) {
300+
return { ...schema, $ref: abstractComponent.$ref };
301+
}
302+
return schema;
303+
});
304+
}
305+
});
306+
}
307+
}
308+
return this.config.Ts.ExpressionGroup(
309+
this.config.Ts.IntersectionType([
310+
this.config.Ts.ObjectWrapper(
311+
this.config.Ts.TypeField({
312+
key: discriminator.propertyName,
313+
value: this.config.Ts.StringValue(key),
314+
}),
315+
),
316+
this.getInlineParseContent(mappingSchema),
317+
]),
318+
);
319+
}),
320+
),
321+
);
322+
323+
return {
324+
...(_.isObject(schema) ? schema : {}),
325+
$parsedSchema: true,
326+
schemaType: SCHEMA_TYPES.COMPLEX,
327+
type: SCHEMA_TYPES.PRIMITIVE,
328+
typeIdentifier: this.config.Ts.Keyword.Type,
329+
name: typeName,
330+
description: this.schemaFormatters.formatDescription(schema.description),
331+
content: this.config.Ts.UnionType(
332+
[abstractSchemaContent, complexSchemaContent, discriminatorSchemaContent].filter(Boolean),
333+
),
334+
};
335+
},
259336
};
260337

261338
getInternalSchemaType = (schema) => {
262339
if (!_.isEmpty(schema.enum) || !_.isEmpty(this.schemaUtils.getEnumNames(schema))) return SCHEMA_TYPES.ENUM;
340+
if (schema.discriminator) return SCHEMA_TYPES.DISCRIMINATOR;
263341
if (schema.allOf || schema.oneOf || schema.anyOf || schema.not) return SCHEMA_TYPES.COMPLEX;
264342
if (!_.isEmpty(schema.properties)) return SCHEMA_TYPES.OBJECT;
265343

templates/base/data-contracts.ejs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ type <%~ config.Ts.CodeGenKeyword.UtilRequiredKeys %><T, K extends keyof T> = Om
2222

2323
<% modelTypes.forEach((contract) => { %>
2424
<%~ includeFile('@base/data-contract-jsdoc.ejs', { ...it, data: { ...contract, ...contract.typeData } }) %>
25-
export <%~ (dataContractTemplates[contract.typeIdentifier] || dataContractTemplates.type)(contract) %>
25+
<%~ contract.internal ? '' : 'export'%> <%~ (dataContractTemplates[contract.typeIdentifier] || dataContractTemplates.type)(contract) %>
2626
2727
2828
<% }) %>

tests/spec/discriminator/expected.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/* eslint-disable */
2+
/* tslint:disable */
3+
/*
4+
* ---------------------------------------------------------------
5+
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
6+
* ## ##
7+
* ## AUTHOR: acacode ##
8+
* ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
9+
* ---------------------------------------------------------------
10+
*/
11+
12+
export type SimpleDiscriminator = SimpleObject | ComplexObject;
13+
14+
export interface SimpleObject {
15+
objectType: string;
16+
}
17+
18+
export interface ComplexObject {
19+
objectType: string;
20+
}
21+
22+
export type BlockDTO =
23+
| AbstractBlockDto
24+
| (CsvBlockDTO | FileBlockDTO)
25+
| (
26+
| ({
27+
type: "csv";
28+
} & CsvBlockDTO)
29+
| ({
30+
type: "file";
31+
} & FileBlockDTO)
32+
);
33+
34+
export type CsvBlockDTO = AbstractBlockDto & {
35+
/** @default "csv" */
36+
type: "csv";
37+
text: string;
38+
};
39+
40+
export type FileBlockDTO = AbstractBlockDto & {
41+
/** @default "file" */
42+
type: "file";
43+
fileId: string;
44+
};
45+
46+
export type Pet =
47+
| AbstractPet
48+
| (
49+
| ({
50+
pet_type: "cachorro";
51+
} & Dog)
52+
| ({
53+
pet_type: "cat";
54+
} & Cat)
55+
);
56+
57+
export type Cat = AbstractPet & {
58+
name?: string;
59+
};
60+
61+
export type Dog = AbstractPet & {
62+
bark?: string;
63+
};
64+
65+
export type Lizard = Pet & {
66+
lovesRocks?: boolean;
67+
};
68+
69+
interface AbstractBlockDto {
70+
title: string;
71+
}
72+
73+
interface AbstractPet {
74+
pet_type: string;
75+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy