diff --git a/aspida.config.js b/aspida.config.js index 09d2f39e..616530bf 100644 --- a/aspida.config.js +++ b/aspida.config.js @@ -54,6 +54,11 @@ module.exports = [ outputEachDir: true, openapi: { inputFile: 'samples/path.yml' }, }, + { + input: 'samples/headers', + outputEachDir: true, + openapi: { inputFile: 'samples/headers.yml' }, + }, // { // input: 'samples/path-at-mark', // outputEachDir: true, diff --git a/samples/headers.yml b/samples/headers.yml new file mode 100644 index 00000000..a9644a59 --- /dev/null +++ b/samples/headers.yml @@ -0,0 +1,33 @@ +openapi: 3.0.0 +info: + version: 1.0.0 + title: Sample +paths: + /ping: + get: + responses: + '200': + description: OK + headers: + X-Simple: + $ref: '#/components/headers/X-Simple' + X-Description: + $ref: '#/components/headers/X-Description' + X-Ref: + $ref: '#/components/headers/X-Ref' + content: + application/json: + schema: + type: string + example: pong +components: + headers: + X-Simple: + schema: + type: string + X-Description: + schema: + type: integer + description: This header has a description. + X-Ref: + $ref: '#/components/headers/X-Simple' diff --git a/samples/headers/$api.ts b/samples/headers/$api.ts new file mode 100644 index 00000000..dfba460a --- /dev/null +++ b/samples/headers/$api.ts @@ -0,0 +1,27 @@ +import type { AspidaClient } from 'aspida'; +import type { Methods as Methods0 } from './ping'; + +const api = ({ baseURL, fetch }: AspidaClient) => { + const prefix = (baseURL === undefined ? '' : baseURL).replace(/\/$/, ''); + const PATH0 = '/ping'; + const GET = 'GET'; + + return { + ping: { + /** + * @returns OK + */ + get: (option?: { config?: T | undefined } | undefined) => + fetch(prefix, PATH0, GET, option).text(), + /** + * @returns OK + */ + $get: (option?: { config?: T | undefined } | undefined) => + fetch(prefix, PATH0, GET, option).text().then(r => r.body), + $path: () => `${prefix}${PATH0}`, + }, + }; +}; + +export type ApiInstance = ReturnType; +export default api; diff --git a/samples/headers/@types/index.ts b/samples/headers/@types/index.ts new file mode 100644 index 00000000..b5f363d3 --- /dev/null +++ b/samples/headers/@types/index.ts @@ -0,0 +1,7 @@ +/* eslint-disable */ +export type X_Simple = string + +/** This header has a description. */ +export type X_Description = number + +export type X_Ref = X_Simple diff --git a/samples/headers/ping/$api.ts b/samples/headers/ping/$api.ts new file mode 100644 index 00000000..c2674312 --- /dev/null +++ b/samples/headers/ping/$api.ts @@ -0,0 +1,25 @@ +import type { AspidaClient } from 'aspida'; +import type { Methods as Methods0 } from '.'; + +const api = ({ baseURL, fetch }: AspidaClient) => { + const prefix = (baseURL === undefined ? '' : baseURL).replace(/\/$/, ''); + const PATH0 = '/ping'; + const GET = 'GET'; + + return { + /** + * @returns OK + */ + get: (option?: { config?: T | undefined } | undefined) => + fetch(prefix, PATH0, GET, option).text(), + /** + * @returns OK + */ + $get: (option?: { config?: T | undefined } | undefined) => + fetch(prefix, PATH0, GET, option).text().then(r => r.body), + $path: () => `${prefix}${PATH0}`, + }; +}; + +export type ApiInstance = ReturnType; +export default api; diff --git a/samples/headers/ping/index.ts b/samples/headers/ping/index.ts new file mode 100644 index 00000000..05689ab8 --- /dev/null +++ b/samples/headers/ping/index.ts @@ -0,0 +1,16 @@ +/* eslint-disable */ +import type * as Types from '../@types' + +export type Methods = { + get: { + status: 200 + /** OK */ + resBody: string + + resHeaders: { + 'X-Simple': Types.X_Simple + 'X-Description': Types.X_Description + 'X-Ref': Types.X_Ref + } + } +} diff --git a/src/buildV3.ts b/src/buildV3.ts index a109809c..ad8220e9 100644 --- a/src/buildV3.ts +++ b/src/buildV3.ts @@ -7,6 +7,7 @@ import { schema2value, } from './builderUtils/converters'; import getDirName from './builderUtils/getDirName'; +import headers2Props from './builderUtils/headers2Props'; import parameters2Props from './builderUtils/parameters2Props'; import type { Prop, PropValue } from './builderUtils/props2String'; import { description2Doc, props2String, value2String } from './builderUtils/props2String'; @@ -28,6 +29,7 @@ export default (openapi: OpenAPIV3.Document) => { const parameters = parameters2Props(openapi.components?.parameters, openapi, false) || []; const requestBodies = requestBodies2Props(openapi.components?.requestBodies) || []; const responses = responses2Props(openapi.components?.responses) || []; + const headers = headers2Props(openapi.components?.headers) || []; files.push( ...Object.keys(openapi.paths) @@ -371,7 +373,7 @@ export default (openapi: OpenAPIV3.Document) => { ); const typesText = - parameters.length + schemas.length + requestBodies.length + responses.length + parameters.length + schemas.length + requestBodies.length + responses.length + headers.length ? [ ...parameters.map(p => ({ name: p.name, @@ -399,6 +401,14 @@ export default (openapi: OpenAPIV3.Document) => { ? r.value : value2String(r.value, '').replace(/\n {2}/g, '\n'), })), + ...headers.map(h => ({ + name: h.name, + description: typeof h.value === 'string' ? null : h.value.description, + text: + typeof h.value === 'string' + ? h.value + : value2String(h.value, '').replace(/\n {2}/g, '\n'), + })), ] .map(p => `\n${description2Doc(p.description, '')}export type ${p.name} = ${p.text}\n`) .join('') diff --git a/src/builderUtils/headers2Props.ts b/src/builderUtils/headers2Props.ts new file mode 100644 index 00000000..9a47915b --- /dev/null +++ b/src/builderUtils/headers2Props.ts @@ -0,0 +1,24 @@ +import type { OpenAPIV3 } from 'openapi-types'; +import { $ref2Type, defKey2defName, isRefObject, schema2value } from './converters'; +import type { PropValue } from './props2String'; + +export type Header = { name: string; value: string | PropValue }; + +export default (headers: OpenAPIV3.ComponentsObject['headers']) => + headers && + Object.keys(headers) + .map(defKey => { + const target = headers[defKey]; + let value: Header['value']; + + if (isRefObject(target)) { + value = $ref2Type(target.$ref); + } else { + const result = schema2value(target.schema, false); + if (!result) return null; + value = { ...result, description: target.description ?? null }; + } + + return { name: defKey2defName(defKey), value }; + }) + .filter((v): v is Header => !!v);