Skip to content

Commit

Permalink
ObjectTypeLiteral support
Browse files Browse the repository at this point in the history
  • Loading branch information
WoH committed Aug 14, 2019
1 parent 200b887 commit ad751d8
Show file tree
Hide file tree
Showing 15 changed files with 133 additions and 8 deletions.
7 changes: 6 additions & 1 deletion src/metadataGeneration/tsoa.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export namespace Tsoa {
validators: Validators;
}

export type TypeStringLiteral = 'string' | 'boolean' | 'double' | 'float' | 'integer' | 'long' | 'enum' | 'array' | 'datetime' | 'date' | 'binary' | 'buffer' | 'byte' | 'void' | 'object' | 'any' | 'refEnum' | 'refObject';
export type TypeStringLiteral = 'string' | 'boolean' | 'double' | 'float' | 'integer' | 'long' | 'enum' | 'array' | 'datetime' | 'date' | 'binary' | 'buffer' | 'byte' | 'void' | 'object' | 'any' | 'refEnum' | 'refObject' | 'objectLiteral';

export type RefTypeLiteral = 'refObject' | 'refEnum';

Expand All @@ -87,6 +87,11 @@ export namespace Tsoa {
enums: string[];
}

export interface ObjectLiteralType extends Type {
dataType: 'objectLiteral';
properties: Property[];
}

