UNPKG

n8n-nodes-larkbase-v3

Version:

n8n node to connect with Larkbase API with UTF-8 support, date filtering, array filter values, text field processing, improved field type handling, pagination fixes, advanced data type conversion, proper date filtering with ExactDate format, updated node

1,049 lines 48.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Larkbase = void 0; const n8n_workflow_1 = require("n8n-workflow"); const batchSearch_1 = require("./modules/batchSearch"); const batchCreate_1 = require("./modules/batchCreate"); const batchUpdate_1 = require("./modules/batchUpdate"); const auth_1 = require("./modules/auth"); const upsert_1 = require("./modules/upsert"); const utils_1 = require("./modules/utils"); const fields_1 = require("./modules/fields"); class Larkbase { constructor() { this.description = { displayName: 'Larkbase V3', name: 'larkbase', icon: 'file:larkbase.svg', group: ['transform'], version: 3, subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', description: 'Tương tác với Larkbase API', defaults: { name: 'Larkbase V3', }, inputs: ['main'], outputs: ['main'], credentials: [ { name: 'larkbaseApi', required: true, displayOptions: { show: { authenticationMethod: [ 'credentials', ], }, }, }, ], properties: [ { displayName: 'Authentication Method', name: 'authenticationMethod', type: 'options', options: [ { name: 'Credentials', value: 'credentials', description: 'Sử dụng thông tin xác thực đã lưu', }, { name: 'Access Token', value: 'accessToken', description: 'Sử dụng access token từ node khác', }, { name: 'Manual Token', value: 'manualToken', description: 'Nhập token thủ công', }, ], default: 'credentials', description: 'Phương thức xác thực', }, { displayName: 'Access Token', name: 'accessToken', type: 'string', default: '', required: true, displayOptions: { show: { authenticationMethod: [ 'accessToken', ], }, }, description: 'Access token từ node Larkbase Auth', }, { displayName: 'Manual Token', name: 'manualToken', type: 'string', default: '', required: true, typeOptions: { password: true, }, displayOptions: { show: { authenticationMethod: [ 'manualToken', ], }, }, description: 'Nhập access token thủ công', }, { displayName: 'Resource', name: 'resource', type: 'options', noDataExpression: true, options: [ { name: 'Record', value: 'record', }, ], default: 'record', required: true, description: 'Resource để thao tác', }, { displayName: 'Operation', name: 'operation', type: 'options', noDataExpression: true, displayOptions: { show: { resource: [ 'record', ], }, }, options: [ { name: 'Batch Search', value: 'batchSearch', description: 'Tìm kiếm records', action: 'Search for records', }, { name: 'Batch Create', value: 'batchCreate', description: 'Tạo nhiều records cùng lúc', action: 'Create records', }, { name: 'Batch Update', value: 'batchUpdate', description: 'Cập nhật nhiều records cùng lúc', action: 'Update records', }, { name: 'Upsert By Field', value: 'upsertByField', description: 'Tạo mới hoặc cập nhật records dựa trên giá trị của một trường', action: 'Upsert records', }, { name: 'Get Fields', value: 'getFields', description: 'Lấy thông tin về các trường trong bảng', action: 'Get fields information', }, ], default: 'batchSearch', }, { displayName: 'App Token', name: 'appToken', type: 'string', default: '', required: true, description: 'App Token của Larkbase', }, { displayName: 'Table ID', name: 'tableId', type: 'string', default: '', required: true, description: 'ID của bảng trong Larkbase', }, { displayName: 'Return All', name: 'returnAll', type: 'boolean', displayOptions: { show: { resource: [ 'record', ], operation: [ 'batchSearch', ], }, }, default: false, description: 'Trả về tất cả các kết quả', }, { displayName: 'Filter', name: 'filterOptions', type: 'fixedCollection', placeholder: 'Add Filter', displayOptions: { show: { resource: [ 'record', ], operation: [ 'batchSearch', ], }, }, default: {}, options: [ { name: 'conditions', displayName: 'Conditions', values: [ { displayName: 'Conjunction', name: 'conjunction', type: 'options', options: [ { name: 'AND', value: 'and', }, { name: 'OR', value: 'or', }, ], default: 'and', description: 'Loại kết hợp giữa các điều kiện', }, { displayName: 'Conditions', name: 'conditions', type: 'fixedCollection', placeholder: 'Add Condition', default: {}, typeOptions: { multipleValues: true, }, options: [ { name: 'condition', displayName: 'Condition', values: [ { displayName: 'Field Name', name: 'fieldName', type: 'options', typeOptions: { loadOptionsMethod: 'getTableFields', }, default: '', description: 'Tên hoặc ID của trường để lọc', }, { displayName: 'Operator', name: 'operator', type: 'options', options: [ { name: 'Is', value: 'is', }, { name: 'Is Not', value: 'isNot', }, { name: 'Contains', value: 'contains', }, { name: 'Does Not Contain', value: 'doesNotContain', }, { name: 'Is Empty', value: 'isEmpty', }, { name: 'Is Not Empty', value: 'isNotEmpty', }, { name: 'Is Greater', value: 'isGreater', }, { name: 'Is Greater Equal', value: 'isGreaterEqual', }, { name: 'Is Less', value: 'isLess', }, { name: 'Is Less Equal', value: 'isLessEqual', }, { name: 'Is One Of', value: 'isOneOf', }, ], default: 'is', description: 'Toán tử để so sánh', }, { displayName: 'Value', name: 'value', type: 'string', default: '', displayOptions: { hide: { operator: [ 'isEmpty', 'isNotEmpty', ], }, }, description: 'Giá trị để so sánh. Đối với toán tử "Is One Of", nhập nhiều giá trị phân cách bằng dấu phẩy.', }, ], }, ], }, ], }, ], }, { displayName: 'Options', name: 'options', type: 'collection', placeholder: 'Add Option', displayOptions: { show: { resource: [ 'record', ], operation: [ 'batchSearch', ], }, }, default: {}, options: [ { displayName: 'Field Names', name: 'fieldNames', type: 'string', default: '', description: 'Danh sách tên trường cần lấy, phân tách bằng dấu phẩy', }, { displayName: 'View ID', name: 'viewId', type: 'string', default: '', description: 'ID của view để lọc kết quả', }, { displayName: 'Date Filter', name: 'dateFilter', type: 'fixedCollection', placeholder: 'Add Date Filter', default: {}, options: [ { name: 'dateRange', displayName: 'Date Range', values: [ { displayName: 'Date Field', name: 'dateField', type: 'string', default: '', required: true, description: 'Tên trường ngày tháng để lọc', }, { displayName: 'Start Date', name: 'startDate', type: 'dateTime', default: '', description: 'Ngày bắt đầu của khoảng thời gian', }, { displayName: 'End Date', name: 'endDate', type: 'dateTime', default: '', description: 'Ngày kết thúc của khoảng thời gian', }, ], }, ], }, { displayName: 'Sort', name: 'sort', type: 'fixedCollection', placeholder: 'Add Sort', default: {}, typeOptions: { multipleValues: true, }, options: [ { name: 'sortRule', displayName: 'Sort Rule', values: [ { displayName: 'Field Name', name: 'fieldName', type: 'options', typeOptions: { loadOptionsMethod: 'getTableFields', }, default: '', description: 'Tên hoặc ID của trường để sắp xếp', }, { displayName: 'Order', name: 'order', type: 'options', options: [ { name: 'Ascending', value: 'asc', }, { name: 'Descending', value: 'desc', }, ], default: 'asc', description: 'Thứ tự sắp xếp', }, ], }, ], }, ], }, { displayName: 'Mapping Mode', name: 'mappingMode', type: 'options', options: [ { name: 'Auto-map Input Fields to Larkbase', value: 'autoMap', description: 'Tự động map các fields dựa vào tên', }, { name: 'Define Fields Mapping', value: 'defineFields', description: 'Map từng field thủ công', }, { name: 'Use JSON Field Mapping', value: 'jsonMap', description: 'Sử dụng một chuỗi JSON để map các fields', }, ], default: 'defineFields', displayOptions: { show: { resource: [ 'record', ], operation: [ 'batchCreate', ], }, }, description: 'Phương thức để map các trường dữ liệu', }, { displayName: 'Load Fields From Larkbase', name: 'loadFieldsFromLarkbase', type: 'boolean', default: true, displayOptions: { show: { resource: [ 'record', ], operation: [ 'batchCreate', ], mappingMode: [ 'defineFields', ], }, }, description: 'Tải các trường từ Larkbase để dễ dàng cấu hình', }, { displayName: 'Fields Mapping', name: 'fieldsMapper', type: 'fixedCollection', typeOptions: { multipleValues: true, sortable: true, }, placeholder: 'Add Field Mapping', default: {}, displayOptions: { show: { resource: [ 'record', ], operation: [ 'batchCreate', ], mappingMode: [ 'defineFields', ], loadFieldsFromLarkbase: [ false, ], }, }, options: [ { name: 'mapping', displayName: 'Field Mapping', values: [ { displayName: 'Source Field', name: 'sourceField', type: 'string', default: '', description: 'Tên trường trong dữ liệu nguồn', }, { displayName: 'Destination Field', name: 'fieldName', type: 'string', default: '', description: 'Tên trường trong Larkbase', }, ], }, ], description: 'Map các trường dữ liệu nguồn với các trường Larkbase', }, { displayName: 'Fields', name: 'fieldValuesUi', placeholder: 'Add Field', type: 'fixedCollection', typeOptions: { multipleValues: true, sortable: true, }, default: {}, displayOptions: { show: { resource: [ 'record', ], operation: [ 'batchCreate', ], mappingMode: [ 'defineFields', ], loadFieldsFromLarkbase: [ true, ], }, }, options: [ { name: 'fields', displayName: 'Fields', values: [ { displayName: 'Field', name: 'fieldId', type: 'options', typeOptions: { loadOptionsMethod: 'getTableFields', }, default: '', description: 'Field ID trong Larkbase', }, { displayName: 'Field Value', name: 'fieldValue', type: 'string', default: '', description: 'Nội dung hoặc biểu thức n8n để lấy giá trị', }, ], }, ], description: 'Giá trị của các trường để tạo record', }, { displayName: 'JSON Field Mapping', name: 'jsonFieldMapping', type: 'string', typeOptions: { alwaysOpenEditWindow: true, }, displayOptions: { show: { resource: [ 'record', ], operation: [ 'batchCreate', ], mappingMode: [ 'jsonMap', ], }, }, default: '{\n "fieldName1": "{{$json.sourceField1}}",\n "fieldName2": "{{$json.sourceField2}}"\n}', description: 'Map các trường dữ liệu từ nguồn đến các trường Larkbase trong định dạng JSON. Bạn có thể dùng các biểu thức.', }, { displayName: 'Record ID Field', name: 'recordIdField', type: 'string', displayOptions: { show: { resource: [ 'record', ], operation: [ 'batchUpdate', ], }, }, default: 'record_id', required: true, description: 'Tên trường chứa record ID trong dữ liệu đầu vào', }, { displayName: 'Field Mapping', name: 'fieldMapping', type: 'fixedCollection', placeholder: 'Add Field Mapping', displayOptions: { show: { resource: [ 'record', ], operation: [ 'batchUpdate', ], }, }, default: {}, typeOptions: { multipleValues: true, }, options: [ { name: 'mapping', displayName: 'Field Mapping', values: [ { displayName: 'Target Field', name: 'targetField', type: 'string', default: '', description: 'Tên trường trong Larkbase', }, { displayName: 'Source Field', name: 'sourceField', type: 'string', default: '', description: 'Tên trường trong dữ liệu đầu vào', }, ], }, ], }, { displayName: 'Key Field', name: 'keyField', type: 'string', displayOptions: { show: { resource: [ 'record', ], operation: [ 'upsertByField', ], }, }, default: '', required: true, description: 'Tên trường làm khóa để xác định record đã tồn tại hay chưa', }, { displayName: 'Field Mapping', name: 'fieldMapping', type: 'fixedCollection', placeholder: 'Add Field Mapping', displayOptions: { show: { resource: [ 'record', ], operation: [ 'upsertByField', ], }, }, default: {}, typeOptions: { multipleValues: true, }, options: [ { name: 'mapping', displayName: 'Field Mapping', values: [ { displayName: 'Target Field', name: 'targetField', type: 'string', default: '', description: 'Tên trường trong Larkbase', }, { displayName: 'Source Field', name: 'sourceField', type: 'string', default: '', description: 'Tên trường trong dữ liệu đầu vào', }, ], }, ], }, { displayName: 'Options', name: 'getFieldsOptions', type: 'collection', placeholder: 'Add Options', default: {}, displayOptions: { show: { resource: [ 'record', ], operation: [ 'getFields', ], }, }, options: [ { displayName: 'View ID', name: 'view_id', type: 'string', default: '', description: 'ID của view', }, { displayName: 'Return Text Fields as Array', name: 'text_field_as_array', type: 'boolean', default: false, description: 'Trả về dữ liệu trường text dưới dạng mảng', }, ], }, ], }; this.methods = { loadOptions: { async getTableFields() { const authenticationMethod = this.getCurrentNodeParameter('authenticationMethod') || 'credentials'; const appToken = this.getCurrentNodeParameter('appToken'); const tableId = this.getCurrentNodeParameter('tableId'); if (!appToken || !tableId) { return [{ name: 'Vui lòng nhập App Token và Table ID trước', value: '' }]; } try { let accessToken = ''; if (authenticationMethod === 'credentials') { const credentials = await this.getCredentials('larkbaseApi'); accessToken = await auth_1.getAccessToken.call(this, credentials); } else if (authenticationMethod === 'accessToken') { accessToken = this.getCurrentNodeParameter('accessToken'); } else if (authenticationMethod === 'manualToken') { accessToken = this.getCurrentNodeParameter('manualAccessToken'); } if (!accessToken) { return [{ name: 'Không thể lấy access token', value: '' }]; } const fields = await fields_1.getAllTableFields.call(this, accessToken, appToken, tableId); return (0, fields_1.mapFieldsToOptions)(fields); } catch (error) { console.error('Lỗi khi tải options:', error); return [{ name: `Lỗi: ${error.message}`, value: '' }]; } }, }, }; } async execute() { const items = this.getInputData(); const returnData = []; const resource = this.getNodeParameter('resource', 0); const operation = this.getNodeParameter('operation', 0); const appToken = this.getNodeParameter('appToken', 0); const tableId = this.getNodeParameter('tableId', 0); const authenticationMethod = this.getNodeParameter('authenticationMethod', 0); let accessToken; if (authenticationMethod === 'credentials') { accessToken = await auth_1.getAccessToken.call(this); } else if (authenticationMethod === 'accessToken') { accessToken = this.getNodeParameter('accessToken', 0); } else { accessToken = this.getNodeParameter('manualToken', 0); } if (resource === 'record') { if (operation === 'getFields') { const options = this.getNodeParameter('getFieldsOptions', 0, {}); try { const fields = await fields_1.getAllTableFields.call(this, accessToken, appToken, tableId, options); const fieldsWithTypes = fields.map(field => { const item = { field_id: field.field_id, field_name: field.field_name, type: field.type, ui_type: field.ui_type || '', is_primary: field.is_primary || false, }; if (field.property) { item.property = field.property; } return item; }); for (const field of fieldsWithTypes) { returnData.push({ json: field, }); } } catch (error) { if (this.continueOnFail()) { returnData.push({ json: { error: error.message }, }); } else { throw error; } } } else if (operation === 'batchSearch') { const returnAll = this.getNodeParameter('returnAll', 0); const options = {}; const filterOptions = this.getNodeParameter('filterOptions', 0); if (filterOptions.conditions) { const conditions = filterOptions.conditions.conditions; const conjunction = filterOptions.conditions.conjunction; if (conditions && conditions.condition) { const conditionValues = conditions.condition; const filterConditions = []; for (const condition of conditionValues) { const fieldName = condition.fieldName; const operator = condition.operator; const value = condition.value; filterConditions.push({ field_name: fieldName, operator, value: value, }); } options.filter = { conditions: filterConditions, conjunction: conjunction, }; } } const additionalOptions = this.getNodeParameter('options', 0); if (additionalOptions.fieldNames) { options.field_names = additionalOptions.fieldNames.split(',').map(field => field.trim()); } if (additionalOptions.viewId) { options.view_id = additionalOptions.viewId; } if (additionalOptions.dateFilter && additionalOptions.dateFilter.dateRange) { const dateRange = additionalOptions.dateFilter.dateRange; if (dateRange.dateField) { options.date_filter = [{ field_name: dateRange.dateField, start_date: dateRange.startDate, end_date: dateRange.endDate, }]; } } if (additionalOptions.sort && additionalOptions.sort.sortRule) { const sortRules = additionalOptions.sort.sortRule; options.sort = sortRules.map(rule => ({ field_name: rule.fieldName, order: rule.order, })); } options.page_size = 500; try { if (!returnAll) { const response = await batchSearch_1.batchSearch.call(this, accessToken, appToken, tableId, options); const records = response.items; for (const record of records) { returnData.push({ json: record, pairedItem: { item: 0, }, }); } } else { const records = await batchSearch_1.batchSearchAll.call(this, accessToken, appToken, tableId, options); for (const record of records) { returnData.push({ json: record, pairedItem: { item: 0, }, }); } } } catch (error) { if (error instanceof Error) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Lỗi khi thực hiện batch search: ${error.message}`, { itemIndex: 0, }); } else { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Lỗi không xác định khi thực hiện batch search', { itemIndex: 0, }); } } } else if (operation === 'batchCreate') { const mappingMode = this.getNodeParameter('mappingMode', 0); let fieldMapping = {}; if (mappingMode === 'autoMap') { } else if (mappingMode === 'defineFields') { const loadFieldsFromLarkbase = this.getNodeParameter('loadFieldsFromLarkbase', 0); if (loadFieldsFromLarkbase) { const fieldsData = this.getNodeParameter('fieldValuesUi.fields', 0, []); for (const fieldData of fieldsData) { const fieldId = fieldData.fieldId; const fieldValue = fieldData.fieldValue; fieldMapping[fieldId] = fieldValue; } } else { const mappingData = this.getNodeParameter('fieldsMapper.mapping', 0, []); for (const map of mappingData) { const sourceField = map.sourceField; const fieldName = map.fieldName; fieldMapping[fieldName] = sourceField; } } } else if (mappingMode === 'jsonMap') { const jsonMapping = this.getNodeParameter('jsonFieldMapping', 0); try { fieldMapping = JSON.parse(jsonMapping); } catch (error) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'JSON Field Mapping không hợp lệ. Vui lòng kiểm tra cú pháp JSON.'); } } const records = (0, utils_1.mapInputToRecords)(items, fieldMapping); if (!records.length) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Không có dữ liệu đầu vào. Vui lòng kiểm tra field mapping.'); } const createdRecords = await batchCreate_1.batchCreateChunked.call(this, accessToken, appToken, tableId, records); for (const record of createdRecords) { returnData.push({ json: record, }); } } else if (operation === 'batchUpdate') { const recordIdField = this.getNodeParameter('recordIdField', 0); const fieldMappingData = this.getNodeParameter('fieldMapping', 0); const fieldMapping = {}; if (fieldMappingData.mapping) { for (const map of fieldMappingData.mapping) { fieldMapping[map.targetField] = map.sourceField; } } const records = (0, utils_1.mapInputToUpdateRecords)(items, fieldMapping, recordIdField); const updatedRecords = await batchUpdate_1.batchUpdateChunked.call(this, accessToken, appToken, tableId, records); for (const record of updatedRecords) { returnData.push({ json: record, pairedItem: { item: 0, }, }); } } else if (operation === 'upsertByField') { const keyField = this.getNodeParameter('keyField', 0); const fieldMappingData = this.getNodeParameter('fieldMapping', 0); const fieldMapping = {}; if (fieldMappingData.mapping) { for (const map of fieldMappingData.mapping) { fieldMapping[map.targetField] = map.sourceField; } } const records = (0, utils_1.mapInputToRecords)(items, fieldMapping); const result = await upsert_1.upsertByField.call(this, accessToken, appToken, tableId, keyField, records); returnData.push({ json: { success: true, stats: result.stats, created: result.created, updated: result.updated, }, pairedItem: { item: 0, }, }); } } return [returnData]; } } exports.Larkbase = Larkbase; //# sourceMappingURL=Larkbase.node.js.map