From 404947a6caf3f8eb125c3d7ce17cb6f17cdce231 Mon Sep 17 00:00:00 2001 From: gamoutatsumi <47162587+gamoutatsumi@users.noreply.github.com> Date: Thu, 17 Nov 2022 14:51:09 +0900 Subject: [PATCH 1/2] Add support for #/components/responses --- src/buildV3.ts | 12 +++++++++++- src/builderUtils/responses2Props.ts | 30 +++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 src/builderUtils/responses2Props.ts diff --git a/src/buildV3.ts b/src/buildV3.ts index 7c366817..dd8cd824 100644 --- a/src/buildV3.ts +++ b/src/buildV3.ts @@ -18,6 +18,7 @@ import getDirName from './builderUtils/getDirName' import schemas2Props from './builderUtils/schemas2Props' import parameters2Props from './builderUtils/parameters2Props' import requestBodies2Props from './builderUtils/requestBodies2Props' +import responses2Props from './builderUtils/responses2Props' const methodNames = ['get', 'post', 'put', 'delete', 'head', 'options', 'patch'] as const @@ -31,6 +32,7 @@ export default (openapi: OpenAPIV3.Document) => { const schemas = schemas2Props(openapi.components?.schemas, openapi) || [] const parameters = parameters2Props(openapi.components?.parameters, openapi, false) || [] const requestBodies = requestBodies2Props(openapi.components?.requestBodies) || [] + const responses = responses2Props(openapi.components?.responses) || [] files.push( ...Object.keys(openapi.paths) @@ -368,7 +370,7 @@ export default (openapi: OpenAPIV3.Document) => { ) const typesText = - parameters.length + schemas.length + requestBodies.length + parameters.length + schemas.length + requestBodies.length + responses.length ? [ ...parameters.map(p => ({ name: p.name, @@ -387,6 +389,14 @@ export default (openapi: OpenAPIV3.Document) => { typeof r.value === 'string' ? r.value : value2String(r.value, '').replace(/\n {2}/g, '\n') + })), + ...responses.map(r => ({ + name: r.name, + description: null, + text: + typeof r.value === 'string' + ? r.value + : value2String(r.value, '').replace(/\n {2}/g, '\n') })) ] .map(p => `\n${description2Doc(p.description, '')}export type ${p.name} = ${p.text}\n`) diff --git a/src/builderUtils/responses2Props.ts b/src/builderUtils/responses2Props.ts new file mode 100644 index 00000000..43ed9f4f --- /dev/null +++ b/src/builderUtils/responses2Props.ts @@ -0,0 +1,30 @@ +import { OpenAPIV3 } from 'openapi-types' +import { isRefObject, defKey2defName, $ref2Type, schema2value } from './converters' +import { PropValue } from './props2String' + +export type Response = { name: string; value: string | PropValue } + +export default (bodies: OpenAPIV3.ComponentsObject['responses']) => + bodies && + Object.keys(bodies) + .map(defKey => { + const target = bodies[defKey] + let value: Response['value'] + + if (isRefObject(target)) { + value = $ref2Type(target.$ref) + } else { + const content = + target.content && + Object.entries(target.content).find(([key]) => key.startsWith('application/'))?.[1] + if (!content) return null + + const result = schema2value(content.schema, false) + if (!result) return null + + value = result + } + + return { name: defKey2defName(defKey), value } + }) + .filter((v): v is Response => !!v) From 76999edc6424ba930af519961b4d0441c507c1de Mon Sep 17 00:00:00 2001 From: gamoutatsumi <47162587+gamoutatsumi@users.noreply.github.com> Date: Thu, 17 Nov 2022 14:51:29 +0900 Subject: [PATCH 2/2] Add samples for #/components/responses --- aspida.config.js | 5 +++++ samples/responses.yml | 34 +++++++++++++++++++++++++++++++ samples/responses/$api.ts | 21 +++++++++++++++++++ samples/responses/@types/index.ts | 7 +++++++ samples/responses/users/$api.ts | 19 +++++++++++++++++ samples/responses/users/index.ts | 9 ++++++++ 6 files changed, 95 insertions(+) create mode 100644 samples/responses.yml create mode 100644 samples/responses/$api.ts create mode 100644 samples/responses/@types/index.ts create mode 100644 samples/responses/users/$api.ts create mode 100644 samples/responses/users/index.ts diff --git a/aspida.config.js b/aspida.config.js index d07c7f87..84ecb492 100644 --- a/aspida.config.js +++ b/aspida.config.js @@ -43,6 +43,11 @@ module.exports = [ input: 'samples/request-bodies', outputEachDir: true, openapi: { inputFile: 'samples/request-bodies.yml' } + }, + { + input: 'samples/responses', + outputEachDir: true, + openapi: { inputFile: 'samples/responses.yml' } } // { // input: 'samples/path-at-mark', diff --git a/samples/responses.yml b/samples/responses.yml new file mode 100644 index 00000000..d95ae4e4 --- /dev/null +++ b/samples/responses.yml @@ -0,0 +1,34 @@ +openapi: 3.0.0 +info: + version: 1.0.0 + title: Sample +paths: + /users: + get: + summary: get users + operationId: post-users + responses: + '200': + $ref: '#/components/responses/UserResponseBody' + description: OK + description: '' +components: + schemas: + User: + type: object + properties: + id: + type: string + name: + type: string + required: + - id + - name + responses: + UserResponseBody: + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/User' diff --git a/samples/responses/$api.ts b/samples/responses/$api.ts new file mode 100644 index 00000000..2949f50f --- /dev/null +++ b/samples/responses/$api.ts @@ -0,0 +1,21 @@ +import type { AspidaClient, BasicHeaders } from 'aspida' +import type { Methods as Methods0 } from './users' + +const api = ({ baseURL, fetch }: AspidaClient) => { + const prefix = (baseURL === undefined ? '' : baseURL).replace(/\/$/, '') + const PATH0 = '/users' + const GET = 'GET' + + return { + users: { + get: (option?: { config?: T | undefined } | undefined) => + fetch(prefix, PATH0, GET, option).json(), + $get: (option?: { config?: T | undefined } | undefined) => + fetch(prefix, PATH0, GET, option).json().then(r => r.body), + $path: () => `${prefix}${PATH0}` + } + } +} + +export type ApiInstance = ReturnType +export default api diff --git a/samples/responses/@types/index.ts b/samples/responses/@types/index.ts new file mode 100644 index 00000000..0202ef61 --- /dev/null +++ b/samples/responses/@types/index.ts @@ -0,0 +1,7 @@ +/* eslint-disable */ +export type User = { + id: string + name: string +} + +export type UserResponseBody = User[] diff --git a/samples/responses/users/$api.ts b/samples/responses/users/$api.ts new file mode 100644 index 00000000..ff136d9f --- /dev/null +++ b/samples/responses/users/$api.ts @@ -0,0 +1,19 @@ +import type { AspidaClient, BasicHeaders } from 'aspida' +import type { Methods as Methods0 } from '.' + +const api = ({ baseURL, fetch }: AspidaClient) => { + const prefix = (baseURL === undefined ? '' : baseURL).replace(/\/$/, '') + const PATH0 = '/users' + const GET = 'GET' + + return { + get: (option?: { config?: T | undefined } | undefined) => + fetch(prefix, PATH0, GET, option).json(), + $get: (option?: { config?: T | undefined } | undefined) => + fetch(prefix, PATH0, GET, option).json().then(r => r.body), + $path: () => `${prefix}${PATH0}` + } +} + +export type ApiInstance = ReturnType +export default api diff --git a/samples/responses/users/index.ts b/samples/responses/users/index.ts new file mode 100644 index 00000000..4700da19 --- /dev/null +++ b/samples/responses/users/index.ts @@ -0,0 +1,9 @@ +/* eslint-disable */ +import type * as Types from '../@types' + +export type Methods = { + get: { + status: 200 + resBody: Types.User[] + } +}