@rxap/n8n-utilities
Version:
This package provides utility functions and classes for n8n nodes, including custom authentication methods (Bearer Auth, Oauth2 Proxy Auth, Base URL), a decorator for capturing execution errors, and base classes for creating nodes from OpenAPI specificati
933 lines • 43 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.OpenApiNode = void 0;
exports.ResolveRef = ResolveRef;
const tslib_1 = require("tslib");
const capture_execution_error_decorator_1 = require("./capture-execution-error.decorator");
const utilities_1 = require("@rxap/utilities");
const FormData = require("form-data");
const fs_1 = require("fs");
const n8n_workflow_1 = require("n8n-workflow");
const path_1 = require("path");
const uuid_1 = require("uuid");
function isParameterObject(parameter) {
return (parameter === null || parameter === void 0 ? void 0 : parameter.in) !== undefined && (parameter === null || parameter === void 0 ? void 0 : parameter.name) !== undefined;
}
function isSchemaObject(schema) {
return (schema === null || schema === void 0 ? void 0 : schema.type) !== undefined;
}
function isRequestBodyObject(requestBody) {
return (requestBody === null || requestBody === void 0 ? void 0 : requestBody.content) !== undefined;
}
function hasQueryParameters(operation) {
var _a, _b;
return (_b = (_a = operation.parameters) === null || _a === void 0 ? void 0 : _a.filter(isParameterObject).some((parameter) => parameter.in === 'query')) !== null && _b !== void 0 ? _b : false;
}
function hasPathParameters(operation) {
var _a, _b;
return (_b = (_a = operation.parameters) === null || _a === void 0 ? void 0 : _a.filter(isParameterObject).some((parameter) => parameter.in === 'path')) !== null && _b !== void 0 ? _b : false;
}
function hasHeaderParameters(operation) {
var _a, _b;
return (_b = (_a = operation.parameters) === null || _a === void 0 ? void 0 : _a.filter(isParameterObject).some((parameter) => parameter.in === 'header')) !== null && _b !== void 0 ? _b : false;
}
function ResolveRef(openApiSpec, node, parent, key) {
var _a;
if (typeof node !== 'object' || node === null) {
return;
}
if (node['$ref']) {
if (node.$ref.startsWith('#/components/schemas')) {
const name = node.$ref.replace('#/components/schemas/', '');
if (parent && key && ((_a = openApiSpec.components) === null || _a === void 0 ? void 0 : _a.schemas)) {
parent[key] = openApiSpec.components.schemas[name];
ResolveRef(openApiSpec, parent[key], parent, key);
}
}
}
else {
for (const [k, v] of Object.entries(node)) {
ResolveRef(openApiSpec, v, node, k);
}
}
}
class OpenApiNode {
constructor(openApiFilePath, description = {}, nameSuffix = '') {
var _a, _b, _c, _d;
this.openApiFilePath = openApiFilePath;
this.openapi = this.loadOpenApiFile();
ResolveRef(this.openapi, this.openapi.paths);
this.operations = this.extractOperationList(this.openapi);
this.tags = this.extractTags(this.operations);
this.operationMethods = this.extractMethods(this.operations);
// use || instead of ?? to ensure an empty string is also replaced
let name = description.name || (0, utilities_1.dasherize)(((_a = this.openapi.info) === null || _a === void 0 ? void 0 : _a.title) || this.constructor.name);
if (nameSuffix) {
name += `-${nameSuffix}`;
}
this.description = Object.assign(Object.assign({ version: 1,
// use || instead of ?? to ensure an empty string is also replaced
description: ((_b = this.openapi.info) === null || _b === void 0 ? void 0 : _b.description) || (0, utilities_1.capitalize)(this.constructor.name), defaults: {
name
}, inputs: ["main" /* NodeConnectionType.Main */], outputs: ["main" /* NodeConnectionType.Main */], subtitle: '={{ "[" + $parameter["method"].toUpperCase() + "] " + $parameter["operation"] + ": " + $parameter["tag"] }}',
// use || instead of ?? to ensure an empty string is also replaced
displayName: ((_c = this.openapi.info) === null || _c === void 0 ? void 0 : _c.title) || (0, utilities_1.capitalize)(this.constructor.name), group: ['input'], credentials: [
{
displayName: 'Bearer Auth',
name: 'httpBearerAuth',
required: false,
displayOptions: {
show: {
authentication: ['httpBearerAuth'],
},
},
},
{
displayName: 'OAuth 2 Proxy Auth',
name: 'oauth2ProxyAuth',
required: false,
displayOptions: {
show: {
authentication: ['oauth2ProxyAuth'],
},
},
},
{
displayName: 'Basic Auth',
name: 'httpBasicAuth',
required: false,
displayOptions: {
show: {
authentication: ['httpBasicAuth'],
},
},
},
{
displayName: 'Custom Auth',
name: 'httpCustomAuth',
required: false,
displayOptions: {
show: {
authentication: ['httpCustomAuth'],
},
},
},
{
displayName: 'Digest Auth',
name: 'httpDigestAuth',
required: false,
displayOptions: {
show: {
authentication: ['httpDigestAuth'],
},
},
},
{
displayName: 'Header Auth',
name: 'httpHeaderAuth',
required: false,
displayOptions: {
show: {
authentication: ['httpHeaderAuth'],
},
},
},
], icon: this.getIcon() }, (0, utilities_1.DeleteUndefinedProperties)(description)), { properties: (_d = description.properties) !== null && _d !== void 0 ? _d : [], name });
this.populateDescription();
}
execute() {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
var _a, _b, _c, _d, _e, _f;
const items = this.getInputData();
const results = Array(items.length).fill(null);
for (let i = 0; i < items.length; i++) {
const item = items[i];
const skipAuthentication = this.getNodeParameter('skipAuthentication', i);
// eslint-disable-next-line no-inner-declarations
function isEmpty(value) {
return !value && value !== 0 && value !== false;
}
const requestOptions = {
baseURL: this.getNodeParameter('baseUrl', i),
method: this.getNodeParameter('method', i).toUpperCase(),
url: this.getNodeParameter('path', i),
};
const operationParameters = this.getNodeParameter('parameterSchema', i, []);
const operationRequestBody = this.getNodeParameter('requestBodySchema', i, {});
// region build parameters
if (operationParameters === null || operationParameters === void 0 ? void 0 : operationParameters.some((p) => isParameterObject(p) && p.in === 'path')) {
const pathParameters = this.getNodeParameter('pathParameters', i);
requestOptions.url = requestOptions.url.replace(/{([^}]+)}/g, (_, name) => {
if (isEmpty(pathParameters[name])) {
throw new Error(`The path parameter "${name}" is missing.`);
}
return encodeURIComponent(pathParameters[name]);
});
}
if (operationParameters === null || operationParameters === void 0 ? void 0 : operationParameters.some((p) => isParameterObject(p) && p.in === 'query')) {
(_a = requestOptions.qs) !== null && _a !== void 0 ? _a : (requestOptions.qs = {});
const queryParameters = this.getNodeParameter('queryParameters', i);
for (const queryParam of operationParameters.filter(isParameterObject).filter((p) => p.in === 'query')) {
if (isEmpty(queryParameters[queryParam.name])) {
if (queryParam.required) {
throw new Error(`The query parameter "${queryParam.name}" is missing.`);
}
}
else {
requestOptions.qs[queryParam.name] = encodeURIComponent(queryParameters[queryParam.name]);
}
}
}
if (operationParameters === null || operationParameters === void 0 ? void 0 : operationParameters.some((p) => isParameterObject(p) && p.in === 'header')) {
(_b = requestOptions.headers) !== null && _b !== void 0 ? _b : (requestOptions.headers = {});
const headerParameters = this.getNodeParameter('headerParameters', i);
for (const headerParam of operationParameters.filter(isParameterObject).filter((p) => p.in === 'header')) {
if (isEmpty(headerParameters[headerParam.name])) {
if (headerParam.required) {
throw new Error(`The header parameter "${headerParam.name}" is missing.`);
}
}
else {
requestOptions.headers[headerParam.name] = headerParameters[headerParam.name];
}
}
}
// endregion
// region build request body
if (isRequestBodyObject(operationRequestBody)) {
const mediaType = this.getNodeParameter('mediaType', i);
const requestBody = this.getNodeParameter('requestBody', i);
switch (mediaType) {
default:
case 'application/json':
requestOptions.body = requestBody;
break;
case 'plain/text':
requestOptions.body = JSON.stringify(requestBody);
break;
case 'multipart/form-data':
// eslint-disable-next-line no-case-declarations
const schema = operationRequestBody.content['multipart/form-data'].schema;
if (isSchemaObject(schema)) {
const formData = requestOptions.formData = new FormData();
for (const key of Object.keys((_c = schema.properties) !== null && _c !== void 0 ? _c : {})) {
const prop = (_d = schema.properties) === null || _d === void 0 ? void 0 : _d[key];
if (isSchemaObject(prop)) {
if (isEmpty(requestBody[key])) {
throw new Error(`The multipart form data parameter "${key}" is missing.`);
}
if (prop.format === 'binary') {
const data = (_e = item.binary) === null || _e === void 0 ? void 0 : _e[requestBody[key]];
if (!data) {
throw new Error(`The binary data for the multipart form data parameter "${key}" is missing.`);
}
const buffer = yield this.helpers.getBinaryDataBuffer(i, requestBody[key]);
const options = (0, utilities_1.DeleteUndefinedProperties)({ filename: data.fileName, filepath: data.directory, contentType: data.mimeType });
if (!buffer) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'No binary data found.');
}
if (!options.filename) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `The filename for the binary data '${key}' is missing.`);
}
formData.append(key, buffer, options);
}
else {
formData.append(key, requestBody[key]);
}
}
}
}
else {
requestOptions.formData = requestBody;
}
break;
}
(_f = requestOptions.headers) !== null && _f !== void 0 ? _f : (requestOptions.headers = {});
requestOptions.headers['Content-Type'] = mediaType;
}
// endregion
let response;
if (skipAuthentication) {
response = yield this.helpers.request(requestOptions);
}
else {
const authentication = this.getNodeParameter('authentication', i);
if (authentication) {
const credentials = yield this.getCredentials(authentication);
if ('baseURL' in credentials && credentials['baseURL'] && typeof credentials['baseURL'] === 'string') {
requestOptions.baseURL = credentials['baseURL'];
}
if ('baseUrl' in credentials && credentials['baseUrl'] && typeof credentials['baseUrl'] === 'string') {
requestOptions.baseURL = credentials['baseUrl'];
}
this.logger.debug('Request options: ' + JSON.stringify(requestOptions, undefined, 2));
response = yield this.helpers.requestWithAuthentication.call(this, authentication, requestOptions, undefined, i);
}
else {
throw new Error('No authentication method selected.');
}
}
let data = response instanceof Buffer ? JSON.parse(response.toString()) : response;
let isJSON = false;
try {
data = JSON.parse(data);
isJSON = true;
}
catch (e) {
// ignore
}
if (isJSON) {
const spread = this.getNodeParameter('spread', i, false);
if (spread) {
let rows = [];
if (Array.isArray(data)) {
rows = data.map(item => ({ json: item }));
}
else if (Array.isArray(data.rows)) {
rows = data.rows.map((item) => ({ json: item }));
}
if (rows.length) {
results[i] = rows.shift();
results.push(...rows);
}
}
else {
results[i] = { json: data };
}
}
else {
results[i] = { json: { response: data } };
}
}
return [results.filter(Boolean)];
});
}
populateDescription() {
var _a;
var _b;
this.description.properties.unshift(...this.buildBaseUrlParameters());
(_a = (_b = this.description).credentials) !== null && _a !== void 0 ? _a : (_b.credentials = []);
this.description.properties.unshift({
displayName: 'Authentication',
name: 'authentication',
type: this.description.credentials.length > 1 ? 'options' : 'hidden',
options: this.description.credentials.map(credential => {
var _a;
return ({
value: credential.name,
name: (_a = credential.displayName) !== null && _a !== void 0 ? _a : (0, utilities_1.classify)(credential.name),
});
}),
default: this.description.credentials[0].name,
displayOptions: {
hide: {
skipAuthentication: [true],
}
},
});
this.description.properties.unshift({
displayName: 'Skip Authentication',
name: 'skipAuthentication',
type: 'boolean',
default: false,
});
this.description.properties.push(this.buildTagProperty(this.tags, this.operations.some(o => { var _a; return !((_a = o.tags) === null || _a === void 0 ? void 0 : _a.length); })));
this.description.properties.push(this.buildMethodProperty(this.operationMethods));
for (const tag of this.tags) {
const property = this.buildOperationProperty(this.operations, tag);
if (property) {
this.description.properties.push(...property);
}
}
const property = this.buildOperationProperty(this.operations.filter(o => { var _a; return !((_a = o.tags) === null || _a === void 0 ? void 0 : _a.length); }));
if (property) {
this.description.properties.push(...property);
}
for (const operation of this.operations) {
if (hasQueryParameters(operation)) {
this.description.properties.push(this.buildOperationQueryParameterProperties(operation));
}
if (hasPathParameters(operation)) {
this.description.properties.push(this.buildOperationPathParameterProperties(operation));
}
if (hasHeaderParameters(operation)) {
this.description.properties.push(this.buildOperationHeaderParameterProperties(operation));
}
if (isRequestBodyObject(operation.requestBody)) {
this.description.properties.push(...this.buildOperationRequestBodyProperties(operation));
}
}
this.buildArrayResponseHandlerProperty(this.operations);
this.buildPagedResponseHandlerProperty(this.operations);
}
buildArrayResponseHandlerProperty(operationList) {
// filter operation that have an array response
operationList = operationList.filter(operation => { var _a, _b, _c, _d; return ((_d = (_c = (_b = (_a = operation.responses['200']) === null || _a === void 0 ? void 0 : _a.content) === null || _b === void 0 ? void 0 : _b['application/json']) === null || _c === void 0 ? void 0 : _c.schema) === null || _d === void 0 ? void 0 : _d.type) === 'array'; });
return {
name: 'spread',
displayName: 'Spread Response',
default: true,
description: 'The items of the array response will be spread into individual items.',
type: 'boolean',
displayOptions: {
show: {
operation: operationList.map(o => o.operationId),
},
},
};
}
buildPagedResponseHandlerProperty(operationList) {
// filter operation that have an array response
operationList = operationList.filter(operation => {
var _a, _b, _c, _d, _e;
const schema = (_c = (_b = (_a = operation.responses['200']) === null || _a === void 0 ? void 0 : _a.content) === null || _b === void 0 ? void 0 : _b['application/json']) === null || _c === void 0 ? void 0 : _c.schema;
if ((schema === null || schema === void 0 ? void 0 : schema.type) === 'object') {
return ((_e = (_d = schema.properties) === null || _d === void 0 ? void 0 : _d.rows) === null || _e === void 0 ? void 0 : _e.type) === 'array';
}
return false;
});
return {
name: 'spread',
displayName: 'Spread Response',
default: true,
description: 'The items of the rows array response will be spread into individual items.',
type: 'boolean',
displayOptions: {
show: {
operation: operationList.map(o => o.operationId),
},
},
};
}
buildBaseUrlParameters() {
var _a, _b;
const parameters = [];
const options = (_b = (_a = this.openapi.servers) === null || _a === void 0 ? void 0 : _a.map(server => ({
name: server.description ? (0, utilities_1.capitalize)(server.description) : server.url,
value: server.url,
}))) !== null && _b !== void 0 ? _b : [];
if (options.length) {
parameters.push({
displayName: 'Base URL',
name: 'baseUrl',
type: 'options',
options,
default: options[0].value,
validateType: 'string',
});
}
else {
parameters.push({
displayName: 'Base URL',
name: 'baseUrl',
type: 'string',
default: '',
});
}
return parameters;
}
getIcon() {
const basePath = (0, path_1.dirname)(this.openApiFilePath);
if ((0, fs_1.existsSync)((0, path_1.join)(basePath, `${this.constructor.name}.png`))) {
return `file:${this.constructor.name}.png`;
}
if ((0, fs_1.existsSync)((0, path_1.join)(basePath, `${this.constructor.name}.svg`))) {
if ((0, fs_1.existsSync)((0, path_1.join)(basePath, `${this.constructor.name}.dark.svg`))) {
return { light: `file:${this.constructor.name}.svg`, dark: `file:${this.constructor.name}.dark.svg` };
}
else {
return `file:${this.constructor.name}.svg`;
}
}
if (this.constructor.name.startsWith('Service')) {
return { light: `file:${this.constructor.name}.png`, dark: `file:${this.constructor.name}.dark.png` };
}
return undefined;
}
buildApplicationJsonProperty(operation) {
const schema = operation.requestBody.content['application/json'].schema;
return Object.assign(Object.assign({}, this.buildPropertyForSchema(null, schema)), { displayName: 'Request Body', name: 'requestBody', displayOptions: {
show: {
operation: [operation.operationId],
mediaType: ['application/json'],
},
} });
}
buildPlainTextProperty(operation) {
return {
displayName: 'Request Body',
name: 'requestBody',
type: 'string',
displayOptions: {
show: {
operation: [operation.operationId],
mediaType: ['plain/text'],
},
},
typeOptions: {
rows: 4,
},
default: '',
};
}
buildMultipartFormDataProperty(operation, mediaType) {
var _a, _b, _c, _d;
const options = [];
const schema = mediaType.schema;
let defaults = {};
if (isSchemaObject(schema)) {
for (const [key, value] of Object.entries((_a = schema.properties) !== null && _a !== void 0 ? _a : {})) {
if (isSchemaObject(value)) {
options.push({
displayName: (0, utilities_1.capitalize)(key),
name: key,
type: this.schemaToNodePropertyType(value),
default: '',
});
}
}
if (Object.keys((_b = schema.properties) !== null && _b !== void 0 ? _b : {}).length === 1) {
const propertyKey = Object.keys((_c = schema.properties) !== null && _c !== void 0 ? _c : {})[0];
defaults = {
[Object.keys((_d = schema.properties) !== null && _d !== void 0 ? _d : {})[0]]: propertyKey === 'file' ? 'data' : '',
};
}
}
return {
displayName: 'Request Body',
name: 'requestBody',
type: 'collection',
displayOptions: {
show: {
operation: [operation.operationId],
mediaType: ['multipart/form-data'],
},
},
default: defaults,
options,
};
}
buildOperationRequestBodyProperties(operation) {
const mediaTypes = Object.keys(operation.requestBody.content);
const properties = [
{
displayName: 'Media Type',
name: 'mediaType',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
operation: [operation.operationId],
},
},
default: mediaTypes[0],
options: mediaTypes.map(mediaType => ({
value: mediaType,
name: mediaType,
})),
},
];
for (const mediaType of mediaTypes) {
switch (mediaType) {
case 'application/json':
properties.push(this.buildApplicationJsonProperty(operation));
break;
case 'plain/text':
properties.push(this.buildPlainTextProperty(operation));
break;
case 'multipart/form-data':
properties.push(this.buildMultipartFormDataProperty(operation, operation.requestBody.content['multipart/form-data']));
break;
default:
properties.push({
displayName: 'Request Body',
name: 'requestBody',
type: 'string',
displayOptions: {
show: {
operation: [operation.operationId],
mediaType: [mediaType],
},
},
typeOptions: {
rows: 4,
},
default: '',
});
break;
}
}
return properties;
}
// region Query, Path, and Header Parameters
schemaToNodePropertyType(schema) {
switch (schema.type) {
case 'boolean':
return 'boolean';
case 'number':
case 'integer':
return 'number';
default:
case 'string':
return 'string';
}
}
buildOperationQueryParameterProperties(operation) {
var _a, _b;
const parameters = (_a = operation.parameters) === null || _a === void 0 ? void 0 : _a.filter(isParameterObject).filter((p) => p.in === 'query');
return {
displayName: 'Query Parameters',
name: 'queryParameters',
type: 'collection',
default: parameters.filter(p => p.required).reduce((acc, p) => (Object.assign(Object.assign({}, acc), { [p.name]: null })), {}),
displayOptions: {
show: {
operation: [operation.operationId],
},
},
options: (_b = parameters.map(p => ({
displayName: (0, utilities_1.capitalize)(p.name),
name: p.name,
type: isSchemaObject(p.schema) ? this.schemaToNodePropertyType(p.schema) : 'string',
default: '',
}))) !== null && _b !== void 0 ? _b : [],
};
}
buildOperationPathParameterProperties(operation) {
var _a, _b;
const parameters = (_a = operation.parameters) === null || _a === void 0 ? void 0 : _a.filter(isParameterObject).filter((p) => p.in === 'path');
return {
displayName: 'Path Parameters',
name: 'pathParameters',
type: 'collection',
default: parameters.filter(p => p.required).reduce((acc, p) => (Object.assign(Object.assign({}, acc), { [p.name]: null })), {}),
displayOptions: {
show: {
operation: [operation.operationId],
},
},
options: (_b = parameters.map(p => ({
displayName: (0, utilities_1.capitalize)(p.name),
name: p.name,
type: isSchemaObject(p.schema) ? this.schemaToNodePropertyType(p.schema) : 'string',
default: '',
}))) !== null && _b !== void 0 ? _b : [],
};
}
// endregion
buildOperationHeaderParameterProperties(operation) {
var _a, _b;
const parameters = (_a = operation.parameters) === null || _a === void 0 ? void 0 : _a.filter(isParameterObject).filter((p) => p.in === 'header');
return {
displayName: 'Header Parameters',
name: 'headerParameters',
type: 'collection',
default: parameters.filter(p => p.required).reduce((acc, p) => (Object.assign(Object.assign({}, acc), { [p.name]: null })), {}),
displayOptions: {
show: {
operation: [operation.operationId],
},
},
options: (_b = parameters.map(p => ({
displayName: (0, utilities_1.capitalize)(p.name),
name: p.name,
type: isSchemaObject(p.schema) ? this.schemaToNodePropertyType(p.schema) : 'string',
default: '',
}))) !== null && _b !== void 0 ? _b : [],
};
}
buildOperationProperty(operationList, tag) {
// Filter the operation list by the selected tag. If no tag is selected, return the full list.
operationList = tag ?
operationList.filter(operation => { var _a; return (_a = operation.tags) === null || _a === void 0 ? void 0 : _a.includes(tag); }) :
operationList;
tag !== null && tag !== void 0 ? tag : (tag = 'any');
if (operationList.length === 0) {
return null;
}
const parameters = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
default: operationList[0].operationId,
displayOptions: {
show: {
resource: [tag],
},
},
options: operationList.map(operation => {
var _a, _b, _c;
return ({
name: (_a = operation.name) !== null && _a !== void 0 ? _a : operation.operationId,
value: operation.operationId,
description: operation.description,
action: (_b = operation.summary) !== null && _b !== void 0 ? _b : operation.operationId,
displayName: (_c = operation.summary) !== null && _c !== void 0 ? _c : operation.name,
displayOptions: {
show: {
method: [operation.method],
}
},
});
}),
}
];
for (const operation of operationList) {
parameters.push({
displayOptions: {
show: {
operation: [operation.operationId],
}
},
displayName: 'Parameter Schema',
name: 'parameterSchema',
default: operation.parameters,
type: 'hidden',
});
parameters.push({
displayOptions: {
show: {
operation: [operation.operationId],
}
},
displayName: 'Request Body Schema',
name: 'requestBodySchema',
default: operation.requestBody,
type: 'hidden',
});
parameters.push({
displayOptions: {
show: {
operation: [operation.operationId],
}
},
displayName: 'Request Path',
name: 'path',
default: operation.path,
type: 'hidden',
});
}
return parameters;
}
buildTagProperty(tags, withAnyTag) {
const options = tags.map(tag => ({
name: tag.split('-').map(utilities_1.capitalize).join(' '),
value: tag,
}));
if (withAnyTag) {
options.unshift({
name: 'Any',
value: 'any',
});
}
return {
// The displayName and name must be 'resource' so that in the n8n UI the different operations are
// shown in a grouped fashion
displayName: 'Resource',
name: 'resource',
type: 'options',
noDataExpression: true,
options,
default: withAnyTag ? 'any' : tags[0],
};
}
buildMethodProperty(methods) {
return {
displayName: 'Method',
name: 'method',
type: 'options',
noDataExpression: true,
options: methods.map(method => ({
name: method.toUpperCase(),
value: method,
})),
default: methods[0],
};
}
extractMethods(operations) {
return Array.from(new Set(operations.map(operation => operation.method)));
}
extractTags(operations) {
return Array.from(new Set(operations.flatMap(operation => { var _a; return (_a = operation.tags) !== null && _a !== void 0 ? _a : []; })));
}
extractOperationList(openapi) {
const list = [];
for (const [path, pathObj] of Object.entries(openapi.paths)) {
for (const [method, operation] of Object.entries(pathObj)) {
if (typeof operation === 'object') {
list.push(Object.assign({ path,
method }, operation));
}
}
}
return list;
}
loadOpenApiFile() {
const filePath = this.openApiFilePath;
if (!(0, fs_1.existsSync)(filePath)) {
throw new Error(`The OpenAPI file "${filePath}" does not exist.`);
}
return JSON.parse((0, fs_1.readFileSync)(filePath, 'utf8'));
}
// region schema to n8n node properties
buildPropertyForSchema(propertyName, schema) {
var _a, _b;
switch (schema.type) {
case 'string':
case 'number':
case 'boolean':
return this.buildPropertyForSchemaPrimitive(propertyName, schema);
case 'array':
return this.buildPropertyForSchemaArray(propertyName, schema);
case 'object':
return this.buildPropertyForSchemaObject(propertyName, schema);
default:
if (Object.keys((_a = schema.properties) !== null && _a !== void 0 ? _a : {}).length) {
return this.buildPropertyForSchemaObject(propertyName, schema);
}
if (schema.items) {
return this.buildPropertyForSchemaArray(propertyName, schema);
}
return {
name: propertyName ? propertyName : 'payload',
displayName: propertyName ? (_b = schema.title) !== null && _b !== void 0 ? _b : propertyName : 'Payload',
type: 'string',
default: '',
description: schema.description,
};
}
}
buildStringPropertyDefault(schema) {
var _a;
switch (schema.format) {
case 'uuid':
return (0, uuid_1.v4)();
default:
return (_a = schema.default) !== null && _a !== void 0 ? _a : '';
}
}
buildPropertyForSchemaPrimitive(propertyName, schema) {
var _a, _b, _c, _d, _e, _f, _g;
switch (schema.type) {
case 'string':
if (schema.enum) {
return {
name: propertyName ? propertyName : 'payload',
displayName: propertyName ? (_a = schema.title) !== null && _a !== void 0 ? _a : propertyName : 'Payload',
type: 'options',
default: (_b = schema.default) !== null && _b !== void 0 ? _b : '',
description: schema.description,
options: schema.enum.map((value) => ({ name: value, value })),
};
}
return {
name: propertyName ? propertyName : 'payload',
displayName: propertyName ? (_c = schema.title) !== null && _c !== void 0 ? _c : propertyName : 'Payload',
type: 'string',
default: this.buildStringPropertyDefault(schema),
description: schema.description,
};
case 'number':
return {
name: propertyName ? propertyName : 'payload',
displayName: propertyName ? (_d = schema.title) !== null && _d !== void 0 ? _d : propertyName : 'Payload',
type: 'number',
default: (_e = schema.default) !== null && _e !== void 0 ? _e : 0,
description: schema.description,
};
case 'boolean':
return {
name: propertyName ? propertyName : 'payload',
displayName: propertyName ? (_f = schema.title) !== null && _f !== void 0 ? _f : propertyName : 'Payload',
type: 'boolean',
default: (_g = schema.default) !== null && _g !== void 0 ? _g : false,
description: schema.description,
};
default:
throw new Error(`Unsupported primitive schema type: ${schema.type}`);
}
}
buildPropertyForSchemaArray(propertyName, schema) {
var _a, _b, _c;
const items = ((_a = schema.items) !== null && _a !== void 0 ? _a : {});
let options = [];
if (Object.keys(items).length) {
options = [this.buildPropertyForSchema('item', items)];
}
else {
options = [
{
name: 'item',
displayName: 'Item',
type: 'json',
default: '',
}
];
}
return {
name: propertyName ? propertyName : 'payload',
displayName: propertyName ? (_b = schema.title) !== null && _b !== void 0 ? _b : propertyName : 'Payload',
description: schema.description,
type: 'fixedCollection',
default: (_c = schema.default) !== null && _c !== void 0 ? _c : [],
typeOptions: {
multipleValues: true,
},
options: [
{
name: 'item',
displayName: 'Item',
values: options,
}
]
};
}
buildPropertyDefault(schema) {
var _a, _b, _c;
const defaultObj = (_a = schema.default) !== null && _a !== void 0 ? _a : {};
for (const propertyName of (_b = schema.required) !== null && _b !== void 0 ? _b : []) {
const property = (_c = schema.properties) === null || _c === void 0 ? void 0 : _c[propertyName];
switch (property === null || property === void 0 ? void 0 : property.type) {
case 'string':
case 'number':
case 'boolean':
if (defaultObj[propertyName] === undefined) {
defaultObj[propertyName] = null;
}
break;
case 'object':
if (defaultObj[propertyName] === undefined) {
defaultObj[propertyName] = {};
}
break;
}
}
// remove empty arrays from the default object
for (const [key, value] of Object.entries(defaultObj)) {
if (Array.isArray(value) && value.length === 0) {
delete defaultObj[key];
}
}
return defaultObj;
}
buildPropertyForSchemaObject(propertyName, schema) {
var _a, _b;
return {
name: propertyName ? propertyName : 'payload',
displayName: propertyName ? (_a = schema.title) !== null && _a !== void 0 ? _a : propertyName : 'Payload',
type: 'collection',
default: this.buildPropertyDefault(schema),
description: schema.description,
options: Object.entries((_b = schema.properties) !== null && _b !== void 0 ? _b : {}).map(([propertyName, schema]) => this.buildPropertyForSchema(propertyName, schema)),
};
}
}
exports.OpenApiNode = OpenApiNode;
tslib_1.__decorate([
(0, capture_execution_error_decorator_1.CaptureExecutionError)(),
tslib_1.__metadata("design:type", Function),
tslib_1.__metadata("design:paramtypes", []),
tslib_1.__metadata("design:returntype", Promise)
], OpenApiNode.prototype, "execute", null);
//# sourceMappingURL=open-api-node.js.map