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
288 lines • 12.7 kB
JavaScript
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.batchSearchAll = exports.batchSearch = void 0;
const axios_1 = __importDefault(require("axios"));
const n8n_workflow_1 = require("n8n-workflow");
function processFilterValue(value, operator) {
if (value === null || value === undefined) {
return '';
}
if (operator === 'isEmpty' || operator === 'isNotEmpty') {
return '';
}
if (operator === 'isOneOf' || operator === 'is' || operator === 'isNot') {
if (Array.isArray(value)) {
return value;
}
if (typeof value === 'string' && value.includes(',')) {
return value.split(',').map(item => item.trim());
}
if (typeof value === 'string') {
return [value];
}
return [value];
}
if (typeof value === 'string' && !isNaN(Number(value)) &&
(operator === 'isGreater' || operator === 'isGreaterEqual' ||
operator === 'isLess' || operator === 'isLessEqual')) {
return Number(value);
}
return value;
}
function processDateFilter(dateFilter, filter) {
if (!dateFilter || dateFilter.length === 0 || !dateFilter[0].field_name) {
return filter || { conditions: [], conjunction: 'and' };
}
const newFilter = filter || { conditions: [], conjunction: 'and' };
for (const dateRange of dateFilter) {
if (dateRange.start_date) {
const startTimestamp = new Date(dateRange.start_date).getTime();
newFilter.conditions.push({
field_name: dateRange.field_name,
operator: 'isGreater',
value: ['ExactDate', startTimestamp],
});
}
if (dateRange.end_date) {
const endTimestamp = new Date(dateRange.end_date).getTime();
newFilter.conditions.push({
field_name: dateRange.field_name,
operator: 'isLess',
value: ['ExactDate', endTimestamp],
});
}
}
return newFilter;
}
function cleanTextFields(data) {
if (Array.isArray(data)) {
return data.map((item) => cleanTextFields(item));
}
const result = {};
if (data.fields && typeof data.fields === 'object' && !Array.isArray(data.fields)) {
const cleanedFields = {};
for (const [key, value] of Object.entries(data.fields)) {
if (value === null || value === undefined) {
cleanedFields[key] = null;
continue;
}
if (Array.isArray(value) && value.length > 0 && typeof value[0] === 'object' && value[0].type === 'text') {
let textValue = '';
for (const item of value) {
if (typeof item === 'object' && item !== null && 'text' in item) {
textValue += item.text;
}
}
cleanedFields[key] = textValue;
continue;
}
if (typeof value === 'object' && !Array.isArray(value) && 'type' in value && 'value' in value) {
const typeValue = value.type;
const actualValue = value.value;
switch (typeValue) {
case 1:
if (Array.isArray(actualValue) && actualValue.length > 0 && typeof actualValue[0] === 'object') {
let textValue = '';
for (const item of actualValue) {
if (typeof item === 'object' && item !== null && 'text' in item) {
textValue += item.text;
}
}
cleanedFields[key] = textValue;
}
else {
cleanedFields[key] = actualValue;
}
break;
case 2:
if (Array.isArray(actualValue) && actualValue.length > 0) {
cleanedFields[key] = actualValue[0];
}
else {
cleanedFields[key] = actualValue;
}
break;
case 5:
if (Array.isArray(actualValue) && actualValue.length > 0) {
let dateStr = '';
if (typeof actualValue[0] === 'object' && actualValue[0] !== null && 'text' in actualValue[0]) {
dateStr = actualValue[0].text;
}
else if (typeof actualValue[0] === 'string') {
dateStr = actualValue[0];
}
if (dateStr) {
try {
const timestamp = new Date(dateStr).getTime();
cleanedFields[key] = isNaN(timestamp) ? dateStr : timestamp;
}
catch (e) {
cleanedFields[key] = dateStr;
}
}
else {
cleanedFields[key] = actualValue;
}
}
else {
cleanedFields[key] = actualValue;
}
break;
default:
cleanedFields[key] = actualValue;
}
continue;
}
if (typeof value === 'string' && /^\d{4}-\d{2}-\d{2}/.test(value)) {
try {
const timestamp = new Date(value).getTime();
cleanedFields[key] = isNaN(timestamp) ? value : timestamp;
}
catch (e) {
cleanedFields[key] = value;
}
continue;
}
cleanedFields[key] = value;
}
result.fields = cleanedFields;
}
else {
result.fields = data.fields;
}
for (const [key, value] of Object.entries(data)) {
if (key !== 'fields') {
result[key] = value;
}
}
return result;
}
async function batchSearch(accessToken, appToken, tableId, options) {
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
const { page_token, page_size, ...bodyOptions } = options;
let url = `https://open.larksuite.com/open-apis/bitable/v1/apps/${appToken}/tables/${tableId}/records/search`;
const queryParams = [];
if (page_size !== undefined) {
queryParams.push(`page_size=${page_size}`);
}
if (page_token) {
queryParams.push(`page_token=${encodeURIComponent(page_token)}`);
}
if (queryParams.length > 0) {
url += `?${queryParams.join('&')}`;
}
if (bodyOptions.date_filter && bodyOptions.date_filter.length > 0) {
bodyOptions.filter = processDateFilter(bodyOptions.date_filter, bodyOptions.filter);
delete bodyOptions.date_filter;
}
if (bodyOptions.filter && bodyOptions.filter.conditions) {
bodyOptions.filter.conditions = bodyOptions.filter.conditions.map(condition => ({
...condition,
value: processFilterValue(condition.value, condition.operator),
}));
}
console.log('DEBUG - Batch Search Request:');
console.log('URL:', url);
console.log('Access Token:', accessToken.substring(0, 10) + '...');
console.log('App Token:', appToken);
console.log('Table ID:', tableId);
console.log('Body Options:', JSON.stringify(bodyOptions, null, 2));
const requestOptions = {
method: 'POST',
url,
headers: {
'Content-Type': 'application/json; charset=utf-8',
Authorization: `Bearer ${accessToken}`,
},
data: bodyOptions,
};
try {
const response = await axios_1.default.request(requestOptions);
console.log('DEBUG - Batch Search Response:');
console.log('Status:', response.status);
console.log('Data:', JSON.stringify(response.data, null, 2));
if (response.data.code !== 0) {
throw new Error(`Lỗi batch search: ${response.data.msg}`);
}
const cleanedData = { ...response.data.data };
if (cleanedData.items && Array.isArray(cleanedData.items)) {
cleanedData.items = cleanTextFields(cleanedData.items);
}
return cleanedData;
}
catch (error) {
console.error('DEBUG - Batch Search Error:');
if (axios_1.default.isAxiosError(error)) {
console.error('Status:', (_a = error.response) === null || _a === void 0 ? void 0 : _a.status);
console.error('Status Text:', (_b = error.response) === null || _b === void 0 ? void 0 : _b.statusText);
console.error('Response Data:', JSON.stringify((_c = error.response) === null || _c === void 0 ? void 0 : _c.data, null, 2));
console.error('Request URL:', (_d = error.config) === null || _d === void 0 ? void 0 : _d.url);
console.error('Request Method:', (_e = error.config) === null || _e === void 0 ? void 0 : _e.method);
console.error('Request Headers:', JSON.stringify((_f = error.config) === null || _f === void 0 ? void 0 : _f.headers, null, 2));
console.error('Request Data:', JSON.stringify((_g = error.config) === null || _g === void 0 ? void 0 : _g.data, null, 2));
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Lỗi batch search: ${error.message} - Status: ${(_h = error.response) === null || _h === void 0 ? void 0 : _h.status} - Message: ${JSON.stringify((_j = error.response) === null || _j === void 0 ? void 0 : _j.data)}`, { itemIndex: 0 });
}
console.error('Error:', error);
throw error;
}
}
exports.batchSearch = batchSearch;
async function batchSearchAll(accessToken, appToken, tableId, options) {
const allItems = [];
let hasMore = true;
let pageToken = '';
let iterationCount = 0;
const MAX_ITERATIONS = 100;
let previousPageToken = null;
if (!options.page_size || options.page_size <= 0) {
options.page_size = 500;
}
try {
while (hasMore && iterationCount < MAX_ITERATIONS) {
iterationCount++;
const searchOptions = { ...options };
if (pageToken) {
searchOptions.page_token = pageToken;
}
else {
delete searchOptions.page_token;
}
console.log(`DEBUG - Batch Search Iteration ${iterationCount}:`);
console.log(`Page Token: ${pageToken || 'None'}`);
console.log(`Items collected so far: ${allItems.length}`);
const response = await batchSearch.call(this, accessToken, appToken, tableId, searchOptions);
if (response.items && Array.isArray(response.items)) {
allItems.push(...response.items);
console.log(`Added ${response.items.length} items`);
}
previousPageToken = pageToken;
hasMore = response.has_more;
pageToken = response.page_token;
if (pageToken && pageToken === previousPageToken) {
console.log('Page token unchanged, possible infinite loop. Stopping pagination.');
hasMore = false;
}
if (!pageToken || pageToken === '') {
console.log('No page token returned, stopping pagination');
hasMore = false;
}
if (hasMore) {
await new Promise(resolve => setTimeout(resolve, 100));
}
}
if (iterationCount >= MAX_ITERATIONS) {
console.warn(`WARNING: Reached maximum number of iterations (${MAX_ITERATIONS}). Some data may be missing.`);
}
console.log(`Total items collected: ${allItems.length}`);
return allItems;
}
catch (error) {
console.error('ERROR in batchSearchAll:', error);
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Lỗi khi lấy tất cả dữ liệu: ${error instanceof Error ? error.message : 'Unknown error'}`, { itemIndex: 0 });
}
}
exports.batchSearchAll = batchSearchAll;
//# sourceMappingURL=batchSearch.js.map
;