Skip to content

Commit

Permalink
add discriminator support
Browse files Browse the repository at this point in the history
  • Loading branch information
Andrey Klimenko committed Mar 25, 2024
1 parent b5d6b50 commit 124184f
Show file tree
Hide file tree
Showing 16 changed files with 413 additions and 97 deletions.
115 changes: 111 additions & 4 deletions .snapshot/all/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,35 @@ import { Guid } from './Guid';
import { toDateIn, toDateOut } from './date-converters';
import type * as $types from './types';

export enum CategoryUnionTypes {
CategoryElectronicsDto = '1',
CategoryMotorsDto = '2'
}

export enum ProductStatus {
InStock = 0,
OutOfStock = -1,
UnderTheOrder = 1
}

interface ICategoryElectronicsDtoBaseInterface {
syntheticTest: $types.TypeOrUndefinedNullable<number>;
}

interface ICategoryMotorsDtoBaseInterface {
volume: $types.TypeOrUndefinedNullable<number>;
}

export interface ICategory {
name: $types.TypeOrUndefinedNullable<string>;
type: $types.TypeOrUndefined<string>;
}

export type ICategoryElectronicsDto = ICategoryElectronicsDtoBaseInterface & ICategory;
export type ICategoryMotorsDto = ICategoryMotorsDtoBaseInterface & ICategory;