export interface ArrayType extends Type {
dataType: 'array';
elementType: Type;
Expand Down
18 changes: 17 additions & 1 deletion src/metadataGeneration/typeResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,23 @@ export class TypeResolver {
}

if (this.typeNode.kind === ts.SyntaxKind.TypeLiteral) {
return { dataType: 'any' } as Tsoa.Type;
const properties = (this.typeNode as ts.TypeLiteralNode)
.members
.filter(member => member.kind === ts.SyntaxKind.PropertySignature)
.reduce((res, propertySignature: ts.PropertySignature) => {
const type = new TypeResolver(propertySignature.type as ts.TypeNode, this.current, this.typeNode).resolve();

return [
{
name: (propertySignature.name as ts.Identifier).text,
required: !propertySignature.questionToken,
type,
} as Tsoa.Property,
...res,
];
}, []);

return { dataType: 'objectLiteral', properties } as Tsoa.ObjectLiteralType;
}

if (this.typeNode.kind === ts.SyntaxKind.ObjectKeyword) {
Expand Down
25 changes: 25 additions & 0 deletions src/swagger/specGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,36 @@ export class SpecGenerator {
return this.getSwaggerTypeForArrayType(type as Tsoa.ArrayType);
} else if (type.dataType === 'enum') {
return this.getSwaggerTypeForEnumType(type as Tsoa.EnumerateType);
} else if (type.dataType === 'objectLiteral') {
return this.getSwaggerTypeForObjectLiteral(type as Tsoa.ObjectLiteralType);
} else {
return assertNever(type.dataType);
}
}

public getSwaggerTypeForObjectLiteral(objectLiteral: Tsoa.ObjectLiteralType): Swagger.Schema {
const properties = objectLiteral.properties.reduce((acc, property: Tsoa.Property) => {
return {
[property.name]: this.getSwaggerType(property.type),
...acc,
};
}, {});

const required =
objectLiteral
.properties
.filter(prop => prop.required)
.map(prop => prop.name);

// An empty list required: [] is not valid.
// If all properties are optional, do not specify the required keyword.
return {
properties,
...(required && required.length && {required}),
type: 'object',
};
}

protected getSwaggerTypeForReferenceType(referenceType: Tsoa.ReferenceType): Swagger.BaseSchema {
return {
// Don't set additionalProperties value here since it will be set within the $ref's model when that $ref gets created
Expand Down
3 changes: 3 additions & 0 deletions tests/fixtures/controllers/getController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ export class GetTestController extends Controller {
modelsArray: new Array<TestSubModel>(),
numberArray: [1, 2, 3],
numberValue: 1,
objLiteral: {
name: 'a string',
},
object: {
a: 'a',
},
Expand Down
3 changes: 3 additions & 0 deletions tests/fixtures/inversify/managedService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ export class ManagedService {
modelsArray: new Array<TestSubModel>(),
numberArray: [1, 2, 3],
numberValue: 1,
objLiteral: {
name: 'hello',
},
object: {
a: 'a',
},
Expand Down
3 changes: 3 additions & 0 deletions tests/fixtures/services/modelService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ export class ModelService {
modelsArray: new Array<TestSubModel>(),
numberArray: [1, 2, 3],
numberValue: 1,
objLiteral: {
name: 'hello',
},
object: {
a: 'a',
},
Expand Down
18 changes: 12 additions & 6 deletions tests/fixtures/testModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,18 @@ export interface TestModel extends Model {
modelsEnumIndirect?: TestSubEnumModelContainer;
typeAliasCase1?: TypeAliasModelCase1;
TypeAliasCase2?: TypeAliasModelCase2;

objLiteral: {
name: string,
nested?: {
bool: boolean,
optional?: number,
allOptional: {
one?: string,
two?: string,
},
},
};
}

export interface TypeAliasModel1 {
Expand Down Expand Up @@ -316,12 +328,6 @@ export class TestClassModel extends TestClassBaseModel {
stringProperty: string;
protected protectedStringProperty: string;

public static typeLiterals = {
booleanTypeLiteral: { $type: Boolean },
numberTypeLiteral: { $type: Number },
stringTypeLiteral: { $type: String },
};

/**
* @param publicConstructorVar This is a description for publicConstructorVar
*/
Expand Down
3 changes: 3 additions & 0 deletions tests/integration/dynamic-controllers-express-server.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -783,6 +783,9 @@ describe('Express Server', () => {
},
numberArray: [1, 2],
numberValue: 5,
objLiteral: {
name: 'hello',
},
object: { foo: 'bar' },
objectArray: [{ foo1: 'bar1'}, { foo2: 'bar2'}],
optionalString: 'test1234',
Expand Down
3 changes: 3 additions & 0 deletions tests/integration/express-server.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -783,6 +783,9 @@ describe('Express Server', () => {
},
numberArray: [1, 2],
numberValue: 5,
objLiteral: {
name: 'hello',
},
object: { foo: 'bar' },
objectArray: [{ foo1: 'bar1'}, { foo2: 'bar2'}],
optionalString: 'test1234',
Expand Down
3 changes: 3 additions & 0 deletions tests/integration/hapi-server.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,9 @@ describe('Hapi Server', () => {
},
numberArray: [1, 2],
numberValue: 5,
objLiteral: {
name: 'hello',
},
object: { foo: 'bar' },
objectArray: [{ foo1: 'bar1' }, { foo2: 'bar2' }],
optionalString: 'test1234',
Expand Down
3 changes: 3 additions & 0 deletions tests/integration/inversify-server.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ describe('Inversify Express Server', () => {
},
numberArray: [1, 2, 3],
numberValue: 1,
objLiteral: {
name: 'hello',
},
object: {
a: 'a',
},
Expand Down
3 changes: 3 additions & 0 deletions tests/integration/koa-server-no-additional-allowed.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,9 @@ describe('Koa Server (with noImplicitAdditionalProperties turned on)', () => {
modelsArray: [{ email: '[email protected]', id: 1 }],
numberArray: [1, 2],
numberValue: 5,
objLiteral: {
name: 'hello',
},
object: { foo: 'bar' },
objectArray: [{ foo1: 'bar1' }, { foo2: 'bar2' }],
optionalString: 'test1234',
Expand Down
3 changes: 3 additions & 0 deletions tests/integration/koa-server.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,9 @@ describe('Koa Server', () => {
modelsArray: [{ email: '[email protected]', id: 1 }],
numberArray: [1, 2],
numberValue: 5,
objLiteral: {
name: 'hello',
},
object: { foo: 'bar' },
objectArray: [{ foo1: 'bar1' }, { foo2: 'bar2' }],
optionalString: 'test1234',
Expand Down
23 changes: 23 additions & 0 deletions tests/unit/swagger/definitionsGeneration/definitions.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,29 @@ describe('Definition generation', () => {
expect(propertySchema).to.not.haveOwnProperty('additionalProperties', `for property ${propertyName}`);
expect(propertySchema['x-nullable']).to.eq(true, `for property ${propertyName}[x-nullable]`);
},
objLiteral: (propertyName, propertySchema) => {
expect(propertySchema).to.deep.include({
properties: {
name: {
type: 'string',
},
nested: {
properties: {
allOptional: {
properties: { one: { type: 'string' }, two: { type: 'string' } },
type: 'object',
},
bool: { type: 'boolean' },
optional: { format: 'double', type: 'number'},
},
required: ['allOptional', 'bool'],
type: 'object',
},
},
required: ['name'],
type: 'object',
});
},
};

Object.keys(assertionsPerProperty).forEach(aPropertyName => {
Expand Down
23 changes: 23 additions & 0 deletions tests/unit/swagger/schemaDetails3.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,29 @@ describe('Definition generation for OpenAPI 3.0.0', () => {
expect(propertySchema.description).to.eq(undefined, `for property ${propertyName}.description`);
expect(propertySchema).to.not.haveOwnProperty('additionalProperties', `for property ${propertyName}`);
},
objLiteral: (propertyName, propertySchema) => {
expect(propertySchema).to.deep.include({
properties: {
name: {
type: 'string',
},
nested: {
properties: {
allOptional: {
properties: { one: { type: 'string' }, two: { type: 'string' } },
type: 'object',
},
bool: { type: 'boolean' },
optional: { format: 'double', type: 'number'},
},
required: ['allOptional', 'bool'],
type: 'object',
},
},
required: ['name'],
type: 'object',
});
},
object: (propertyName, propertySchema) => {
expect(propertySchema.type).to.eq('object', `for property ${propertyName}`);
if (currentSpec.specName === 'specWithNoImplicitExtras') {
Expand Down

0 comments on commit ad751d8

Please sign in to comment.