n8n-nodes-close-crm
Version:
N8N community node for Close CRM integration with comprehensive lead, opportunity, task, note, call management, and enhanced triggers for workflow automation
172 lines (171 loc) • 7.39 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.closeApiRequest = closeApiRequest;
exports.closeApiRequestAllItems = closeApiRequestAllItems;
exports.convertPlainTextToHTML = convertPlainTextToHTML;
const n8n_workflow_1 = require("n8n-workflow");
const n8n_workflow_2 = require("n8n-workflow");
async function closeApiRequest(method, resource, body = {}, qs = {}, option = {}) {
var _a, _b, _c;
const options = {
headers: {
'Content-Type': 'application/json',
},
method,
qs,
body,
url: `https://api.close.com/api/v1${resource}`,
json: true,
};
// For PUT/PATCH requests, always send a body (at least empty JSON {})
// For other methods, delete empty body
if (Object.keys(body).length === 0 && method !== 'PUT' && method !== 'PATCH') {
delete options.body;
}
if (Object.keys(option).length !== 0) {
Object.assign(options, option);
}
const credentials = await this.getCredentials('closeApi');
if (!credentials || !credentials.apiKey) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Close API credentials are missing or invalid');
}
options.auth = {
username: credentials.apiKey,
password: '',
};
try {
return await this.helpers.httpRequest(options);
}
catch (error) {
if (error.response) {
const statusCode = error.response.status;
const errorMessage = ((_a = error.response.data) === null || _a === void 0 ? void 0 : _a.error) || ((_b = error.response.data) === null || _b === void 0 ? void 0 : _b.message) || error.message;
const errorDetails = ((_c = error.response.data) === null || _c === void 0 ? void 0 : _c.errors) || error.response.data;
switch (statusCode) {
case 400:
throw new n8n_workflow_2.NodeApiError(this.getNode(), {
message: `Bad Request: ${errorMessage}`,
description: `The request was invalid. Details: ${JSON.stringify(errorDetails, null, 2)}\n\nRequest: ${method} ${resource}\nBody: ${JSON.stringify(body, null, 2)}`,
});
case 401:
throw new n8n_workflow_2.NodeApiError(this.getNode(), {
message: 'Invalid API key. Please check your Close CRM credentials.',
description: 'The API key provided is not valid or has expired.',
});
case 403:
throw new n8n_workflow_2.NodeApiError(this.getNode(), {
message: 'Access forbidden. Your API key does not have permission for this operation.',
description: 'Check that your Close account has the necessary permissions.',
});
case 404:
throw new n8n_workflow_2.NodeApiError(this.getNode(), {
message: 'Resource not found.',
description: 'The requested resource does not exist or may have been deleted.',
});
case 429:
throw new n8n_workflow_2.NodeApiError(this.getNode(), {
message: 'Rate limit exceeded. Please try again later.',
description: 'You have made too many requests. Please wait before trying again.',
});
case 500:
throw new n8n_workflow_2.NodeApiError(this.getNode(), {
message: 'Close CRM server error. Please try again later.',
description: "There was an internal server error on Close CRM's side.",
});
default:
throw new n8n_workflow_2.NodeApiError(this.getNode(), {
message: `Close CRM API error (${statusCode}): ${errorMessage}`,
description: 'An unexpected error occurred while communicating with Close CRM.',
});
}
}
throw new n8n_workflow_2.NodeApiError(this.getNode(), error);
}
}
async function closeApiRequestAllItems(propertyName, method, endpoint, body = {}, query = {}) {
const returnData = [];
let responseData;
query._limit = 100;
query._skip = 0;
do {
responseData = await closeApiRequest.call(this, method, endpoint, body, query);
query._skip = returnData.length;
returnData.push.apply(returnData, responseData[propertyName]);
} while (responseData.has_more !== false);
return returnData;
}
/**
* Convert plain text with newlines to proper HTML format required by Close CRM
* Close CRM expects rich text fields to have proper HTML structure with <p> tags
*/
/**
* Convert plain text with newlines to proper HTML format required by Close CRM
* Close CRM expects rich text fields to have proper HTML structure with <p> tags
*/
function convertPlainTextToHTML(text) {
// If the text is already HTML with body tags, return as-is
if (text.includes('<body>') || text.includes('<body ')) {
return text;
}
// Split by lines first to analyze the structure
const lines = text.split('\n');
const result = [];
let i = 0;
while (i < lines.length) {
const line = lines[i];
const trimmed = line.trim();
// Skip empty lines
if (!trimmed) {
i++;
continue;
}
// Check if this line starts a list
if (trimmed.startsWith('-') || trimmed.startsWith('*')) {
// Collect consecutive list items
const listItems = [];
while (i < lines.length) {
const currentLine = lines[i].trim();
if (!currentLine) {
i++;
break; // Empty line ends the list
}
if (currentLine.startsWith('-') || currentLine.startsWith('*')) {
// Remove the leading - or * and any whitespace
const content = currentLine.replace(/^[-*]\s*/, '');
listItems.push(`<li>${content}</li>`);
i++;
}
else {
// Non-list line ends the list
break;
}
}
if (listItems.length > 0) {
result.push(`<ul>${listItems.join('')}</ul>`);
}
}
else {
// Regular paragraph - collect lines until empty line or list
const paragraphLines = [trimmed];
i++;
while (i < lines.length) {
const nextLine = lines[i];
const nextTrimmed = nextLine.trim();
// Empty line ends paragraph
if (!nextTrimmed) {
i++;
break;
}
// List item ends paragraph
if (nextTrimmed.startsWith('-') || nextTrimmed.startsWith('*')) {
break;
}
paragraphLines.push(nextTrimmed);
i++;
}
result.push(`<p>${paragraphLines.join('<br>')}</p>`);
}
}
// Wrap in body tags
return `<body>${result.join('')}</body>`;
}