Skip to content

Commit

Permalink
Merge pull request #116 from Luxoft/develop
Browse files Browse the repository at this point in the history
Release candidate 11
  • Loading branch information
sevaru authored Jun 21, 2022
2 parents e0bc44a + aef16fb commit 93388b2
Show file tree
Hide file tree
Showing 11 changed files with 149 additions and 49 deletions.
68 changes: 68 additions & 0 deletions __tests__/swagger/OpenAPIService.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,74 @@ describe('OpenAPIService tests', () => {
paths: {}
};

describe('getSchemas', () => {
const schema: IOpenAPI3 = {
...defaultSpec,
openapi: '3.0.1',
components: {
schemas: {
modelName1: {
type: 'object',
properties: {
id: {
type: 'string',
format: 'uuid'
}
}
},
modelName2: {
type: 'object',
properties: {
id: {
type: 'string',
format: 'uuid'
}
}
},
modelName3: {
type: 'object',
properties: {
id: {
type: 'string',
format: 'uuid'
},
prop1: {
$ref: '#/components/schemas/modelName1'
},
prop2: {
$ref: '#/components/schemas/modelName2'
}
}
}
}
}
};

test('should return all models if no whitelist specified', () => {
const service = new OpenAPIService(schema, guard);
const result = service.getSchemas();
expect(Object.values(result).length).toBe(3);
});

test('should return only selected model if it has no ref fields', () => {
const service = new OpenAPIService(schema, guard);
const result = service.getSchemas(['modelName1']);
expect(Object.values(result).length).toBe(1);
});

test('should return model and all of ref field models', () => {
const service = new OpenAPIService(schema, guard);
const result = service.getSchemas(['modelName3']);
expect(Object.values(result).length).toBe(3);
});

test('should be empty if no model found', () => {
const service = new OpenAPIService(schema, guard);
const result = service.getSchemas(['someOtherModel']);
expect(Object.values(result).length).toBe(0);
});
});

describe('ctor', () => {
test('old OpenApi version', () => {
const spec = { ...defaultSpec, openapi: '1.0.1' };
Expand Down
16 changes: 8 additions & 8 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.0.0-rc.11",
"version": "1.0.0-rc.12",
"description": "Tool for generating models and Angular services based on OpenAPIs and Swagger's JSON",
"bin": {
"gengen": "./bin/index.js"
Expand Down
2 changes: 1 addition & 1 deletion src/generators/ModelsGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import { lowerFirst } from '../utils';
import { NULL_STRING, TYPES_NAMESPACE, UNDEFINED_STRING } from './utils/consts';
import { InterfacesGenerator } from './models-generator/InterfacesGenerator';
import { TypeSerializer } from './utils/TypeSerializer';
import { typeOrUndefined } from './utils/typeOrUndefined';

const TO_DTO_METHOD = 'toDTO';
const FROM_DTO_METHOD = 'fromDTO';
Expand Down Expand Up @@ -92,6 +91,7 @@ export class ModelsGenerator {
isStatic: true,
name: TO_DTO_METHOD,
parameters: [{ name: z.property.name, type: z.property.type }],
// TODO: would find first identity interface everytime
returnType: interfaces.find(
(i) =>
i.properties.length === 1 &&
Expand Down
8 changes: 6 additions & 2 deletions src/generators/models-generator/InterfacesGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,14 @@ export class InterfacesGenerator {
}));
}

private getInterfaceProperty(model: IInterfacePropertyModel): OptionalKind<PropertySignatureStructure> {
protected getInterfaceProperty(model: IInterfacePropertyModel): OptionalKind<PropertySignatureStructure> {
return {
name: model.name,
type: TypeSerializer.fromInterfaceProperty(model).toString()
type: this.getInterfacePropertyType(model)
};
}

protected getInterfacePropertyType(model: IInterfacePropertyModel): string {
return TypeSerializer.fromInterfaceProperty(model).toString();
}
}
12 changes: 6 additions & 6 deletions src/services/ModelMappingService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import { OpenAPITypesGuard } from '../swagger/OpenAPITypesGuard';
import { IOpenAPI3Reference } from '../swagger/v3/reference';
import { IOpenAPI3EnumSchema } from '../swagger/v3/schemas/enum-schema';
import { IOpenAPI3GuidSchema } from '../swagger/v3/schemas/guid-schema';
import { IOpenAPI3ObjectSchema, OpenAPI3ObjectPropertySchema } from '../swagger/v3/schemas/object-schema';
import { OpenAPI3SchemaContainer, OpenAPI3SimpleSchema } from '../swagger/v3/schemas/schema';
import { IOpenAPI3ObjectSchema } from '../swagger/v3/schemas/object-schema';
import { OpenAPI3Schema, OpenAPI3SchemaContainer, OpenAPI3SimpleSchema } from '../swagger/v3/schemas/schema';
import { first, sortBy } from '../utils';
import { TypesService } from './TypesService';

Expand Down Expand Up @@ -47,7 +47,7 @@ export class ModelMappingService {
}
});
} else {
objects.push(this.toObjectModel(schemas, name, schema));
objects.push(this.toObjectModel(name, schema));
}
}
});
Expand All @@ -72,21 +72,21 @@ export class ModelMappingService {
};
}