export interface IProduct {
category: $types.TypeOrUndefinedNullable<ICategory>;
category: $types.TypeOrUndefinedNullable<ICategory[]>;
colors: $types.TypeOrUndefined<string[]>;
expireDate: $types.TypeOrUndefined<string>;
externalId: $types.TypeOrUndefinedNullable<string>;
Expand All @@ -27,6 +44,47 @@ export interface IProductIdentityDTO {
id: $types.TypeOrUndefined<string>;
}

export type CategoryUnion = Category | CategoryElectronicsDto | CategoryMotorsDto;
export type ICategoryUnion = ICategory | ICategoryElectronicsDto | ICategoryMotorsDto;

export class CategoryUnionClass {
public static fromDTO(dto: ICategoryUnion): CategoryUnion {
if (this.isCategoryElectronicsDto(dto)) {
return CategoryElectronicsDto.fromDTO(dto);
}
if (this.isCategoryMotorsDto(dto)) {
return CategoryMotorsDto.fromDTO(dto);
}
return Category.fromDTO(dto);
}

public static toDTO(model: CategoryUnion): ICategoryUnion {
if (this.isICategoryElectronicsDto(model)) {
return CategoryElectronicsDto.toDTO(model);
}
if (this.isICategoryMotorsDto(model)) {
return CategoryMotorsDto.toDTO(model);
}
return Category.toDTO(model);
}

private static isCategoryElectronicsDto(dto: ICategoryUnion): dto is ICategoryElectronicsDto {
return dto.type === CategoryUnionTypes.CategoryElectronicsDto;
}

private static isCategoryMotorsDto(dto: ICategoryUnion): dto is ICategoryMotorsDto {
return dto.type === CategoryUnionTypes.CategoryMotorsDto;
}

private static isICategoryElectronicsDto(dto: CategoryUnion): dto is CategoryElectronicsDto {
return dto.type === CategoryUnionTypes.CategoryElectronicsDto;
}

private static isICategoryMotorsDto(dto: CategoryUnion): dto is CategoryMotorsDto {
return dto.type === CategoryUnionTypes.CategoryMotorsDto;
}
}

export class ProductIdentityDTO {
public id: Guid;
private __productIdentityDTO!: string;
Expand All @@ -42,23 +100,72 @@ export class ProductIdentityDTO {

export class Category {
public name: $types.TypeOrUndefinedNullable<string> = undefined;
public type: $types.TypeOrUndefined<string> = undefined;
private __category!: string;

public static toDTO(model: Partial<Category>): ICategory {
return {
name: model.name,
type: model.type,
};
}

public static fromDTO(dto: ICategory): Category {
const model = new Category();
model.name = dto.name;
model.type = dto.type;
return model;
}
}

export class CategoryElectronicsDto {
public name: $types.TypeOrUndefinedNullable<string> = undefined;
public type: $types.TypeOrUndefined<string> = undefined;
public syntheticTest: $types.TypeOrUndefinedNullable<number> = undefined;
private __categoryElectronicsDto!: string;

public static toDTO(model: Partial<CategoryElectronicsDto>): ICategoryElectronicsDto {
return {
syntheticTest: model.syntheticTest,
name: model.name,
type: model.type,
};
}

public static fromDTO(dto: ICategoryElectronicsDto): CategoryElectronicsDto {
const model = new CategoryElectronicsDto();
model.syntheticTest = dto.syntheticTest;
model.name = dto.name;
model.type = dto.type;
return model;
}
}

export class CategoryMotorsDto {
public name: $types.TypeOrUndefinedNullable<string> = undefined;
public type: $types.TypeOrUndefined<string> = undefined;
public volume: $types.TypeOrUndefinedNullable<number> = undefined;
private __categoryMotorsDto!: string;

public static toDTO(model: Partial<CategoryMotorsDto>): ICategoryMotorsDto {
return {
volume: model.volume,
name: model.name,
type: model.type,
};
}

public static fromDTO(dto: ICategoryMotorsDto): CategoryMotorsDto {
const model = new CategoryMotorsDto();
model.volume = dto.volume;
model.name = dto.name;
model.type = dto.type;
return model;
}
}

export class Product {
public category: $types.TypeOrUndefinedNullable<Category> = undefined;
public category: CategoryUnion[] = [];
public colors: string[] = [];
public expireDate: $types.TypeOrUndefined<Date> = undefined;
public externalId: $types.TypeOrUndefinedNullable<Guid> = undefined;
Expand All @@ -70,7 +177,7 @@ export class Product {

public static toDTO(model: Partial<Product>): IProduct {
return {
category: model.category ? Category.toDTO(model.category) : undefined,
category: model.category ? model.category.map(x => CategoryUnionClass.toDTO(x)) : undefined,
colors: model.colors,
expireDate: toDateOut(model.expireDate),
externalId: model.externalId ? model.externalId.toString() : null,
Expand All @@ -83,7 +190,7 @@ export class Product {

public static fromDTO(dto: IProduct): Product {
const model = new Product();
model.category = dto.category ? Category.fromDTO(dto.category) : undefined;
model.category = dto.category ? dto.category.map(x => CategoryUnionClass.fromDTO(x)) : [];
model.colors = dto.colors ? dto.colors : [];
model.expireDate = toDateIn(dto.expireDate);
model.externalId = dto.externalId ? new Guid(dto.externalId) : null;
Expand Down
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@luxbss/gengen",
"version": "1.2.6",
"version": "1.2.7",
"description": "Tool for generating models and Angular services based on OpenAPIs and Swagger's JSON",
"bin": {
"gengen": "./bin/index.js"
Expand Down
81 changes: 76 additions & 5 deletions src/generators/ModelsGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ import {
} from 'ts-morph';
import { IEnumModel } from '../models/EnumModel';
import { IIdentityModel } from '../models/IdentityModel';
import { IInterfaceModel } from '../models/InterfaceModel';
import { IInterfaceModel, IInterfaceUnionModel } from '../models/InterfaceModel';
import { IModelsContainer } from '../models/ModelsContainer';
import { IObjectModel, IObjectPropertyModel } from '../models/ObjectModel';
import { PropertyKind } from '../models/kinds/PropertyKind';
import { IOptions } from '../options';
import { PathBuilder } from '../services/PathBuilder';
import { lowerFirst } from '../utils';
import { getInterfaceName, lowerFirst } from '../utils';
import { InterfacesGenerator } from './models-generator/InterfacesGenerator';
import { TypeSerializer } from './utils/TypeSerializer';
import { ARRAY_STRING, NULL_STRING, TYPES_NAMESPACE, UNDEFINED_STRING } from './utils/consts';
Expand All @@ -37,6 +37,8 @@ export class ModelsGenerator {
...this.getImports(),
...this.getEnums(models.enums),
...this.interfaceGenerator.getCodeStructure(models.interfaces),
...this.interfaceGenerator.getCodeUnionsStructure(models.unionInterfaces),
...this.getUnionObjects(models.unionInterfaces),
...this.getIdentities(models.identities, models.interfaces),
...this.getObjects(models.objects)
];
Expand Down Expand Up @@ -120,6 +122,71 @@ export class ModelsGenerator {
})
);
}
private getUnionObjects(objects: IInterfaceUnionModel[]): ClassDeclarationStructure[] {
return objects.map((z) => ({
kind: StructureKind.Class,
isExported: true,
name: z.name + 'Class',
properties: [],
methods: [
{
scope: Scope.Public,
isStatic: true,
name: FROM_DTO_METHOD,
parameters: [{ name: 'dto', type: getInterfaceName(z.name) }],
returnType: z.name,
statements: (x) => {
z.unionInterfaces.forEach((i) => {
x.writeLine('if (this.is' + i + '(dto)){');
x.writeLine('return ' + i + '.fromDTO(dto);');
x.writeLine('}');
});

x.writeLine('return ' + z.parentInterface + '.fromDTO(dto);');
}
},

{
scope: Scope.Public,
isStatic: true,
name: TO_DTO_METHOD,
parameters: [{ name: 'model', type: z.name }],
returnType: getInterfaceName(z.name),
statements: (x) => {
z.unionInterfaces.forEach((i) => {
x.writeLine('if (this.is' + getInterfaceName(i) + '(model)){');
x.writeLine('return ' + i + '.toDTO(model);');
x.writeLine('}');
});

x.writeLine('return ' + z.parentInterface + '.toDTO(model);');
}
},

...z.unionInterfaces.map((i) => ({
scope: Scope.Private,
isStatic: true,
name: 'is' + i,
parameters: [{ name: 'dto', type: getInterfaceName(z.name) }],
returnType: 'dto is ' + getInterfaceName(i),
statements: (x: CodeBlockWriter) => {
x.writeLine('return dto.type === ' + z.name + 'Types.' + i + ';');
}
})),

...z.unionInterfaces.map((i) => ({
scope: Scope.Private,
isStatic: true,
name: 'is' + getInterfaceName(i),
parameters: [{ name: 'dto', type: z.name }],
returnType: 'dto is ' + i,
statements: (x: CodeBlockWriter) => {
x.writeLine('return dto.type === ' + z.name + 'Types.' + i + ';');
}
}))
]
}));
}

private getObjects(objects: IObjectModel[]): ClassDeclarationStructure[] {
return objects.map((z) => ({
Expand Down Expand Up @@ -191,7 +258,7 @@ export class ModelsGenerator {
scope: Scope.Public,
name: objectProperty.name,
type: new TypeSerializer({
type: { name: objectProperty.type },
type: { name: objectProperty.typeAlias ?? objectProperty.type },
isNullable: objectProperty.isNullable,
isCollection: objectProperty.isCollection
}).toString(),
Expand Down Expand Up @@ -239,7 +306,9 @@ export class ModelsGenerator {

case PropertyKind.Object:
if (property.isCollection) {
return `${modelProperty} ? ${modelProperty}.map(x => ${property.type}.${TO_DTO_METHOD}(x)) : ${UNDEFINED_STRING}`;
return `${modelProperty} ? ${modelProperty}.map(x => ${
property.dtoTypeAlias ?? property.type
}.${TO_DTO_METHOD}(x)) : ${UNDEFINED_STRING}`;
}

return `${modelProperty} ? ${property.type}.${TO_DTO_METHOD}(${modelProperty}) : ${UNDEFINED_STRING}`;
Expand Down Expand Up @@ -279,7 +348,9 @@ export class ModelsGenerator {

case PropertyKind.Object:
if (property.isCollection) {
return `${dtoProperty} ? ${dtoProperty}.map(x => ${property.type}.${FROM_DTO_METHOD}(x)) : ${ARRAY_STRING}`;
return `${dtoProperty} ? ${dtoProperty}.map(x => ${
property.dtoTypeAlias ?? property.type
}.${FROM_DTO_METHOD}(x)) : ${ARRAY_STRING}`;
}

return `${dtoProperty} ? ${property.type}.${FROM_DTO_METHOD}(${dtoProperty}) : ${UNDEFINED_STRING}`;
Expand Down
23 changes: 22 additions & 1 deletion src/generators/models-generator/InterfacesGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import {
TypeAliasDeclarationStructure
} from 'ts-morph';

import { IInterfaceModel, IInterfacePropertyModel } from '../../models/InterfaceModel';
import { IInterfaceModel, IInterfacePropertyModel, IInterfaceUnionModel } from '../../models/InterfaceModel';
import { TypeSerializer } from '../utils/TypeSerializer';
import { getInterfaceName } from '../../utils';

export class InterfacesGenerator {
public getCodeStructure(interfaces: IInterfaceModel[]): (InterfaceDeclarationStructure | TypeAliasDeclarationStructure)[] {
Expand Down Expand Up @@ -39,6 +40,26 @@ export class InterfacesGenerator {
return [...baseInterfaces, ...types];
}

public getCodeUnionsStructure(interfaces: IInterfaceUnionModel[]): TypeAliasDeclarationStructure[] {
const classUnion: TypeAliasDeclarationStructure[] = interfaces.map((z) => {
return {
kind: StructureKind.TypeAlias,
type: z.parentInterface + '|' + z.unionInterfaces.join(' | '),
name: z.name,
isExported: true
};
});
const interfacesUnion: TypeAliasDeclarationStructure[] = interfaces.map((z) => {
return {
kind: StructureKind.TypeAlias,
type: getInterfaceName(z.parentInterface) + '|' + z.unionInterfaces.map((x) => getInterfaceName(x)).join(' | '),
name: getInterfaceName(z.name),
isExported: true
};
});
return [...classUnion, ...interfacesUnion];
}

protected getInterfaceProperty(model: IInterfacePropertyModel): OptionalKind<PropertySignatureStructure> {
return {
name: model.name,
Expand Down
2 changes: 1 addition & 1 deletion src/models/EnumModel.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export interface IEnumModel {
name: string;
isNullable: boolean;
items: { key: string; value: number }[];
items: { key: string; value: number | string }[];
}
6 changes: 6 additions & 0 deletions src/models/InterfaceModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,9 @@ export interface IInterfaceModel {
properties: IInterfacePropertyModel[];
combineInterfaces: string[];
}

export interface IInterfaceUnionModel {
name: string;
parentInterface: string;
unionInterfaces: string[];
}
Loading

0 comments on commit 124184f

Please sign in to comment.