UNPKG

@luka-cat-mimi/n8n-nodes-feishu-project

Version:
494 lines 23.6 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.FeishuProject = void 0; const n8n_workflow_1 = require("n8n-workflow"); const ResourceFactory_1 = __importDefault(require("../help/builder/ResourceFactory")); const parameters_1 = require("../help/utils/parameters"); const GenericFunctions_1 = require("./GenericFunctions"); const resourceBuilder = ResourceFactory_1.default.build(__dirname); function getBatchConfig(context) { var _a, _b, _c, _d, _e, _f, _g; try { const options = context.getNodeParameter('options', 0, {}); const batchingEnabled = ((_a = options === null || options === void 0 ? void 0 : options.batching) === null || _a === void 0 ? void 0 : _a.batch) !== undefined; const batchSize = (_d = (_c = (_b = options === null || options === void 0 ? void 0 : options.batching) === null || _b === void 0 ? void 0 : _b.batch) === null || _c === void 0 ? void 0 : _c.batchSize) !== null && _d !== void 0 ? _d : 50; const batchInterval = (_g = (_f = (_e = options === null || options === void 0 ? void 0 : options.batching) === null || _e === void 0 ? void 0 : _e.batch) === null || _f === void 0 ? void 0 : _f.batchInterval) !== null && _g !== void 0 ? _g : 0; return { enabled: batchingEnabled, batchSize: batchSize === 0 ? 1 : batchSize, batchInterval, }; } catch { return { enabled: false, batchSize: 50, batchInterval: 0 }; } } function getErrorMessage(error) { if (error instanceof Error) { return error.message; } return String(error); } function getErrorStack(error) { if (error instanceof Error) { return error.stack; } return undefined; } function isMultipleOutputResponse(response) { return (response !== null && typeof response === 'object' && 'outputType' in response && response.outputType !== undefined); } function processResponseData(context, responseData, itemIndex) { if (isMultipleOutputResponse(responseData)) { const { outputType, outputData } = responseData; if (outputType === "multiple" && outputData) { return { data: null, multipleOutput: outputData }; } if (outputType === "none") { return { data: null }; } } const executionData = context.helpers.constructExecutionMetaData(context.helpers.returnJsonArray(responseData), { itemData: { item: itemIndex } }); return { data: executionData }; } function createErrorData(context, error, itemIndex) { var _a, _b; const errorObj = error; return context.helpers.constructExecutionMetaData(context.helpers.returnJsonArray({ error: (_a = errorObj.description) !== null && _a !== void 0 ? _a : getErrorMessage(error), ...(errorObj.name === 'NodeApiError' && ((_b = errorObj.cause) === null || _b === void 0 ? void 0 : _b.error) ? { details: errorObj.cause.error } : {}), }), { itemData: { item: itemIndex } }); } async function executeSerial(context, items, callFunc, resource, operation) { const returnData = [[]]; for (let itemIndex = 0; itemIndex < items.length; itemIndex++) { try { context.logger.debug('call function (serial)', { resource, operation, itemIndex, }); const responseData = await callFunc.call(context, itemIndex); const processed = processResponseData(context, responseData, itemIndex); if (processed.multipleOutput) { return processed.multipleOutput; } if (processed.data === null && !processed.multipleOutput) { return []; } if (processed.data) { returnData[0].push(...processed.data); } } catch (error) { context.logger.error('call function error (serial)', { resource, operation, itemIndex, errorMessage: getErrorMessage(error), stack: getErrorStack(error), }); if (context.continueOnFail()) { returnData[0].push(...createErrorData(context, error, itemIndex)); continue; } throw error; } } return returnData; } async function executeParallel(context, items, callFunc, batchConfig, resource, operation) { const { batchSize, batchInterval } = batchConfig; const requestPromises = []; const returnData = [[]]; for (let itemIndex = 0; itemIndex < items.length; itemIndex++) { if (itemIndex > 0 && batchSize > 0 && batchInterval > 0) { if (itemIndex % batchSize === 0) { await (0, n8n_workflow_1.sleep)(batchInterval); } } context.logger.debug('call function (parallel)', { resource, operation, itemIndex, batch: batchSize > 0 ? Math.floor(itemIndex / batchSize) : 0, }); const requestPromise = callFunc .call(context, itemIndex) .then((result) => ({ itemIndex, result })) .catch((error) => ({ itemIndex, error: error })); requestPromises.push(requestPromise); } const promisesResponses = await Promise.allSettled(requestPromises); for (let i = 0; i < promisesResponses.length; i++) { const response = promisesResponses[i]; if (response.status === 'rejected') { const error = response.reason; context.logger.error('call function error (parallel - rejected)', { resource, operation, itemIndex: i, errorMessage: getErrorMessage(error), }); if (context.continueOnFail()) { returnData[0].push(...createErrorData(context, error, i)); continue; } throw error; } const { itemIndex, result, error } = response.value; if (error) { context.logger.error('call function error (parallel)', { resource, operation, itemIndex, errorMessage: error.message, stack: error.stack, }); if (context.continueOnFail()) { returnData[0].push(...createErrorData(context, error, itemIndex)); continue; } throw error; } if (result === undefined) { continue; } const processed = processResponseData(context, result, itemIndex); if (processed.multipleOutput) { return processed.multipleOutput; } if (processed.data === null && !processed.multipleOutput) { continue; } if (processed.data) { returnData[0].push(...processed.data); } } return returnData; } class FeishuProject { constructor() { this.description = { displayName: '飞书项目', subtitle: '={{ $parameter.resource }}:{{ $parameter.operation }}', name: 'feishuProject', icon: 'file:icon.svg', group: ['transform'], version: [1], defaultVersion: 1, description: '飞书项目管理 API 集成,支持工作项、空间、用户、工作流等完整功能', defaults: { name: '飞书项目', }, usableAsTool: true, inputs: [n8n_workflow_1.NodeConnectionTypes.Main], outputs: `={{(${parameters_1.configuredOutputs})($parameter)}}`, credentials: [ { name: "feishuProjectApi", required: true, }, ], properties: [...resourceBuilder.build()], }; this.methods = { listSearch: { async searchSpaces() { const spaceIds = await GenericFunctions_1.getSpaceList.call(this); if (!spaceIds || spaceIds.length === 0) { return { results: [] }; } const spaceDetails = await GenericFunctions_1.getSpaceDetails.call(this, spaceIds); return { results: spaceDetails.map((space) => ({ name: space.name, value: space.project_key, })), }; }, async searchWorkItemTypes() { const projectKey = this.getNodeParameter('project_key', undefined, { extractValue: true, }); if (!projectKey) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), '请先选择空间'); } const workItemTypes = await GenericFunctions_1.getWorkItemTypes.call(this, projectKey); return { results: workItemTypes .filter((type) => type.is_disable === 2) .map((type) => ({ name: type.name, value: type.type_key, description: `工作项类型唯一key:${type.type_key}`, })), }; }, }, loadOptions: { async loadSpaces() { try { const spaceIds = await GenericFunctions_1.getSpaceList.call(this); if (!spaceIds || spaceIds.length === 0) { return []; } const spaceDetails = await GenericFunctions_1.getSpaceDetails.call(this, spaceIds); return spaceDetails.map((space) => ({ name: `${space.name} (${space.project_key})`, value: space.project_key, })); } catch { return []; } }, async loadWorkItemTypes() { try { const projectKey = this.getNodeParameter('project_key', undefined, { extractValue: true, }); if (!projectKey) { return []; } const workItemTypes = await GenericFunctions_1.getWorkItemTypes.call(this, projectKey); return workItemTypes .filter((type) => type.is_disable === 2) .map((type) => ({ name: type.name, value: type.type_key, description: `工作项类型唯一key:${type.type_key}`, })); } catch { return []; } }, async loadWorkItemFields() { const excludedFieldTypes = [ 'multi_file', ]; try { const projectKey = this.getNodeParameter('project_key', undefined, { extractValue: true, }); const workItemTypeKey = this.getNodeParameter('work_item_type_key'); if (!projectKey || !workItemTypeKey) { return []; } const fieldMeta = await GenericFunctions_1.getWorkItemFieldMeta.call(this, projectKey, workItemTypeKey); return fieldMeta .filter((field) => !excludedFieldTypes.includes(field.field_type_key || '')) .map((field) => ({ name: `${field.field_name}${field.is_required === 1 ? ' *' : ''}`, value: field.field_key, description: `类型: ${field.field_type_key || '未知'}, field_key: ${field.field_key}${field.is_required === 1 ? ' (必填)' : field.is_required === 3 ? ' (条件必填)' : ''}`, })); } catch { return []; } }, async loadWorkItemFieldsForWrite() { const excludedFieldTypes = [ 'multi_file', ]; const excludedFieldKeys = [ 'current_status_operator', 'start_time', 'finish_time', 'owner', 'archiving_date', 'issue_reporter', 'archiving_status', 'template', ]; try { const projectKey = this.getNodeParameter('project_key', undefined, { extractValue: true, }); const workItemTypeKey = this.getNodeParameter('work_item_type_key'); if (!projectKey || !workItemTypeKey) { return []; } const fieldMeta = await GenericFunctions_1.getWorkItemFieldMeta.call(this, projectKey, workItemTypeKey); return fieldMeta .filter((field) => !excludedFieldTypes.includes(field.field_type_key || '') && !excludedFieldKeys.includes(field.field_key || '')) .map((field) => ({ name: `${field.field_name}${field.is_required === 1 ? ' *' : ''}`, value: field.field_key, description: `类型: ${field.field_type_key || '未知'}, field_key: ${field.field_key}${field.is_required === 1 ? ' (必填)' : field.is_required === 3 ? ' (条件必填)' : ''}`, })); } catch { return []; } }, async loadAttachmentFields() { try { const projectKey = this.getNodeParameter('project_key', undefined, { extractValue: true, }); const workItemTypeKey = this.getNodeParameter('work_item_type_key'); if (!projectKey || !workItemTypeKey) { return []; } const fieldMeta = await GenericFunctions_1.getWorkItemFieldMeta.call(this, projectKey, workItemTypeKey); return fieldMeta .filter((field) => field.field_type_key === 'multi_file') .map((field) => ({ name: field.field_name, value: field.field_key, })); } catch { return []; } }, async loadWorkItemFieldsAll() { const excludedFieldTypes = [ 'multi_file', ]; try { const projectKey = this.getNodeParameter('project_key', undefined, { extractValue: true, }); const workItemTypeKey = this.getNodeParameter('work_item_type_key'); if (!projectKey || !workItemTypeKey) { return []; } const fields = await GenericFunctions_1.getWorkItemFieldsAll.call(this, projectKey, workItemTypeKey); return fields .filter((field) => !excludedFieldTypes.includes(field.field_type_key || '')) .map((field) => ({ name: field.field_name, value: field.field_key, description: `类型: ${field.field_type_key || '未知'}, field_key: ${field.field_key}${field.is_custom_field ? ' (自定义字段)' : ''}`, })); } catch { return []; } }, async loadWorkflowTemplates() { try { const projectKey = this.getNodeParameter('project_key', undefined, { extractValue: true, }); const workItemTypeKey = this.getNodeParameter('work_item_type_key'); if (!projectKey || !workItemTypeKey) { return []; } const templates = await GenericFunctions_1.getWorkflowTemplates.call(this, projectKey, workItemTypeKey); return templates.map((tpl) => ({ name: tpl.template_name, value: tpl.template_id, description: `Template_id:${tpl.template_id}, version:${tpl.version}`, })); } catch { return []; } }, }, resourceMapping: { async getWorkItemFieldMapping() { var _a, _b; const excludedFieldTypes = ['multi_file']; try { const projectKey = this.getNodeParameter('project_key', undefined, { extractValue: true, }); const workItemTypeKey = this.getNodeParameter('work_item_type_key'); if (!projectKey || !workItemTypeKey) { return { fields: [] }; } const [fieldMeta, fieldDetails] = await Promise.all([ GenericFunctions_1.getWorkItemFieldMeta.call(this, projectKey, workItemTypeKey), GenericFunctions_1.getWorkItemFieldsAll.call(this, projectKey, workItemTypeKey), ]); const metaMap = new Map(fieldMeta.map((f) => [f.field_key, f])); const detailMap = new Map(fieldDetails.map((f) => [f.field_key, f])); const allFieldKeys = new Set([ ...fieldMeta.map((f) => f.field_key), ...fieldDetails.map((f) => f.field_key), ]); const fields = []; for (const fieldKey of allFieldKeys) { const meta = metaMap.get(fieldKey); const detail = detailMap.get(fieldKey); const fieldTypeKey = (meta === null || meta === void 0 ? void 0 : meta.field_type_key) || (detail === null || detail === void 0 ? void 0 : detail.field_type_key) || ''; if (excludedFieldTypes.includes(fieldTypeKey)) continue; let type = (0, GenericFunctions_1.mapFeishuFieldTypeToN8n)(fieldTypeKey); const hasOptions = (detail === null || detail === void 0 ? void 0 : detail.options) && Array.isArray(detail.options) && detail.options.length > 0; if (type === 'options' && !hasOptions) { type = 'string'; } const fieldName = (meta === null || meta === void 0 ? void 0 : meta.field_name) || (detail === null || detail === void 0 ? void 0 : detail.field_name) || fieldKey; const isRequired = (meta === null || meta === void 0 ? void 0 : meta.is_required) === 1; const field = { id: fieldKey, displayName: `${fieldName}${isRequired ? ' *' : ''} (${fieldTypeKey || '未知'})`, defaultMatch: false, required: isRequired, display: true, type, }; if (type === 'options' && hasOptions) { field.options = detail.options .filter((opt) => opt.is_disabled !== 1) .map((opt) => ({ name: opt.label || String(opt.value || ''), value: JSON.stringify(opt), })); } if (((_a = meta === null || meta === void 0 ? void 0 : meta.default_value) === null || _a === void 0 ? void 0 : _a.value) !== undefined && ((_b = meta === null || meta === void 0 ? void 0 : meta.default_value) === null || _b === void 0 ? void 0 : _b.value) !== null) { const defaultVal = meta.default_value.value; if (typeof defaultVal === 'string' || typeof defaultVal === 'number' || typeof defaultVal === 'boolean') { field.defaultValue = defaultVal; } else { field.defaultValue = JSON.stringify(defaultVal); } } fields.push(field); } return { fields }; } catch { return { fields: [] }; } }, }, }; } async execute() { const items = this.getInputData(); const resource = this.getNodeParameter('resource', 0); const operation = this.getNodeParameter('operation', 0); const callFunc = resourceBuilder.getCall(resource, operation); if (!callFunc) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), `未实现方法: ${resource}.${operation}`); } const batchConfig = getBatchConfig(this); if (batchConfig.enabled) { return executeParallel(this, items, callFunc, batchConfig, resource, operation); } return executeSerial(this, items, callFunc, resource, operation); } } exports.FeishuProject = FeishuProject; //# sourceMappingURL=FeishuProject.node.js.map