private toObjectModel(schemas: OpenAPI3SchemaContainer, name: string, schema: IOpenAPI3ObjectSchema): IObjectModel {
private toObjectModel(name: string, schema: IOpenAPI3ObjectSchema): IObjectModel {
const model: IObjectModel = { name, dtoType: this.getInterfaceName(name), properties: [] };
if (!schema.properties) {
return model;
}

Object.entries(schema.properties)
.filter(([name]) => !IGNORE_PROPERTIES.includes(name))
.forEach(([name, propertySchema]) => this.addProperty(schemas, model, name, propertySchema));
.forEach(([name, propertySchema]) => this.addProperty(model, name, propertySchema));

model.properties = model.properties.sort(sortBy((z) => z.name));
return model;
}

private addProperty(schemas: OpenAPI3SchemaContainer, model: IObjectModel, name: string, schema: OpenAPI3ObjectPropertySchema): void {
private addProperty(model: IObjectModel, name: string, schema: OpenAPI3Schema): void {
if (this.typesGuard.isSimple(schema)) {
model.properties.push(this.getSimpleProperty(name, schema));
return;
Expand Down
19 changes: 10 additions & 9 deletions src/services/ServiceMappingService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { IOpenAPI3Operation } from '../swagger/v3/operation';
import { IOpenAPI3Parameter } from '../swagger/v3/parameter';
import { IOpenAPI3Reference } from '../swagger/v3/reference';
import { IOpenAPI3ArraySchema } from '../swagger/v3/schemas/array-schema';
import { OpenAPI3ResponseSchema } from '../swagger/v3/schemas/schema';
import { OpenAPI3Schema } from '../swagger/v3/schemas/schema';
import { first, lowerFirst, sortBy } from '../utils';
import { EndpointNameResolver } from './EndpointNameResolver';
import { EndpointsService, IEndpointInfo } from './EndpointsService';
Expand All @@ -38,7 +38,8 @@ export class ServiceMappingService {
private readonly typesGuard: OpenAPITypesGuard,
private readonly endpointsService: EndpointsService,
private readonly endpointNameResolver: EndpointNameResolver,
private readonly settings: IOptions) {}
private readonly settings: IOptions
) {}

public toServiceModels(operations: IOpenAPI3Operations, models: IModelsContainer): IServiceModel[] {
const endpointInfos = Object.keys(operations).reduce<IEndpointInfo[]>((infos, endpoint) => {
Expand All @@ -54,18 +55,18 @@ export class ServiceMappingService {
this.endpointNameResolver.checkDuplicates(endpointInfos);

const services = Object.entries(operations).reduce<IServiceModel[]>((store, [endpoint, model]) => {
const info = endpointInfos.find(z => z.origin === endpoint);
const info = endpointInfos.find((z) => z.origin === endpoint);
if (!info) {
return store;
}

const service = store.find((z) => z.name === info.name);

model.forEach(z => {
const action = model.length > 1 ?
info.actions.find(x => x.name.startsWith(MethodOperation[z.method].toLocaleLowerCase()))
:
first(info.actions);
model.forEach((z) => {
const action =
model.length > 1
? info.actions.find((x) => x.name.startsWith(MethodOperation[z.method].toLocaleLowerCase()))
: first(info.actions);

if (!action) {
throw new Error(`Cannot find action in service ${info.name} by method ${z}`);
Expand Down Expand Up @@ -199,7 +200,7 @@ export class ServiceMappingService {
};
}

private getReturnType(schema: OpenAPI3ResponseSchema | undefined, models: IModelsContainer): IReturnType | undefined {
private getReturnType(schema: OpenAPI3Schema | undefined, models: IModelsContainer): IReturnType | undefined {
let model: IModel | undefined;
let isCollection = false;

Expand Down
54 changes: 43 additions & 11 deletions src/swagger/OpenAPIService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { IOpenAPI3Operation } from './v3/operation';
import { IOpenAPI3Reference } from './v3/reference';
import { IOpenAPI3EnumSchema } from './v3/schemas/enum-schema';
import { IOpenAPI3ObjectSchema } from './v3/schemas/object-schema';
import { OpenAPI3ResponseSchema, OpenAPI3SchemaContainer } from './v3/schemas/schema';
import { OpenAPI3Schema, OpenAPI3SchemaContainer } from './v3/schemas/schema';

const SUPPORTED_VERSION = 3;

Expand Down Expand Up @@ -56,10 +56,40 @@ export class OpenAPIService {
}

const refs = operations.flatMap((z) => this.getReferencesByOperation(z.operation));
return { ...store, ...this.getSchemas(refs) };
return { ...store, ...this.getSchemasByRefs(refs) };
}, {});
}

public getSchemas(modelNames?: string[]): OpenAPI3SchemaContainer {
if (!modelNames) {
return this.spec.components.schemas;
}

return modelNames.reduce<OpenAPI3SchemaContainer>((store, modelName) => {
const modelSchema = this.spec.components.schemas[modelName];

if (!modelSchema) {
return store;
}

const result = {
...store,
[modelName]: modelSchema
};

if (!this.typesGuard.isObject(modelSchema)) {
return result;
}

const refs: IOpenAPI3Reference[] = [];
Object.values(modelSchema.properties).forEach((propertySchema) => {
refs.push(...this.getRefsFromSchema(propertySchema));
});

return { ...result, ...this.getSchemasByRefs(refs) };
}, {});
}

public getOperationsByEndpoints(endpoints: Set<string>): IOpenAPI3Operations {
if (!endpoints?.size) {
return {};
Expand Down Expand Up @@ -135,10 +165,11 @@ export class OpenAPIService {
}
});

this.getSchemaFromContent(refs, operation.requestBody?.content['application/json']?.schema);
this.getSchemaFromContent(refs, operation.responses[200].content?.['application/json']?.schema);

return refs;
return [
...refs,
...this.getRefsFromSchema(operation.requestBody?.content['application/json']?.schema),
...this.getRefsFromSchema(operation.responses[200].content?.['application/json']?.schema)
];
}

private getReferencesByObject(object: IOpenAPI3ObjectSchema, objectRef: IOpenAPI3Reference): IOpenAPI3Reference[] {
Expand Down Expand Up @@ -169,17 +200,18 @@ export class OpenAPIService {
return refs;
}

private getSchemaFromContent(refs: IOpenAPI3Reference[], schema: OpenAPI3ResponseSchema | undefined): void {
private getRefsFromSchema(schema: OpenAPI3Schema | undefined): IOpenAPI3Reference[] {
const refs: IOpenAPI3Reference[] = [];
if (this.typesGuard.isCollection(schema) && this.typesGuard.isReference(schema.items)) {
refs.push(schema.items);
} else if (this.typesGuard.isReference(schema)) {
refs.push(schema);
}
return refs;
}

private getSchemas(refs: IOpenAPI3Reference[]): OpenAPI3SchemaContainer {
private getSchemasByRefs(refs: IOpenAPI3Reference[]): OpenAPI3SchemaContainer {
const keys = new Set<string>();
const keysFromObjects = new Set<string>();

refs.forEach((ref) => {
const schemaKey = this.getSchemaKey(ref);
Expand All @@ -192,12 +224,12 @@ export class OpenAPIService {
const schema = this.spec.components.schemas[schemaKey];
if (this.typesGuard.isObject(schema)) {
this.getReferencesByObject(schema, ref).forEach((x) => {
keysFromObjects.add(this.getSchemaKey(x));
keys.add(this.getSchemaKey(x));
});
}
});

return [...new Set<string>([...keys, ...keysFromObjects])].reduce<OpenAPI3SchemaContainer>((store, key) => {
return [...keys].reduce<OpenAPI3SchemaContainer>((store, key) => {
store[key] = this.spec.components.schemas[key];
return store;
}, {});
Expand Down
4 changes: 2 additions & 2 deletions src/swagger/v3/operation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { IOpenAPI3Reference } from './reference';
import { IOpenAPI3ArraySchema } from './schemas/array-schema';
import { IOpenAPI3BinarySchema } from './schemas/binary-schema';
import { IOpenAPI3ObjectSchema } from './schemas/object-schema';
import { OpenAPI3ResponseSchema } from './schemas/schema';
import { OpenAPI3Schema } from './schemas/schema';

export interface IOpenAPI3Operation {
tags?: string[];
Expand All @@ -13,7 +13,7 @@ export interface IOpenAPI3Operation {
description?: 'Success';
content?: {
'application/json'?: {
schema: OpenAPI3ResponseSchema;
schema: OpenAPI3Schema;
};
'application/octet-stream'?: {
schema: IOpenAPI3BinarySchema;
Expand Down
9 changes: 2 additions & 7 deletions src/swagger/v3/schemas/object-schema.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
import { IOpenAPI3Reference } from '../reference';
import { IOpenAPI3AllOfSchema } from './all-of-schema';
import { IOpenAPI3ArraySchema } from './array-schema';
import { IOpenAPI3BaseSchema } from './base-schema';
import { OpenAPI3SimpleSchema } from './schema';

export type OpenAPI3ObjectPropertySchema = OpenAPI3SimpleSchema | IOpenAPI3ArraySchema | IOpenAPI3Reference | IOpenAPI3AllOfSchema;
import { OpenAPI3Schema } from './schema';

export interface IOpenAPI3ObjectSchema extends IOpenAPI3BaseSchema {
type: 'object';
properties: {
[key: string]: OpenAPI3ObjectPropertySchema;
[key: string]: OpenAPI3Schema;
};
}
Loading

0 comments on commit 93388b2

Please sign in to comment.