Skip to content

Commit

Permalink
feat(openapi-v3): add support for openapi responses
Browse files Browse the repository at this point in the history
  • Loading branch information
virkt25 committed Aug 29, 2018
1 parent 0228fed commit 0dbf2e1
Show file tree
Hide file tree
Showing 12 changed files with 332 additions and 182 deletions.
52 changes: 49 additions & 3 deletions examples/todo/src/controllers/todo.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,16 @@

import {inject} from '@loopback/core';
import {repository} from '@loopback/repository';
import {del, get, param, patch, post, put, requestBody} from '@loopback/rest';
import {
del,
get,
param,
patch,
post,
put,
requestBody,
responses,
} from '@loopback/rest';
import {Todo} from '../models';
import {TodoRepository} from '../repositories';
import {GeocoderService} from '../services';
Expand All @@ -16,8 +25,14 @@ export class TodoController {
@inject('services.GeocoderService') protected geoService: GeocoderService,
) {}

@responses({
'200': {
description: 'Todo model instance',
content: {'application/json': {schema: Todo}},
},
})
@post('/todos')
async createTodo(@requestBody() todo: Todo) {
async createTodo(@requestBody() todo: Todo): Promise<Todo> {
if (todo.remindAtAddress) {
// TODO(bajtos) handle "address not found"
const geo = await this.geoService.geocode(todo.remindAtAddress);
Expand All @@ -26,10 +41,15 @@ export class TodoController {
// https://gis.stackexchange.com/q/7379
todo.remindAtGeo = `${geo[0].y},${geo[0].x}`;
}

return await this.todoRepo.create(todo);
}

@responses({
'200': {
description: 'Todo model instance',
content: {'application/json': {schema: Todo}},
},
})
@get('/todos/{id}')
async findTodoById(
@param.path.number('id') id: number,
Expand All @@ -38,11 +58,25 @@ export class TodoController {
return await this.todoRepo.findById(id);
}

@responses({
'200': {
description: 'Array of Todo model instances',
content: {
'application/json': {schema: {type: 'array', items: Todo}},
},
},
})
@get('/todos')
async findTodos(): Promise<Todo[]> {
return await this.todoRepo.find();
}

@responses({
'200': {
description: 'Put success',
content: {'application/json': {schema: {type: 'boolean'}}},
},
})
@put('/todos/{id}')
async replaceTodo(
@param.path.number('id') id: number,
Expand All @@ -51,6 +85,12 @@ export class TodoController {
return await this.todoRepo.replaceById(id, todo);
}

@responses({
'200': {
description: 'Patch success',
content: {'application/json': {schema: {type: 'boolean'}}},
},
})
@patch('/todos/{id}')
async updateTodo(
@param.path.number('id') id: number,
Expand All @@ -59,6 +99,12 @@ export class TodoController {
return await this.todoRepo.updateById(id, todo);
}

@responses({
'200': {
description: 'Delete success',
content: {'application/json': {schema: {type: 'boolean'}}},
},
})
@del('/todos/{id}')
async deleteTodo(@param.path.number('id') id: number): Promise<boolean> {
return await this.todoRepo.deleteById(id);
Expand Down
2 changes: 1 addition & 1 deletion packages/openapi-spec-builder/src/openapi-spec-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ export class OpenApiSpecBuilder extends BuilderBase<OpenApiSpec> {
export class OperationSpecBuilder extends BuilderBase<OperationObject> {
constructor() {
super({
responses: {},
responses: {'200': {description: ''}},
});
}

Expand Down
48 changes: 47 additions & 1 deletion packages/openapi-v3/src/controller-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@ import {
PathObject,
ComponentsObject,
RequestBodyObject,
ResponsesObject,
} from '@loopback/openapi-v3-types';
import {getJsonSchema} from '@loopback/repository-json-schema';
import {OAI3Keys} from './keys';
import {jsonToSchemaObject} from './json-to-schema';
import * as _ from 'lodash';

const debug = require('debug')('loopback:openapi3:metadata');
const debug = require('debug')('loopback:openapi3:metadata:controller-spec');

// tslint:disable:no-any

Expand Down Expand Up @@ -103,6 +104,47 @@ function resolveControllerSpec(constructor: Function): ControllerSpec {
}
debug(' operation for method %s: %j', op, endpoint);

debug(' processing responses for method %s', op);
let responses = MetadataInspector.getMethodMetadata<ResponsesObject>(
OAI3Keys.RESPONSES_KEY,
constructor.prototype,
op,
);

debug(' decorated responses for method %s: %o', op, responses);

const defaultResponse = {
'200': {description: ''},
};

operationSpec.responses =
responses || operationSpec.responses || defaultResponse;

for (const code in operationSpec.responses) {
for (const c in operationSpec.responses[code].content) {
debug(' evaluating response code %s with content %o', code, c);
if (
typeof operationSpec.responses[code].content[c].schema === 'function'
) {
debug(' responses schema detected as a model / function');
operationSpec.responses[code].content[c].schema = getModelRef(
operationSpec.responses[code].content[c].schema,
);
} else if (
operationSpec.responses[code].content[c].schema.type === 'array' &&
typeof operationSpec.responses[code].content[c].schema.items ===
'function'
) {
debug(
'responses schema of type array detected with items as a model / function',
);
operationSpec.responses[code].content[c].schema.items = getModelRef(
operationSpec.responses[code].content[c].schema.items,
);
}
}
}

debug(' processing parameters for method %s', op);
let params = MetadataInspector.getAllParameterMetadata<ParameterObject>(
OAI3Keys.PARAMETERS_KEY,
Expand Down Expand Up @@ -243,3 +285,7 @@ export function getControllerSpec(constructor: Function): ControllerSpec {
}
return spec;
}

function getModelRef(fn: Function) {
return {$ref: `#/components/schemas/${fn.name}`};
}
1 change: 1 addition & 0 deletions packages/openapi-v3/src/decorators/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export * from './api.decorator';
export * from './operation.decorator';
export * from './parameter.decorator';
export * from './request-body.decorator';
export * from './responses.decorator';
164 changes: 0 additions & 164 deletions packages/openapi-v3/src/decorators/response.decorator.ts

This file was deleted.

Loading

0 comments on commit 0dbf2e1

Please sign in to comment.