@hemmilicious/n8n-nodes-ninjapipe 0.2.2

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/INSTALL.md ADDED
@@ -0,0 +1,30 @@
1
+ # Install and publish
2
+
3
+ ## Build locally
4
+
5
+ ```bash
6
+ cd ~/Desktop/n8n-nodes-ninjapipe
7
+ npm install
8
+ npm run build
9
+ ```
10
+
11
+ ## Dry run
12
+
13
+ ```bash
14
+ npm pack --dry-run
15
+ ```
16
+
17
+ ## Publish to npm
18
+
19
+ ```bash
20
+ npm login
21
+ npm whoami
22
+ npm publish --access public
23
+ ```
24
+
25
+ ## Publish update
26
+
27
+ ```bash
28
+ npm version patch
29
+ npm publish --access public
30
+ ```
package/README.md ADDED
@@ -0,0 +1,61 @@
1
+ # @hemmilicious/n8n-nodes-ninjapipe
2
+
3
+ Community node for using the NinjaPipe API inside n8n.
4
+
5
+ ## Included resources
6
+
7
+ - Contacts
8
+ - Companies
9
+ - Deals
10
+ - Products
11
+ - Budgets
12
+ - Lists
13
+ - Pipelines
14
+ - Pipeline Items
15
+ - Invoices
16
+ - Orders
17
+ - Projects
18
+ - Tasks
19
+ - Databins
20
+ - Custom API Request
21
+
22
+ ## Included operations
23
+
24
+ For most resources
25
+
26
+ - Create
27
+ - Get
28
+ - Get Many
29
+ - Update
30
+ - Delete
31
+
32
+ Extra operations
33
+
34
+ - Contact: Enable Client Portal
35
+ - Contact: Disable Client Portal
36
+ - Budget: Create Expense
37
+ - Custom API Request: Execute any endpoint manually
38
+
39
+ ## Why this node is broader now
40
+
41
+ NinjaPipe publicly documents API authentication with workspace specific API keys and lists API groups for Contacts, Deals, Products and Budgets. NinjaPipe product documentation also lists broader data areas such as Contacts, Companies, Products, Lists, Deals, Pipelines and Items, Invoices, Orders, Budgets, Projects and Tasks, and Databins. Pagination documentation states `page`, `limit` and a maximum page size of 100. Because not every broader endpoint was exposed in an equally detailed static view, this node includes a resource path override and a custom request mode so you can still map your workspace fully without waiting for a new release.
42
+
43
+ ## Credentials
44
+
45
+ - Base URL, default `https://www.ninjapipe.app/api`
46
+ - API Key
47
+
48
+ ## Notes
49
+
50
+ - `Get Many` supports `Return All`
51
+ - `Filters JSON` lets you pass extra query params
52
+ - `Resource Path Override` lets you correct or extend endpoint paths
53
+ - `Custom API Request` lets you hit any NinjaPipe path manually
54
+
55
+ ## Local install in n8n
56
+
57
+ ```bash
58
+ npm install @hemmilicious/n8n-nodes-ninjapipe
59
+ ```
60
+
61
+ Or for self hosted n8n community nodes, install it where your n8n instance loads community nodes.
@@ -0,0 +1,32 @@
1
+ import type { ICredentialType, INodeProperties } from 'n8n-workflow';
2
+
3
+ export class NinjaPipeApi implements ICredentialType {
4
+ name = 'ninjaPipeApi';
5
+
6
+ displayName = 'NinjaPipe API';
7
+
8
+ documentationUrl = 'https://docs.ninjapipe.app/api-docs';
9
+
10
+ properties: INodeProperties[] = [
11
+ {
12
+ displayName: 'Base URL',
13
+ name: 'baseUrl',
14
+ type: 'string',
15
+ default: 'https://www.ninjapipe.app/api',
16
+ placeholder: 'https://www.ninjapipe.app/api',
17
+ required: true,
18
+ description: 'Base URL of the NinjaPipe API',
19
+ },
20
+ {
21
+ displayName: 'API Key',
22
+ name: 'apiKey',
23
+ type: 'string',
24
+ typeOptions: {
25
+ password: true,
26
+ },
27
+ default: '',
28
+ required: true,
29
+ description: 'NinjaPipe API key',
30
+ },
31
+ ];
32
+ }
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NinjaPipeApi = void 0;
4
+ class NinjaPipeApi {
5
+ constructor() {
6
+ this.name = 'ninjaPipeApi';
7
+ this.displayName = 'NinjaPipe API';
8
+ this.documentationUrl = 'https://docs.ninjapipe.app/api-docs';
9
+ this.properties = [
10
+ {
11
+ displayName: 'Base URL',
12
+ name: 'baseUrl',
13
+ type: 'string',
14
+ default: 'https://www.ninjapipe.app/api',
15
+ placeholder: 'https://www.ninjapipe.app/api',
16
+ required: true,
17
+ description: 'Base URL of the NinjaPipe API',
18
+ },
19
+ {
20
+ displayName: 'API Key',
21
+ name: 'apiKey',
22
+ type: 'string',
23
+ typeOptions: {
24
+ password: true,
25
+ },
26
+ default: '',
27
+ required: true,
28
+ description: 'NinjaPipe API key',
29
+ },
30
+ ];
31
+ }
32
+ }
33
+ exports.NinjaPipeApi = NinjaPipeApi;
34
+ //# sourceMappingURL=NinjaPipeApi.credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NinjaPipeApi.credentials.js","sourceRoot":"","sources":["../../credentials/NinjaPipeApi.credentials.ts"],"names":[],"mappings":";;;AAEA,MAAa,YAAY;IAAzB;QACC,SAAI,GAAG,cAAc,CAAC;QAEtB,gBAAW,GAAG,eAAe,CAAC;QAE9B,qBAAgB,GAAG,qCAAqC,CAAC;QAEzD,eAAU,GAAsB;YAC/B;gBACC,WAAW,EAAE,UAAU;gBACvB,IAAI,EAAE,SAAS;gBACf,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,+BAA+B;gBACxC,WAAW,EAAE,+BAA+B;gBAC5C,QAAQ,EAAE,IAAI;gBACd,WAAW,EAAE,+BAA+B;aAC5C;YACD;gBACC,WAAW,EAAE,SAAS;gBACtB,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE;oBACZ,QAAQ,EAAE,IAAI;iBACd;gBACD,OAAO,EAAE,EAAE;gBACX,QAAQ,EAAE,IAAI;gBACd,WAAW,EAAE,mBAAmB;aAChC;SACD,CAAC;IACH,CAAC;CAAA;AA7BD,oCA6BC"}
@@ -0,0 +1,441 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NinjaPipe = void 0;
4
+ const n8n_workflow_1 = require("n8n-workflow");
5
+ const resourceConfig = {
6
+ contact: { path: 'contacts', supportsCrud: true },
7
+ company: { path: 'companies', supportsCrud: true },
8
+ deal: { path: 'deals', supportsCrud: true },
9
+ product: { path: 'products', supportsCrud: true },
10
+ budget: { path: 'budgets', supportsCrud: true },
11
+ list: { path: 'lists', supportsCrud: true },
12
+ pipeline: { path: 'pipelines', supportsCrud: true },
13
+ pipelineItem: { path: 'pipeline-items', supportsCrud: true },
14
+ invoice: { path: 'invoices', supportsCrud: true },
15
+ order: { path: 'orders', supportsCrud: true },
16
+ project: { path: 'projects', supportsCrud: true },
17
+ task: { path: 'tasks', supportsCrud: true },
18
+ databin: { path: 'databins', supportsCrud: true },
19
+ };
20
+ async function ninjaPipeApiRequest(context, method, endpoint, body, qs) {
21
+ const credentials = await context.getCredentials('ninjaPipeApi');
22
+ const baseUrl = String(credentials.baseUrl || '').replace(/\/+$/, '');
23
+ const apiKey = String(credentials.apiKey || '');
24
+ if (!baseUrl) {
25
+ throw new n8n_workflow_1.NodeOperationError(context.getNode(), 'Missing NinjaPipe base URL in credentials');
26
+ }
27
+ if (!apiKey) {
28
+ throw new n8n_workflow_1.NodeOperationError(context.getNode(), 'Missing NinjaPipe API key in credentials');
29
+ }
30
+ try {
31
+ return await context.helpers.httpRequest({
32
+ method,
33
+ url: `${baseUrl}${endpoint}`,
34
+ headers: {
35
+ Authorization: `Bearer ${apiKey}`,
36
+ Accept: 'application/json',
37
+ 'Content-Type': 'application/json',
38
+ },
39
+ body,
40
+ qs,
41
+ json: true,
42
+ });
43
+ }
44
+ catch (error) {
45
+ throw new n8n_workflow_1.NodeApiError(context.getNode(), error);
46
+ }
47
+ }
48
+ function parseJsonParameter(context, name, itemIndex) {
49
+ const rawValue = context.getNodeParameter(name, itemIndex);
50
+ if (typeof rawValue !== 'string')
51
+ return rawValue;
52
+ if (!rawValue || rawValue.trim() === '')
53
+ return {};
54
+ try {
55
+ return JSON.parse(rawValue);
56
+ }
57
+ catch (error) {
58
+ throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Invalid JSON in field \"${name}\": ${error.message}`, {
59
+ itemIndex,
60
+ });
61
+ }
62
+ }
63
+ function normalizePath(path) {
64
+ const trimmed = path.trim();
65
+ if (!trimmed)
66
+ return '';
67
+ return trimmed.startsWith('/') ? trimmed : `/${trimmed}`;
68
+ }
69
+ function getResourcePath(resource, overridePath) {
70
+ if (overridePath && overridePath.trim()) {
71
+ return normalizePath(overridePath);
72
+ }
73
+ return `/${resourceConfig[resource].path}`;
74
+ }
75
+ function extractItems(response) {
76
+ if (Array.isArray(response))
77
+ return response;
78
+ if (response && typeof response === 'object') {
79
+ const obj = response;
80
+ for (const key of ['data', 'items', 'results', 'contacts', 'deals', 'products', 'budgets']) {
81
+ const value = obj[key];
82
+ if (Array.isArray(value))
83
+ return value;
84
+ }
85
+ return [obj];
86
+ }
87
+ return [{ value: response }];
88
+ }
89
+ async function getAllPages(context, endpoint, limit, search, filters) {
90
+ const aggregated = [];
91
+ let page = 1;
92
+ while (true) {
93
+ const qs = { page, limit, ...(filters || {}) };
94
+ if (search)
95
+ qs.search = search;
96
+ const response = await ninjaPipeApiRequest(context, 'GET', endpoint, undefined, qs);
97
+ const pageItems = extractItems(response);
98
+ aggregated.push(...pageItems);
99
+ if (pageItems.length < limit)
100
+ break;
101
+ page += 1;
102
+ if (page > 1000)
103
+ break;
104
+ }
105
+ return aggregated;
106
+ }
107
+ const genericResourceOptions = [
108
+ { name: 'Budget', value: 'budget' },
109
+ { name: 'Company', value: 'company' },
110
+ { name: 'Contact', value: 'contact' },
111
+ { name: 'Databin', value: 'databin' },
112
+ { name: 'Deal', value: 'deal' },
113
+ { name: 'Invoice', value: 'invoice' },
114
+ { name: 'List', value: 'list' },
115
+ { name: 'Order', value: 'order' },
116
+ { name: 'Pipeline', value: 'pipeline' },
117
+ { name: 'Pipeline Item', value: 'pipelineItem' },
118
+ { name: 'Product', value: 'product' },
119
+ { name: 'Project', value: 'project' },
120
+ { name: 'Task', value: 'task' },
121
+ ];
122
+ const commonCrudOperations = [
123
+ { name: 'Create', value: 'create', action: 'Create the selected resource' },
124
+ { name: 'Delete', value: 'delete', action: 'Delete the selected resource' },
125
+ { name: 'Get', value: 'get', action: 'Get the selected resource' },
126
+ { name: 'Get Many', value: 'getAll', action: 'Get many records for the selected resource' },
127
+ { name: 'Update', value: 'update', action: 'Update the selected resource' },
128
+ ];
129
+ class NinjaPipe {
130
+ constructor() {
131
+ this.description = {
132
+ displayName: 'NinjaPipe',
133
+ name: 'ninjaPipe',
134
+ icon: 'file:ninjapipe.svg',
135
+ group: ['transform'],
136
+ version: 1,
137
+ subtitle: '={{$parameter["resource"] + " " + $parameter["operation"]}}',
138
+ description: 'Consume the NinjaPipe API',
139
+ defaults: {
140
+ name: 'NinjaPipe',
141
+ },
142
+ inputs: ['main'],
143
+ outputs: ['main'],
144
+ credentials: [
145
+ {
146
+ name: 'ninjaPipeApi',
147
+ required: true,
148
+ },
149
+ ],
150
+ properties: [
151
+ {
152
+ displayName: 'Resource',
153
+ name: 'resource',
154
+ type: 'options',
155
+ noDataExpression: true,
156
+ options: [
157
+ ...genericResourceOptions,
158
+ { name: 'Custom API Request', value: 'customRequest' },
159
+ ],
160
+ default: 'contact',
161
+ description: 'Covers the publicly documented API areas and a flexible custom request mode',
162
+ },
163
+ {
164
+ displayName: 'Operation',
165
+ name: 'operation',
166
+ type: 'options',
167
+ displayOptions: {
168
+ show: {
169
+ resource: genericResourceOptions.map((option) => option.value),
170
+ },
171
+ },
172
+ options: [
173
+ ...commonCrudOperations,
174
+ { name: 'Create Expense', value: 'createExpense', action: 'Create a budget expense' },
175
+ { name: 'Enable Client Portal', value: 'enableClientPortal', action: 'Enable the client portal for a contact' },
176
+ { name: 'Disable Client Portal', value: 'disableClientPortal', action: 'Disable the client portal for a contact' },
177
+ ],
178
+ default: 'getAll',
179
+ },
180
+ {
181
+ displayName: 'Operation',
182
+ name: 'operation',
183
+ type: 'options',
184
+ displayOptions: { show: { resource: ['customRequest'] } },
185
+ options: [{ name: 'Execute', value: 'execute', action: 'Execute a custom API request' }],
186
+ default: 'execute',
187
+ },
188
+ {
189
+ displayName: 'Resource Path Override',
190
+ name: 'resourcePathOverride',
191
+ type: 'string',
192
+ default: '',
193
+ displayOptions: {
194
+ show: {
195
+ resource: genericResourceOptions.map((option) => option.value),
196
+ },
197
+ },
198
+ placeholder: '/contacts',
199
+ description: 'Optional custom endpoint path. Leave empty to use the built-in path for the selected resource',
200
+ },
201
+ {
202
+ displayName: 'ID',
203
+ name: 'id',
204
+ type: 'string',
205
+ required: true,
206
+ default: '',
207
+ displayOptions: {
208
+ show: {
209
+ resource: genericResourceOptions.map((option) => option.value),
210
+ operation: ['get', 'update', 'delete', 'enableClientPortal', 'disableClientPortal'],
211
+ },
212
+ },
213
+ },
214
+ {
215
+ displayName: 'Return All',
216
+ name: 'returnAll',
217
+ type: 'boolean',
218
+ default: false,
219
+ displayOptions: {
220
+ show: {
221
+ resource: genericResourceOptions.map((option) => option.value),
222
+ operation: ['getAll'],
223
+ },
224
+ },
225
+ description: 'Whether to return all results by requesting all pages',
226
+ },
227
+ {
228
+ displayName: 'Page',
229
+ name: 'page',
230
+ type: 'number',
231
+ default: 1,
232
+ displayOptions: {
233
+ show: {
234
+ resource: genericResourceOptions.map((option) => option.value),
235
+ operation: ['getAll'],
236
+ returnAll: [false],
237
+ },
238
+ },
239
+ },
240
+ {
241
+ displayName: 'Limit',
242
+ name: 'limit',
243
+ type: 'number',
244
+ default: 20,
245
+ typeOptions: {
246
+ minValue: 1,
247
+ maxValue: 100,
248
+ },
249
+ displayOptions: {
250
+ show: {
251
+ resource: genericResourceOptions.map((option) => option.value),
252
+ operation: ['getAll'],
253
+ },
254
+ },
255
+ description: 'The public docs state a maximum page size of 100',
256
+ },
257
+ {
258
+ displayName: 'Search',
259
+ name: 'search',
260
+ type: 'string',
261
+ default: '',
262
+ displayOptions: {
263
+ show: {
264
+ resource: genericResourceOptions.map((option) => option.value),
265
+ operation: ['getAll'],
266
+ },
267
+ },
268
+ description: 'Optional free-text search string',
269
+ },
270
+ {
271
+ displayName: 'Filters JSON',
272
+ name: 'filters',
273
+ type: 'json',
274
+ default: '{}',
275
+ displayOptions: {
276
+ show: {
277
+ resource: genericResourceOptions.map((option) => option.value),
278
+ operation: ['getAll'],
279
+ },
280
+ },
281
+ description: 'Optional additional query parameters as JSON, for example status, owner, category or date filters',
282
+ },
283
+ {
284
+ displayName: 'Body',
285
+ name: 'body',
286
+ type: 'json',
287
+ default: '{}',
288
+ required: true,
289
+ displayOptions: {
290
+ show: {
291
+ resource: genericResourceOptions.map((option) => option.value),
292
+ operation: ['create', 'update', 'createExpense'],
293
+ },
294
+ },
295
+ description: 'JSON request body passed directly to NinjaPipe',
296
+ },
297
+ {
298
+ displayName: 'Portal Body',
299
+ name: 'portalBody',
300
+ type: 'json',
301
+ default: '{}',
302
+ required: false,
303
+ displayOptions: {
304
+ show: {
305
+ resource: ['contact'],
306
+ operation: ['enableClientPortal', 'disableClientPortal'],
307
+ },
308
+ },
309
+ description: 'Optional JSON body for the client portal action',
310
+ },
311
+ {
312
+ displayName: 'Method',
313
+ name: 'customMethod',
314
+ type: 'options',
315
+ displayOptions: { show: { resource: ['customRequest'], operation: ['execute'] } },
316
+ options: [
317
+ { name: 'DELETE', value: 'DELETE' },
318
+ { name: 'GET', value: 'GET' },
319
+ { name: 'PATCH', value: 'PATCH' },
320
+ { name: 'POST', value: 'POST' },
321
+ { name: 'PUT', value: 'PUT' },
322
+ ],
323
+ default: 'GET',
324
+ },
325
+ {
326
+ displayName: 'Path',
327
+ name: 'customPath',
328
+ type: 'string',
329
+ displayOptions: { show: { resource: ['customRequest'], operation: ['execute'] } },
330
+ default: '/contacts',
331
+ description: 'API path relative to the Base URL',
332
+ },
333
+ {
334
+ displayName: 'Query Parameters',
335
+ name: 'customQuery',
336
+ type: 'json',
337
+ displayOptions: { show: { resource: ['customRequest'], operation: ['execute'] } },
338
+ default: '{}',
339
+ },
340
+ {
341
+ displayName: 'Request Body',
342
+ name: 'customBody',
343
+ type: 'json',
344
+ displayOptions: { show: { resource: ['customRequest'], operation: ['execute'] } },
345
+ default: '{}',
346
+ },
347
+ ],
348
+ };
349
+ }
350
+ async execute() {
351
+ const items = this.getInputData();
352
+ const returnData = [];
353
+ for (let i = 0; i < items.length; i++) {
354
+ try {
355
+ const resource = this.getNodeParameter('resource', i);
356
+ const operation = this.getNodeParameter('operation', i);
357
+ let responseData;
358
+ if (resource === 'customRequest') {
359
+ const method = this.getNodeParameter('customMethod', i);
360
+ const path = normalizePath(this.getNodeParameter('customPath', i));
361
+ const qs = parseJsonParameter(this, 'customQuery', i);
362
+ const body = ['GET', 'DELETE'].includes(method) ? undefined : parseJsonParameter(this, 'customBody', i);
363
+ responseData = await ninjaPipeApiRequest(this, method, path, body, qs);
364
+ }
365
+ else {
366
+ const resourcePathOverride = this.getNodeParameter('resourcePathOverride', i, '');
367
+ const basePath = getResourcePath(resource, resourcePathOverride);
368
+ if (operation === 'create') {
369
+ responseData = await ninjaPipeApiRequest(this, 'POST', basePath, parseJsonParameter(this, 'body', i));
370
+ }
371
+ else if (operation === 'get') {
372
+ const id = this.getNodeParameter('id', i);
373
+ responseData = await ninjaPipeApiRequest(this, 'GET', `${basePath}/${id}`);
374
+ }
375
+ else if (operation === 'getAll') {
376
+ const limit = this.getNodeParameter('limit', i);
377
+ const search = this.getNodeParameter('search', i, '');
378
+ const filters = parseJsonParameter(this, 'filters', i);
379
+ const returnAll = this.getNodeParameter('returnAll', i, false);
380
+ if (returnAll) {
381
+ responseData = await getAllPages(this, basePath, limit, search, filters);
382
+ }
383
+ else {
384
+ const page = this.getNodeParameter('page', i);
385
+ const qs = { page, limit, ...(filters || {}) };
386
+ if (search)
387
+ qs.search = search;
388
+ responseData = await ninjaPipeApiRequest(this, 'GET', basePath, undefined, qs);
389
+ }
390
+ }
391
+ else if (operation === 'update') {
392
+ const id = this.getNodeParameter('id', i);
393
+ responseData = await ninjaPipeApiRequest(this, 'PUT', `${basePath}/${id}`, parseJsonParameter(this, 'body', i));
394
+ }
395
+ else if (operation === 'delete') {
396
+ const id = this.getNodeParameter('id', i);
397
+ responseData = await ninjaPipeApiRequest(this, 'DELETE', `${basePath}/${id}`);
398
+ }
399
+ else if (operation === 'createExpense') {
400
+ if (resource !== 'budget') {
401
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Create Expense is only supported for the Budget resource', { itemIndex: i });
402
+ }
403
+ const id = this.getNodeParameter('id', i);
404
+ responseData = await ninjaPipeApiRequest(this, 'POST', `${basePath}/${id}/expenses`, parseJsonParameter(this, 'body', i));
405
+ }
406
+ else if (operation === 'enableClientPortal') {
407
+ if (resource !== 'contact') {
408
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Enable Client Portal is only supported for the Contact resource', { itemIndex: i });
409
+ }
410
+ const id = this.getNodeParameter('id', i);
411
+ responseData = await ninjaPipeApiRequest(this, 'PUT', `${basePath}/${id}/enable-client-portal`, parseJsonParameter(this, 'portalBody', i));
412
+ }
413
+ else if (operation === 'disableClientPortal') {
414
+ if (resource !== 'contact') {
415
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Disable Client Portal is only supported for the Contact resource', { itemIndex: i });
416
+ }
417
+ const id = this.getNodeParameter('id', i);
418
+ responseData = await ninjaPipeApiRequest(this, 'PUT', `${basePath}/${id}/disable-client-portal`, parseJsonParameter(this, 'portalBody', i));
419
+ }
420
+ else {
421
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Unsupported operation: ${operation}`, { itemIndex: i });
422
+ }
423
+ }
424
+ const responseItems = extractItems(responseData);
425
+ for (const entry of responseItems) {
426
+ returnData.push({ json: entry });
427
+ }
428
+ }
429
+ catch (error) {
430
+ if (this.continueOnFail()) {
431
+ returnData.push({ json: { error: error.message }, pairedItem: i });
432
+ continue;
433
+ }
434
+ throw error;
435
+ }
436
+ }
437
+ return [returnData];
438
+ }
439
+ }
440
+ exports.NinjaPipe = NinjaPipe;
441
+ //# sourceMappingURL=NinjaPipe.node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NinjaPipe.node.js","sourceRoot":"","sources":["../../../nodes/NinjaPipe/NinjaPipe.node.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ <svg width="120" height="120" viewBox="0 0 120 120" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <rect width="120" height="120" rx="24" fill="#111111"/>
3
+ <path d="M27 88V32H37L64 69V32H75V88H65L38 51V88H27Z" fill="white"/>
4
+ <path d="M84 88V32H93C101.837 32 109 39.1634 109 48C109 56.8366 101.837 64 93 64H89V88H84ZM89 59H93C99.0751 59 104 54.0751 104 48C104 41.9249 99.0751 37 93 37H89V59Z" fill="white"/>
5
+ </svg>
@@ -0,0 +1,484 @@
1
+ import type {
2
+ IDataObject,
3
+ IExecuteFunctions,
4
+ ILoadOptionsFunctions,
5
+ INodeExecutionData,
6
+ INodeProperties,
7
+ INodeType,
8
+ INodeTypeDescription,
9
+ JsonObject,
10
+ IHttpRequestMethods,
11
+ } from 'n8n-workflow';
12
+ import { NodeApiError, NodeOperationError } from 'n8n-workflow';
13
+
14
+ type Resource =
15
+ | 'contact'
16
+ | 'company'
17
+ | 'deal'
18
+ | 'product'
19
+ | 'budget'
20
+ | 'list'
21
+ | 'pipeline'
22
+ | 'pipelineItem'
23
+ | 'invoice'
24
+ | 'order'
25
+ | 'project'
26
+ | 'task'
27
+ | 'databin'
28
+ | 'customRequest';
29
+
30
+ type Operation =
31
+ | 'create'
32
+ | 'get'
33
+ | 'getAll'
34
+ | 'update'
35
+ | 'delete'
36
+ | 'createExpense'
37
+ | 'enableClientPortal'
38
+ | 'disableClientPortal'
39
+ | 'execute';
40
+
41
+ const resourceConfig: Record<Exclude<Resource, 'customRequest'>, { path: string; supportsCrud: boolean }> = {
42
+ contact: { path: 'contacts', supportsCrud: true },
43
+ company: { path: 'companies', supportsCrud: true },
44
+ deal: { path: 'deals', supportsCrud: true },
45
+ product: { path: 'products', supportsCrud: true },
46
+ budget: { path: 'budgets', supportsCrud: true },
47
+ list: { path: 'lists', supportsCrud: true },
48
+ pipeline: { path: 'pipelines', supportsCrud: true },
49
+ pipelineItem: { path: 'pipeline-items', supportsCrud: true },
50
+ invoice: { path: 'invoices', supportsCrud: true },
51
+ order: { path: 'orders', supportsCrud: true },
52
+ project: { path: 'projects', supportsCrud: true },
53
+ task: { path: 'tasks', supportsCrud: true },
54
+ databin: { path: 'databins', supportsCrud: true },
55
+ };
56
+
57
+ async function ninjaPipeApiRequest(
58
+ context: IExecuteFunctions | ILoadOptionsFunctions,
59
+ method: IHttpRequestMethods,
60
+ endpoint: string,
61
+ body?: IDataObject,
62
+ qs?: IDataObject,
63
+ ) {
64
+ const credentials = await context.getCredentials('ninjaPipeApi');
65
+ const baseUrl = String(credentials.baseUrl || '').replace(/\/+$/, '');
66
+ const apiKey = String(credentials.apiKey || '');
67
+
68
+ if (!baseUrl) {
69
+ throw new NodeOperationError(context.getNode(), 'Missing NinjaPipe base URL in credentials');
70
+ }
71
+
72
+ if (!apiKey) {
73
+ throw new NodeOperationError(context.getNode(), 'Missing NinjaPipe API key in credentials');
74
+ }
75
+
76
+ try {
77
+ return await context.helpers.httpRequest({
78
+ method,
79
+ url: `${baseUrl}${endpoint}`,
80
+ headers: {
81
+ Authorization: `Bearer ${apiKey}`,
82
+ Accept: 'application/json',
83
+ 'Content-Type': 'application/json',
84
+ },
85
+ body,
86
+ qs,
87
+ json: true,
88
+ });
89
+ } catch (error) {
90
+ throw new NodeApiError(context.getNode(), error as JsonObject);
91
+ }
92
+ }
93
+
94
+ function parseJsonParameter(context: IExecuteFunctions, name: string, itemIndex: number): IDataObject {
95
+ const rawValue = context.getNodeParameter(name, itemIndex) as string | IDataObject;
96
+ if (typeof rawValue !== 'string') return rawValue as IDataObject;
97
+ if (!rawValue || rawValue.trim() === '') return {};
98
+ try {
99
+ return JSON.parse(rawValue) as IDataObject;
100
+ } catch (error) {
101
+ throw new NodeOperationError(context.getNode(), `Invalid JSON in field \"${name}\": ${(error as Error).message}`, {
102
+ itemIndex,
103
+ });
104
+ }
105
+ }
106
+
107
+ function normalizePath(path: string): string {
108
+ const trimmed = path.trim();
109
+ if (!trimmed) return '';
110
+ return trimmed.startsWith('/') ? trimmed : `/${trimmed}`;
111
+ }
112
+
113
+ function getResourcePath(resource: Exclude<Resource, 'customRequest'>, overridePath?: string): string {
114
+ if (overridePath && overridePath.trim()) {
115
+ return normalizePath(overridePath);
116
+ }
117
+ return `/${resourceConfig[resource].path}`;
118
+ }
119
+
120
+ function extractItems(response: unknown): IDataObject[] {
121
+ if (Array.isArray(response)) return response as IDataObject[];
122
+ if (response && typeof response === 'object') {
123
+ const obj = response as IDataObject;
124
+ for (const key of ['data', 'items', 'results', 'contacts', 'deals', 'products', 'budgets']) {
125
+ const value = obj[key];
126
+ if (Array.isArray(value)) return value as IDataObject[];
127
+ }
128
+ return [obj];
129
+ }
130
+ return [{ value: response } as IDataObject];
131
+ }
132
+
133
+ async function getAllPages(
134
+ context: IExecuteFunctions,
135
+ endpoint: string,
136
+ limit: number,
137
+ search: string,
138
+ filters: IDataObject,
139
+ ): Promise<IDataObject[]> {
140
+ const aggregated: IDataObject[] = [];
141
+ let page = 1;
142
+
143
+ while (true) {
144
+ const qs: IDataObject = { page, limit, ...(filters || {}) };
145
+ if (search) qs.search = search;
146
+ const response = await ninjaPipeApiRequest(context, 'GET', endpoint, undefined, qs);
147
+ const pageItems = extractItems(response);
148
+ aggregated.push(...pageItems);
149
+ if (pageItems.length < limit) break;
150
+ page += 1;
151
+ if (page > 1000) break;
152
+ }
153
+
154
+ return aggregated;
155
+ }
156
+
157
+ const genericResourceOptions = [
158
+ { name: 'Budget', value: 'budget' },
159
+ { name: 'Company', value: 'company' },
160
+ { name: 'Contact', value: 'contact' },
161
+ { name: 'Databin', value: 'databin' },
162
+ { name: 'Deal', value: 'deal' },
163
+ { name: 'Invoice', value: 'invoice' },
164
+ { name: 'List', value: 'list' },
165
+ { name: 'Order', value: 'order' },
166
+ { name: 'Pipeline', value: 'pipeline' },
167
+ { name: 'Pipeline Item', value: 'pipelineItem' },
168
+ { name: 'Product', value: 'product' },
169
+ { name: 'Project', value: 'project' },
170
+ { name: 'Task', value: 'task' },
171
+ ] as const;
172
+
173
+ const commonCrudOperations = [
174
+ { name: 'Create', value: 'create', action: 'Create the selected resource' },
175
+ { name: 'Delete', value: 'delete', action: 'Delete the selected resource' },
176
+ { name: 'Get', value: 'get', action: 'Get the selected resource' },
177
+ { name: 'Get Many', value: 'getAll', action: 'Get many records for the selected resource' },
178
+ { name: 'Update', value: 'update', action: 'Update the selected resource' },
179
+ ];
180
+
181
+ export class NinjaPipe implements INodeType {
182
+ description: INodeTypeDescription = {
183
+ displayName: 'NinjaPipe',
184
+ name: 'ninjaPipe',
185
+ icon: 'file:ninjapipe.svg',
186
+ group: ['transform'],
187
+ version: 1,
188
+ subtitle: '={{$parameter["resource"] + " " + $parameter["operation"]}}',
189
+ description: 'Consume the NinjaPipe API',
190
+ defaults: {
191
+ name: 'NinjaPipe',
192
+ },
193
+ inputs: ['main'],
194
+ outputs: ['main'],
195
+ credentials: [
196
+ {
197
+ name: 'ninjaPipeApi',
198
+ required: true,
199
+ },
200
+ ],
201
+ properties: [
202
+ {
203
+ displayName: 'Resource',
204
+ name: 'resource',
205
+ type: 'options',
206
+ noDataExpression: true,
207
+ options: [
208
+ ...genericResourceOptions,
209
+ { name: 'Custom API Request', value: 'customRequest' },
210
+ ],
211
+ default: 'contact',
212
+ description: 'Covers the publicly documented API areas and a flexible custom request mode',
213
+ },
214
+ {
215
+ displayName: 'Operation',
216
+ name: 'operation',
217
+ type: 'options',
218
+ displayOptions: {
219
+ show: {
220
+ resource: genericResourceOptions.map((option) => option.value),
221
+ },
222
+ },
223
+ options: [
224
+ ...commonCrudOperations,
225
+ { name: 'Create Expense', value: 'createExpense', action: 'Create a budget expense' },
226
+ { name: 'Enable Client Portal', value: 'enableClientPortal', action: 'Enable the client portal for a contact' },
227
+ { name: 'Disable Client Portal', value: 'disableClientPortal', action: 'Disable the client portal for a contact' },
228
+ ],
229
+ default: 'getAll',
230
+ },
231
+ {
232
+ displayName: 'Operation',
233
+ name: 'operation',
234
+ type: 'options',
235
+ displayOptions: { show: { resource: ['customRequest'] } },
236
+ options: [{ name: 'Execute', value: 'execute', action: 'Execute a custom API request' }],
237
+ default: 'execute',
238
+ },
239
+ {
240
+ displayName: 'Resource Path Override',
241
+ name: 'resourcePathOverride',
242
+ type: 'string',
243
+ default: '',
244
+ displayOptions: {
245
+ show: {
246
+ resource: genericResourceOptions.map((option) => option.value),
247
+ },
248
+ },
249
+ placeholder: '/contacts',
250
+ description: 'Optional custom endpoint path. Leave empty to use the built-in path for the selected resource',
251
+ },
252
+ {
253
+ displayName: 'ID',
254
+ name: 'id',
255
+ type: 'string',
256
+ required: true,
257
+ default: '',
258
+ displayOptions: {
259
+ show: {
260
+ resource: genericResourceOptions.map((option) => option.value),
261
+ operation: ['get', 'update', 'delete', 'enableClientPortal', 'disableClientPortal'],
262
+ },
263
+ },
264
+ },
265
+ {
266
+ displayName: 'Return All',
267
+ name: 'returnAll',
268
+ type: 'boolean',
269
+ default: false,
270
+ displayOptions: {
271
+ show: {
272
+ resource: genericResourceOptions.map((option) => option.value),
273
+ operation: ['getAll'],
274
+ },
275
+ },
276
+ description: 'Whether to return all results by requesting all pages',
277
+ },
278
+ {
279
+ displayName: 'Page',
280
+ name: 'page',
281
+ type: 'number',
282
+ default: 1,
283
+ displayOptions: {
284
+ show: {
285
+ resource: genericResourceOptions.map((option) => option.value),
286
+ operation: ['getAll'],
287
+ returnAll: [false],
288
+ },
289
+ },
290
+ },
291
+ {
292
+ displayName: 'Limit',
293
+ name: 'limit',
294
+ type: 'number',
295
+ default: 20,
296
+ typeOptions: {
297
+ minValue: 1,
298
+ maxValue: 100,
299
+ },
300
+ displayOptions: {
301
+ show: {
302
+ resource: genericResourceOptions.map((option) => option.value),
303
+ operation: ['getAll'],
304
+ },
305
+ },
306
+ description: 'The public docs state a maximum page size of 100',
307
+ },
308
+ {
309
+ displayName: 'Search',
310
+ name: 'search',
311
+ type: 'string',
312
+ default: '',
313
+ displayOptions: {
314
+ show: {
315
+ resource: genericResourceOptions.map((option) => option.value),
316
+ operation: ['getAll'],
317
+ },
318
+ },
319
+ description: 'Optional free-text search string',
320
+ },
321
+ {
322
+ displayName: 'Filters JSON',
323
+ name: 'filters',
324
+ type: 'json',
325
+ default: '{}',
326
+ displayOptions: {
327
+ show: {
328
+ resource: genericResourceOptions.map((option) => option.value),
329
+ operation: ['getAll'],
330
+ },
331
+ },
332
+ description: 'Optional additional query parameters as JSON, for example status, owner, category or date filters',
333
+ },
334
+ {
335
+ displayName: 'Body',
336
+ name: 'body',
337
+ type: 'json',
338
+ default: '{}',
339
+ required: true,
340
+ displayOptions: {
341
+ show: {
342
+ resource: genericResourceOptions.map((option) => option.value),
343
+ operation: ['create', 'update', 'createExpense'],
344
+ },
345
+ },
346
+ description: 'JSON request body passed directly to NinjaPipe',
347
+ },
348
+ {
349
+ displayName: 'Portal Body',
350
+ name: 'portalBody',
351
+ type: 'json',
352
+ default: '{}',
353
+ required: false,
354
+ displayOptions: {
355
+ show: {
356
+ resource: ['contact'],
357
+ operation: ['enableClientPortal', 'disableClientPortal'],
358
+ },
359
+ },
360
+ description: 'Optional JSON body for the client portal action',
361
+ },
362
+ {
363
+ displayName: 'Method',
364
+ name: 'customMethod',
365
+ type: 'options',
366
+ displayOptions: { show: { resource: ['customRequest'], operation: ['execute'] } },
367
+ options: [
368
+ { name: 'DELETE', value: 'DELETE' },
369
+ { name: 'GET', value: 'GET' },
370
+ { name: 'PATCH', value: 'PATCH' },
371
+ { name: 'POST', value: 'POST' },
372
+ { name: 'PUT', value: 'PUT' },
373
+ ],
374
+ default: 'GET',
375
+ },
376
+ {
377
+ displayName: 'Path',
378
+ name: 'customPath',
379
+ type: 'string',
380
+ displayOptions: { show: { resource: ['customRequest'], operation: ['execute'] } },
381
+ default: '/contacts',
382
+ description: 'API path relative to the Base URL',
383
+ },
384
+ {
385
+ displayName: 'Query Parameters',
386
+ name: 'customQuery',
387
+ type: 'json',
388
+ displayOptions: { show: { resource: ['customRequest'], operation: ['execute'] } },
389
+ default: '{}',
390
+ },
391
+ {
392
+ displayName: 'Request Body',
393
+ name: 'customBody',
394
+ type: 'json',
395
+ displayOptions: { show: { resource: ['customRequest'], operation: ['execute'] } },
396
+ default: '{}',
397
+ },
398
+ ] as INodeProperties[],
399
+ };
400
+
401
+ async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
402
+ const items = this.getInputData();
403
+ const returnData: INodeExecutionData[] = [];
404
+
405
+ for (let i = 0; i < items.length; i++) {
406
+ try {
407
+ const resource = this.getNodeParameter('resource', i) as Resource;
408
+ const operation = this.getNodeParameter('operation', i) as Operation;
409
+ let responseData: unknown;
410
+
411
+ if (resource === 'customRequest') {
412
+ const method = this.getNodeParameter('customMethod', i) as IHttpRequestMethods;
413
+ const path = normalizePath(this.getNodeParameter('customPath', i) as string);
414
+ const qs = parseJsonParameter(this, 'customQuery', i);
415
+ const body = ['GET', 'DELETE'].includes(method) ? undefined : parseJsonParameter(this, 'customBody', i);
416
+ responseData = await ninjaPipeApiRequest(this, method, path, body, qs);
417
+ } else {
418
+ const resourcePathOverride = this.getNodeParameter('resourcePathOverride', i, '') as string;
419
+ const basePath = getResourcePath(resource, resourcePathOverride);
420
+
421
+ if (operation === 'create') {
422
+ responseData = await ninjaPipeApiRequest(this, 'POST', basePath, parseJsonParameter(this, 'body', i));
423
+ } else if (operation === 'get') {
424
+ const id = this.getNodeParameter('id', i) as string;
425
+ responseData = await ninjaPipeApiRequest(this, 'GET', `${basePath}/${id}`);
426
+ } else if (operation === 'getAll') {
427
+ const limit = this.getNodeParameter('limit', i) as number;
428
+ const search = this.getNodeParameter('search', i, '') as string;
429
+ const filters = parseJsonParameter(this, 'filters', i);
430
+ const returnAll = this.getNodeParameter('returnAll', i, false) as boolean;
431
+
432
+ if (returnAll) {
433
+ responseData = await getAllPages(this, basePath, limit, search, filters);
434
+ } else {
435
+ const page = this.getNodeParameter('page', i) as number;
436
+ const qs: IDataObject = { page, limit, ...(filters || {}) };
437
+ if (search) qs.search = search;
438
+ responseData = await ninjaPipeApiRequest(this, 'GET', basePath, undefined, qs);
439
+ }
440
+ } else if (operation === 'update') {
441
+ const id = this.getNodeParameter('id', i) as string;
442
+ responseData = await ninjaPipeApiRequest(this, 'PUT', `${basePath}/${id}`, parseJsonParameter(this, 'body', i));
443
+ } else if (operation === 'delete') {
444
+ const id = this.getNodeParameter('id', i) as string;
445
+ responseData = await ninjaPipeApiRequest(this, 'DELETE', `${basePath}/${id}`);
446
+ } else if (operation === 'createExpense') {
447
+ if (resource !== 'budget') {
448
+ throw new NodeOperationError(this.getNode(), 'Create Expense is only supported for the Budget resource', { itemIndex: i });
449
+ }
450
+ const id = this.getNodeParameter('id', i) as string;
451
+ responseData = await ninjaPipeApiRequest(this, 'POST', `${basePath}/${id}/expenses`, parseJsonParameter(this, 'body', i));
452
+ } else if (operation === 'enableClientPortal') {
453
+ if (resource !== 'contact') {
454
+ throw new NodeOperationError(this.getNode(), 'Enable Client Portal is only supported for the Contact resource', { itemIndex: i });
455
+ }
456
+ const id = this.getNodeParameter('id', i) as string;
457
+ responseData = await ninjaPipeApiRequest(this, 'PUT', `${basePath}/${id}/enable-client-portal`, parseJsonParameter(this, 'portalBody', i));
458
+ } else if (operation === 'disableClientPortal') {
459
+ if (resource !== 'contact') {
460
+ throw new NodeOperationError(this.getNode(), 'Disable Client Portal is only supported for the Contact resource', { itemIndex: i });
461
+ }
462
+ const id = this.getNodeParameter('id', i) as string;
463
+ responseData = await ninjaPipeApiRequest(this, 'PUT', `${basePath}/${id}/disable-client-portal`, parseJsonParameter(this, 'portalBody', i));
464
+ } else {
465
+ throw new NodeOperationError(this.getNode(), `Unsupported operation: ${operation}`, { itemIndex: i });
466
+ }
467
+ }
468
+
469
+ const responseItems = extractItems(responseData);
470
+ for (const entry of responseItems) {
471
+ returnData.push({ json: entry });
472
+ }
473
+ } catch (error) {
474
+ if (this.continueOnFail()) {
475
+ returnData.push({ json: { error: (error as Error).message }, pairedItem: i });
476
+ continue;
477
+ }
478
+ throw error;
479
+ }
480
+ }
481
+
482
+ return [returnData];
483
+ }
484
+ }
@@ -0,0 +1,5 @@
1
+ <svg width="120" height="120" viewBox="0 0 120 120" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <rect width="120" height="120" rx="24" fill="#111111"/>
3
+ <path d="M27 88V32H37L64 69V32H75V88H65L38 51V88H27Z" fill="white"/>
4
+ <path d="M84 88V32H93C101.837 32 109 39.1634 109 48C109 56.8366 101.837 64 93 64H89V88H84ZM89 59H93C99.0751 59 104 54.0751 104 48C104 41.9249 99.0751 37 93 37H89V59Z" fill="white"/>
5
+ </svg>
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@hemmilicious/n8n-nodes-ninjapipe",
3
+ "version": "0.2.2",
4
+ "description": "NinjaPipe community node for n8n",
5
+ "keywords": [
6
+ "n8n-community-node-package",
7
+ "n8n",
8
+ "ninjapipe",
9
+ "crm",
10
+ "automation"
11
+ ],
12
+ "license": "MIT",
13
+ "homepage": "https://github.com/hemmilicious/n8n-nodes-ninjapipe",
14
+ "author": {
15
+ "name": "Michael Hemmersbach"
16
+ },
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "https://github.com/hemmilicious/n8n-nodes-ninjapipe.git"
20
+ },
21
+ "publishConfig": {
22
+ "access": "public"
23
+ },
24
+ "main": "index.js",
25
+ "files": [
26
+ "credentials",
27
+ "dist",
28
+ "nodes",
29
+ "README.md",
30
+ "INSTALL.md"
31
+ ],
32
+ "scripts": {
33
+ "build": "tsc && gulp build:icons",
34
+ "dev": "n8n-node dev",
35
+ "lint": "eslint nodes credentials package.json",
36
+ "prepublishOnly": "npm run build"
37
+ },
38
+ "n8n": {
39
+ "n8nNodesApiVersion": 1,
40
+ "credentials": [
41
+ "dist/credentials/NinjaPipeApi.credentials.js"
42
+ ],
43
+ "nodes": [
44
+ "dist/nodes/NinjaPipe/NinjaPipe.node.js"
45
+ ]
46
+ },
47
+ "devDependencies": {
48
+ "@n8n/node-cli": "^0.8.0",
49
+ "@types/node": "^20.17.0",
50
+ "eslint": "^9.0.0",
51
+ "gulp": "^5.0.0",
52
+ "n8n-workflow": "^1.82.0",
53
+ "typescript": "^5.6.3"
54
+ },
55
+ "peerDependencies": {
56
+ "n8n-workflow": "*"
57
+ }
58
+ }