UNPKG

n8n

Version:

n8n Workflow Automation Tool

173 lines • 7.13 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getSharedResponseSchemas = getSharedResponseSchemas; exports.buildRequestBodyJsonSchema = buildRequestBodyJsonSchema; exports.getDecoratorGeneratedOperations = getDecoratorGeneratedOperations; require("./zod-extend"); require("../controllers"); const zod_to_openapi_1 = require("@asteasolutions/zod-to-openapi"); const is_record_1 = require("@n8n/utils/is-record"); const n8n_workflow_1 = require("n8n-workflow"); const zod_1 = require("zod"); const public_api_route_resolver_1 = require("../../../public-api/public-api-route-resolver"); const REQUEST_BODY_COMPONENT = 'RequestBody'; const SHARED_PAGINATION_PARAMS = { limit: { $ref: '../../../../shared/spec/parameters/limit.yml' }, cursor: { $ref: '../../../../shared/spec/parameters/cursor.yml' }, offset: { $ref: '../../../../shared/spec/parameters/offset.yml' }, }; const ERROR_RESPONSE_REFS = { 400: { $ref: '../../../../shared/spec/responses/badRequest.yml' }, 401: { $ref: '../../../../shared/spec/responses/unauthorized.yml' }, 402: { $ref: '../../../../shared/spec/responses/paymentRequired.yml' }, 403: { $ref: '../../../../shared/spec/responses/forbidden.yml' }, 404: { $ref: '../../../../shared/spec/responses/notFound.yml' }, 409: { $ref: '../../../../shared/spec/responses/conflict.yml' }, }; function hasNamedSchema(dto) { if (dto === null || (typeof dto !== 'object' && typeof dto !== 'function')) { return false; } return 'schema' in dto && 'name' in dto && typeof dto.name === 'string'; } const inlineResolver = (_dto, schema) => schema; function getSharedResponseSchemas() { const seen = new Set(); const shared = new Map(); for (const route of (0, public_api_route_resolver_1.resolvePublicApiRoutes)()) { if (!route.responseDto || !hasNamedSchema(route.responseDto)) { continue; } const dto = route.responseDto; if (seen.has(dto) && !shared.has(dto)) { shared.set(dto, dto.schema); } else { seen.add(dto); } } return shared; } function buildQueryConfig(route) { if (!route.requestQueryDto) return {}; const shape = route.requestQueryDto.schema.shape; const generatedShape = {}; const parameters = []; for (const [key, fieldSchema] of Object.entries(shape)) { const sharedParam = SHARED_PAGINATION_PARAMS[key]; if (sharedParam) { parameters.push(sharedParam); continue; } generatedShape[key] = fieldSchema; } return { parameters: parameters.length ? parameters : undefined, requestQuery: Object.keys(generatedShape).length ? zod_1.z.object(generatedShape) : undefined, }; } function buildPathParams(route) { const shape = {}; for (const arg of route.args) { if (arg.type === 'param') shape[arg.key] = zod_1.z.string(); } return Object.keys(shape).length ? zod_1.z.object(shape) : undefined; } function buildRequestBody(route) { if (!route.requestBodyDto) return undefined; return { content: { 'application/json': { schema: route.requestBodyDto.schema, }, }, }; } function buildRequestBodyJsonSchema(route) { if (!route.requestBodyDto) return undefined; const registry = new zod_to_openapi_1.OpenAPIRegistry(); registry.register(REQUEST_BODY_COMPONENT, route.requestBodyDto.schema); const { components } = new zod_to_openapi_1.OpenApiGeneratorV3(registry.definitions).generateComponents(); const schema = components?.schemas?.[REQUEST_BODY_COMPONENT]; return (0, is_record_1.isRecord)(schema) ? schema : undefined; } function buildResponses(route, resolveSchema) { const responses = { [route.successStatus]: { description: 'Operation successful.', ...(route.responseDto && hasNamedSchema(route.responseDto) ? { content: { 'application/json': { schema: resolveSchema(route.responseDto, route.responseDto.schema), }, }, } : {}), }, }; if (route.requestBodyDto ?? route.requestQueryDto) { responses[400] = ERROR_RESPONSE_REFS[400]; } responses[401] = ERROR_RESPONSE_REFS[401]; if (route.apiKeyScope) { responses[403] = ERROR_RESPONSE_REFS[403]; } for (const status of route.errorResponses ?? []) { const ref = ERROR_RESPONSE_REFS[status]; if (!ref) { throw new n8n_workflow_1.UnexpectedError(`@ApiErrorResponse(${status}) on ${route.controllerName}.${route.handlerName} has no ` + 'matching shared response file in ERROR_RESPONSE_REFS - add one to shared/spec/responses/ ' + 'and register it there.'); } responses[status] = ref; } return responses; } function getDecoratorGeneratedOperations(resolveSchema = inlineResolver) { const publicApiRoutes = (0, public_api_route_resolver_1.resolvePublicApiRoutes)(); return publicApiRoutes.map((route) => { const pathKey = (0, public_api_route_resolver_1.toOpenApiPathTemplate)(route.path); const { parameters, requestQuery } = buildQueryConfig(route); const requestParams = buildPathParams(route); const requestBody = buildRequestBody(route); const resource = route.path.split('/').find(Boolean) ?? 'root'; const config = { method: route.method, path: pathKey, operationId: route.handlerName, ...(route.tags?.length ? { tags: route.tags } : {}), ...(route.summary ? { summary: route.summary } : {}), ...(route.description ? { description: route.description } : {}), ...(route.apiKeyScope ? { 'x-required-scope': (0, public_api_route_resolver_1.scopeRequirementToString)(route.apiKeyScope) } : {}), ...(route.deprecated ? { deprecated: true } : {}), ...(parameters ? { parameters } : {}), ...((requestParams ?? requestQuery ?? requestBody) ? { request: { ...(requestParams ? { params: requestParams } : {}), ...(requestQuery ? { query: requestQuery } : {}), ...(requestBody ? { body: requestBody } : {}), }, } : {}), responses: buildResponses(route, resolveSchema), 'x-eov-operation-id': 'unreachable', 'x-eov-operation-handler': 'v1/handlers/decorator-routed.handler', 'x-decorator-routed': true, }; return { outputPath: `handlers/${resource}/spec/paths/${route.handlerName}.generated.yml`, pathKey, method: route.method, config, }; }); } //# sourceMappingURL=decorator-routes.js.map