@tuanltntu/n8n-nodes-bitrix24
Version:
Comprehensive n8n community node for Bitrix24 API integration with CRM, Tasks, Chat, Telephony, and more
936 lines • 44 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TimelineResourceHandler = void 0;
const n8n_workflow_1 = require("n8n-workflow");
const ResourceHandlerBase_1 = require("./ResourceHandlerBase");
/**
* Handler for Timeline resource operations in Bitrix24
*/
class TimelineResourceHandler extends ResourceHandlerBase_1.ResourceHandlerBase {
constructor() {
super(...arguments);
this.resourceEndpoints = {
timeline: {
// Comment methods
getComments: "crm.timeline.comment.list",
getComment: "crm.timeline.comment.get",
addComment: "crm.timeline.comment.add",
updateComment: "crm.timeline.comment.update",
deleteComment: "crm.timeline.comment.delete",
getCommentFields: "crm.timeline.comment.fields",
// Note methods
getNote: "crm.timeline.note.get",
saveNote: "crm.timeline.note.save",
deleteNote: "crm.timeline.note.delete",
// Bindings methods
bind: "crm.timeline.bindings.bind",
getBindings: "crm.timeline.bindings.list",
unbind: "crm.timeline.bindings.unbind",
getBindingsFields: "crm.timeline.bindings.fields",
// Layout blocks methods
setLayoutBlocks: "crm.timeline.layout.blocks.set",
getLayoutBlocks: "crm.timeline.layout.blocks.get",
deleteLayoutBlocks: "crm.timeline.layout.blocks.delete",
// Log message methods
addLogMessage: "crm.timeline.logmessage.add",
getLogMessage: "crm.timeline.logmessage.get",
getLogMessages: "crm.timeline.logmessage.list",
deleteLogMessage: "crm.timeline.logmessage.delete",
},
};
}
/**
* Helper function to convert entityTypeId (number) to entityType (string)
*/
getEntityTypeFromId(entityTypeId) {
const entityTypeMap = {
1: "lead",
2: "deal",
3: "contact",
4: "company",
7: "quote",
31: "invoice",
45: "smart_process",
};
return entityTypeMap[entityTypeId] || "unknown";
}
/**
* Prepare filter parameters for timeline API calls, converting ENTITY_TYPE_ID to ENTITY_TYPE when needed
*/
prepareTimelineFilter(filter, debugMode = false) {
const preparedFilter = { ...filter };
// Convert ENTITY_TYPE_ID to ENTITY_TYPE if present
if (preparedFilter.ENTITY_TYPE_ID !== undefined &&
preparedFilter.ENTITY_TYPE === undefined) {
const entityTypeId = typeof preparedFilter.ENTITY_TYPE_ID === "string"
? parseInt(preparedFilter.ENTITY_TYPE_ID, 10)
: preparedFilter.ENTITY_TYPE_ID;
const entityType = this.getEntityTypeFromId(entityTypeId);
if (debugMode) {
console.log(`[DEBUG] Converting ENTITY_TYPE_ID ${entityTypeId} to ENTITY_TYPE: ${entityType}`);
}
// Set the correct parameter
preparedFilter.ENTITY_TYPE = entityType;
// Remove the incorrect parameter to avoid conflicts
delete preparedFilter.ENTITY_TYPE_ID;
}
// Ensure ENTITY_ID is a number
if (preparedFilter.ENTITY_ID &&
typeof preparedFilter.ENTITY_ID === "string") {
preparedFilter.ENTITY_ID = parseInt(preparedFilter.ENTITY_ID, 10);
if (debugMode) {
console.log(`[DEBUG] Converted ENTITY_ID to number: ${preparedFilter.ENTITY_ID}`);
}
}
return preparedFilter;
}
/**
* Handle API errors for timeline operations
*/
async handleTimelineError(error, itemIndex, entityInfo) {
// Check if this is an access denied error
const errorMsg = error.message || "";
const isAccessDeniedError = errorMsg.toLowerCase().includes("access denied") ||
(error.statusCode &&
(error.statusCode === 401 || error.statusCode === 403));
if (isAccessDeniedError) {
// Create a helpful response instead of throwing an error
const helpfulResponse = {
result: [],
error: true,
error_description: "Access denied to timeline API",
troubleshooting: [
"Make sure your webhook has the 'crm' scope enabled",
"The entity you're trying to access may not exist or you don't have permission to view it",
"Timeline API often requires administrator permissions in Bitrix24",
"Some Bitrix24 plans restrict access to timeline API",
],
};
// Try to get entity data if available
if (entityInfo && entityInfo.entityType && entityInfo.entityId) {
try {
// Try to get the entity data directly
const entityResponse = await this.makeApiCall(`crm.${entityInfo.entityType}.get`, { id: entityInfo.entityId }, {}, itemIndex);
if (entityResponse && entityResponse.result) {
helpfulResponse.entity_data = entityResponse.result;
helpfulResponse.note =
"Access denied to timeline API, but entity data is available";
}
}
catch (entityError) {
// Ignore errors when getting entity data
}
}
return helpfulResponse;
}
// For other types of errors, rethrow
throw error;
}
/**
* Try alternative approaches to get timeline data when the primary method fails
*/
async tryAlternativeTimelineApproach(entityType, entityId, itemIndex) {
try {
// Try general timeline endpoint instead of comments-specific endpoint
const endpoint = "crm.timeline.list";
const filter = {
ENTITY_TYPE: entityType,
ENTITY_ID: entityId,
};
console.log(`[DEBUG] Trying alternative endpoint ${endpoint} for entity type ${entityType}, ID ${entityId}`);
const response = await this.makeApiCall(endpoint, { FILTER: filter }, {}, itemIndex);
if (response && response.result) {
// Filter for comments only
const timelineItems = response.result;
const commentItems = Array.isArray(timelineItems)
? timelineItems.filter((item) => item.TYPE_ID === "comment" || item.COMMENT)
: [];
// Return a modified response
return {
result: commentItems,
total: commentItems.length,
note: "Retrieved from general timeline API due to access restrictions",
_approach: "alternative_endpoint",
};
}
}
catch (error) {
console.log(`[DEBUG] Alternative timeline approach failed: ${error.message}`);
}
return null;
}
/**
* Process all timeline operations
*/
async process() {
for (let i = 0; i < this.items.length; i++) {
try {
const operation = this.getNodeParameter("operation", i);
switch (operation) {
// Comment operations
case "getComments":
await this.handleGetComments(i);
break;
case "getComment":
await this.handleGetComment(i);
break;
case "addComment":
await this.handleAddComment(i);
break;
case "updateComment":
await this.handleUpdateComment(i);
break;
case "deleteComment":
await this.handleDeleteComment(i);
break;
case "getCommentFields":
await this.handleGetCommentFields(i);
break;
// Note operations
case "getNote":
await this.handleGetNote(i);
break;
case "saveNote":
await this.handleSaveNote(i);
break;
case "deleteNote":
await this.handleDeleteNote(i);
break;
// Bindings operations
case "bind":
await this.handleBind(i);
break;
case "getBindings":
await this.handleGetBindings(i);
break;
case "unbind":
await this.handleUnbind(i);
break;
case "getBindingsFields":
await this.handleGetBindingsFields(i);
break;
// Layout blocks operations
case "setLayoutBlocks":
await this.handleSetLayoutBlocks(i);
break;
case "getLayoutBlocks":
await this.handleGetLayoutBlocks(i);
break;
case "deleteLayoutBlocks":
await this.handleDeleteLayoutBlocks(i);
break;
// Log message operations
case "addLogMessage":
await this.handleAddLogMessage(i);
break;
case "getLogMessage":
await this.handleGetLogMessage(i);
break;
case "getLogMessages":
await this.handleGetLogMessages(i);
break;
case "deleteLogMessage":
await this.handleDeleteLogMessage(i);
break;
case "getTimeline":
throw new n8n_workflow_1.NodeOperationError(this.executeFunctions.getNode(), `Operation 'getTimeline' is not supported. Please use specific endpoints like getComments, getLogMessages, etc.`, { itemIndex: i });
default:
throw new n8n_workflow_1.NodeOperationError(this.executeFunctions.getNode(), `Operation '${operation}' is not supported for Timeline resource`, { itemIndex: i });
}
}
catch (error) {
if (this.continueOnFail()) {
this.addErrorToReturnData(error, i);
continue;
}
throw error;
}
}
return this.returnData;
}
/**
* Handles retrieving comments from a timeline
*/
async handleGetComments(itemIndex) {
const entityTypeId = parseInt(this.getNodeParameter("entityTypeId", itemIndex), 10);
const entityId = parseInt(this.getNodeParameter("entityId", itemIndex), 10);
const options = this.getNodeParameter("options", itemIndex, {});
// Check if entityTypeId and entityId are valid numbers
if (isNaN(entityTypeId)) {
throw new n8n_workflow_1.NodeOperationError(this.executeFunctions.getNode(), "Entity Type ID must be a valid number", { itemIndex });
}
if (isNaN(entityId)) {
throw new n8n_workflow_1.NodeOperationError(this.executeFunctions.getNode(), "Entity ID must be a valid number", { itemIndex });
}
// Convert entity type ID to entity type string as required by the API
const entityType = this.getEntityTypeFromId(entityTypeId);
// Prepare filter parameters
const filter = {
ENTITY_TYPE: entityType,
ENTITY_ID: entityId,
};
// Add additional filter if specified
if (options.filter) {
try {
const customFilter = JSON.parse(options.filter);
Object.assign(filter, customFilter);
}
catch (error) {
throw new n8n_workflow_1.NodeOperationError(this.executeFunctions.getNode(), "Filter must be a valid JSON", { itemIndex });
}
}
// Build the request parameters according to Bitrix24 API format
const parameters = {};
// Using uppercase parameter names per Bitrix24 API standards
parameters.FILTER = filter;
// Add order if specified
if (options.order) {
parameters.ORDER = { CREATED: options.order };
}
// Add select if specified
if (options.select) {
parameters.SELECT = options.select;
}
// Add raw parameters if specified
if (options.rawParameters) {
try {
const rawParams = JSON.parse(options.rawParameters);
Object.assign(parameters, rawParams);
}
catch (error) {
throw new n8n_workflow_1.NodeOperationError(this.executeFunctions.getNode(), "Raw Parameters must be a valid JSON", { itemIndex });
}
}
const endpoint = this.resourceEndpoints.timeline.getComments;
// Make the API call
try {
const responseData = await this.makeApiCall(endpoint, parameters, {}, itemIndex, false);
// Store the response in the node data - Fix for 'call' undefined issue
if (this.executeFunctions.helpers &&
this.executeFunctions.helpers.constructExecutionMetaData) {
this.executeFunctions.helpers.constructExecutionMetaData(this.executeFunctions.helpers.returnJsonArray([responseData]), { itemData: { item: 0 } });
}
else {
// Fallback if helpers or constructExecutionMetaData is not available
this.returnData.push({
json: responseData,
});
}
}
catch (error) {
// Try to handle the error
try {
const alternativeData = await this.handleTimelineError(error, itemIndex, {
entityType,
entityId,
});
// If we get here, we have a useful alternative response - Fix for 'call' undefined issue
if (this.executeFunctions.helpers &&
this.executeFunctions.helpers.constructExecutionMetaData) {
this.executeFunctions.helpers.constructExecutionMetaData(this.executeFunctions.helpers.returnJsonArray([alternativeData]), { itemData: { item: 0 } });
}
else {
// Fallback if helpers or constructExecutionMetaData is not available
this.returnData.push({
json: alternativeData,
});
}
}
catch (finalError) {
// If all else fails, throw the original error
throw error;
}
}
}
/**
* Handles adding a comment to an entity
*/
async handleAddComment(itemIndex) {
const entityTypeId = parseInt(this.getNodeParameter("entityTypeId", itemIndex), 10);
const entityId = parseInt(this.getNodeParameter("entityId", itemIndex), 10);
const comment = this.getNodeParameter("comment", itemIndex);
const additionalFields = this.getNodeParameter("additionalFields", itemIndex, {});
// Check if entityTypeId and entityId are valid numbers
if (isNaN(entityTypeId)) {
throw new n8n_workflow_1.NodeOperationError(this.executeFunctions.getNode(), "Entity Type ID must be a valid number", { itemIndex });
}
if (isNaN(entityId)) {
throw new n8n_workflow_1.NodeOperationError(this.executeFunctions.getNode(), "Entity ID must be a valid number", { itemIndex });
}
// Validate that comment isn't empty
if (!comment || comment.trim() === "") {
throw new n8n_workflow_1.NodeOperationError(this.executeFunctions.getNode(), "Comment text cannot be empty", { itemIndex });
}
// Validate that entityTypeId is one of the accepted values
const validEntityTypeIds = [1, 2, 3, 4, 7, 31, 45];
if (!validEntityTypeIds.includes(entityTypeId)) {
throw new n8n_workflow_1.NodeOperationError(this.executeFunctions.getNode(), `Invalid Entity Type ID: ${entityTypeId}. Must be one of these values: ${validEntityTypeIds.join(", ")}`, { itemIndex });
}
// Convert entityTypeId to entityType string
const entityType = this.getEntityTypeFromId(entityTypeId);
// Create fields object according to API docs
const fields = {
ENTITY_ID: entityId,
ENTITY_TYPE: entityType,
COMMENT: comment,
};
// Add author ID if specified (must be an integer)
if (additionalFields.authorId) {
const authorId = parseInt(additionalFields.authorId, 10);
if (isNaN(authorId)) {
throw new n8n_workflow_1.NodeOperationError(this.executeFunctions.getNode(), "Author ID must be a valid number", { itemIndex });
}
fields.AUTHOR_ID = authorId;
}
// Add files if specified (must be an array of file IDs)
if (additionalFields.files) {
const filesInput = additionalFields.files;
if (filesInput.trim() !== "") {
const fileIds = filesInput.split(",").map((id) => id.trim());
// Validate each file ID is a number
const invalidIds = fileIds.filter((id) => isNaN(parseInt(id, 10)));
if (invalidIds.length > 0) {
throw new n8n_workflow_1.NodeOperationError(this.executeFunctions.getNode(), `Invalid file IDs: ${invalidIds.join(", ")}. All file IDs must be valid numbers.`, { itemIndex });
}
fields.FILES = fileIds;
}
}
// Parameter structure according to API docs
const parameters = {
FIELDS: fields,
};
const endpoint = this.resourceEndpoints.timeline.addComment;
try {
const responseData = await this.makeApiCall(endpoint, parameters, {}, itemIndex);
// Add null check for helpers object
if (this.executeFunctions.helpers &&
this.executeFunctions.helpers.constructExecutionMetaData) {
this.executeFunctions.helpers.constructExecutionMetaData(this.executeFunctions.helpers.returnJsonArray([responseData]), { itemData: { item: 0 } });
}
else {
// Fallback if helpers or constructExecutionMetaData is not available
this.returnData.push({
json: responseData,
});
}
}
catch (error) {
throw error;
}
}
/**
* Handles updating a comment
*/
async handleUpdateComment(itemIndex) {
const commentId = this.getNodeParameter("commentId", itemIndex);
const comment = this.getNodeParameter("comment", itemIndex);
const updateFields = this.getNodeParameter("updateFields", itemIndex, {});
// Validate commentId is not empty
if (!commentId || commentId.trim() === "") {
throw new n8n_workflow_1.NodeOperationError(this.executeFunctions.getNode(), "Comment ID cannot be empty", { itemIndex });
}
// Validate that comment isn't empty
if (!comment || comment.trim() === "") {
throw new n8n_workflow_1.NodeOperationError(this.executeFunctions.getNode(), "Comment text cannot be empty", { itemIndex });
}
// Create fields object according to API docs
const fields = {
COMMENT: comment,
};
// Add files if specified (must be an array of file IDs)
if (updateFields.files) {
const filesInput = updateFields.files;
if (filesInput.trim() !== "") {
const fileIds = filesInput.split(",").map((id) => id.trim());
// Validate each file ID is a number
const invalidIds = fileIds.filter((id) => isNaN(parseInt(id, 10)));
if (invalidIds.length > 0) {
throw new n8n_workflow_1.NodeOperationError(this.executeFunctions.getNode(), `Invalid file IDs: ${invalidIds.join(", ")}. All file IDs must be valid numbers.`, { itemIndex });
}
fields.FILES = fileIds;
}
}
// Parameter structure according to API docs
const parameters = {
ID: commentId,
FIELDS: fields,
};
const endpoint = this.resourceEndpoints.timeline.updateComment;
const responseData = this.makeApiCall(endpoint, parameters, {}, itemIndex);
this.addResponseToReturnData(responseData, itemIndex);
}
/**
* Handles deleting a comment
*/
async handleDeleteComment(itemIndex) {
const commentId = this.getNodeParameter("commentId", itemIndex);
const parameters = {
ID: commentId,
};
const endpoint = this.resourceEndpoints.timeline.deleteComment;
try {
const responseData = await this.makeApiCall(endpoint, parameters, {}, itemIndex);
// Add null check for helpers object
if (this.executeFunctions.helpers &&
this.executeFunctions.helpers.constructExecutionMetaData) {
this.executeFunctions.helpers.constructExecutionMetaData(this.executeFunctions.helpers.returnJsonArray([responseData]), { itemData: { item: 0 } });
}
else {
// Fallback if helpers or constructExecutionMetaData is not available
this.returnData.push({
json: responseData,
});
}
}
catch (error) {
throw error;
}
}
/**
* Handles binding an entity to another entity's timeline
*/
async handleBind(itemIndex) {
const entityTypeId = parseInt(this.getNodeParameter("entityTypeId", itemIndex), 10);
const entityId = parseInt(this.getNodeParameter("entityId", itemIndex), 10);
const targetEntityTypeId = parseInt(this.getNodeParameter("targetEntityTypeId", itemIndex), 10);
const targetEntityId = parseInt(this.getNodeParameter("targetEntityId", itemIndex), 10);
// Check if all IDs are valid numbers
if (isNaN(entityTypeId) ||
isNaN(entityId) ||
isNaN(targetEntityTypeId) ||
isNaN(targetEntityId)) {
throw new n8n_workflow_1.NodeOperationError(this.executeFunctions.getNode(), "Entity Type ID, Entity ID, Target Entity Type ID, and Target Entity ID must be valid numbers", { itemIndex });
}
// Convert entityTypeId and targetEntityTypeId to entityType strings
const entityType = this.getEntityTypeFromId(entityTypeId);
const targetEntityType = this.getEntityTypeFromId(targetEntityTypeId);
const parameters = {
ENTITY_TYPE: entityType,
ENTITY_ID: entityId,
TARGET_ENTITY_TYPE: targetEntityType,
TARGET_ENTITY_ID: targetEntityId,
};
const endpoint = this.resourceEndpoints.timeline.bind;
const responseData = this.makeApiCall(endpoint, parameters, {}, itemIndex);
this.addResponseToReturnData(responseData, itemIndex);
}
/**
* Handles unbinding an entity from another entity's timeline
*/
async handleUnbind(itemIndex) {
const entityTypeId = parseInt(this.getNodeParameter("entityTypeId", itemIndex), 10);
const entityId = parseInt(this.getNodeParameter("entityId", itemIndex), 10);
const targetEntityTypeId = parseInt(this.getNodeParameter("targetEntityTypeId", itemIndex), 10);
const targetEntityId = parseInt(this.getNodeParameter("targetEntityId", itemIndex), 10);
// Check if all IDs are valid numbers
if (isNaN(entityTypeId) ||
isNaN(entityId) ||
isNaN(targetEntityTypeId) ||
isNaN(targetEntityId)) {
throw new n8n_workflow_1.NodeOperationError(this.executeFunctions.getNode(), "Entity Type ID, Entity ID, Target Entity Type ID, and Target Entity ID must be valid numbers", { itemIndex });
}
// Convert entityTypeId and targetEntityTypeId to entityType strings
const entityType = this.getEntityTypeFromId(entityTypeId);
const targetEntityType = this.getEntityTypeFromId(targetEntityTypeId);
// Use uppercase parameters per Bitrix24 API standards
const parameters = {
ENTITY_TYPE: entityType,
ENTITY_ID: entityId,
TARGET_ENTITY_TYPE: targetEntityType,
TARGET_ENTITY_ID: targetEntityId,
};
const endpoint = this.resourceEndpoints.timeline.unbind;
try {
const responseData = await this.makeApiCall(endpoint, parameters, {}, itemIndex);
// Add null check for helpers object
if (this.executeFunctions.helpers &&
this.executeFunctions.helpers.constructExecutionMetaData) {
this.executeFunctions.helpers.constructExecutionMetaData(this.executeFunctions.helpers.returnJsonArray([responseData]), { itemData: { item: 0 } });
}
else {
// Fallback if helpers or constructExecutionMetaData is not available
this.returnData.push({
json: responseData,
});
}
}
catch (error) {
// Try to provide a more helpful error message for common issues
if (error.message && error.message.includes("not found")) {
throw new n8n_workflow_1.NodeOperationError(this.executeFunctions.getNode(), `The binding between the entities could not be found. Please verify the entity IDs are correct and that a binding exists.`, { itemIndex });
}
throw error;
}
}
/**
* Handles getting bindings for an entity
*/
async handleGetBindings(itemIndex) {
const entityTypeId = parseInt(this.getNodeParameter("entityTypeId", itemIndex), 10);
const entityId = parseInt(this.getNodeParameter("entityId", itemIndex), 10);
// Check if entityTypeId and entityId are valid numbers
if (isNaN(entityTypeId) || isNaN(entityId)) {
throw new n8n_workflow_1.NodeOperationError(this.executeFunctions.getNode(), "Entity Type ID and Entity ID must be valid numbers", { itemIndex });
}
// Validate entity type ID is valid
const validEntityTypeIds = [1, 2, 3, 4, 7, 31, 45]; // Valid entity type IDs in Bitrix24
if (!validEntityTypeIds.includes(entityTypeId)) {
throw new n8n_workflow_1.NodeOperationError(this.executeFunctions.getNode(), `Invalid Entity Type ID: ${entityTypeId}. Must be one of: ${validEntityTypeIds.join(", ")}`, { itemIndex });
}
// Convert entityTypeId to entityType string
const entityType = this.getEntityTypeFromId(entityTypeId);
// Use uppercase parameter names per Bitrix24 API standards
const parameters = {
ENTITY_TYPE: entityType,
ENTITY_ID: entityId,
};
const endpoint = this.resourceEndpoints.timeline.getBindings;
try {
const responseData = await this.makeApiCall(endpoint, parameters, {}, itemIndex);
// Add null check for helpers object
if (this.executeFunctions.helpers &&
this.executeFunctions.helpers.constructExecutionMetaData) {
this.executeFunctions.helpers.constructExecutionMetaData(this.executeFunctions.helpers.returnJsonArray([responseData]), { itemData: { item: 0 } });
}
else {
// Fallback if helpers or constructExecutionMetaData is not available
this.returnData.push({
json: responseData,
});
}
}
catch (error) {
// Try to provide helpful context for common errors
if (error.message && error.message.includes("access denied")) {
throw new n8n_workflow_1.NodeOperationError(this.executeFunctions.getNode(), `Access denied to get bindings. Make sure your webhook has sufficient permissions for entity type ${entityType} (ID: ${entityId}).`, { itemIndex });
}
else if (error.message && error.message.includes("not found")) {
throw new n8n_workflow_1.NodeOperationError(this.executeFunctions.getNode(), `Entity with type ${entityType} and ID ${entityId} not found or has no bindings.`, { itemIndex });
}
throw error;
}
}
/**
* Handles getting a specific comment
*/
async handleGetComment(itemIndex) {
const commentId = this.getNodeParameter("commentId", itemIndex);
// Check if commentId is valid
if (!commentId || commentId.trim() === "") {
throw new n8n_workflow_1.NodeOperationError(this.executeFunctions.getNode(), "Comment ID cannot be empty", { itemIndex });
}
// Use uppercase parameter name per Bitrix24 API standards
const parameters = {
ID: commentId,
};
const endpoint = this.resourceEndpoints.timeline.getComment;
try {
const responseData = await this.makeApiCall(endpoint, parameters, {}, itemIndex);
// Add null check for helpers object
if (this.executeFunctions.helpers &&
this.executeFunctions.helpers.constructExecutionMetaData) {
this.executeFunctions.helpers.constructExecutionMetaData(this.executeFunctions.helpers.returnJsonArray([responseData]), { itemData: { item: 0 } });
}
else {
// Fallback if helpers or constructExecutionMetaData is not available
this.returnData.push({
json: responseData,
});
}
}
catch (error) {
throw error;
}
}
/**
* Handles getting comment fields
*/
async handleGetCommentFields(itemIndex) {
const endpoint = this.resourceEndpoints.timeline.getCommentFields;
const responseData = this.makeApiCall(endpoint, {}, {}, itemIndex);
this.addResponseToReturnData(responseData, itemIndex);
}
/**
* Handles getting a note
*/
async handleGetNote(itemIndex) {
const noteId = this.getNodeParameter("noteId", itemIndex);
const parameters = {
ID: noteId,
};
const endpoint = this.resourceEndpoints.timeline.getNote;
const responseData = this.makeApiCall(endpoint, parameters, {}, itemIndex);
this.addResponseToReturnData(responseData, itemIndex);
}
/**
* Handles saving a note
*/
async handleSaveNote(itemIndex) {
const entityTypeId = parseInt(this.getNodeParameter("entityTypeId", itemIndex), 10);
const entityId = parseInt(this.getNodeParameter("entityId", itemIndex), 10);
const comment = this.getNodeParameter("COMMENT", itemIndex);
const noteFields = this.getNodeParameter("noteFields", itemIndex, {});
// Check if entityTypeId and entityId are valid numbers
if (isNaN(entityTypeId) || isNaN(entityId)) {
throw new n8n_workflow_1.NodeOperationError(this.executeFunctions.getNode(), "Entity Type ID and Entity ID must be valid numbers", { itemIndex });
}
// Convert entityTypeId to entityType string
const entityType = this.getEntityTypeFromId(entityTypeId);
// Create the fields object with the required parameters
const fields = {
ENTITY_ID: entityId,
ENTITY_TYPE: entityType,
COMMENT: comment,
};
// Add additional fields if specified
if (noteFields && Object.keys(noteFields).length > 0) {
Object.assign(fields, noteFields);
}
// Format parameters according to API requirements
const parameters = {
FIELDS: fields,
};
const endpoint = this.resourceEndpoints.timeline.saveNote;
const responseData = this.makeApiCall(endpoint, parameters, {}, itemIndex);
this.addResponseToReturnData(responseData, itemIndex);
}
/**
* Handles deleting a note
*/
async handleDeleteNote(itemIndex) {
const noteId = this.getNodeParameter("noteId", itemIndex);
const parameters = {
ID: noteId,
};
const endpoint = this.resourceEndpoints.timeline.deleteNote;
const responseData = this.makeApiCall(endpoint, parameters, {}, itemIndex);
this.addResponseToReturnData(responseData, itemIndex);
}
/**
* Handles getting binding fields
*/
async handleGetBindingsFields(itemIndex) {
const endpoint = this.resourceEndpoints.timeline.getBindingsFields;
const responseData = this.makeApiCall(endpoint, {}, {}, itemIndex);
this.addResponseToReturnData(responseData, itemIndex);
}
/**
* Handles setting layout blocks
*/
async handleSetLayoutBlocks(itemIndex) {
const entityTypeId = this.getNodeParameter("entityTypeId", itemIndex);
const entityId = this.getNodeParameter("entityId", itemIndex);
const blocks = this.getNodeParameter("blocks", itemIndex);
let parsedBlocks;
try {
parsedBlocks = JSON.parse(blocks);
}
catch (error) {
throw new n8n_workflow_1.NodeOperationError(this.executeFunctions.getNode(), "Blocks must be a valid JSON array", { itemIndex });
}
const parameters = {
ENTITY_TYPE_ID: entityTypeId,
ENTITY_ID: entityId,
BLOCKS: parsedBlocks,
};
const endpoint = this.resourceEndpoints.timeline.setLayoutBlocks;
const responseData = this.makeApiCall(endpoint, parameters, {}, itemIndex);
this.addResponseToReturnData(responseData, itemIndex);
}
/**
* Handles getting layout blocks
*/
async handleGetLayoutBlocks(itemIndex) {
const entityTypeId = this.getNodeParameter("entityTypeId", itemIndex);
const entityId = this.getNodeParameter("entityId", itemIndex);
const parameters = {
ENTITY_TYPE_ID: entityTypeId,
ENTITY_ID: entityId,
};
const endpoint = this.resourceEndpoints.timeline.getLayoutBlocks;
const responseData = this.makeApiCall(endpoint, parameters, {}, itemIndex);
this.addResponseToReturnData(responseData, itemIndex);
}
/**
* Handles deleting layout blocks
*/
async handleDeleteLayoutBlocks(itemIndex) {
const entityTypeId = this.getNodeParameter("entityTypeId", itemIndex);
const entityId = this.getNodeParameter("entityId", itemIndex);
const parameters = {
ENTITY_TYPE_ID: entityTypeId,
ENTITY_ID: entityId,
};
const endpoint = this.resourceEndpoints.timeline.deleteLayoutBlocks;
const responseData = this.makeApiCall(endpoint, parameters, {}, itemIndex);
this.addResponseToReturnData(responseData, itemIndex);
}
/**
* Handles adding a log message
*/
async handleAddLogMessage(itemIndex) {
const entityTypeId = parseInt(this.getNodeParameter("entityTypeId", itemIndex), 10);
const entityId = parseInt(this.getNodeParameter("entityId", itemIndex), 10);
const messageText = this.getNodeParameter("messageText", itemIndex);
const additionalFields = this.getNodeParameter("additionalFields", itemIndex, {});
// Check if entityTypeId and entityId are valid numbers
if (isNaN(entityTypeId) || isNaN(entityId)) {
throw new n8n_workflow_1.NodeOperationError(this.executeFunctions.getNode(), "Entity Type ID and Entity ID must be valid numbers", { itemIndex });
}
// Convert entityTypeId to entityType string
const entityType = this.getEntityTypeFromId(entityTypeId);
const fields = {
ENTITY_ID: entityId,
ENTITY_TYPE: entityType,
MESSAGE: messageText,
};
// Add title if specified
if (additionalFields.title) {
fields.TITLE = additionalFields.title;
}
// Add author ID if specified
if (additionalFields.authorId) {
fields.AUTHOR_ID = parseInt(additionalFields.authorId, 10);
}
const parameters = {
FIELDS: fields,
};
const endpoint = this.resourceEndpoints.timeline.addLogMessage;
const responseData = this.makeApiCall(endpoint, parameters, {}, itemIndex);
this.addResponseToReturnData(responseData, itemIndex);
}
/**
* Handles getting a log message
*/
async handleGetLogMessage(itemIndex) {
const logMessageId = this.getNodeParameter("logMessageId", itemIndex);
const parameters = {
ID: logMessageId,
};
const endpoint = this.resourceEndpoints.timeline.getLogMessage;
const responseData = this.makeApiCall(endpoint, parameters, {}, itemIndex);
this.addResponseToReturnData(responseData, itemIndex);
}
/**
* Handles getting log messages
*/
async handleGetLogMessages(itemIndex) {
// Initialize the parameters object
const parameters = {};
// Create the filter object
let filter = {};
// Get entityTypeId (optional)
let entityTypeParam = this.getNodeParameter("entityTypeId", itemIndex, "");
if (entityTypeParam && entityTypeParam.trim() !== "") {
const entityTypeId = parseInt(entityTypeParam, 10);
// Check if entityTypeId is a valid number
if (isNaN(entityTypeId)) {
throw new n8n_workflow_1.NodeOperationError(this.executeFunctions.getNode(), "Entity Type ID must be a valid number", { itemIndex });
}
// Validate that entityTypeId is one of the accepted values
const validEntityTypeIds = [1, 2, 3, 4, 7, 31, 45];
if (!validEntityTypeIds.includes(entityTypeId)) {
throw new n8n_workflow_1.NodeOperationError(this.executeFunctions.getNode(), `Invalid Entity Type ID: ${entityTypeId}. Must be one of these values: ${validEntityTypeIds.join(", ")}`, { itemIndex });
}
// For the Bitrix24 API, we need ENTITY_TYPE_ID in uppercase
filter.ENTITY_TYPE_ID = entityTypeId;
}
// Get entityId (optional)
let entityIdParam = this.getNodeParameter("entityId", itemIndex, "");
if (entityIdParam && entityIdParam.trim() !== "") {
const entityId = parseInt(entityIdParam, 10);
// Check if entityId is a valid number
if (isNaN(entityId)) {
throw new n8n_workflow_1.NodeOperationError(this.executeFunctions.getNode(), "Entity ID must be a valid number", { itemIndex });
}
// Add to filter - use uppercase for API compatibility
filter.ENTITY_ID = entityId;
}
// Get options
const options = this.getNodeParameter("logMessageOptions", itemIndex, {});
// Add custom filter if specified and merge with entity filter
if (options.filter) {
try {
const filterJson = options.filter;
if (filterJson) {
const parsedFilter = JSON.parse(filterJson);
// Bitrix24 API expects uppercase keys in filter
const upperCaseFilter = {};
Object.keys(parsedFilter).forEach((key) => {
upperCaseFilter[key.toUpperCase()] = parsedFilter[key];
});
// Merge the parsed filter with our entity filter
filter = {
...filter,
...upperCaseFilter,
};
}
}
catch (error) {
throw new n8n_workflow_1.NodeOperationError(this.executeFunctions.getNode(), "Filter must be a valid JSON", { itemIndex });
}
}
// Only add filter if it has keys
if (Object.keys(filter).length > 0) {
parameters.FILTER = filter;
}
// Add order if specified (uppercase)
if (options.order) {
// Check if order is a valid option
const validOrderValues = ["ASC", "DESC", "asc", "desc"];
if (!validOrderValues.includes(options.order)) {
throw new n8n_workflow_1.NodeOperationError(this.executeFunctions.getNode(), `Invalid order value: ${options.order}. Must be one of: ASC, DESC`, { itemIndex });
}
parameters.ORDER = { CREATED: options.order };
}
// Add select if specified
if (options.select) {
// If select is a string, split it by commas and trim each value
if (typeof options.select === "string") {
const selectFields = options.select
.split(",")
.map((field) => field.trim());
parameters.SELECT = selectFields;
}
else {
parameters.SELECT = options.select;
}
}
const endpoint = this.resourceEndpoints.timeline.getLogMessages;
try {
const responseData = await this.makeApiCall(endpoint, parameters, {}, itemIndex);
// Add null check for helpers object
if (this.executeFunctions.helpers &&
this.executeFunctions.helpers.constructExecutionMetaData) {
this.executeFunctions.helpers.constructExecutionMetaData(this.executeFunctions.helpers.returnJsonArray([responseData]), { itemData: { item: 0 } });
}
else {
// Fallback if helpers or constructExecutionMetaData is not available
this.returnData.push({
json: responseData,
});
}
}
catch (error) {
// Provide helpful error messages for common issues
if (error.message && error.message.includes("access denied")) {
throw new n8n_workflow_1.NodeOperationError(this.executeFunctions.getNode(), "Access denied to log messages. Ensure your webhook has sufficient permissions for the timeline module.", { itemIndex });
}
throw error;
}
}
/**
* Handles deleting a log message
*/
async handleDeleteLogMessage(itemIndex) {
const logMessageId = this.getNodeParameter("logMessageId", itemIndex);
const parameters = {
ID: logMessageId,
};
const endpoint = this.resourceEndpoints.timeline.deleteLogMessage;
const responseData = this.makeApiCall(endpoint, parameters, {}, itemIndex);
this.addResponseToReturnData(responseData, itemIndex);
}
}
exports.TimelineResourceHandler = TimelineResourceHandler;
//# sourceMappingURL=TimelineResourceHandler.js.map