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
JavaScript
"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