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

288 lines 12.7 kB
"use strict"; 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