diff --git a/kong/petKong.yaml b/kong/petKong.yaml index 204bb46..458690a 100644 --- a/kong/petKong.yaml +++ b/kong/petKong.yaml @@ -1,62 +1,92 @@ -_format_version: "3.0" +_format_version: '3.0' services: -- host: pet-store-kong-3abae03b4c44.herokuapp.com - id: 6ce82fd3-ffaa-564d-a98e-7b6cf11d7b5a - name: petstore - path: /v1 - plugins: [] - port: 80 - protocol: http - routes: - - id: fcabe360-daec-5fbf-a2be-50f393c23e38 - methods: - - GET - name: petstore_listpets - paths: - - ~/pets$ + - host: pet-store-kong-3abae03b4c44.herokuapp.com + id: 6ce82fd3-ffaa-564d-a98e-7b6cf11d7b5a + name: petstore + path: /v1 plugins: [] - regex_priority: 200 - strip_path: false + port: 80 + protocol: http + routes: + - id: 9e26c451-2d9f-5150-88b5-2f54569f11e2 + methods: + - GET + name: petstore_listhumans + paths: + - ~/humans$ + plugins: [] + regex_priority: 200 + strip_path: false + tags: [] + - id: 1e5e50c3-df1a-5ef5-8504-125fe42f3a78 + methods: + - POST + name: petstore_createhuman + paths: + - ~/humans$ + plugins: [] + regex_priority: 200 + strip_path: false + tags: [] + - id: 8e00b833-e7e9-5be2-8f97-9f205cac53e7 + methods: + - GET + name: petstore_gethuman + paths: + - ~/humans/(?[^#?/]+)$ + plugins: [] + regex_priority: 100 + strip_path: false + tags: [] + - id: fcabe360-daec-5fbf-a2be-50f393c23e38 + methods: + - GET + name: petstore_listpets + paths: + - ~/pets$ + plugins: [] + regex_priority: 200 + strip_path: false + tags: [] + - id: b3862911-a318-51f8-99e7-5f6f3685c61f + methods: + - POST + name: petstore_createpet + paths: + - ~/pets$ + plugins: [] + regex_priority: 200 + strip_path: false + tags: [] + - id: fe28075b-dc30-5646-869e-567b749aa8e9 + methods: + - DELETE + name: petstore_deletepet + paths: + - ~/pets/(?[^#?/]+)$ + plugins: [] + regex_priority: 100 + strip_path: false + tags: [] + - id: 0ef5ebf6-6973-5d23-a051-ab9367803be3 + methods: + - GET + name: petstore_getpet + paths: + - ~/pets/(?[^#?/]+)$ + plugins: [] + regex_priority: 100 + strip_path: false + tags: [] + - id: 7f1a9c95-7df0-5a54-8619-a59ace7f5604 + methods: + - PUT + name: petstore_updatepet + paths: + - ~/pets/(?[^#?/]+)$ + plugins: [] + regex_priority: 100 + strip_path: false + tags: [] tags: [] - - id: b3862911-a318-51f8-99e7-5f6f3685c61f - methods: - - POST - name: petstore_createpet - paths: - - ~/pets$ - plugins: [] - regex_priority: 200 - strip_path: false - tags: [] - - id: fe28075b-dc30-5646-869e-567b749aa8e9 - methods: - - DELETE - name: petstore_deletepet - paths: - - ~/pets/(?[^#?/]+)$ - plugins: [] - regex_priority: 100 - strip_path: false - tags: [] - - id: 0ef5ebf6-6973-5d23-a051-ab9367803be3 - methods: - - GET - name: petstore_getpet - paths: - - ~/pets/(?[^#?/]+)$ - plugins: [] - regex_priority: 100 - strip_path: false - tags: [] - - id: 7f1a9c95-7df0-5a54-8619-a59ace7f5604 - methods: - - PUT - name: petstore_updatepet - paths: - - ~/pets/(?[^#?/]+)$ - plugins: [] - regex_priority: 100 - strip_path: false - tags: [] - tags: [] upstreams: [] diff --git a/spec/petStore.json b/spec/petStore.json index 864585a..11d12d1 100644 --- a/spec/petStore.json +++ b/spec/petStore.json @@ -18,6 +18,294 @@ } ], "paths": { + "/humans": { + "get": { + "operationId": "listHumans", + "description": "Lists all humans in the PetStore", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HumansList" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ErrorBody" + }, + { + "properties": { + "message": { + "type": "string", + "enum": [ + "User is not authorized" + ], + "nullable": false + } + }, + "required": [ + "message" + ], + "type": "object" + } + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ErrorBody" + }, + { + "properties": { + "message": { + "type": "string", + "enum": [ + "User is forbidden to access the resource" + ], + "nullable": false + } + }, + "required": [ + "message" + ], + "type": "object" + } + ] + } + } + } + } + }, + "security": [ + { + "api_key": [] + } + ], + "tags": [ + "Read" + ] + }, + "post": { + "operationId": "createHuman", + "description": "Create a new pet with its adorable name", + "parameters": [], + "requestBody": { + "description": "the information to create the pet", + "required": true, + "content": { + "application/json": { + "schema": { + "description": "the information to create the pet", + "$ref": "#/components/schemas/CreateHumanRequest" + } + } + } + }, + "responses": { + "201": { + "description": "Created", + "headers": { + "LocationHeader": { + "schema": { + "$ref": "#/components/schemas/LocationHeader" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Human" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ErrorBody" + }, + { + "properties": { + "message": { + "type": "string", + "enum": [ + "User is not authorized" + ], + "nullable": false + } + }, + "required": [ + "message" + ], + "type": "object" + } + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ErrorBody" + }, + { + "properties": { + "message": { + "type": "string", + "enum": [ + "User is forbidden to access the resource" + ], + "nullable": false + } + }, + "required": [ + "message" + ], + "type": "object" + } + ] + } + } + } + } + }, + "security": [ + { + "api_key": [] + } + ], + "tags": [ + "Write" + ] + } + }, + "/humans/{id}": { + "get": { + "operationId": "getHuman", + "description": "Get a human by its id", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "id of the human to fetch", + "required": true, + "schema": { + "type": "number", + "format": "double" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/Human" + } + ], + "nullable": true + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ErrorBody" + }, + { + "properties": { + "message": { + "type": "string", + "enum": [ + "User is not authorized" + ], + "nullable": false + } + }, + "required": [ + "message" + ], + "type": "object" + } + ] + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ErrorBody" + }, + { + "properties": { + "message": { + "type": "string", + "enum": [ + "User is forbidden to access the resource" + ], + "nullable": false + } + }, + "required": [ + "message" + ], + "type": "object" + } + ] + } + } + } + } + }, + "security": [ + { + "api_key": [] + } + ], + "tags": [ + "Read" + ] + } + }, "/pets": { "get": { "operationId": "listPets", @@ -700,6 +988,75 @@ "Date" ] }, + "Human": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "id": { + "description": "The id of the human, used for update and fetching", + "type": "number", + "format": "double" + } + }, + "example": { + "id": 2, + "name": "Dan" + }, + "additionalProperties": false, + "required": [ + "name", + "id" + ] + }, + "HumansList": { + "type": "object", + "properties": { + "items": { + "description": "The list of humans", + "type": "array", + "items": { + "$ref": "#/components/schemas/Human" + } + } + }, + "additionalProperties": false, + "required": [ + "items" + ] + }, + "LocationHeader": { + "type": "object", + "properties": { + "location": { + "description": "The URI of the Human", + "type": "string" + } + }, + "example": { + "location": "https://petstore.com/v1/humans/1" + }, + "additionalProperties": false, + "required": [ + "location" + ] + }, + "CreateHumanRequest": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "example": { + "name": "Dan" + }, + "additionalProperties": false, + "required": [ + "name" + ] + }, "Pet": { "type": "object", "properties": { diff --git a/src/controllers/HumanController.ts b/src/controllers/HumanController.ts new file mode 100644 index 0000000..5eb0402 --- /dev/null +++ b/src/controllers/HumanController.ts @@ -0,0 +1,65 @@ +import { Route, Get, Post, Path, Body, OperationId, Tags, SuccessResponse, Response, Security } from 'tsoa'; + +import { Controller } from '@tsoa/runtime'; +import { HumansList, getHumans, getHumanById, Human, CreateHumanRequest, ErrorBody, createHuman } from '../models/Human'; + +/** + * @tsoaModel + * @example + * { + * "location": "https://petstore.com/v1/humans/1" + * } + */ +interface LocationHeader { + /** + * The URI of the Human + */ + location: string; +} + +@Route('humans') +@Response(401, 'Unauthorized') +@Response(403, 'Forbidden') +@Security('api_key') +export class HumanController extends Controller { + /** + * Lists all humans in the PetStore + */ + @Get() + @OperationId('listHumans') + @Tags('Read') + @SuccessResponse('200', 'OK') + public async getHumans(): Promise { + return getHumans(); + } + + /** + * Get a human by its id + * @param id id of the human to fetch + */ + @Get('{id}') + @Tags('Read') + @OperationId('getHuman') + @SuccessResponse('200', 'OK') + public async getHuman(@Path() id: number): Promise { + const human = getHumanById(id) || null; + if (human === null) { + this.setStatus(404); + return null; + } + return human; + } + + /** + * Create a new pet with its adorable name + * @param createHumanRequest the information to create the pet + */ + @Post() + @Tags('Write') + @SuccessResponse('201', 'Created') + @OperationId('createHuman') + public async createHuman(@Body() createHumanRequest: CreateHumanRequest): Promise { + this.setStatus(201); + return createHuman(createHumanRequest); + } +} diff --git a/src/models/Human.ts b/src/models/Human.ts new file mode 100644 index 0000000..a2952f0 --- /dev/null +++ b/src/models/Human.ts @@ -0,0 +1,82 @@ +/** + * @tsoaModel + * @example + * { + * items: [ + * { + * "id": 1, + * "name": "Dan" + * }, + * { + * "id": 2, + * "name": "Liz" + * } + * ] + * } + */ +export interface HumansList { + /** + * The list of humans + */ + items: Human[]; +} + +/** + * @tsoaModel + * @example + * { + * "id": 2, + * "name": "Dan" + * } + */ +export interface Human extends CreateHumanRequest { + /** + * The id of the human, used for update and fetching + */ + id: number; +} + +/** + * @tsoaModel + * @example + * { + * "name": "Dan" + * } + */ +export interface CreateHumanRequest { + name: string; +} + +/** + * Error response returned on failure + */ +export interface ErrorBody { + /** + * The message informing users of their error + */ + message: string; +} + +//in memory db for now +export let humans: Human[] = []; +let nextId = 1; + +export function createHuman(createHumanRequest: CreateHumanRequest): Human { + const newHuman = { + id: nextId++, + name: createHumanRequest.name, + }; + + humans.push(newHuman); + return newHuman; +} + +export function getHumanById(id: number): Human | undefined { + return humans.find((human) => human.id === id); +} + +export function getHumans(): HumansList { + return { + items: humans, + }; +} diff --git a/src/routes/routes.ts b/src/routes/routes.ts index 860f00c..33f2d50 100644 --- a/src/routes/routes.ts +++ b/src/routes/routes.ts @@ -12,6 +12,8 @@ import { fetchMiddlewares, } from '@tsoa/runtime'; // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa +import { HumanController } from './../controllers/HumanController'; +// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa import { PetController } from './../controllers/PetController'; import { expressAuthentication } from './../auth'; // @ts-ignore - no great way to install types from subpackage @@ -38,6 +40,39 @@ const models: TsoaRoute.Models = { additionalProperties: false, }, // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + Human: { + dataType: 'refObject', + properties: { + name: { dataType: 'string', required: true }, + id: { dataType: 'double', required: true }, + }, + additionalProperties: false, + }, + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + HumansList: { + dataType: 'refObject', + properties: { + items: { dataType: 'array', array: { dataType: 'refObject', ref: 'Human' }, required: true }, + }, + additionalProperties: false, + }, + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + LocationHeader: { + dataType: 'refObject', + properties: { + location: { dataType: 'string', required: true }, + }, + additionalProperties: false, + }, + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + CreateHumanRequest: { + dataType: 'refObject', + properties: { + name: { dataType: 'string', required: true }, + }, + additionalProperties: false, + }, + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa Pet: { dataType: 'refObject', properties: { @@ -93,6 +128,85 @@ export function RegisterRoutes(app: Router) { // NOTE: If you do not see routes for all of your controllers in this file, then you might not have informed tsoa of where to look // Please look into the "controllerPathGlobs" config option described in the readme: https://github.com/lukeautry/tsoa // ########################################################################################################### + app.get( + '/v1/humans', + authenticateMiddleware([{ api_key: [] }]), + ...fetchMiddlewares(HumanController), + ...fetchMiddlewares(HumanController.prototype.getHumans), + + function HumanController_getHumans(request: any, response: any, next: any) { + const args = {}; + + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + + let validatedArgs: any[] = []; + try { + validatedArgs = getValidatedArgs(args, request, response); + + const controller = new HumanController(); + + const promise = controller.getHumans.apply(controller, validatedArgs as any); + promiseHandler(controller, promise, response, 200, next); + } catch (err) { + return next(err); + } + }, + ); + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + app.get( + '/v1/humans/:id', + authenticateMiddleware([{ api_key: [] }]), + ...fetchMiddlewares(HumanController), + ...fetchMiddlewares(HumanController.prototype.getHuman), + + function HumanController_getHuman(request: any, response: any, next: any) { + const args = { + id: { in: 'path', name: 'id', required: true, dataType: 'double' }, + }; + + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + + let validatedArgs: any[] = []; + try { + validatedArgs = getValidatedArgs(args, request, response); + + const controller = new HumanController(); + + const promise = controller.getHuman.apply(controller, validatedArgs as any); + promiseHandler(controller, promise, response, 200, next); + } catch (err) { + return next(err); + } + }, + ); + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + app.post( + '/v1/humans', + authenticateMiddleware([{ api_key: [] }]), + ...fetchMiddlewares(HumanController), + ...fetchMiddlewares(HumanController.prototype.createHuman), + + function HumanController_createHuman(request: any, response: any, next: any) { + const args = { + createHumanRequest: { in: 'body', name: 'createHumanRequest', required: true, ref: 'CreateHumanRequest' }, + }; + + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa + + let validatedArgs: any[] = []; + try { + validatedArgs = getValidatedArgs(args, request, response); + + const controller = new HumanController(); + + const promise = controller.createHuman.apply(controller, validatedArgs as any); + promiseHandler(controller, promise, response, 201, next); + } catch (err) { + return next(err); + } + }, + ); + // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa app.get( '/v1/pets', authenticateMiddleware([{ api_key: [] }]),