/** * Creates sentence cased string * @param string {string | undefined} * @returns {string} */ export function capitalize(string) { return `${string?.charAt(0).toUpperCase()}${string?.slice(1).toLowerCase()}`; } /** * Generate documentation snippet for one route * @typedef {import('../types.js').RouteMeta} RouteMeta * * @param routeMeta {RouteMeta} A route instance with its meta information * @param schemaName {string} Path to directory that will contain relevant schemas for the route * @param tagsToKeep {string[]} Tags / keywords that can be used for grouping routes * @returns {import('openapi-types').OpenAPIV3.PathItemObject} */ export function generateOpenAPIForRoute({route, description, tags}, schemaName, tagsToKeep) { /** @type {(name: string) => ({$ref: string})} */ const schema = name => ({$ref: `./${schemaName}#/definitions/${name}`}); /** @type {import('openapi-types').OpenAPIV3.ResponsesObject} */ const responses = Object.fromEntries( route.instance.errorNames .map(RouteError => new RouteError()) .map(error => [ error.statusCode, { description: error.message ?? capitalize(error.name.replaceAll(/([A-Z][a-z])/g, ' $1').replace('SC ', '')), content: { 'application/json': { schema: schema(error.name), }, }, }, ]), ); /** @type {import('openapi-types').OpenAPIV3.ParameterObject[]} */ const parameters = Object.entries(route.instance.obligatoryParameters ?? {}).map( ([parameter, schemaDefinition]) => ({ in: 'path', name: parameter, required: true, schema: schema(schemaDefinition), }), ); /** @type {import('openapi-types').OpenAPIV3.OperationObject} */ const operation = { summary: capitalize(description?.replace(/(Route to |Route for )/gim, '')), requestBody: { description: route.responseBodyDescription ?? undefined, content: { 'application/json': { schema: schema(route.instance.requestBodyName), }, }, }, parameters: [ { name: 'X-StApps-Version', in: 'header', schema: { type: 'string', example: '2.0.0', }, required: true, }, ...parameters, ], responses: { 200: { description: route.responseBodyDescription, content: { 'application/json': { schema: schema(route.instance.responseBodyName), }, }, }, ...responses, }, tags: tags?.filter(value => tagsToKeep.includes(value)), }; return {[route.instance.method.toLowerCase()]: operation}; }