UNPKG

n8n

Version:

n8n Workflow Automation Tool

179 lines • 7.91 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.buildDiscoverResponse = buildDiscoverResponse; exports._resetCache = _resetCache; const json_schema_ref_parser_1 = __importDefault(require("@apidevtools/json-schema-ref-parser")); const is_record_1 = require("@n8n/utils/is-record"); const path_1 = __importDefault(require("path")); const public_api_route_resolver_1 = require("../../../public-api-route-resolver"); const decorator_routes_1 = require("../../openapi-gen/decorator-routes"); const public_api_scope_lookup_1 = require("../../shared/public-api-scope-lookup"); require("../../controllers"); let cachedEndpointsPromise; function extractRequestSchema(operation) { if (!(0, is_record_1.isRecord)(operation.requestBody)) return undefined; const content = operation.requestBody.content; if (!(0, is_record_1.isRecord)(content)) return undefined; const json = content['application/json']; if (!(0, is_record_1.isRecord)(json)) return undefined; const schema = json.schema; return (0, is_record_1.isRecord)(schema) ? schema : undefined; } async function parseEndpointsFromSpec() { cachedEndpointsPromise ??= buildAllEndpoints(); return await cachedEndpointsPromise; } async function buildAllEndpoints() { return [...(await buildEovEndpoints()), ...buildDecoratorEndpoints()]; } async function buildEovEndpoints() { const specPath = path_1.default.join(__dirname, '..', '..', 'openapi.yml'); const publicApiRoot = path_1.default.join(__dirname, '..', '..', '..'); const spec = await json_schema_ref_parser_1.default.dereference(specPath); if (!(0, is_record_1.isRecord)(spec) || !(0, is_record_1.isRecord)(spec.paths)) return []; const endpoints = []; const handlerCache = new Map(); for (const [pathKey, pathValue] of Object.entries(spec.paths)) { if (!(0, is_record_1.isRecord)(pathValue)) continue; for (const method of public_api_route_resolver_1.HTTP_METHODS) { const operation = pathValue[method]; if (!(0, is_record_1.isRecord)(operation)) continue; if (operation['x-decorator-routed'] === true) continue; const operationId = operation['x-eov-operation-id']; const handlerPath = operation['x-eov-operation-handler']; if (typeof operationId !== 'string' || typeof handlerPath !== 'string') continue; const tags = Array.isArray(operation.tags) ? operation.tags : []; const tag = typeof tags[0] === 'string' ? tags[0] : 'Other'; let handlerModule = handlerCache.get(handlerPath); if (!handlerModule) { try { const fullHandlerPath = path_1.default.join(publicApiRoot, `${handlerPath}.js`); const imported = await import(fullHandlerPath); if (!(0, is_record_1.isRecord)(imported)) continue; const loaded = (0, is_record_1.isRecord)(imported.default) ? imported.default : imported; if (!(0, is_record_1.isRecord)(loaded)) continue; handlerModule = loaded; handlerCache.set(handlerPath, handlerModule); } catch { continue; } } const middlewareChain = handlerModule[operationId]; const scope = Array.isArray(middlewareChain) ? (0, public_api_scope_lookup_1.extractScopeFromEovHandlerChain)(middlewareChain) : undefined; endpoints.push({ method: method.toUpperCase(), path: `/api/v1${pathKey}`, operationId, tag, scope: scope ? (0, public_api_route_resolver_1.scopeRequirementFromString)(scope) : null, requestSchema: extractRequestSchema(operation), }); } } return endpoints; } function buildDecoratorEndpoints() { return (0, public_api_route_resolver_1.resolvePublicApiRoutes)().map((route) => ({ method: route.method.toUpperCase(), path: `/api/v1${(0, public_api_route_resolver_1.toOpenApiPathTemplate)(route.path)}`, operationId: route.handlerName, tag: route.tags?.[0] ?? 'Other', scope: route.apiKeyScope ?? null, requestSchema: (0, decorator_routes_1.buildRequestBodyJsonSchema)(route), })); } async function buildDiscoverResponse(callerScopes, options) { const allEndpoints = await parseEndpointsFromSpec(); const includeSchemas = options?.includeSchemas === true; const filtered = allEndpoints.filter((ep) => ep.scope === null || (0, public_api_route_resolver_1.apiKeyScopesSatisfy)(callerScopes, ep.scope)); const operationsOf = (scope) => scope === null ? [] : (0, public_api_route_resolver_1.scopesInRequirement)(scope) .map((s) => s.split(':')[1]) .filter((operation) => Boolean(operation)); const resources = {}; for (const ep of filtered) { const resourceKey = ep.tag.toLowerCase(); if (!resources[resourceKey]) { resources[resourceKey] = { operations: [], endpoints: [] }; } const entry = { method: ep.method, path: ep.path, operationId: ep.operationId, }; if (includeSchemas && ep.requestSchema) { entry.requestSchema = ep.requestSchema; } resources[resourceKey].endpoints.push(entry); for (const operation of operationsOf(ep.scope)) { if (!resources[resourceKey].operations.includes(operation)) { resources[resourceKey].operations.push(operation); } } } const resourceFilter = options?.resource?.toLowerCase(); const operationFilter = options?.operation?.toLowerCase(); let filteredResources = resources; if (resourceFilter) { const match = filteredResources[resourceFilter]; filteredResources = match ? { [resourceFilter]: match } : {}; } if (operationFilter) { const operationsByOperationId = new Map(filtered.map((f) => [ f.operationId, new Set(operationsOf(f.scope).map((operation) => operation.toLowerCase())), ])); const result = {}; for (const [key, info] of Object.entries(filteredResources)) { const matchingEndpoints = info.endpoints.filter((ep) => operationsByOperationId.get(ep.operationId)?.has(operationFilter)); if (matchingEndpoints.length > 0) { result[key] = { operations: info.operations.filter((o) => o.toLowerCase() === operationFilter), endpoints: matchingEndpoints, }; } } filteredResources = result; } const allOperations = [...new Set(Object.values(resources).flatMap((r) => r.operations))]; return { scopes: callerScopes, resources: filteredResources, filters: { resource: { description: 'Filter to a specific resource', values: Object.keys(resources), }, operation: { description: 'Filter to a specific operation', values: allOperations, }, include: { description: 'Include additional data', values: ['schemas'], }, }, specUrl: '/api/v1/openapi.yml', }; } function _resetCache() { cachedEndpointsPromise = undefined; } //# sourceMappingURL=discover.service.js.map