n8n
Version:
n8n Workflow Automation Tool
173 lines • 7.13 kB
JavaScript
;
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