n8n-nodes-recruspace 0.1.2 → 0.2.0

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.
@@ -25,26 +25,80 @@ class Recruspace {
25
25
  },
26
26
  ],
27
27
  properties: [
28
+ {
29
+ displayName: 'Resource',
30
+ name: 'resource',
31
+ type: 'options',
32
+ noDataExpression: true,
33
+ options: [
34
+ {
35
+ name: 'Candidate',
36
+ value: 'candidate',
37
+ },
38
+ {
39
+ name: 'Talent Pool',
40
+ value: 'talentPool',
41
+ },
42
+ ],
43
+ default: 'candidate',
44
+ },
28
45
  {
29
46
  displayName: 'Operation',
30
47
  name: 'operation',
31
48
  type: 'options',
32
49
  noDataExpression: true,
50
+ displayOptions: {
51
+ show: {
52
+ resource: ['candidate'],
53
+ },
54
+ },
33
55
  options: [
34
56
  {
35
- name: 'Create Candidate',
36
- value: 'createCandidate',
57
+ name: 'Create',
58
+ value: 'create',
37
59
  description: 'Create a new candidate',
38
60
  action: 'Create candidate',
39
61
  },
40
62
  {
41
- name: 'Add Candidate Note',
42
- value: 'addCandidateNote',
63
+ name: 'Add Note',
64
+ value: 'addNote',
43
65
  description: 'Add a note to an existing candidate',
44
66
  action: 'Add candidate note',
45
67
  },
68
+ {
69
+ name: 'Add Tag',
70
+ value: 'addTag',
71
+ description: 'Add a tag to an existing candidate',
72
+ action: 'Add candidate tag',
73
+ },
74
+ {
75
+ name: 'Search',
76
+ value: 'search',
77
+ description: 'Search for a candidate by email',
78
+ action: 'Search candidate',
79
+ },
46
80
  ],
47
- default: 'createCandidate',
81
+ default: 'create',
82
+ },
83
+ {
84
+ displayName: 'Operation',
85
+ name: 'operation',
86
+ type: 'options',
87
+ noDataExpression: true,
88
+ displayOptions: {
89
+ show: {
90
+ resource: ['talentPool'],
91
+ },
92
+ },
93
+ options: [
94
+ {
95
+ name: 'Create',
96
+ value: 'create',
97
+ description: 'Create a new talent pool',
98
+ action: 'Create talent pool',
99
+ },
100
+ ],
101
+ default: 'create',
48
102
  },
49
103
  {
50
104
  displayName: 'First Name',
@@ -55,7 +109,8 @@ class Recruspace {
55
109
  description: 'The first name of the candidate',
56
110
  displayOptions: {
57
111
  show: {
58
- operation: ['createCandidate'],
112
+ resource: ['candidate'],
113
+ operation: ['create'],
59
114
  },
60
115
  },
61
116
  },
@@ -68,7 +123,8 @@ class Recruspace {
68
123
  description: 'The last name of the candidate',
69
124
  displayOptions: {
70
125
  show: {
71
- operation: ['createCandidate'],
126
+ resource: ['candidate'],
127
+ operation: ['create'],
72
128
  },
73
129
  },
74
130
  },
@@ -82,7 +138,39 @@ class Recruspace {
82
138
  description: 'The email of the candidate',
83
139
  displayOptions: {
84
140
  show: {
85
- operation: ['createCandidate'],
141
+ resource: ['candidate'],
142
+ operation: ['create', 'search'],
143
+ },
144
+ },
145
+ },
146
+ {
147
+ displayName: 'If Multiple Results Found',
148
+ name: 'resultStrategy',
149
+ type: 'options',
150
+ noDataExpression: true,
151
+ options: [
152
+ {
153
+ name: 'Return First Result',
154
+ value: 'Select First',
155
+ description: 'Return only the first matching candidate',
156
+ },
157
+ {
158
+ name: 'Stop Execution',
159
+ value: 'Select None',
160
+ description: 'Return empty result if multiple candidates found',
161
+ },
162
+ {
163
+ name: 'Return All Results',
164
+ value: 'Select All',
165
+ description: 'Return all matching candidates as separate items',
166
+ },
167
+ ],
168
+ default: 'Select First',
169
+ description: 'How to handle when multiple candidates match the email',
170
+ displayOptions: {
171
+ show: {
172
+ resource: ['candidate'],
173
+ operation: ['search'],
86
174
  },
87
175
  },
88
176
  },
@@ -101,7 +189,8 @@ class Recruspace {
101
189
  description: 'Where to get the CV from (only binary data is supported for candidates)',
102
190
  displayOptions: {
103
191
  show: {
104
- operation: ['createCandidate'],
192
+ resource: ['candidate'],
193
+ operation: ['create'],
105
194
  },
106
195
  },
107
196
  },
@@ -115,7 +204,8 @@ class Recruspace {
115
204
  placeholder: 'data',
116
205
  displayOptions: {
117
206
  show: {
118
- operation: ['createCandidate'],
207
+ resource: ['candidate'],
208
+ operation: ['create'],
119
209
  },
120
210
  },
121
211
  },
@@ -139,7 +229,8 @@ class Recruspace {
139
229
  description: 'Where to associate the candidate',
140
230
  displayOptions: {
141
231
  show: {
142
- operation: ['createCandidate'],
232
+ resource: ['candidate'],
233
+ operation: ['create'],
143
234
  },
144
235
  },
145
236
  },
@@ -151,7 +242,8 @@ class Recruspace {
151
242
  required: true,
152
243
  displayOptions: {
153
244
  show: {
154
- operation: ['createCandidate'],
245
+ resource: ['candidate'],
246
+ operation: ['create'],
155
247
  associationType: ['jobPost'],
156
248
  },
157
249
  },
@@ -182,7 +274,8 @@ class Recruspace {
182
274
  required: true,
183
275
  displayOptions: {
184
276
  show: {
185
- operation: ['createCandidate'],
277
+ resource: ['candidate'],
278
+ operation: ['create'],
186
279
  associationType: ['talentPool'],
187
280
  },
188
281
  },
@@ -222,7 +315,8 @@ class Recruspace {
222
315
  default: {},
223
316
  displayOptions: {
224
317
  show: {
225
- operation: ['createCandidate'],
318
+ resource: ['candidate'],
319
+ operation: ['create'],
226
320
  },
227
321
  },
228
322
  options: [
@@ -245,7 +339,8 @@ class Recruspace {
245
339
  placeholder: '12345',
246
340
  displayOptions: {
247
341
  show: {
248
- operation: ['addCandidateNote'],
342
+ resource: ['candidate'],
343
+ operation: ['addNote'],
249
344
  },
250
345
  },
251
346
  },
@@ -261,7 +356,53 @@ class Recruspace {
261
356
  description: 'The note text to add',
262
357
  displayOptions: {
263
358
  show: {
264
- operation: ['addCandidateNote'],
359
+ resource: ['candidate'],
360
+ operation: ['addNote'],
361
+ },
362
+ },
363
+ },
364
+ {
365
+ displayName: 'Candidate ID',
366
+ name: 'tagCandidateId',
367
+ type: 'number',
368
+ required: true,
369
+ default: 0,
370
+ description: 'The ID of the candidate to add tag to',
371
+ placeholder: '12345',
372
+ displayOptions: {
373
+ show: {
374
+ resource: ['candidate'],
375
+ operation: ['addTag'],
376
+ },
377
+ },
378
+ },
379
+ {
380
+ displayName: 'Tag Title',
381
+ name: 'tagTitle',
382
+ type: 'string',
383
+ required: true,
384
+ default: '',
385
+ placeholder: 'Important',
386
+ description: 'The title of the tag to add',
387
+ displayOptions: {
388
+ show: {
389
+ resource: ['candidate'],
390
+ operation: ['addTag'],
391
+ },
392
+ },
393
+ },
394
+ {
395
+ displayName: 'Name',
396
+ name: 'talentPoolName',
397
+ type: 'string',
398
+ required: true,
399
+ default: '',
400
+ placeholder: 'Software Developer Talents',
401
+ description: 'The name of the talent pool',
402
+ displayOptions: {
403
+ show: {
404
+ resource: ['talentPool'],
405
+ operation: ['create'],
265
406
  },
266
407
  },
267
408
  },
@@ -321,180 +462,276 @@ class Recruspace {
321
462
  };
322
463
  }
323
464
  async execute() {
324
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
465
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
325
466
  const items = this.getInputData();
326
467
  const returnData = [];
327
468
  const credentials = await this.getCredentials('recruspaceApi');
328
469
  const baseUrl = 'https://n8n.api.recruspace.com';
329
- const operation = this.getNodeParameter('operation', 0);
330
470
  for (let i = 0; i < items.length; i++) {
331
471
  try {
472
+ const resource = this.getNodeParameter('resource', i);
473
+ const operation = this.getNodeParameter('operation', i);
332
474
  const cleanBaseUrl = baseUrl.replace(/\/$/, '');
333
- if (operation === 'createCandidate') {
334
- const firstName = this.getNodeParameter('firstName', i);
335
- const lastName = this.getNodeParameter('lastName', i);
336
- const email = this.getNodeParameter('email', i);
337
- const cvSource = this.getNodeParameter('cvSource', i);
338
- const associationType = this.getNodeParameter('associationType', i);
339
- let jobPost = '';
340
- let talentPoolId = 0;
341
- if (associationType === 'jobPost') {
342
- const jobPostParam = this.getNodeParameter('jobPost', i);
343
- jobPost =
344
- typeof jobPostParam === 'object' && jobPostParam !== null
345
- ? jobPostParam.value
346
- : (jobPostParam || '');
347
- }
348
- else if (associationType === 'talentPool') {
349
- const talentPoolIdParam = this.getNodeParameter('talentPoolId', i);
350
- talentPoolId =
351
- typeof talentPoolIdParam === 'object' && talentPoolIdParam !== null
352
- ? parseInt(String(talentPoolIdParam.value), 10)
353
- : parseInt(String(talentPoolIdParam || '0'), 10);
354
- }
355
- const additionalFields = this.getNodeParameter('additionalFields', i);
356
- if (cvSource !== 'binaryData') {
357
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Only "Binary Data" is supported as CV Source for creating candidates. Please provide CV as binary data from a previous node.', { itemIndex: i });
358
- }
359
- const associateWithJobPost = associationType === 'jobPost';
360
- const associateWithTalentPool = associationType === 'talentPool';
361
- if (associateWithJobPost) {
362
- if (!jobPost || jobPost.trim() === '') {
363
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Job Post Hash is required when associating the candidate with a Job Post.', { itemIndex: i });
475
+ if (resource === 'candidate') {
476
+ if (operation === 'create') {
477
+ const firstName = this.getNodeParameter('firstName', i);
478
+ const lastName = this.getNodeParameter('lastName', i);
479
+ const email = this.getNodeParameter('email', i);
480
+ const cvSource = this.getNodeParameter('cvSource', i);
481
+ const associationType = this.getNodeParameter('associationType', i);
482
+ let jobPost = '';
483
+ let talentPoolId = 0;
484
+ if (associationType === 'jobPost') {
485
+ const jobPostParam = this.getNodeParameter('jobPost', i);
486
+ jobPost =
487
+ typeof jobPostParam === 'object' && jobPostParam !== null
488
+ ? jobPostParam.value
489
+ : (jobPostParam || '');
364
490
  }
365
- }
366
- else if (associateWithTalentPool) {
367
- if (!talentPoolId || talentPoolId <= 0) {
368
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Talent Pool ID is required when associating the candidate with a Talent Pool.', { itemIndex: i });
491
+ else if (associationType === 'talentPool') {
492
+ const talentPoolIdParam = this.getNodeParameter('talentPoolId', i);
493
+ talentPoolId =
494
+ typeof talentPoolIdParam === 'object' && talentPoolIdParam !== null
495
+ ? parseInt(String(talentPoolIdParam.value), 10)
496
+ : parseInt(String(talentPoolIdParam || '0'), 10);
369
497
  }
370
- }
371
- else {
372
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Invalid association type. Please choose either Job Post or Talent Pool.', { itemIndex: i });
373
- }
374
- const apiUrl = `${cleanBaseUrl}/add-candidate`;
375
- try {
376
- const binaryProperty = this.getNodeParameter('binaryProperty', i);
377
- const binaryData = this.helpers.assertBinaryData(i, binaryProperty);
378
- const cvBuffer = await this.helpers.getBinaryDataBuffer(i, binaryProperty);
379
- const cvFileName = binaryData.fileName || 'cv.pdf';
380
- const cvMimeType = binaryData.mimeType || 'application/pdf';
381
- const boundary = `----n8nFormBoundary${Date.now().toString(16)}`;
382
- const createTextField = (name, value) => {
383
- return `--${boundary}\r\nContent-Disposition: form-data; name="${name}"\r\n\r\n${value}\r\n`;
384
- };
385
- let formBody = '';
386
- formBody += createTextField('first_name', firstName);
387
- formBody += createTextField('last_name', lastName);
388
- formBody += createTextField('email', email);
389
- if (associateWithJobPost) {
390
- formBody += createTextField('job_post', jobPost);
498
+ const additionalFields = this.getNodeParameter('additionalFields', i);
499
+ if (cvSource !== 'binaryData') {
500
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Only "Binary Data" is supported as CV Source for creating candidates. Please provide CV as binary data from a previous node.', { itemIndex: i });
391
501
  }
392
- if (associateWithTalentPool) {
393
- formBody += createTextField('talent_pool_id', talentPoolId.toString());
502
+ const associateWithJobPost = associationType === 'jobPost';
503
+ const associateWithTalentPool = associationType === 'talentPool';
504
+ if (associateWithJobPost) {
505
+ if (!jobPost || jobPost.trim() === '') {
506
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Job Post Hash is required when associating the candidate with a Job Post.', { itemIndex: i });
507
+ }
394
508
  }
395
- if (additionalFields.phone_number) {
396
- formBody += createTextField('phone_number', additionalFields.phone_number);
509
+ else if (associateWithTalentPool) {
510
+ if (!talentPoolId || talentPoolId <= 0) {
511
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Talent Pool ID is required when associating the candidate with a Talent Pool.', { itemIndex: i });
512
+ }
397
513
  }
398
- const fileHeader = `--${boundary}\r\nContent-Disposition: form-data; name="cv_file"; filename="${cvFileName}"\r\nContent-Type: ${cvMimeType}\r\n\r\n`;
399
- const fileFooter = `\r\n--${boundary}--\r\n`;
400
- const bodyBuffer = Buffer.concat([
401
- Buffer.from(formBody, 'utf-8'),
402
- Buffer.from(fileHeader, 'utf-8'),
403
- cvBuffer,
404
- Buffer.from(fileFooter, 'utf-8'),
405
- ]);
406
- const response = await this.helpers.httpRequest({
407
- method: 'POST',
408
- url: apiUrl,
409
- headers: {
410
- 'x-api-key': credentials.apiKey,
411
- 'Content-Type': `multipart/form-data; boundary=${boundary}`,
412
- },
413
- body: bodyBuffer,
414
- });
415
- const responseObject = response;
416
- const responseContent = (_a = responseObject.content) !== null && _a !== void 0 ? _a : responseObject;
417
- returnData.push({
418
- json: responseContent,
419
- pairedItem: { item: i },
420
- });
421
- }
422
- catch (error) {
423
- let errorMessage = 'Unknown error occurred';
424
- let statusCode = 'unknown';
425
- if (typeof error === 'object' && error !== null) {
426
- const errorObj = error;
427
- const responseData = (_b = errorObj.response) === null || _b === void 0 ? void 0 : _b.data;
428
- if (typeof responseData === 'object' && responseData !== null) {
429
- const dataObj = responseData;
430
- errorMessage =
431
- (_f = (_e = (_d = (_c = dataObj.detail) !== null && _c !== void 0 ? _c : dataObj.message) !== null && _d !== void 0 ? _d : responseData) !== null && _e !== void 0 ? _e : errorObj.message) !== null && _f !== void 0 ? _f : errorMessage;
514
+ const apiUrl = `${cleanBaseUrl}/add-candidate`;
515
+ try {
516
+ const binaryProperty = this.getNodeParameter('binaryProperty', i);
517
+ const binaryData = this.helpers.assertBinaryData(i, binaryProperty);
518
+ const cvBuffer = await this.helpers.getBinaryDataBuffer(i, binaryProperty);
519
+ const cvFileName = binaryData.fileName || 'cv.pdf';
520
+ const cvMimeType = binaryData.mimeType || 'application/pdf';
521
+ const boundary = `----n8nFormBoundary${Date.now().toString(16)}`;
522
+ const createTextField = (name, value) => {
523
+ return `--${boundary}\r\nContent-Disposition: form-data; name="${name}"\r\n\r\n${value}\r\n`;
524
+ };
525
+ let formBody = '';
526
+ formBody += createTextField('first_name', firstName);
527
+ formBody += createTextField('last_name', lastName);
528
+ formBody += createTextField('email', email);
529
+ if (associateWithJobPost) {
530
+ formBody += createTextField('job_post', jobPost);
432
531
  }
433
- else {
434
- errorMessage = (_g = responseData !== null && responseData !== void 0 ? responseData : errorObj.message) !== null && _g !== void 0 ? _g : errorMessage;
532
+ if (associateWithTalentPool) {
533
+ formBody += createTextField('talent_pool_id', talentPoolId.toString());
435
534
  }
436
- if ((_h = errorObj.response) === null || _h === void 0 ? void 0 : _h.status) {
437
- statusCode = errorObj.response.status;
535
+ if (additionalFields.phone_number) {
536
+ formBody += createTextField('phone_number', additionalFields.phone_number);
438
537
  }
538
+ const fileHeader = `--${boundary}\r\nContent-Disposition: form-data; name="cv_file"; filename="${cvFileName}"\r\nContent-Type: ${cvMimeType}\r\n\r\n`;
539
+ const fileFooter = `\r\n--${boundary}--\r\n`;
540
+ const bodyBuffer = Buffer.concat([
541
+ Buffer.from(formBody, 'utf-8'),
542
+ Buffer.from(fileHeader, 'utf-8'),
543
+ cvBuffer,
544
+ Buffer.from(fileFooter, 'utf-8'),
545
+ ]);
546
+ const response = await this.helpers.httpRequest({
547
+ method: 'POST',
548
+ url: apiUrl,
549
+ headers: {
550
+ 'x-api-key': credentials.apiKey,
551
+ 'Content-Type': `multipart/form-data; boundary=${boundary}`,
552
+ },
553
+ body: bodyBuffer,
554
+ });
555
+ const responseObject = response;
556
+ const responseContent = (_a = responseObject.content) !== null && _a !== void 0 ? _a : responseObject;
557
+ returnData.push({
558
+ json: responseContent,
559
+ pairedItem: { item: i },
560
+ });
561
+ }
562
+ catch (error) {
563
+ let errorMessage = 'Unknown error occurred';
564
+ let statusCode = 'unknown';
565
+ if (typeof error === 'object' && error !== null) {
566
+ const errorObj = error;
567
+ const responseData = (_b = errorObj.response) === null || _b === void 0 ? void 0 : _b.data;
568
+ if (typeof responseData === 'object' && responseData !== null) {
569
+ const dataObj = responseData;
570
+ errorMessage =
571
+ (_f = (_e = (_d = (_c = dataObj.detail) !== null && _c !== void 0 ? _c : dataObj.message) !== null && _d !== void 0 ? _d : responseData) !== null && _e !== void 0 ? _e : errorObj.message) !== null && _f !== void 0 ? _f : errorMessage;
572
+ }
573
+ else {
574
+ errorMessage = (_g = responseData !== null && responseData !== void 0 ? responseData : errorObj.message) !== null && _g !== void 0 ? _g : errorMessage;
575
+ }
576
+ if ((_h = errorObj.response) === null || _h === void 0 ? void 0 : _h.status) {
577
+ statusCode = errorObj.response.status;
578
+ }
579
+ }
580
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Failed to create candidate (${statusCode}): ${JSON.stringify(errorMessage)}\n\nBase URL: ${baseUrl}\nFull URL: ${apiUrl}`, { itemIndex: i });
439
581
  }
440
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Failed to create candidate (${statusCode}): ${JSON.stringify(errorMessage)}\n\nBase URL: ${baseUrl}\nFull URL: ${apiUrl}`, { itemIndex: i });
441
- }
442
- }
443
- else if (operation === 'addCandidateNote') {
444
- const candidateId = this.getNodeParameter('candidateId', i);
445
- const noteText = this.getNodeParameter('comment', i);
446
- if (!candidateId || candidateId === 0) {
447
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Candidate ID is required. Please provide a valid Candidate ID.', { itemIndex: i });
448
582
  }
449
- if (!noteText || noteText.trim() === '') {
450
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Note text is required.', { itemIndex: i });
583
+ else if (operation === 'addNote') {
584
+ const candidateId = this.getNodeParameter('candidateId', i);
585
+ const noteText = this.getNodeParameter('comment', i);
586
+ if (!candidateId || candidateId === 0) {
587
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Candidate ID is required. Please provide a valid Candidate ID.', { itemIndex: i });
588
+ }
589
+ if (!noteText || noteText.trim() === '') {
590
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Note text is required.', { itemIndex: i });
591
+ }
592
+ const apiUrl = `${cleanBaseUrl}/add-note`;
593
+ try {
594
+ const response = await this.helpers.httpRequest({
595
+ method: 'POST',
596
+ url: apiUrl,
597
+ headers: {
598
+ 'x-api-key': credentials.apiKey,
599
+ 'Content-Type': 'application/json',
600
+ },
601
+ body: {
602
+ candidate_id: candidateId,
603
+ text: noteText,
604
+ },
605
+ json: true,
606
+ });
607
+ returnData.push({
608
+ json: {
609
+ ...response,
610
+ candidate_id: candidateId,
611
+ },
612
+ pairedItem: { item: i },
613
+ });
614
+ }
615
+ catch (error) {
616
+ let errorMessage = 'Unknown error occurred';
617
+ let statusCode = 'unknown';
618
+ if (typeof error === 'object' && error !== null) {
619
+ const errorObj = error;
620
+ const responseData = (_j = errorObj.response) === null || _j === void 0 ? void 0 : _j.data;
621
+ if (typeof responseData === 'object' && responseData !== null) {
622
+ const dataObj = responseData;
623
+ errorMessage =
624
+ (_o = (_m = (_l = (_k = dataObj.detail) !== null && _k !== void 0 ? _k : dataObj.message) !== null && _l !== void 0 ? _l : responseData) !== null && _m !== void 0 ? _m : errorObj.message) !== null && _o !== void 0 ? _o : errorMessage;
625
+ }
626
+ else {
627
+ errorMessage = (_p = responseData !== null && responseData !== void 0 ? responseData : errorObj.message) !== null && _p !== void 0 ? _p : errorMessage;
628
+ }
629
+ if ((_q = errorObj.response) === null || _q === void 0 ? void 0 : _q.status) {
630
+ statusCode = errorObj.response.status;
631
+ }
632
+ }
633
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Failed to add note (${statusCode}): ${JSON.stringify(errorMessage)}\n\nURL: ${apiUrl}\nRequest body: ${JSON.stringify({ candidate_id: candidateId, text: noteText }, null, 2)}`, { itemIndex: i });
634
+ }
451
635
  }
452
- const apiUrl = `${cleanBaseUrl}/add-note`;
453
- try {
454
- const response = await this.helpers.httpRequest({
455
- method: 'POST',
456
- url: apiUrl,
457
- headers: {
458
- 'x-api-key': credentials.apiKey,
459
- 'Content-Type': 'application/json',
460
- },
461
- body: {
462
- candidate_id: candidateId,
463
- text: noteText,
464
- },
465
- json: true,
466
- });
467
- returnData.push({
468
- json: {
469
- ...response,
470
- candidate_id: candidateId,
471
- },
472
- pairedItem: { item: i },
473
- });
636
+ else if (operation === 'search') {
637
+ const email = this.getNodeParameter('email', i);
638
+ const resultStrategy = this.getNodeParameter('resultStrategy', i);
639
+ if (!email || email.trim() === '') {
640
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Email is required to search for a candidate.', { itemIndex: i });
641
+ }
642
+ try {
643
+ const response = await this.helpers.httpRequest({
644
+ method: 'GET',
645
+ url: `${cleanBaseUrl}/actions/candidates/search-by-email`,
646
+ headers: {
647
+ 'x-api-key': credentials.apiKey,
648
+ 'Content-Type': 'application/json',
649
+ },
650
+ qs: {
651
+ email: email.trim(),
652
+ type: resultStrategy,
653
+ },
654
+ json: true,
655
+ });
656
+ const results = Array.isArray(response) ? response : [response];
657
+ for (const result of results) {
658
+ returnData.push({
659
+ json: result,
660
+ pairedItem: { item: i },
661
+ });
662
+ }
663
+ }
664
+ catch (error) {
665
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Failed to search candidate: ${error.message}`, { itemIndex: i });
666
+ }
474
667
  }
475
- catch (error) {
476
- let errorMessage = 'Unknown error occurred';
477
- let statusCode = 'unknown';
478
- if (typeof error === 'object' && error !== null) {
668
+ else if (operation === 'addTag') {
669
+ const candidateId = this.getNodeParameter('tagCandidateId', i);
670
+ const tagTitle = this.getNodeParameter('tagTitle', i);
671
+ if (!candidateId) {
672
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Candidate ID is required to add a tag.', { itemIndex: i });
673
+ }
674
+ if (!tagTitle || tagTitle.trim() === '') {
675
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Tag Title is required.', { itemIndex: i });
676
+ }
677
+ try {
678
+ const response = await this.helpers.httpRequest({
679
+ method: 'POST',
680
+ url: `${cleanBaseUrl}/actions/candidates/add-tag`,
681
+ headers: {
682
+ 'x-api-key': credentials.apiKey,
683
+ 'Content-Type': 'application/json',
684
+ },
685
+ body: {
686
+ candidate_id: candidateId,
687
+ title: tagTitle,
688
+ },
689
+ json: true,
690
+ });
691
+ returnData.push({
692
+ json: response,
693
+ pairedItem: { item: i },
694
+ });
695
+ }
696
+ catch (error) {
479
697
  const errorObj = error;
480
- const responseData = (_j = errorObj.response) === null || _j === void 0 ? void 0 : _j.data;
481
- if (typeof responseData === 'object' && responseData !== null) {
482
- const dataObj = responseData;
483
- errorMessage =
484
- (_o = (_m = (_l = (_k = dataObj.detail) !== null && _k !== void 0 ? _k : dataObj.message) !== null && _l !== void 0 ? _l : responseData) !== null && _m !== void 0 ? _m : errorObj.message) !== null && _o !== void 0 ? _o : errorMessage;
485
- }
486
- else {
487
- errorMessage = (_p = responseData !== null && responseData !== void 0 ? responseData : errorObj.message) !== null && _p !== void 0 ? _p : errorMessage;
488
- }
489
- if ((_q = errorObj.response) === null || _q === void 0 ? void 0 : _q.status) {
490
- statusCode = errorObj.response.status;
698
+ let errorMessage = error.message;
699
+ if ((_r = errorObj.response) === null || _r === void 0 ? void 0 : _r.data) {
700
+ const data = errorObj.response.data;
701
+ errorMessage = String(data.message || data.error || JSON.stringify(data));
491
702
  }
703
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Failed to add tag: ${errorMessage}`, { itemIndex: i });
492
704
  }
493
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Failed to add note (${statusCode}): ${JSON.stringify(errorMessage)}\n\nURL: ${apiUrl}\nRequest body: ${JSON.stringify({ candidate_id: candidateId, text: noteText }, null, 2)}`, { itemIndex: i });
494
705
  }
495
706
  }
496
- else {
497
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), `The operation "${operation}" is not supported.`, { itemIndex: i });
707
+ else if (resource === 'talentPool') {
708
+ if (operation === 'create') {
709
+ const talentPoolName = this.getNodeParameter('talentPoolName', i);
710
+ if (!talentPoolName || talentPoolName.trim() === '') {
711
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Talent Pool Name is required.', { itemIndex: i });
712
+ }
713
+ try {
714
+ const response = await this.helpers.httpRequest({
715
+ method: 'POST',
716
+ url: `${cleanBaseUrl}/create-talent-pool`,
717
+ headers: {
718
+ 'x-api-key': credentials.apiKey,
719
+ 'Content-Type': 'application/json',
720
+ },
721
+ body: {
722
+ name: talentPoolName,
723
+ },
724
+ json: true,
725
+ });
726
+ returnData.push({
727
+ json: response,
728
+ pairedItem: { item: i },
729
+ });
730
+ }
731
+ catch (error) {
732
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Failed to create talent pool: ${error.message}`, { itemIndex: i });
733
+ }
734
+ }
498
735
  }
499
736
  }
500
737
  catch (error) {