n8n-nodes-idb2b 3.2.4 → 3.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -3
- package/dist/nodes/IDB2B/IDB2B.node.js +83 -9
- package/dist/nodes/IDB2B/config/constants.d.ts +1 -0
- package/dist/nodes/IDB2B/config/constants.js +1 -0
- package/dist/nodes/IDB2B/descriptions/activityProperties.d.ts +3 -0
- package/dist/nodes/IDB2B/descriptions/activityProperties.js +258 -0
- package/dist/nodes/IDB2B/handlers/ActivityHandler.d.ts +39 -0
- package/dist/nodes/IDB2B/handlers/ActivityHandler.js +131 -0
- package/dist/nodes/IDB2B/handlers/handlerFactory.d.ts +1 -0
- package/dist/nodes/IDB2B/handlers/handlerFactory.js +6 -2
- package/dist/nodes/IDB2B/interfaces/IDB2BActivity.d.ts +14 -0
- package/dist/nodes/IDB2B/interfaces/IDB2BActivity.js +2 -0
- package/dist/nodes/IDB2B/utils/httpClient.d.ts +1 -0
- package/dist/nodes/IDB2BTrigger/IDB2BTrigger.node.d.ts +5 -0
- package/dist/nodes/IDB2BTrigger/IDB2BTrigger.node.js +165 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -47,13 +47,23 @@ npm install n8n-nodes-idb2b
|
|
|
47
47
|
| Update | Update an existing company |
|
|
48
48
|
| Delete | Delete a company |
|
|
49
49
|
|
|
50
|
+
### Activity
|
|
51
|
+
|
|
52
|
+
| Operation | Description |
|
|
53
|
+
|-----------|-------------|
|
|
54
|
+
| Get All | Retrieve all activities for a specific company/lead |
|
|
55
|
+
| Get | Fetch a single activity by ID |
|
|
56
|
+
| Create | Create a new activity linked to a company or contact |
|
|
57
|
+
| Update | Update an existing activity |
|
|
58
|
+
| Delete | Delete an activity |
|
|
59
|
+
|
|
50
60
|
## How to Use
|
|
51
61
|
|
|
52
62
|
### Basic workflow
|
|
53
63
|
|
|
54
|
-
1. Add the **IDB2B
|
|
64
|
+
1. Add the **IDB2B API** node to your workflow
|
|
55
65
|
2. Select your saved credential under **Credential to connect with**
|
|
56
|
-
3. Choose a **Resource** (Contact or Company)
|
|
66
|
+
3. Choose a **Resource** (Activity, Contact, or Company)
|
|
57
67
|
4. Choose an **Operation** (Get All, Get, Create, Update, Delete)
|
|
58
68
|
5. Fill in the required parameters and execute
|
|
59
69
|
|
|
@@ -90,7 +100,31 @@ Required:
|
|
|
90
100
|
- **Name**: Company name
|
|
91
101
|
|
|
92
102
|
Optional (under Additional Fields):
|
|
93
|
-
-
|
|
103
|
+
- **Website**, **Description**, **Industry ID**, **Size ID**, **Status ID**, **Source ID**, **Owner ID**
|
|
104
|
+
|
|
105
|
+
### Get All Activities
|
|
106
|
+
|
|
107
|
+
Required:
|
|
108
|
+
- **Company ID**: The company/lead to list activities for
|
|
109
|
+
- **Limit** / **Page**: Pagination controls
|
|
110
|
+
|
|
111
|
+
### Create Activity
|
|
112
|
+
|
|
113
|
+
Required:
|
|
114
|
+
- **Subject**: Title of the activity
|
|
115
|
+
- **Associate With**: Choose **Company** or **Contact**
|
|
116
|
+
- **Company ID** or **Contact ID** depending on the above
|
|
117
|
+
|
|
118
|
+
Optional (under Additional Fields):
|
|
119
|
+
- **Description**, **Date & Time**, **Icon**, **User ID**
|
|
120
|
+
|
|
121
|
+
### Update Activity
|
|
122
|
+
|
|
123
|
+
Required:
|
|
124
|
+
- **Activity ID**
|
|
125
|
+
|
|
126
|
+
Optional (under Additional Fields):
|
|
127
|
+
- Any fields to change: **Subject**, **Description**, **Date & Time**, **Icon**, **User ID**
|
|
94
128
|
|
|
95
129
|
## Example Workflows
|
|
96
130
|
|
|
@@ -110,6 +144,17 @@ Webhook → IDB2B Create Contact
|
|
|
110
144
|
- Additional Fields.LinkedIn URL: {{ $json.linkedin_url }}
|
|
111
145
|
```
|
|
112
146
|
|
|
147
|
+
### Log a call activity after a deal closes
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
Webhook → IDB2B Create Activity
|
|
151
|
+
- Subject: "Call with {{ $json.contact_name }}"
|
|
152
|
+
- Associate With: Company
|
|
153
|
+
- Company ID: {{ $json.company_id }}
|
|
154
|
+
- Additional Fields.Description: {{ $json.call_notes }}
|
|
155
|
+
- Additional Fields.Date & Time: {{ $json.call_time }}
|
|
156
|
+
```
|
|
157
|
+
|
|
113
158
|
### Paginate through all contacts
|
|
114
159
|
|
|
115
160
|
```
|
|
@@ -136,6 +181,15 @@ npm run lint
|
|
|
136
181
|
|
|
137
182
|
## Version History
|
|
138
183
|
|
|
184
|
+
### v3.2.5
|
|
185
|
+
- Added **Activity** resource with full CRUD operations (Get All, Get, Create, Update, Delete)
|
|
186
|
+
- Activities can be linked to a company or a contact
|
|
187
|
+
- Get All Activities scoped to a specific company via `GET /leads/:id/activities`
|
|
188
|
+
|
|
189
|
+
### v3.2.4
|
|
190
|
+
- Added LinkedIn URL and social links support for contacts
|
|
191
|
+
- Included socials in response data for create/update operations
|
|
192
|
+
|
|
139
193
|
### v2.0.3
|
|
140
194
|
- Fixed endpoint paths (`/contacts`, `/companies`)
|
|
141
195
|
- Fixed access token parsing from login response
|
|
@@ -4,6 +4,7 @@ exports.IDB2B = void 0;
|
|
|
4
4
|
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
5
|
const contactProperties_1 = require("./descriptions/contactProperties");
|
|
6
6
|
const companyProperties_1 = require("./descriptions/companyProperties");
|
|
7
|
+
const activityProperties_1 = require("./descriptions/activityProperties");
|
|
7
8
|
// Import new utility modules
|
|
8
9
|
const validators_1 = require("./utils/validators");
|
|
9
10
|
const errorHandler_1 = require("./utils/errorHandler");
|
|
@@ -39,6 +40,10 @@ class IDB2B {
|
|
|
39
40
|
type: "options",
|
|
40
41
|
noDataExpression: true,
|
|
41
42
|
options: [
|
|
43
|
+
{
|
|
44
|
+
name: "Activity",
|
|
45
|
+
value: "activity",
|
|
46
|
+
},
|
|
42
47
|
{
|
|
43
48
|
name: "Contact",
|
|
44
49
|
value: "contact",
|
|
@@ -50,8 +55,10 @@ class IDB2B {
|
|
|
50
55
|
],
|
|
51
56
|
default: "contact",
|
|
52
57
|
},
|
|
58
|
+
activityProperties_1.activityOperations,
|
|
53
59
|
contactProperties_1.contactOperations,
|
|
54
60
|
companyProperties_1.companyOperations,
|
|
61
|
+
...activityProperties_1.activityFields,
|
|
55
62
|
...contactProperties_1.contactFields,
|
|
56
63
|
...companyProperties_1.companyFields,
|
|
57
64
|
],
|
|
@@ -82,6 +89,7 @@ class IDB2B {
|
|
|
82
89
|
let body = undefined;
|
|
83
90
|
let qs = {};
|
|
84
91
|
let initialBody = undefined;
|
|
92
|
+
let useFormData = false;
|
|
85
93
|
if (resource === "contact") {
|
|
86
94
|
if (operation === "getAll") {
|
|
87
95
|
method = "GET";
|
|
@@ -144,6 +152,79 @@ class IDB2B {
|
|
|
144
152
|
endpoint = `${constants_1.ENDPOINTS.CONTACTS}/${(0, common_1.sanitizeId)(contactId)}`;
|
|
145
153
|
}
|
|
146
154
|
}
|
|
155
|
+
else if (resource === "activity") {
|
|
156
|
+
if (operation === "getAll") {
|
|
157
|
+
method = "GET";
|
|
158
|
+
const getAllScope = this.getNodeParameter("getAllScope", i);
|
|
159
|
+
const limit = this.getNodeParameter("limit", i, constants_1.PAGINATION.DEFAULT_LIMIT);
|
|
160
|
+
const page = this.getNodeParameter("page", i, constants_1.PAGINATION.DEFAULT_PAGE);
|
|
161
|
+
if (getAllScope === "contact") {
|
|
162
|
+
const contactId = this.getNodeParameter("getAllContactId", i);
|
|
163
|
+
endpoint = `/contacts/${(0, common_1.sanitizeId)(contactId)}/activities`;
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
const companyId = this.getNodeParameter("companyId", i);
|
|
167
|
+
endpoint = `/leads/${(0, common_1.sanitizeId)(companyId)}/activities`;
|
|
168
|
+
}
|
|
169
|
+
qs.limit = limit;
|
|
170
|
+
qs.page = page;
|
|
171
|
+
}
|
|
172
|
+
else if (operation === "get") {
|
|
173
|
+
method = "GET";
|
|
174
|
+
const activityId = this.getNodeParameter("activityId", i);
|
|
175
|
+
endpoint = `${constants_1.ENDPOINTS.ACTIVITIES}/${(0, common_1.sanitizeId)(activityId)}`;
|
|
176
|
+
}
|
|
177
|
+
else if (operation === "create") {
|
|
178
|
+
method = "POST";
|
|
179
|
+
endpoint = constants_1.ENDPOINTS.ACTIVITIES;
|
|
180
|
+
const subject = this.getNodeParameter("subject", i);
|
|
181
|
+
const associateWith = this.getNodeParameter("associateWith", i);
|
|
182
|
+
const additionalFields = this.getNodeParameter("additionalFields", i, {});
|
|
183
|
+
if (!subject || !subject.trim()) {
|
|
184
|
+
throw new Error("Subject is required to create an activity");
|
|
185
|
+
}
|
|
186
|
+
// Must send as multipart/form-data — the /activities endpoint uses
|
|
187
|
+
// FilesInterceptor which does not parse JSON bodies reliably
|
|
188
|
+
const formPayload = { subject: subject.trim() };
|
|
189
|
+
if (associateWith === "company") {
|
|
190
|
+
const companyId = this.getNodeParameter("activityCompanyId", i);
|
|
191
|
+
formPayload.company_id = companyId;
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
const contactId = this.getNodeParameter("activityContactId", i);
|
|
195
|
+
formPayload.contact_id = contactId;
|
|
196
|
+
}
|
|
197
|
+
// Merge optional additional fields
|
|
198
|
+
Object.entries(additionalFields).forEach(([key, value]) => {
|
|
199
|
+
if (value !== undefined && value !== "" && key !== "subject") {
|
|
200
|
+
formPayload[key] = value;
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
body = formPayload;
|
|
204
|
+
initialBody = formPayload;
|
|
205
|
+
useFormData = true;
|
|
206
|
+
}
|
|
207
|
+
else if (operation === "update") {
|
|
208
|
+
method = "PATCH";
|
|
209
|
+
const activityId = this.getNodeParameter("activityId", i);
|
|
210
|
+
endpoint = `${constants_1.ENDPOINTS.ACTIVITIES}/${(0, common_1.sanitizeId)(activityId)}`;
|
|
211
|
+
const additionalFields = this.getNodeParameter("additionalFields", i, {});
|
|
212
|
+
const updatePayload = {};
|
|
213
|
+
Object.entries(additionalFields).forEach(([key, value]) => {
|
|
214
|
+
if (value !== undefined && value !== "") {
|
|
215
|
+
updatePayload[key] = value;
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
body = updatePayload;
|
|
219
|
+
initialBody = updatePayload;
|
|
220
|
+
useFormData = true;
|
|
221
|
+
}
|
|
222
|
+
else if (operation === "delete") {
|
|
223
|
+
method = "DELETE";
|
|
224
|
+
const activityId = this.getNodeParameter("activityId", i);
|
|
225
|
+
endpoint = `${constants_1.ENDPOINTS.ACTIVITIES}/${(0, common_1.sanitizeId)(activityId)}`;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
147
228
|
else if (resource === "company") {
|
|
148
229
|
if (operation === "getAll") {
|
|
149
230
|
method = "GET";
|
|
@@ -202,16 +283,9 @@ class IDB2B {
|
|
|
202
283
|
endpoint = `${constants_1.ENDPOINTS.COMPANIES}/${(0, common_1.sanitizeId)(companyId)}`;
|
|
203
284
|
}
|
|
204
285
|
}
|
|
205
|
-
const response = await httpClient.makeRequest({
|
|
206
|
-
method,
|
|
207
|
-
url: `${credentials.baseUrl}${endpoint}`,
|
|
208
|
-
headers: {
|
|
286
|
+
const response = await httpClient.makeRequest(Object.assign(Object.assign({ method, url: `${credentials.baseUrl}${endpoint}`, headers: {
|
|
209
287
|
Authorization: `Bearer ${accessToken}`,
|
|
210
|
-
},
|
|
211
|
-
body,
|
|
212
|
-
qs,
|
|
213
|
-
json: true,
|
|
214
|
-
});
|
|
288
|
+
} }, (useFormData ? { formData: body } : { body })), { qs, json: true }));
|
|
215
289
|
let processedResponse = (0, common_1.processResponse)(response, operation, initialBody);
|
|
216
290
|
// Apply field filtering for getAll operations
|
|
217
291
|
if (operation === "getAll") {
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.activityFields = exports.activityOperations = void 0;
|
|
4
|
+
exports.activityOperations = {
|
|
5
|
+
displayName: 'Operation',
|
|
6
|
+
name: 'operation',
|
|
7
|
+
type: 'options',
|
|
8
|
+
noDataExpression: true,
|
|
9
|
+
displayOptions: {
|
|
10
|
+
show: {
|
|
11
|
+
resource: ['activity'],
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
options: [
|
|
15
|
+
{
|
|
16
|
+
name: 'Get All',
|
|
17
|
+
value: 'getAll',
|
|
18
|
+
action: 'Get all activities for a company',
|
|
19
|
+
description: 'Retrieve all activities for a specific company/lead',
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
name: 'Get',
|
|
23
|
+
value: 'get',
|
|
24
|
+
action: 'Get an activity',
|
|
25
|
+
description: 'Get a single activity by ID',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: 'Create',
|
|
29
|
+
value: 'create',
|
|
30
|
+
action: 'Create an activity',
|
|
31
|
+
description: 'Create a new activity for a company or contact',
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: 'Update',
|
|
35
|
+
value: 'update',
|
|
36
|
+
action: 'Update an activity',
|
|
37
|
+
description: 'Update an existing activity',
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: 'Delete',
|
|
41
|
+
value: 'delete',
|
|
42
|
+
action: 'Delete an activity',
|
|
43
|
+
description: 'Delete an activity by ID',
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
default: 'getAll',
|
|
47
|
+
};
|
|
48
|
+
exports.activityFields = [
|
|
49
|
+
// Activity ID — required for get, update, delete
|
|
50
|
+
{
|
|
51
|
+
displayName: 'Activity ID',
|
|
52
|
+
name: 'activityId',
|
|
53
|
+
type: 'string',
|
|
54
|
+
default: '',
|
|
55
|
+
required: true,
|
|
56
|
+
displayOptions: {
|
|
57
|
+
show: {
|
|
58
|
+
resource: ['activity'],
|
|
59
|
+
operation: ['get', 'update', 'delete'],
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
description: 'ID of the activity',
|
|
63
|
+
},
|
|
64
|
+
// Scope selector for getAll
|
|
65
|
+
{
|
|
66
|
+
displayName: 'Scope',
|
|
67
|
+
name: 'getAllScope',
|
|
68
|
+
type: 'options',
|
|
69
|
+
default: 'company',
|
|
70
|
+
required: true,
|
|
71
|
+
displayOptions: {
|
|
72
|
+
show: {
|
|
73
|
+
resource: ['activity'],
|
|
74
|
+
operation: ['getAll'],
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
options: [
|
|
78
|
+
{ name: 'Company', value: 'company' },
|
|
79
|
+
{ name: 'Contact', value: 'contact' },
|
|
80
|
+
],
|
|
81
|
+
description: 'Whether to list activities for a company or a contact',
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
displayName: 'Company ID',
|
|
85
|
+
name: 'companyId',
|
|
86
|
+
type: 'string',
|
|
87
|
+
default: '',
|
|
88
|
+
required: true,
|
|
89
|
+
displayOptions: {
|
|
90
|
+
show: {
|
|
91
|
+
resource: ['activity'],
|
|
92
|
+
operation: ['getAll'],
|
|
93
|
+
getAllScope: ['company'],
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
description: 'ID of the company/lead to list activities for',
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
displayName: 'Contact ID',
|
|
100
|
+
name: 'getAllContactId',
|
|
101
|
+
type: 'string',
|
|
102
|
+
default: '',
|
|
103
|
+
required: true,
|
|
104
|
+
displayOptions: {
|
|
105
|
+
show: {
|
|
106
|
+
resource: ['activity'],
|
|
107
|
+
operation: ['getAll'],
|
|
108
|
+
getAllScope: ['contact'],
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
description: 'ID of the contact to list activities for',
|
|
112
|
+
},
|
|
113
|
+
// Subject — required for create
|
|
114
|
+
{
|
|
115
|
+
displayName: 'Subject',
|
|
116
|
+
name: 'subject',
|
|
117
|
+
type: 'string',
|
|
118
|
+
default: '',
|
|
119
|
+
required: true,
|
|
120
|
+
displayOptions: {
|
|
121
|
+
show: {
|
|
122
|
+
resource: ['activity'],
|
|
123
|
+
operation: ['create'],
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
description: 'Subject of the activity',
|
|
127
|
+
},
|
|
128
|
+
// Association — required for create (company OR contact)
|
|
129
|
+
{
|
|
130
|
+
displayName: 'Associate With',
|
|
131
|
+
name: 'associateWith',
|
|
132
|
+
type: 'options',
|
|
133
|
+
default: 'company',
|
|
134
|
+
required: true,
|
|
135
|
+
displayOptions: {
|
|
136
|
+
show: {
|
|
137
|
+
resource: ['activity'],
|
|
138
|
+
operation: ['create'],
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
options: [
|
|
142
|
+
{ name: 'Company', value: 'company' },
|
|
143
|
+
{ name: 'Contact', value: 'contact' },
|
|
144
|
+
],
|
|
145
|
+
description: 'Whether to link this activity to a company or a contact',
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
displayName: 'Company ID',
|
|
149
|
+
name: 'activityCompanyId',
|
|
150
|
+
type: 'string',
|
|
151
|
+
default: '',
|
|
152
|
+
required: true,
|
|
153
|
+
displayOptions: {
|
|
154
|
+
show: {
|
|
155
|
+
resource: ['activity'],
|
|
156
|
+
operation: ['create'],
|
|
157
|
+
associateWith: ['company'],
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
description: 'ID of the company/lead to associate this activity with',
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
displayName: 'Contact ID',
|
|
164
|
+
name: 'activityContactId',
|
|
165
|
+
type: 'string',
|
|
166
|
+
default: '',
|
|
167
|
+
required: true,
|
|
168
|
+
displayOptions: {
|
|
169
|
+
show: {
|
|
170
|
+
resource: ['activity'],
|
|
171
|
+
operation: ['create'],
|
|
172
|
+
associateWith: ['contact'],
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
description: 'ID of the contact to associate this activity with',
|
|
176
|
+
},
|
|
177
|
+
// Additional fields for create/update
|
|
178
|
+
{
|
|
179
|
+
displayName: 'Additional Fields',
|
|
180
|
+
name: 'additionalFields',
|
|
181
|
+
type: 'collection',
|
|
182
|
+
placeholder: 'Add Field',
|
|
183
|
+
default: {},
|
|
184
|
+
displayOptions: {
|
|
185
|
+
show: {
|
|
186
|
+
resource: ['activity'],
|
|
187
|
+
operation: ['create', 'update'],
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
options: [
|
|
191
|
+
{
|
|
192
|
+
displayName: 'Subject',
|
|
193
|
+
name: 'subject',
|
|
194
|
+
type: 'string',
|
|
195
|
+
default: '',
|
|
196
|
+
description: 'Subject of the activity (for update)',
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
displayName: 'Description',
|
|
200
|
+
name: 'description',
|
|
201
|
+
type: 'string',
|
|
202
|
+
typeOptions: {
|
|
203
|
+
rows: 4,
|
|
204
|
+
},
|
|
205
|
+
default: '',
|
|
206
|
+
description: 'Description or notes for the activity',
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
displayName: 'Date & Time',
|
|
210
|
+
name: 'datetime',
|
|
211
|
+
type: 'dateTime',
|
|
212
|
+
default: '',
|
|
213
|
+
description: 'Date and time of the activity (ISO 8601)',
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
displayName: 'Icon',
|
|
217
|
+
name: 'icon',
|
|
218
|
+
type: 'string',
|
|
219
|
+
default: '',
|
|
220
|
+
description: 'Icon identifier for the activity',
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
displayName: 'User ID',
|
|
224
|
+
name: 'user_id',
|
|
225
|
+
type: 'string',
|
|
226
|
+
default: '',
|
|
227
|
+
description: 'UUID of the user to associate with the activity',
|
|
228
|
+
},
|
|
229
|
+
],
|
|
230
|
+
},
|
|
231
|
+
// Pagination for getAll
|
|
232
|
+
{
|
|
233
|
+
displayName: 'Limit',
|
|
234
|
+
name: 'limit',
|
|
235
|
+
type: 'number',
|
|
236
|
+
default: 50,
|
|
237
|
+
description: 'Maximum number of activities to return',
|
|
238
|
+
displayOptions: {
|
|
239
|
+
show: {
|
|
240
|
+
resource: ['activity'],
|
|
241
|
+
operation: ['getAll'],
|
|
242
|
+
},
|
|
243
|
+
},
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
displayName: 'Page',
|
|
247
|
+
name: 'page',
|
|
248
|
+
type: 'number',
|
|
249
|
+
default: 1,
|
|
250
|
+
description: 'Page number to retrieve',
|
|
251
|
+
displayOptions: {
|
|
252
|
+
show: {
|
|
253
|
+
resource: ['activity'],
|
|
254
|
+
operation: ['getAll'],
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
},
|
|
258
|
+
];
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Activity resource handler
|
|
3
|
+
* Implements CRUD operations for lead/contact activities
|
|
4
|
+
*/
|
|
5
|
+
import { HttpClient } from "../utils/httpClient";
|
|
6
|
+
import { DataValidator } from "../utils/validators";
|
|
7
|
+
import { ErrorHandler } from "../utils/errorHandler";
|
|
8
|
+
import { IResourceHandler, GetAllParams, GetParams, CreateParams, UpdateParams, DeleteParams } from "./handlerFactory";
|
|
9
|
+
export interface ActivityGetAllParams extends GetAllParams {
|
|
10
|
+
companyId: string;
|
|
11
|
+
}
|
|
12
|
+
export interface ActivityCreateParams extends CreateParams {
|
|
13
|
+
}
|
|
14
|
+
export declare class ActivityHandler implements IResourceHandler {
|
|
15
|
+
private httpClient;
|
|
16
|
+
private validator;
|
|
17
|
+
private errorHandler;
|
|
18
|
+
constructor(httpClient: HttpClient, validator: DataValidator, errorHandler: ErrorHandler);
|
|
19
|
+
/**
|
|
20
|
+
* Get all activities for a specific company/lead
|
|
21
|
+
*/
|
|
22
|
+
getAll(params: ActivityGetAllParams): Promise<any>;
|
|
23
|
+
/**
|
|
24
|
+
* Get single activity by ID
|
|
25
|
+
*/
|
|
26
|
+
get(params: GetParams): Promise<any>;
|
|
27
|
+
/**
|
|
28
|
+
* Create a new activity (linked to a company or contact)
|
|
29
|
+
*/
|
|
30
|
+
create(params: CreateParams): Promise<any>;
|
|
31
|
+
/**
|
|
32
|
+
* Update an existing activity
|
|
33
|
+
*/
|
|
34
|
+
update(params: UpdateParams): Promise<any>;
|
|
35
|
+
/**
|
|
36
|
+
* Delete an activity
|
|
37
|
+
*/
|
|
38
|
+
delete(params: DeleteParams): Promise<any>;
|
|
39
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Activity resource handler
|
|
4
|
+
* Implements CRUD operations for lead/contact activities
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.ActivityHandler = void 0;
|
|
8
|
+
const constants_1 = require("../config/constants");
|
|
9
|
+
const common_1 = require("../utils/common");
|
|
10
|
+
class ActivityHandler {
|
|
11
|
+
constructor(httpClient, validator, errorHandler) {
|
|
12
|
+
this.httpClient = httpClient;
|
|
13
|
+
this.validator = validator;
|
|
14
|
+
this.errorHandler = errorHandler;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Get all activities for a specific company/lead
|
|
18
|
+
*/
|
|
19
|
+
async getAll(params) {
|
|
20
|
+
var _a, _b;
|
|
21
|
+
const limit = (_a = params.limit) !== null && _a !== void 0 ? _a : constants_1.PAGINATION.DEFAULT_LIMIT;
|
|
22
|
+
const page = (_b = params.page) !== null && _b !== void 0 ? _b : constants_1.PAGINATION.DEFAULT_PAGE;
|
|
23
|
+
const qs = { limit, page };
|
|
24
|
+
if (params.queryParameters) {
|
|
25
|
+
Object.assign(qs, params.queryParameters);
|
|
26
|
+
}
|
|
27
|
+
const options = {
|
|
28
|
+
method: "GET",
|
|
29
|
+
url: `${params.baseUrl}/leads/${(0, common_1.sanitizeId)(params.companyId)}/activities`,
|
|
30
|
+
headers: {
|
|
31
|
+
Authorization: `Bearer ${params.accessToken}`,
|
|
32
|
+
},
|
|
33
|
+
qs,
|
|
34
|
+
json: true,
|
|
35
|
+
};
|
|
36
|
+
return this.httpClient.makeRequest(options);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Get single activity by ID
|
|
40
|
+
*/
|
|
41
|
+
async get(params) {
|
|
42
|
+
const options = {
|
|
43
|
+
method: "GET",
|
|
44
|
+
url: `${params.baseUrl}${constants_1.ENDPOINTS.ACTIVITIES}/${(0, common_1.sanitizeId)(params.id)}`,
|
|
45
|
+
headers: {
|
|
46
|
+
Authorization: `Bearer ${params.accessToken}`,
|
|
47
|
+
},
|
|
48
|
+
json: true,
|
|
49
|
+
};
|
|
50
|
+
return this.httpClient.makeRequest(options);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Create a new activity (linked to a company or contact)
|
|
54
|
+
*/
|
|
55
|
+
async create(params) {
|
|
56
|
+
const { company_id, contact_id, subject, description, datetime, icon, user_id } = params.data;
|
|
57
|
+
if (!subject || !subject.trim()) {
|
|
58
|
+
throw new Error("Subject is required to create an activity");
|
|
59
|
+
}
|
|
60
|
+
if (!company_id && !contact_id) {
|
|
61
|
+
throw new Error("Either company_id or contact_id is required");
|
|
62
|
+
}
|
|
63
|
+
const body = {
|
|
64
|
+
subject: subject.trim(),
|
|
65
|
+
};
|
|
66
|
+
if (company_id)
|
|
67
|
+
body.company_id = company_id;
|
|
68
|
+
if (contact_id)
|
|
69
|
+
body.contact_id = contact_id;
|
|
70
|
+
if (description)
|
|
71
|
+
body.description = description;
|
|
72
|
+
if (datetime)
|
|
73
|
+
body.datetime = datetime;
|
|
74
|
+
if (icon)
|
|
75
|
+
body.icon = icon;
|
|
76
|
+
if (user_id)
|
|
77
|
+
body.user_id = user_id;
|
|
78
|
+
const options = {
|
|
79
|
+
method: "POST",
|
|
80
|
+
url: `${params.baseUrl}${constants_1.ENDPOINTS.ACTIVITIES}`,
|
|
81
|
+
headers: {
|
|
82
|
+
Authorization: `Bearer ${params.accessToken}`,
|
|
83
|
+
},
|
|
84
|
+
body,
|
|
85
|
+
json: true,
|
|
86
|
+
};
|
|
87
|
+
return this.httpClient.makeRequest(options);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Update an existing activity
|
|
91
|
+
*/
|
|
92
|
+
async update(params) {
|
|
93
|
+
const { subject, description, datetime, icon, user_id } = params.data;
|
|
94
|
+
const body = {};
|
|
95
|
+
if (subject)
|
|
96
|
+
body.subject = subject.trim();
|
|
97
|
+
if (description !== undefined && description !== "")
|
|
98
|
+
body.description = description;
|
|
99
|
+
if (datetime)
|
|
100
|
+
body.datetime = datetime;
|
|
101
|
+
if (icon)
|
|
102
|
+
body.icon = icon;
|
|
103
|
+
if (user_id)
|
|
104
|
+
body.user_id = user_id;
|
|
105
|
+
const options = {
|
|
106
|
+
method: "PATCH",
|
|
107
|
+
url: `${params.baseUrl}${constants_1.ENDPOINTS.ACTIVITIES}/${(0, common_1.sanitizeId)(params.id)}`,
|
|
108
|
+
headers: {
|
|
109
|
+
Authorization: `Bearer ${params.accessToken}`,
|
|
110
|
+
},
|
|
111
|
+
body,
|
|
112
|
+
json: true,
|
|
113
|
+
};
|
|
114
|
+
return this.httpClient.makeRequest(options);
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Delete an activity
|
|
118
|
+
*/
|
|
119
|
+
async delete(params) {
|
|
120
|
+
const options = {
|
|
121
|
+
method: "DELETE",
|
|
122
|
+
url: `${params.baseUrl}${constants_1.ENDPOINTS.ACTIVITIES}/${(0, common_1.sanitizeId)(params.id)}`,
|
|
123
|
+
headers: {
|
|
124
|
+
Authorization: `Bearer ${params.accessToken}`,
|
|
125
|
+
},
|
|
126
|
+
json: true,
|
|
127
|
+
};
|
|
128
|
+
return this.httpClient.makeRequest(options);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
exports.ActivityHandler = ActivityHandler;
|
|
@@ -69,6 +69,7 @@ export declare class HandlerFactory {
|
|
|
69
69
|
private errorHandler;
|
|
70
70
|
private contactHandler;
|
|
71
71
|
private companyHandler;
|
|
72
|
+
private activityHandler;
|
|
72
73
|
constructor(executeFunctions: IExecuteFunctions, httpClient: HttpClient, validator: DataValidator, errorHandler: ErrorHandler);
|
|
73
74
|
/**
|
|
74
75
|
* Get handler for specified resource
|
|
@@ -8,6 +8,7 @@ exports.HandlerFactory = void 0;
|
|
|
8
8
|
exports.createHandlerFactory = createHandlerFactory;
|
|
9
9
|
const ContactHandler_1 = require("./ContactHandler");
|
|
10
10
|
const CompanyHandler_1 = require("./CompanyHandler");
|
|
11
|
+
const ActivityHandler_1 = require("./ActivityHandler");
|
|
11
12
|
/**
|
|
12
13
|
* Handler factory class for creating handlers
|
|
13
14
|
*/
|
|
@@ -19,6 +20,7 @@ class HandlerFactory {
|
|
|
19
20
|
this.errorHandler = errorHandler;
|
|
20
21
|
this.contactHandler = new ContactHandler_1.ContactHandler(httpClient, validator, errorHandler);
|
|
21
22
|
this.companyHandler = new CompanyHandler_1.CompanyHandler(httpClient, validator, errorHandler);
|
|
23
|
+
this.activityHandler = new ActivityHandler_1.ActivityHandler(httpClient, validator, errorHandler);
|
|
22
24
|
}
|
|
23
25
|
/**
|
|
24
26
|
* Get handler for specified resource
|
|
@@ -29,6 +31,8 @@ class HandlerFactory {
|
|
|
29
31
|
return this.contactHandler;
|
|
30
32
|
case "company":
|
|
31
33
|
return this.companyHandler;
|
|
34
|
+
case "activity":
|
|
35
|
+
return this.activityHandler;
|
|
32
36
|
default:
|
|
33
37
|
throw new Error(`Unknown resource: ${resource}`);
|
|
34
38
|
}
|
|
@@ -37,13 +41,13 @@ class HandlerFactory {
|
|
|
37
41
|
* Check if resource is supported
|
|
38
42
|
*/
|
|
39
43
|
isResourceSupported(resource) {
|
|
40
|
-
return ["contact", "company"].includes(resource);
|
|
44
|
+
return ["contact", "company", "activity"].includes(resource);
|
|
41
45
|
}
|
|
42
46
|
/**
|
|
43
47
|
* Get list of supported resources
|
|
44
48
|
*/
|
|
45
49
|
getSupportedResources() {
|
|
46
|
-
return ["contact", "company"];
|
|
50
|
+
return ["contact", "company", "activity"];
|
|
47
51
|
}
|
|
48
52
|
}
|
|
49
53
|
exports.HandlerFactory = HandlerFactory;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface IDB2BActivity {
|
|
2
|
+
id: string;
|
|
3
|
+
company_id: string | null;
|
|
4
|
+
contact_id: string | null;
|
|
5
|
+
user_id: string | null;
|
|
6
|
+
organization_id: string;
|
|
7
|
+
icon: string | null;
|
|
8
|
+
subject: string;
|
|
9
|
+
description: string | null;
|
|
10
|
+
datetime: string | null;
|
|
11
|
+
type: string | null;
|
|
12
|
+
created_at: string | null;
|
|
13
|
+
updated_at: string | null;
|
|
14
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.IDB2BTrigger = void 0;
|
|
4
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
+
const httpClient_1 = require("../IDB2B/utils/httpClient");
|
|
6
|
+
const tokenCache_1 = require("../IDB2B/utils/tokenCache");
|
|
7
|
+
const common_1 = require("../IDB2B/utils/common");
|
|
8
|
+
const constants_1 = require("../IDB2B/config/constants");
|
|
9
|
+
class IDB2BTrigger {
|
|
10
|
+
constructor() {
|
|
11
|
+
this.description = {
|
|
12
|
+
displayName: "IDB2B Trigger",
|
|
13
|
+
name: "idb2bTrigger",
|
|
14
|
+
icon: "file:../IDB2B/Icon.svg",
|
|
15
|
+
group: ["trigger"],
|
|
16
|
+
version: 1,
|
|
17
|
+
subtitle: '={{$parameter["event"]}}',
|
|
18
|
+
description: "Polls IDB2B for new or updated contacts, companies, and activities",
|
|
19
|
+
defaults: {
|
|
20
|
+
name: "IDB2B Trigger",
|
|
21
|
+
},
|
|
22
|
+
polling: true,
|
|
23
|
+
inputs: [],
|
|
24
|
+
outputs: ["main"],
|
|
25
|
+
credentials: [
|
|
26
|
+
{
|
|
27
|
+
name: "idb2bApi",
|
|
28
|
+
required: true,
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
properties: [
|
|
32
|
+
{
|
|
33
|
+
displayName: "Event",
|
|
34
|
+
name: "event",
|
|
35
|
+
type: "options",
|
|
36
|
+
noDataExpression: true,
|
|
37
|
+
options: [
|
|
38
|
+
{
|
|
39
|
+
name: "Contact Created",
|
|
40
|
+
value: "contactCreated",
|
|
41
|
+
description: "Triggers when a new contact is created",
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: "Contact Updated",
|
|
45
|
+
value: "contactUpdated",
|
|
46
|
+
description: "Triggers when a contact is updated",
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: "Company Created",
|
|
50
|
+
value: "companyCreated",
|
|
51
|
+
description: "Triggers when a new company is created",
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: "Company Updated",
|
|
55
|
+
value: "companyUpdated",
|
|
56
|
+
description: "Triggers when a company is updated",
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: "Activity Created (Under Company)",
|
|
60
|
+
value: "activityCreated",
|
|
61
|
+
description: "Triggers when a new activity is created for a specific company",
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
default: "contactCreated",
|
|
65
|
+
},
|
|
66
|
+
// Company ID required for activity trigger
|
|
67
|
+
{
|
|
68
|
+
displayName: "Company ID",
|
|
69
|
+
name: "companyId",
|
|
70
|
+
type: "string",
|
|
71
|
+
default: "",
|
|
72
|
+
required: true,
|
|
73
|
+
displayOptions: {
|
|
74
|
+
show: {
|
|
75
|
+
event: ["activityCreated"],
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
description: "ID of the company/lead to watch for new activities",
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
displayName: "Limit",
|
|
82
|
+
name: "limit",
|
|
83
|
+
type: "number",
|
|
84
|
+
default: constants_1.PAGINATION.DEFAULT_LIMIT,
|
|
85
|
+
description: "Maximum number of records to fetch per poll cycle",
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
async poll() {
|
|
91
|
+
const webhookData = this.getWorkflowStaticData("node");
|
|
92
|
+
const event = this.getNodeParameter("event");
|
|
93
|
+
const limit = this.getNodeParameter("limit", constants_1.PAGINATION.DEFAULT_LIMIT);
|
|
94
|
+
const credentials = await this.getCredentials("idb2bApi");
|
|
95
|
+
const baseUrl = credentials.baseUrl;
|
|
96
|
+
tokenCache_1.secureTokenCache.cleanupIfNeeded();
|
|
97
|
+
const accessToken = await (0, httpClient_1.getAccessToken)(this, credentials);
|
|
98
|
+
const httpClient = new httpClient_1.HttpClient(this);
|
|
99
|
+
// Determine the timestamp field and endpoint for this event
|
|
100
|
+
const isUpdate = event === "contactUpdated" || event === "companyUpdated";
|
|
101
|
+
const timestampField = isUpdate ? "updated_at" : "created_at";
|
|
102
|
+
// On first run, use now minus 1 minute as the baseline
|
|
103
|
+
const now = new Date();
|
|
104
|
+
const lastCheck = webhookData.lastCheck;
|
|
105
|
+
const since = lastCheck !== null && lastCheck !== void 0 ? lastCheck : new Date(now.getTime() - 60000).toISOString();
|
|
106
|
+
let endpoint;
|
|
107
|
+
if (event === "contactCreated" || event === "contactUpdated") {
|
|
108
|
+
endpoint = constants_1.ENDPOINTS.CONTACTS;
|
|
109
|
+
}
|
|
110
|
+
else if (event === "companyCreated" || event === "companyUpdated") {
|
|
111
|
+
endpoint = constants_1.ENDPOINTS.COMPANIES;
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
// activityCreated
|
|
115
|
+
const companyId = this.getNodeParameter("companyId");
|
|
116
|
+
if (!companyId) {
|
|
117
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), "Company ID is required for the Activity Created trigger");
|
|
118
|
+
}
|
|
119
|
+
endpoint = `/leads/${(0, common_1.sanitizeId)(companyId)}/activities`;
|
|
120
|
+
}
|
|
121
|
+
let response;
|
|
122
|
+
try {
|
|
123
|
+
response = await httpClient.makeRequest({
|
|
124
|
+
method: "GET",
|
|
125
|
+
url: `${baseUrl}${endpoint}`,
|
|
126
|
+
headers: {
|
|
127
|
+
Authorization: `Bearer ${accessToken}`,
|
|
128
|
+
},
|
|
129
|
+
qs: {
|
|
130
|
+
limit,
|
|
131
|
+
page: 1,
|
|
132
|
+
sort_by: timestampField,
|
|
133
|
+
sort_order: "desc",
|
|
134
|
+
},
|
|
135
|
+
json: true,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), error);
|
|
140
|
+
}
|
|
141
|
+
// Update the last check timestamp
|
|
142
|
+
webhookData.lastCheck = now.toISOString();
|
|
143
|
+
const records = Array.isArray(response === null || response === void 0 ? void 0 : response.data)
|
|
144
|
+
? response.data
|
|
145
|
+
: Array.isArray(response)
|
|
146
|
+
? response
|
|
147
|
+
: [];
|
|
148
|
+
// Filter to only records newer than our last check
|
|
149
|
+
const newRecords = records.filter((record) => {
|
|
150
|
+
const ts = record[timestampField];
|
|
151
|
+
if (!ts)
|
|
152
|
+
return false;
|
|
153
|
+
return new Date(ts) > new Date(since);
|
|
154
|
+
});
|
|
155
|
+
if (newRecords.length === 0) {
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
return [
|
|
159
|
+
newRecords.map((record) => ({
|
|
160
|
+
json: record,
|
|
161
|
+
})),
|
|
162
|
+
];
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
exports.IDB2BTrigger = IDB2BTrigger;
|