@dereekb/zoho 13.0.5 → 13.0.7
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/index.cjs.js +2164 -231
- package/index.esm.js +2143 -232
- package/nestjs/LICENSE +21 -0
- package/nestjs/README.md +11 -0
- package/nestjs/docs/configuration.md +165 -0
- package/nestjs/docs/crm-getting-started.md +296 -0
- package/nestjs/index.cjs.js +653 -5
- package/nestjs/index.esm.js +647 -7
- package/nestjs/package.json +3 -3
- package/nestjs/src/lib/crm/crm.api.d.ts +46 -0
- package/nestjs/src/lib/crm/crm.config.d.ts +16 -1
- package/nestjs/src/lib/crm/crm.module.d.ts +77 -8
- package/nestjs/src/lib/index.d.ts +2 -0
- package/nestjs/src/lib/recruit/recruit.api.d.ts +50 -0
- package/nestjs/src/lib/recruit/recruit.config.d.ts +16 -1
- package/nestjs/src/lib/recruit/recruit.module.d.ts +77 -8
- package/nestjs/src/lib/sign/index.d.ts +3 -0
- package/nestjs/src/lib/sign/sign.api.d.ts +54 -0
- package/nestjs/src/lib/sign/sign.config.d.ts +10 -0
- package/nestjs/src/lib/sign/sign.module.d.ts +94 -0
- package/package.json +2 -2
- package/src/lib/accounts/accounts.api.d.ts +149 -3
- package/src/lib/accounts/accounts.factory.d.ts +73 -6
- package/src/lib/crm/crm.api.d.ts +599 -62
- package/src/lib/crm/crm.api.notes.d.ts +46 -3
- package/src/lib/crm/crm.api.tags.d.ts +65 -2
- package/src/lib/crm/crm.config.d.ts +30 -0
- package/src/lib/crm/crm.criteria.d.ts +60 -3
- package/src/lib/crm/crm.error.api.d.ts +42 -0
- package/src/lib/crm/crm.factory.d.ts +39 -3
- package/src/lib/crm/crm.notes.d.ts +36 -0
- package/src/lib/crm/crm.tags.d.ts +3 -0
- package/src/lib/index.d.ts +1 -0
- package/src/lib/recruit/recruit.api.candidates.d.ts +44 -3
- package/src/lib/recruit/recruit.api.d.ts +719 -57
- package/src/lib/recruit/recruit.api.notes.d.ts +140 -0
- package/src/lib/recruit/recruit.api.tags.d.ts +122 -14
- package/src/lib/recruit/recruit.config.d.ts +30 -0
- package/src/lib/recruit/recruit.criteria.d.ts +55 -3
- package/src/lib/recruit/recruit.error.api.d.ts +39 -0
- package/src/lib/recruit/recruit.factory.d.ts +39 -3
- package/src/lib/recruit/recruit.notes.d.ts +21 -0
- package/src/lib/recruit/recruit.tags.d.ts +3 -0
- package/src/lib/shared/criteria.d.ts +95 -11
- package/src/lib/shared/criteria.util.d.ts +19 -4
- package/src/lib/sign/index.d.ts +6 -0
- package/src/lib/sign/sign.api.d.ts +397 -0
- package/src/lib/sign/sign.api.page.d.ts +109 -0
- package/src/lib/sign/sign.config.d.ts +24 -0
- package/src/lib/sign/sign.d.ts +225 -0
- package/src/lib/sign/sign.error.api.d.ts +7 -0
- package/src/lib/sign/sign.factory.d.ts +58 -0
- package/src/lib/zoho.api.page.d.ts +41 -10
- package/src/lib/zoho.config.d.ts +24 -9
- package/src/lib/zoho.limit.d.ts +41 -9
- package/src/lib/zoho.type.d.ts +24 -8
package/index.cjs.js
CHANGED
|
@@ -5,7 +5,10 @@ var fetch = require('@dereekb/util/fetch');
|
|
|
5
5
|
var makeError = require('make-error');
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
8
|
+
* Creates an empty {@link ZohoPageResult} with no data and `more_records: false`.
|
|
9
|
+
* Useful as a fallback when the API returns `null` instead of an empty result.
|
|
10
|
+
*
|
|
11
|
+
* @returns An empty page result with default pagination info
|
|
9
12
|
*/
|
|
10
13
|
function emptyZohoPageResult() {
|
|
11
14
|
return {
|
|
@@ -19,11 +22,26 @@ function emptyZohoPageResult() {
|
|
|
19
22
|
};
|
|
20
23
|
}
|
|
21
24
|
/**
|
|
22
|
-
* Creates a
|
|
25
|
+
* Creates a page factory that wraps a Zoho fetch function with automatic pagination.
|
|
23
26
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
+
* The factory reads `info.more_records` from each response to determine if additional
|
|
28
|
+
* pages exist, and automatically increments the `page` parameter for subsequent requests.
|
|
29
|
+
*
|
|
30
|
+
* @param fetch - The Zoho fetch function to paginate over
|
|
31
|
+
* @param defaults - Optional default configuration for the page factory
|
|
32
|
+
* @returns A page factory that produces iterable page fetchers
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```typescript
|
|
36
|
+
* const pageFactory = zohoFetchPageFactory(zohoCrmSearchRecords(context));
|
|
37
|
+
*
|
|
38
|
+
* const fetchPage = pageFactory({ module: 'Contacts', word: 'Smith', per_page: 10 });
|
|
39
|
+
* const firstPage = await fetchPage.fetchNext();
|
|
40
|
+
*
|
|
41
|
+
* if (firstPage.result.info.more_records) {
|
|
42
|
+
* const secondPage = await firstPage.fetchNext();
|
|
43
|
+
* }
|
|
44
|
+
* ```
|
|
27
45
|
*/
|
|
28
46
|
function zohoFetchPageFactory(fetch$1, defaults) {
|
|
29
47
|
return fetch.fetchPageFactory({
|
|
@@ -40,7 +58,13 @@ function zohoFetchPageFactory(fetch$1, defaults) {
|
|
|
40
58
|
});
|
|
41
59
|
}
|
|
42
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Service identifier used to distinguish Zoho Recruit from other Zoho services when managing access tokens and API routing.
|
|
63
|
+
*/
|
|
43
64
|
const ZOHO_RECRUIT_SERVICE_NAME = 'recruit';
|
|
65
|
+
/**
|
|
66
|
+
* Resolves an environment key or passthrough URL to the full Zoho Recruit API URL.
|
|
67
|
+
*/
|
|
44
68
|
function zohoRecruitConfigApiUrl(input) {
|
|
45
69
|
switch (input) {
|
|
46
70
|
case 'sandbox':
|
|
@@ -88,9 +112,33 @@ const ZOHO_RECRUIT_RECORD_ATTACHMENT_METADATA_ATTACH_TYPE_RESUME = 'Resume';
|
|
|
88
112
|
*/
|
|
89
113
|
const MAX_ZOHO_SEARCH_MODULE_RECORDS_CRITERIA = 10;
|
|
90
114
|
/**
|
|
91
|
-
*
|
|
115
|
+
* Compiles a {@link ZohoSearchRecordsCriteriaTreeElement} into a URL-ready criteria string.
|
|
116
|
+
*
|
|
117
|
+
* Accepts any supported input shape: a raw criteria string (passed through),
|
|
118
|
+
* an array of {@link ZohoSearchRecordsCriteriaEntry} (AND-joined), or a full
|
|
119
|
+
* {@link ZohoSearchRecordsCriteriaTree} with nested AND/OR groups. Returns
|
|
120
|
+
* `undefined` when the input is nullish or empty.
|
|
92
121
|
*
|
|
93
|
-
*
|
|
122
|
+
* @param input - Criteria tree element, entry array, or raw criteria string
|
|
123
|
+
* @returns Compiled criteria string, or `undefined` if input is empty
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* ```typescript
|
|
127
|
+
* // From an entry array (AND-joined):
|
|
128
|
+
* const criteria = zohoSearchRecordsCriteriaString([
|
|
129
|
+
* { field: 'Last_Name', filter: 'equals', value: 'Smith' },
|
|
130
|
+
* { field: 'Email', filter: 'contains', value: 'example.com' }
|
|
131
|
+
* ]);
|
|
132
|
+
* // => '((Last_Name:equals:Smith)and(Email:contains:example.com))'
|
|
133
|
+
*
|
|
134
|
+
* // From a tree with OR:
|
|
135
|
+
* const orCriteria = zohoSearchRecordsCriteriaString({
|
|
136
|
+
* or: [
|
|
137
|
+
* [{ field: 'Status', filter: 'equals', value: 'Active' }],
|
|
138
|
+
* [{ field: 'Status', filter: 'equals', value: 'Pending' }]
|
|
139
|
+
* ]
|
|
140
|
+
* });
|
|
141
|
+
* ```
|
|
94
142
|
*/
|
|
95
143
|
function zohoSearchRecordsCriteriaString(input) {
|
|
96
144
|
let result;
|
|
@@ -113,6 +161,14 @@ function zohoSearchRecordsCriteriaString(input) {
|
|
|
113
161
|
}
|
|
114
162
|
return result;
|
|
115
163
|
}
|
|
164
|
+
/**
|
|
165
|
+
* Compiles a {@link ZohoSearchRecordsCriteriaTree} into a criteria string by
|
|
166
|
+
* recursively resolving nested AND/OR groups. When both `and` and `or` are
|
|
167
|
+
* present at the same level, the OR group is merged into the AND group.
|
|
168
|
+
*
|
|
169
|
+
* @param tree - Criteria tree containing `and` and/or `or` branches
|
|
170
|
+
* @returns Compiled criteria string, or `undefined` if the tree is empty
|
|
171
|
+
*/
|
|
116
172
|
function zohoSearchRecordsCriteriaStringForTree(tree) {
|
|
117
173
|
function convertToString(value) {
|
|
118
174
|
let result;
|
|
@@ -146,7 +202,9 @@ function zohoSearchRecordsCriteriaStringForTree(tree) {
|
|
|
146
202
|
return result;
|
|
147
203
|
}
|
|
148
204
|
/**
|
|
149
|
-
*
|
|
205
|
+
* Escapes parentheses and commas in a field value for use in a {@link ZohoSearchRecordsCriteriaString}.
|
|
206
|
+
*
|
|
207
|
+
* Characters `(`, `)`, and `,` are escaped with a double-backslash prefix as required by the Zoho API.
|
|
150
208
|
*/
|
|
151
209
|
const escapeZohoFieldValueForCriteriaString = util.escapeStringCharactersFunction({
|
|
152
210
|
/**
|
|
@@ -156,10 +214,22 @@ const escapeZohoFieldValueForCriteriaString = util.escapeStringCharactersFunctio
|
|
|
156
214
|
escapeCharacter: (char) => `\\\\${char}`
|
|
157
215
|
});
|
|
158
216
|
/**
|
|
159
|
-
* Converts
|
|
217
|
+
* Converts a single {@link ZohoSearchRecordsCriteriaEntry} into a parenthesized criteria string.
|
|
160
218
|
*
|
|
161
|
-
* @
|
|
162
|
-
*
|
|
219
|
+
* Automatically escapes parentheses and commas in the value via {@link escapeZohoFieldValueForCriteriaString}.
|
|
220
|
+
*
|
|
221
|
+
* @param entry - The criteria entry to convert
|
|
222
|
+
* @returns Criteria string in the format `(field:filter:escapedValue)`
|
|
223
|
+
*
|
|
224
|
+
* @example
|
|
225
|
+
* ```typescript
|
|
226
|
+
* const str = zohoSearchRecordsCriteriaEntryToCriteriaString({
|
|
227
|
+
* field: 'Last_Name',
|
|
228
|
+
* filter: 'equals',
|
|
229
|
+
* value: 'Smith'
|
|
230
|
+
* });
|
|
231
|
+
* // => '(Last_Name:equals:Smith)'
|
|
232
|
+
* ```
|
|
163
233
|
*/
|
|
164
234
|
function zohoSearchRecordsCriteriaEntryToCriteriaString(entry) {
|
|
165
235
|
const escapedValue = escapeZohoFieldValueForCriteriaString(entry.value);
|
|
@@ -167,15 +237,35 @@ function zohoSearchRecordsCriteriaEntryToCriteriaString(entry) {
|
|
|
167
237
|
}
|
|
168
238
|
|
|
169
239
|
/**
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
* https://www.zoho.com/recruit/developer-guide/apiv2/search-records.html
|
|
240
|
+
* Maximum number of criteria allowed per Recruit search query (10).
|
|
173
241
|
*
|
|
174
242
|
* "You can search for a maximum of 10 criteria (with the same or different column) with equals and starts_with conditions."
|
|
243
|
+
*
|
|
244
|
+
* Re-exports {@link MAX_ZOHO_SEARCH_MODULE_RECORDS_CRITERIA}.
|
|
245
|
+
*
|
|
246
|
+
* @see https://www.zoho.com/recruit/developer-guide/apiv2/search-records.html
|
|
175
247
|
*/
|
|
176
248
|
const MAX_ZOHO_RECRUIT_SEARCH_MODULE_RECORDS_CRITERIA = MAX_ZOHO_SEARCH_MODULE_RECORDS_CRITERIA;
|
|
249
|
+
/**
|
|
250
|
+
* Recruit-specific re-export of {@link zohoSearchRecordsCriteriaString}.
|
|
251
|
+
*
|
|
252
|
+
* Compiles a criteria tree element (entry array, nested tree, or raw string) into a
|
|
253
|
+
* URL-ready criteria string. Returns `undefined` when the input is nullish or empty.
|
|
254
|
+
*/
|
|
177
255
|
const zohoRecruitSearchRecordsCriteriaString = zohoSearchRecordsCriteriaString;
|
|
256
|
+
/**
|
|
257
|
+
* Recruit-specific re-export of {@link zohoSearchRecordsCriteriaStringForTree}.
|
|
258
|
+
*
|
|
259
|
+
* Compiles a {@link ZohoRecruitSearchRecordsCriteriaTree} into a criteria string by
|
|
260
|
+
* recursively resolving nested AND/OR groups.
|
|
261
|
+
*/
|
|
178
262
|
const zohoRecruitSearchRecordsCriteriaStringForTree = zohoSearchRecordsCriteriaStringForTree;
|
|
263
|
+
/**
|
|
264
|
+
* Recruit-specific re-export of {@link zohoSearchRecordsCriteriaEntryToCriteriaString}.
|
|
265
|
+
*
|
|
266
|
+
* Converts a single criteria entry into a parenthesized criteria string
|
|
267
|
+
* in the format `(field:filter:escapedValue)`.
|
|
268
|
+
*/
|
|
179
269
|
const zohoRecruitSearchRecordsCriteriaEntryToCriteriaString = zohoSearchRecordsCriteriaEntryToCriteriaString;
|
|
180
270
|
|
|
181
271
|
/**
|
|
@@ -482,19 +572,37 @@ class ZohoRecruitRecordNoContentError extends makeError.BaseError {
|
|
|
482
572
|
this.recordId = recordId;
|
|
483
573
|
}
|
|
484
574
|
}
|
|
575
|
+
/**
|
|
576
|
+
* Base error for Zoho Recruit record CRUD operations, carrying structured error details from the API response.
|
|
577
|
+
*/
|
|
485
578
|
class ZohoRecruitRecordCrudError extends ZohoServerError {
|
|
486
579
|
}
|
|
580
|
+
/**
|
|
581
|
+
* Thrown when a required field is missing from the record data submitted to Zoho Recruit.
|
|
582
|
+
*/
|
|
487
583
|
class ZohoRecruitRecordCrudMandatoryFieldNotFoundError extends ZohoRecruitRecordCrudError {
|
|
488
584
|
}
|
|
585
|
+
/**
|
|
586
|
+
* Thrown when a record cannot be created or updated because it would produce duplicate data in Zoho Recruit.
|
|
587
|
+
*/
|
|
489
588
|
class ZohoRecruitRecordCrudDuplicateDataError extends ZohoRecruitRecordCrudError {
|
|
490
589
|
}
|
|
590
|
+
/**
|
|
591
|
+
* Thrown when the Zoho Recruit API rejects record data as invalid. Provides per-field error details via {@link invalidFieldDetails}.
|
|
592
|
+
*/
|
|
491
593
|
class ZohoRecruitRecordCrudInvalidDataError extends ZohoRecruitRecordCrudError {
|
|
492
594
|
get invalidFieldDetails() {
|
|
493
595
|
return this.error.details;
|
|
494
596
|
}
|
|
495
597
|
}
|
|
598
|
+
/**
|
|
599
|
+
* Thrown when the 'id' field in the submitted data does not match any existing Zoho Recruit record.
|
|
600
|
+
*/
|
|
496
601
|
class ZohoRecruitRecordCrudNoMatchingRecordError extends ZohoRecruitRecordCrudInvalidDataError {
|
|
497
602
|
}
|
|
603
|
+
/**
|
|
604
|
+
* Creates a typed CRUD error subclass based on the error code returned by the Zoho Recruit API, enabling callers to catch specific failure modes.
|
|
605
|
+
*/
|
|
498
606
|
function zohoRecruitRecordCrudError(error) {
|
|
499
607
|
let result;
|
|
500
608
|
switch (error.code) {
|
|
@@ -519,6 +627,9 @@ function zohoRecruitRecordCrudError(error) {
|
|
|
519
627
|
}
|
|
520
628
|
return result;
|
|
521
629
|
}
|
|
630
|
+
/**
|
|
631
|
+
* Returns an assertion function that throws {@link ZohoRecruitRecordNoContentError} when the data array result is empty or null, indicating the requested record does not exist.
|
|
632
|
+
*/
|
|
522
633
|
function assertZohoRecruitRecordDataArrayResultHasContent(moduleName, recordId) {
|
|
523
634
|
return (x) => {
|
|
524
635
|
if (x == null || !x.data?.length) {
|
|
@@ -529,7 +640,13 @@ function assertZohoRecruitRecordDataArrayResultHasContent(moduleName, recordId)
|
|
|
529
640
|
}
|
|
530
641
|
};
|
|
531
642
|
}
|
|
643
|
+
/**
|
|
644
|
+
* Logs Zoho Recruit server errors to the console, prefixed with the 'ZohoRecruit' service label.
|
|
645
|
+
*/
|
|
532
646
|
const logZohoRecruitErrorToConsole = logZohoServerErrorFunction('ZohoRecruit');
|
|
647
|
+
/**
|
|
648
|
+
* Parses the JSON body of a failed Zoho Recruit fetch response into a structured error, returning undefined if the body cannot be parsed.
|
|
649
|
+
*/
|
|
533
650
|
async function parseZohoRecruitError(responseError) {
|
|
534
651
|
const data = await responseError.response.json().catch((x) => undefined);
|
|
535
652
|
let result;
|
|
@@ -538,6 +655,9 @@ async function parseZohoRecruitError(responseError) {
|
|
|
538
655
|
}
|
|
539
656
|
return result;
|
|
540
657
|
}
|
|
658
|
+
/**
|
|
659
|
+
* Converts raw Zoho Recruit error response data into a parsed error, delegating to Recruit-specific handlers for known error codes and falling back to the shared Zoho parser.
|
|
660
|
+
*/
|
|
541
661
|
function parseZohoRecruitServerErrorResponseData(errorResponseData, responseError) {
|
|
542
662
|
let result;
|
|
543
663
|
const error = tryFindZohoServerErrorData(errorResponseData, responseError);
|
|
@@ -552,7 +672,13 @@ function parseZohoRecruitServerErrorResponseData(errorResponseData, responseErro
|
|
|
552
672
|
}
|
|
553
673
|
return result;
|
|
554
674
|
}
|
|
675
|
+
/**
|
|
676
|
+
* Intercepts Zoho Recruit API responses that return HTTP 200 but contain an error payload, re-throwing them as proper errors so callers are not silently misled by a success status.
|
|
677
|
+
*/
|
|
555
678
|
const interceptZohoRecruit200StatusWithErrorResponse = interceptZohoErrorResponseFactory(parseZohoRecruitServerErrorResponseData);
|
|
679
|
+
/**
|
|
680
|
+
* Wraps a fetch client to automatically parse Zoho Recruit error responses, log them, and invoke a custom handler (e.g., for token refresh on authentication failures).
|
|
681
|
+
*/
|
|
556
682
|
const handleZohoRecruitErrorFetch = handleZohoErrorFetchFactory(parseZohoRecruitError, logZohoRecruitErrorToConsole);
|
|
557
683
|
// MARK: Compat
|
|
558
684
|
/**
|
|
@@ -568,9 +694,10 @@ const assertRecordDataArrayResultHasContent = assertZohoRecruitRecordDataArrayRe
|
|
|
568
694
|
*/
|
|
569
695
|
const ZOHO_RECRUIT_CRUD_FUNCTION_MAX_RECORDS_LIMIT = 100;
|
|
570
696
|
/**
|
|
571
|
-
*
|
|
697
|
+
* Shared implementation for the Insert, Upsert, and Update endpoints, which all share the same request/response structure.
|
|
572
698
|
*
|
|
573
|
-
*
|
|
699
|
+
* When a single record is provided, the function returns the change details directly or throws on error.
|
|
700
|
+
* When multiple records are provided, it returns a paired success/error result.
|
|
574
701
|
*/
|
|
575
702
|
function updateRecordLikeFunction$1(context, fetchUrlPrefix, fetchMethod) {
|
|
576
703
|
return (({ data, module }) => context.fetchJson(`/v2/${module}${fetchUrlPrefix}`, zohoRecruitApiFetchJsonInput(fetchMethod, { data: util.asArray(data) })).then((x) => {
|
|
@@ -591,45 +718,145 @@ function updateRecordLikeFunction$1(context, fetchUrlPrefix, fetchMethod) {
|
|
|
591
718
|
}));
|
|
592
719
|
}
|
|
593
720
|
/**
|
|
594
|
-
*
|
|
721
|
+
* Creates a {@link ZohoRecruitInsertRecordFunction} bound to the given context.
|
|
595
722
|
*
|
|
596
|
-
*
|
|
723
|
+
* Inserts one or more records into a Recruit module. When a single record is
|
|
724
|
+
* provided, returns the {@link ZohoRecruitChangeObjectDetails} directly or
|
|
725
|
+
* throws on error. When multiple records are provided, returns a
|
|
726
|
+
* {@link ZohoRecruitUpdateRecordResult} with paired success/error arrays.
|
|
597
727
|
*
|
|
598
|
-
* @
|
|
599
|
-
*
|
|
728
|
+
* Maximum of {@link ZOHO_RECRUIT_CRUD_FUNCTION_MAX_RECORDS_LIMIT} records per call.
|
|
729
|
+
*
|
|
730
|
+
* @param context - Authenticated Zoho Recruit context providing fetch and rate limiting
|
|
731
|
+
* @returns Function that inserts records into the specified module
|
|
732
|
+
*
|
|
733
|
+
* @example
|
|
734
|
+
* ```typescript
|
|
735
|
+
* const insertRecord = zohoRecruitInsertRecord(context);
|
|
736
|
+
*
|
|
737
|
+
* // Single record — returns details directly or throws on error:
|
|
738
|
+
* const details = await insertRecord({
|
|
739
|
+
* module: ZOHO_RECRUIT_CANDIDATES_MODULE,
|
|
740
|
+
* data: { First_Name: 'Jane', Last_Name: 'Doe', Email: 'jane@example.com' }
|
|
741
|
+
* });
|
|
742
|
+
*
|
|
743
|
+
* // Multiple records — returns paired success/error arrays:
|
|
744
|
+
* const result = await insertRecord({
|
|
745
|
+
* module: ZOHO_RECRUIT_CANDIDATES_MODULE,
|
|
746
|
+
* data: [
|
|
747
|
+
* { First_Name: 'Jane', Last_Name: 'Doe', Email: 'jane@example.com' },
|
|
748
|
+
* { First_Name: 'John', Last_Name: 'Doe', Email: 'john@example.com' }
|
|
749
|
+
* ]
|
|
750
|
+
* });
|
|
751
|
+
* ```
|
|
752
|
+
*
|
|
753
|
+
* @see https://www.zoho.com/recruit/developer-guide/apiv2/insert-records.html
|
|
600
754
|
*/
|
|
601
755
|
function zohoRecruitInsertRecord(context) {
|
|
602
756
|
return updateRecordLikeFunction$1(context, '', 'POST');
|
|
603
757
|
}
|
|
604
758
|
/**
|
|
605
|
-
*
|
|
759
|
+
* Creates a {@link ZohoRecruitUpsertRecordFunction} bound to the given context.
|
|
606
760
|
*
|
|
607
|
-
*
|
|
761
|
+
* Inserts or updates one or more records in a Recruit module based on whether
|
|
762
|
+
* each record includes an `id`. Uses the `/upsert` endpoint. Single-record
|
|
763
|
+
* calls return details directly or throw; multi-record calls return paired
|
|
764
|
+
* success/error arrays.
|
|
608
765
|
*
|
|
609
|
-
* @
|
|
610
|
-
*
|
|
766
|
+
* Maximum of {@link ZOHO_RECRUIT_CRUD_FUNCTION_MAX_RECORDS_LIMIT} records per call.
|
|
767
|
+
*
|
|
768
|
+
* @param context - Authenticated Zoho Recruit context providing fetch and rate limiting
|
|
769
|
+
* @returns Function that upserts records in the specified module
|
|
770
|
+
*
|
|
771
|
+
* @example
|
|
772
|
+
* ```typescript
|
|
773
|
+
* const upsertRecord = zohoRecruitUpsertRecord(context);
|
|
774
|
+
*
|
|
775
|
+
* // Create (no id) — returns details directly:
|
|
776
|
+
* const created = await upsertRecord({
|
|
777
|
+
* module: ZOHO_RECRUIT_CANDIDATES_MODULE,
|
|
778
|
+
* data: { Email: 'new@example.com', Last_Name: 'New' }
|
|
779
|
+
* });
|
|
780
|
+
*
|
|
781
|
+
* // Update (with id) — returns details directly:
|
|
782
|
+
* const updated = await upsertRecord({
|
|
783
|
+
* module: ZOHO_RECRUIT_CANDIDATES_MODULE,
|
|
784
|
+
* data: { id: existingId, First_Name: 'Updated' }
|
|
785
|
+
* });
|
|
786
|
+
*
|
|
787
|
+
* // Mixed create and update — returns paired arrays:
|
|
788
|
+
* const result = await upsertRecord({
|
|
789
|
+
* module: ZOHO_RECRUIT_CANDIDATES_MODULE,
|
|
790
|
+
* data: [
|
|
791
|
+
* { Email: 'create@example.com', Last_Name: 'Create' },
|
|
792
|
+
* { id: existingId, First_Name: 'Update' }
|
|
793
|
+
* ]
|
|
794
|
+
* });
|
|
795
|
+
* ```
|
|
796
|
+
*
|
|
797
|
+
* @see https://www.zoho.com/recruit/developer-guide/apiv2/upsert-records.html
|
|
611
798
|
*/
|
|
612
799
|
function zohoRecruitUpsertRecord(context) {
|
|
613
800
|
return updateRecordLikeFunction$1(context, '/upsert', 'POST');
|
|
614
801
|
}
|
|
615
802
|
/**
|
|
616
|
-
*
|
|
803
|
+
* Creates a {@link ZohoRecruitUpdateRecordFunction} bound to the given context.
|
|
617
804
|
*
|
|
618
|
-
*
|
|
805
|
+
* Updates one or more existing records in a Recruit module. Each record must
|
|
806
|
+
* include an `id` field. Single-record calls return details directly or throw;
|
|
807
|
+
* multi-record calls return paired success/error arrays.
|
|
619
808
|
*
|
|
620
|
-
* @
|
|
621
|
-
*
|
|
809
|
+
* Maximum of {@link ZOHO_RECRUIT_CRUD_FUNCTION_MAX_RECORDS_LIMIT} records per call.
|
|
810
|
+
*
|
|
811
|
+
* @param context - Authenticated Zoho Recruit context providing fetch and rate limiting
|
|
812
|
+
* @returns Function that updates records in the specified module
|
|
813
|
+
*
|
|
814
|
+
* @example
|
|
815
|
+
* ```typescript
|
|
816
|
+
* const updateRecord = zohoRecruitUpdateRecord(context);
|
|
817
|
+
*
|
|
818
|
+
* // Single record — returns details directly:
|
|
819
|
+
* const details = await updateRecord({
|
|
820
|
+
* module: ZOHO_RECRUIT_CANDIDATES_MODULE,
|
|
821
|
+
* data: { id: recordId, First_Name: 'Updated Name' }
|
|
822
|
+
* });
|
|
823
|
+
*
|
|
824
|
+
* // Multiple records — returns paired arrays:
|
|
825
|
+
* const result = await updateRecord({
|
|
826
|
+
* module: ZOHO_RECRUIT_CANDIDATES_MODULE,
|
|
827
|
+
* data: [
|
|
828
|
+
* { id: recordId1, First_Name: 'Updated 1' },
|
|
829
|
+
* { id: recordId2, First_Name: 'Updated 2' }
|
|
830
|
+
* ]
|
|
831
|
+
* });
|
|
832
|
+
* ```
|
|
833
|
+
*
|
|
834
|
+
* @see https://www.zoho.com/recruit/developer-guide/apiv2/update-records.html
|
|
622
835
|
*/
|
|
623
836
|
function zohoRecruitUpdateRecord(context) {
|
|
624
837
|
return updateRecordLikeFunction$1(context, '', 'PUT');
|
|
625
838
|
}
|
|
626
839
|
/**
|
|
627
|
-
*
|
|
840
|
+
* Creates a {@link ZohoRecruitDeleteRecordFunction} bound to the given context.
|
|
628
841
|
*
|
|
629
|
-
*
|
|
842
|
+
* Deletes one or more records from a Recruit module by their IDs. Supports
|
|
843
|
+
* an optional `wf_trigger` flag to execute workflow rules on deletion. Returns
|
|
844
|
+
* a response with separated success and error entries.
|
|
630
845
|
*
|
|
631
|
-
* @param context
|
|
632
|
-
* @returns
|
|
846
|
+
* @param context - Authenticated Zoho Recruit context providing fetch and rate limiting
|
|
847
|
+
* @returns Function that deletes records from the specified module
|
|
848
|
+
*
|
|
849
|
+
* @example
|
|
850
|
+
* ```typescript
|
|
851
|
+
* const deleteRecord = zohoRecruitDeleteRecord(context);
|
|
852
|
+
*
|
|
853
|
+
* const result = await deleteRecord({
|
|
854
|
+
* module: ZOHO_RECRUIT_CANDIDATES_MODULE,
|
|
855
|
+
* ids: candidateId
|
|
856
|
+
* });
|
|
857
|
+
* ```
|
|
858
|
+
*
|
|
859
|
+
* @see https://www.zoho.com/recruit/developer-guide/apiv2/delete-records.html
|
|
633
860
|
*/
|
|
634
861
|
function zohoRecruitDeleteRecord(context) {
|
|
635
862
|
return ({ ids, module, wf_trigger }) => {
|
|
@@ -637,12 +864,26 @@ function zohoRecruitDeleteRecord(context) {
|
|
|
637
864
|
};
|
|
638
865
|
}
|
|
639
866
|
/**
|
|
640
|
-
*
|
|
867
|
+
* Creates a {@link ZohoRecruitGetRecordByIdFunction} bound to the given context.
|
|
641
868
|
*
|
|
642
|
-
*
|
|
869
|
+
* Retrieves a single record from a Recruit module by its ID. The response is
|
|
870
|
+
* unwrapped from the standard data array, returning the record directly.
|
|
871
|
+
* Throws if the record is not found.
|
|
643
872
|
*
|
|
644
|
-
* @param context
|
|
645
|
-
* @returns
|
|
873
|
+
* @param context - Authenticated Zoho Recruit context providing fetch and rate limiting
|
|
874
|
+
* @returns Function that retrieves a record by module name and ID
|
|
875
|
+
*
|
|
876
|
+
* @example
|
|
877
|
+
* ```typescript
|
|
878
|
+
* const getRecordById = zohoRecruitGetRecordById(context);
|
|
879
|
+
*
|
|
880
|
+
* const record = await getRecordById({
|
|
881
|
+
* module: ZOHO_RECRUIT_CANDIDATES_MODULE,
|
|
882
|
+
* id: candidateId
|
|
883
|
+
* });
|
|
884
|
+
* ```
|
|
885
|
+
*
|
|
886
|
+
* @see https://www.zoho.com/recruit/developer-guide/apiv2/get-records.html
|
|
646
887
|
*/
|
|
647
888
|
function zohoRecruitGetRecordById(context) {
|
|
648
889
|
return (input) => context
|
|
@@ -651,23 +892,61 @@ function zohoRecruitGetRecordById(context) {
|
|
|
651
892
|
.then((x) => x.data[0]);
|
|
652
893
|
}
|
|
653
894
|
/**
|
|
654
|
-
*
|
|
895
|
+
* Creates a {@link ZohoRecruitGetRecordsFunction} bound to the given context.
|
|
655
896
|
*
|
|
656
|
-
*
|
|
897
|
+
* Retrieves a paginated list of records from a Recruit module. Supports field
|
|
898
|
+
* selection, sorting, custom view filtering, territory filtering, and
|
|
899
|
+
* conversion/approval status filters via {@link ZohoRecruitGetRecordsInput}.
|
|
657
900
|
*
|
|
658
|
-
* @param context
|
|
659
|
-
* @returns
|
|
901
|
+
* @param context - Authenticated Zoho Recruit context providing fetch and rate limiting
|
|
902
|
+
* @returns Function that retrieves paginated records from a module
|
|
903
|
+
*
|
|
904
|
+
* @example
|
|
905
|
+
* ```typescript
|
|
906
|
+
* const getRecords = zohoRecruitGetRecords(context);
|
|
907
|
+
*
|
|
908
|
+
* const page = await getRecords({
|
|
909
|
+
* module: ZOHO_RECRUIT_CANDIDATES_MODULE,
|
|
910
|
+
* per_page: 10
|
|
911
|
+
* });
|
|
912
|
+
* ```
|
|
913
|
+
*
|
|
914
|
+
* @see https://www.zoho.com/recruit/developer-guide/apiv2/get-records.html
|
|
660
915
|
*/
|
|
661
916
|
function zohoRecruitGetRecords(context) {
|
|
662
917
|
return ((input) => context.fetchJson(`/v2/${input.module}?${zohoRecruitUrlSearchParamsMinusModule(input).toString()}`, zohoRecruitApiFetchJsonInput('GET')));
|
|
663
918
|
}
|
|
664
919
|
/**
|
|
665
|
-
*
|
|
920
|
+
* Creates a {@link ZohoRecruitSearchRecordsFunction} bound to the given context.
|
|
666
921
|
*
|
|
667
|
-
*
|
|
922
|
+
* Searches records in a Recruit module using one of: criteria tree (compiled
|
|
923
|
+
* via {@link zohoRecruitSearchRecordsCriteriaString}), email, phone, or keyword.
|
|
924
|
+
* At least one search parameter must be provided. Returns a paginated result,
|
|
925
|
+
* defaulting to an empty data array when no matches are found.
|
|
668
926
|
*
|
|
669
|
-
* @param context
|
|
670
|
-
* @returns
|
|
927
|
+
* @param context - Authenticated Zoho Recruit context providing fetch and rate limiting
|
|
928
|
+
* @returns Function that searches records in the specified module
|
|
929
|
+
* @throws {Error} If none of `criteria`, `email`, `phone`, or `word` are provided
|
|
930
|
+
*
|
|
931
|
+
* @example
|
|
932
|
+
* ```typescript
|
|
933
|
+
* const searchRecords = zohoRecruitSearchRecords(context);
|
|
934
|
+
*
|
|
935
|
+
* // Search by criteria:
|
|
936
|
+
* const result = await searchRecords({
|
|
937
|
+
* module: ZOHO_RECRUIT_CANDIDATES_MODULE,
|
|
938
|
+
* criteria: [{ field: 'Last_Name', filter: 'starts_with', value: 'Smith' }],
|
|
939
|
+
* per_page: 10
|
|
940
|
+
* });
|
|
941
|
+
*
|
|
942
|
+
* // Search by keyword:
|
|
943
|
+
* const wordResult = await searchRecords({
|
|
944
|
+
* module: ZOHO_RECRUIT_CANDIDATES_MODULE,
|
|
945
|
+
* word: 'engineer'
|
|
946
|
+
* });
|
|
947
|
+
* ```
|
|
948
|
+
*
|
|
949
|
+
* @see https://www.zoho.com/recruit/developer-guide/apiv2/search-records.html
|
|
671
950
|
*/
|
|
672
951
|
function zohoRecruitSearchRecords(context) {
|
|
673
952
|
function searchRecordsUrlSearchParams(input) {
|
|
@@ -685,16 +964,60 @@ function zohoRecruitSearchRecords(context) {
|
|
|
685
964
|
}
|
|
686
965
|
return ((input) => context.fetchJson(`/v2/${input.module}/search?${searchRecordsUrlSearchParams(input).toString()}`, zohoRecruitApiFetchJsonInput('GET')).then((x) => x ?? { data: [], info: { more_records: false } }));
|
|
687
966
|
}
|
|
967
|
+
/**
|
|
968
|
+
* Creates a {@link ZohoRecruitSearchRecordsPageFactory} bound to the given context.
|
|
969
|
+
*
|
|
970
|
+
* Returns a page factory that automatically handles Zoho Recruit's pagination,
|
|
971
|
+
* making it easy to iterate through all search results across multiple pages.
|
|
972
|
+
*
|
|
973
|
+
* @param context - Authenticated Zoho Recruit context providing fetch and rate limiting
|
|
974
|
+
* @returns Page factory for iterating over search results
|
|
975
|
+
*
|
|
976
|
+
* @example
|
|
977
|
+
* ```typescript
|
|
978
|
+
* const pageFactory = zohoRecruitSearchRecordsPageFactory(context);
|
|
979
|
+
*
|
|
980
|
+
* const fetchPage = pageFactory({
|
|
981
|
+
* module: ZOHO_RECRUIT_CANDIDATES_MODULE,
|
|
982
|
+
* criteria: [{ field: 'Last_Name', filter: 'starts_with', value: 'Smith' }],
|
|
983
|
+
* per_page: 5
|
|
984
|
+
* });
|
|
985
|
+
*
|
|
986
|
+
* const firstPage = await fetchPage.fetchNext();
|
|
987
|
+
* const secondPage = await firstPage.fetchNext();
|
|
988
|
+
* ```
|
|
989
|
+
*/
|
|
688
990
|
function zohoRecruitSearchRecordsPageFactory(context) {
|
|
689
991
|
return zohoFetchPageFactory(zohoRecruitSearchRecords(context));
|
|
690
992
|
}
|
|
691
993
|
/**
|
|
692
|
-
* Creates a ZohoRecruitGetRelatedRecordsFunctionFactory
|
|
994
|
+
* Creates a {@link ZohoRecruitGetRelatedRecordsFunctionFactory} bound to the given context.
|
|
995
|
+
*
|
|
996
|
+
* Returns a factory that produces typed functions for fetching related records
|
|
997
|
+
* (e.g. Notes, Emails, Attachments) of a specific target module. The factory
|
|
998
|
+
* accepts a {@link ZohoRecruitGetRelatedRecordsFunctionConfig} to specify the
|
|
999
|
+
* target module and empty-result behavior. By default, returns an empty page
|
|
1000
|
+
* result instead of null when no records are found.
|
|
1001
|
+
*
|
|
1002
|
+
* @param context - Authenticated Zoho Recruit context providing fetch and rate limiting
|
|
1003
|
+
* @returns Factory that creates typed related-records retrieval functions
|
|
693
1004
|
*
|
|
694
|
-
*
|
|
1005
|
+
* @example
|
|
1006
|
+
* ```typescript
|
|
1007
|
+
* const factory = zohoRecruitGetRelatedRecordsFunctionFactory(context);
|
|
695
1008
|
*
|
|
696
|
-
*
|
|
697
|
-
*
|
|
1009
|
+
* // Create a typed function for fetching related Notes:
|
|
1010
|
+
* const getNotesForRecord = factory<ZohoRecruitRecordNote>({
|
|
1011
|
+
* targetModule: ZOHO_RECRUIT_NOTES_MODULE
|
|
1012
|
+
* });
|
|
1013
|
+
*
|
|
1014
|
+
* const notes = await getNotesForRecord({
|
|
1015
|
+
* module: ZOHO_RECRUIT_CANDIDATES_MODULE,
|
|
1016
|
+
* id: candidateId
|
|
1017
|
+
* });
|
|
1018
|
+
* ```
|
|
1019
|
+
*
|
|
1020
|
+
* @see https://www.zoho.com/recruit/developer-guide/apiv2/get-related-records.html
|
|
698
1021
|
*/
|
|
699
1022
|
function zohoRecruitGetRelatedRecordsFunctionFactory(context) {
|
|
700
1023
|
return (config) => {
|
|
@@ -702,15 +1025,126 @@ function zohoRecruitGetRelatedRecordsFunctionFactory(context) {
|
|
|
702
1025
|
return (input) => context.fetchJson(`/v2/${input.module}/${input.id}/${targetModule}?${zohoRecruitUrlSearchParamsMinusIdAndModule(input, input.filter).toString()}`, zohoRecruitApiFetchJsonInput('GET')).then((x) => x ?? (returnEmptyRecordsInsteadOfNull !== false ? emptyZohoPageResult() : x));
|
|
703
1026
|
};
|
|
704
1027
|
}
|
|
1028
|
+
/**
|
|
1029
|
+
* Creates a {@link ZohoRecruitGetEmailsForRecordFunction} bound to the given context.
|
|
1030
|
+
*
|
|
1031
|
+
* Retrieves email metadata related to a specific record by targeting the
|
|
1032
|
+
* Emails module via the related records API. Returns a paginated result of
|
|
1033
|
+
* {@link ZohoRecruitRecordEmailMetadata} entries. When no emails exist for the
|
|
1034
|
+
* record, the result contains an empty data array rather than null.
|
|
1035
|
+
*
|
|
1036
|
+
* @param context - Authenticated Zoho Recruit context providing fetch and rate limiting
|
|
1037
|
+
* @returns Function that retrieves emails for a record
|
|
1038
|
+
*
|
|
1039
|
+
* @example
|
|
1040
|
+
* ```typescript
|
|
1041
|
+
* const getEmailsForRecord = zohoRecruitGetEmailsForRecord(context);
|
|
1042
|
+
*
|
|
1043
|
+
* const result = await getEmailsForRecord({
|
|
1044
|
+
* id: candidateId,
|
|
1045
|
+
* module: ZOHO_RECRUIT_CANDIDATES_MODULE
|
|
1046
|
+
* });
|
|
1047
|
+
* ```
|
|
1048
|
+
*
|
|
1049
|
+
* @see https://www.zoho.com/recruit/developer-guide/apiv2/get-related-records.html
|
|
1050
|
+
*/
|
|
705
1051
|
function zohoRecruitGetEmailsForRecord(context) {
|
|
706
1052
|
return zohoRecruitGetRelatedRecordsFunctionFactory(context)({ targetModule: ZOHO_RECRUIT_EMAILS_MODULE });
|
|
707
1053
|
}
|
|
1054
|
+
/**
|
|
1055
|
+
* Creates a {@link ZohoRecruitGetEmailsForRecordPageFactory} bound to the given context.
|
|
1056
|
+
*
|
|
1057
|
+
* Returns a page factory for iterating over emails related to a record across
|
|
1058
|
+
* multiple pages. Wraps {@link zohoRecruitGetEmailsForRecord} with automatic
|
|
1059
|
+
* pagination handling via {@link zohoFetchPageFactory}.
|
|
1060
|
+
*
|
|
1061
|
+
* @param context - Authenticated Zoho Recruit context providing fetch and rate limiting
|
|
1062
|
+
* @returns Page factory for iterating over record emails
|
|
1063
|
+
*
|
|
1064
|
+
* @example
|
|
1065
|
+
* ```typescript
|
|
1066
|
+
* const pageFactory = zohoRecruitGetEmailsForRecordPageFactory(context);
|
|
1067
|
+
*
|
|
1068
|
+
* const fetchPage = pageFactory({
|
|
1069
|
+
* id: candidateId,
|
|
1070
|
+
* module: ZOHO_RECRUIT_CANDIDATES_MODULE,
|
|
1071
|
+
* per_page: 5
|
|
1072
|
+
* });
|
|
1073
|
+
*
|
|
1074
|
+
* const firstPage = await fetchPage.fetchNext();
|
|
1075
|
+
*
|
|
1076
|
+
* if (firstPage.result.info.more_records) {
|
|
1077
|
+
* const secondPage = await firstPage.fetchNext();
|
|
1078
|
+
* }
|
|
1079
|
+
* ```
|
|
1080
|
+
*
|
|
1081
|
+
* @see https://www.zoho.com/recruit/developer-guide/apiv2/get-related-records.html
|
|
1082
|
+
*/
|
|
708
1083
|
function zohoRecruitGetEmailsForRecordPageFactory(context) {
|
|
709
1084
|
return zohoFetchPageFactory(zohoRecruitGetEmailsForRecord(context));
|
|
710
1085
|
}
|
|
1086
|
+
/**
|
|
1087
|
+
* Creates a {@link ZohoRecruitGetAttachmentsForRecordFunction} bound to the given context.
|
|
1088
|
+
*
|
|
1089
|
+
* Retrieves attachment metadata related to a specific record by targeting the
|
|
1090
|
+
* Attachments module via the related records API. Returns a paginated result of
|
|
1091
|
+
* {@link ZohoRecruitRecordAttachmentMetadata} entries including file names, sizes,
|
|
1092
|
+
* and category information. When no attachments exist for the record, the result
|
|
1093
|
+
* contains an empty data array rather than null.
|
|
1094
|
+
*
|
|
1095
|
+
* Each attachment entry includes a `$type` field that distinguishes between
|
|
1096
|
+
* directly uploaded attachments (`'Attachment'`) and linked attachments.
|
|
1097
|
+
*
|
|
1098
|
+
* @param context - Authenticated Zoho Recruit context providing fetch and rate limiting
|
|
1099
|
+
* @returns Function that retrieves attachments for a record
|
|
1100
|
+
*
|
|
1101
|
+
* @example
|
|
1102
|
+
* ```typescript
|
|
1103
|
+
* const getAttachmentsForRecord = zohoRecruitGetAttachmentsForRecord(context);
|
|
1104
|
+
*
|
|
1105
|
+
* const result = await getAttachmentsForRecord({
|
|
1106
|
+
* id: candidateId,
|
|
1107
|
+
* module: ZOHO_RECRUIT_CANDIDATES_MODULE
|
|
1108
|
+
* });
|
|
1109
|
+
*
|
|
1110
|
+
* // Filter to only directly uploaded attachments (downloadable):
|
|
1111
|
+
* const downloadable = result.data.filter((x) => x.$type === 'Attachment');
|
|
1112
|
+
* ```
|
|
1113
|
+
*
|
|
1114
|
+
* @see https://www.zoho.com/recruit/developer-guide/apiv2/get-related-records.html
|
|
1115
|
+
*/
|
|
711
1116
|
function zohoRecruitGetAttachmentsForRecord(context) {
|
|
712
1117
|
return zohoRecruitGetRelatedRecordsFunctionFactory(context)({ targetModule: ZOHO_RECRUIT_ATTACHMENTS_MODULE });
|
|
713
1118
|
}
|
|
1119
|
+
/**
|
|
1120
|
+
* Creates a {@link ZohoRecruitGetAttachmentsForRecordPageFactory} bound to the given context.
|
|
1121
|
+
*
|
|
1122
|
+
* Returns a page factory for iterating over attachments related to a record
|
|
1123
|
+
* across multiple pages. Wraps {@link zohoRecruitGetAttachmentsForRecord} with
|
|
1124
|
+
* automatic pagination handling via {@link zohoFetchPageFactory}.
|
|
1125
|
+
*
|
|
1126
|
+
* @param context - Authenticated Zoho Recruit context providing fetch and rate limiting
|
|
1127
|
+
* @returns Page factory for iterating over record attachments
|
|
1128
|
+
*
|
|
1129
|
+
* @example
|
|
1130
|
+
* ```typescript
|
|
1131
|
+
* const pageFactory = zohoRecruitGetAttachmentsForRecordPageFactory(context);
|
|
1132
|
+
*
|
|
1133
|
+
* const fetchPage = pageFactory({
|
|
1134
|
+
* id: candidateId,
|
|
1135
|
+
* module: ZOHO_RECRUIT_CANDIDATES_MODULE,
|
|
1136
|
+
* per_page: 10
|
|
1137
|
+
* });
|
|
1138
|
+
*
|
|
1139
|
+
* const firstPage = await fetchPage.fetchNext();
|
|
1140
|
+
*
|
|
1141
|
+
* if (firstPage.result.info.more_records) {
|
|
1142
|
+
* const secondPage = await firstPage.fetchNext();
|
|
1143
|
+
* }
|
|
1144
|
+
* ```
|
|
1145
|
+
*
|
|
1146
|
+
* @see https://www.zoho.com/recruit/developer-guide/apiv2/get-related-records.html
|
|
1147
|
+
*/
|
|
714
1148
|
function zohoRecruitGetAttachmentsForRecordPageFactory(context) {
|
|
715
1149
|
return zohoFetchPageFactory(zohoRecruitGetAttachmentsForRecord(context));
|
|
716
1150
|
}
|
|
@@ -721,88 +1155,121 @@ function zohoRecruitGetAttachmentsForRecordPageFactory(context) {
|
|
|
721
1155
|
*/
|
|
722
1156
|
const ZOHO_RECRUIT_ATTACHMENT_MAX_SIZE = 20 * 1024 * 1024;
|
|
723
1157
|
/**
|
|
724
|
-
*
|
|
1158
|
+
* Creates a {@link ZohoRecruitUploadAttachmentForRecordFunction} bound to the given context.
|
|
725
1159
|
*
|
|
726
|
-
*
|
|
1160
|
+
* Uploads an attachment to a specific record. Supports either a direct
|
|
1161
|
+
* {@link File} upload (sent as multipart/form-data) or a URL from which Zoho
|
|
1162
|
+
* will fetch the file. An attachment category must be specified by ID or name.
|
|
1163
|
+
* Maximum file size is {@link ZOHO_RECRUIT_ATTACHMENT_MAX_SIZE} (20MB).
|
|
727
1164
|
*
|
|
728
|
-
* @param context
|
|
729
|
-
* @returns
|
|
1165
|
+
* @param context - Authenticated Zoho Recruit context providing fetch and rate limiting
|
|
1166
|
+
* @returns Function that uploads an attachment to a record
|
|
1167
|
+
* @throws {Error} If neither `file` nor `attachmentUrl` is provided
|
|
1168
|
+
* @throws {Error} If neither `attachmentCategoryId` nor `attachmentCategoryName` is provided
|
|
1169
|
+
*
|
|
1170
|
+
* @example
|
|
1171
|
+
* ```typescript
|
|
1172
|
+
* const uploadAttachment = zohoRecruitUploadAttachmentForRecord(context);
|
|
1173
|
+
*
|
|
1174
|
+
* // Upload a file directly:
|
|
1175
|
+
* await uploadAttachment({
|
|
1176
|
+
* module: ZOHO_RECRUIT_CANDIDATES_MODULE,
|
|
1177
|
+
* id: candidateId,
|
|
1178
|
+
* file: new File(['content'], 'resume.pdf', { type: 'application/pdf' }),
|
|
1179
|
+
* attachmentCategoryName: 'Resume'
|
|
1180
|
+
* });
|
|
1181
|
+
*
|
|
1182
|
+
* // Upload from a URL:
|
|
1183
|
+
* await uploadAttachment({
|
|
1184
|
+
* module: ZOHO_RECRUIT_CANDIDATES_MODULE,
|
|
1185
|
+
* id: candidateId,
|
|
1186
|
+
* attachmentUrl: 'https://example.com/document.pdf',
|
|
1187
|
+
* attachmentCategoryName: 'Others'
|
|
1188
|
+
* });
|
|
1189
|
+
* ```
|
|
1190
|
+
*
|
|
1191
|
+
* @see https://www.zoho.com/recruit/developer-guide/apiv2/upload-attachment.html
|
|
730
1192
|
*/
|
|
731
1193
|
function zohoRecruitUploadAttachmentForRecord(context) {
|
|
732
1194
|
return (input) => {
|
|
733
|
-
const { attachmentCategoryId, attachmentCategoryName,
|
|
1195
|
+
const { attachmentCategoryId, attachmentCategoryName, file, attachmentUrl } = input;
|
|
734
1196
|
const urlParams = {
|
|
735
1197
|
attachments_category_id: util.joinStringsWithCommas(attachmentCategoryId),
|
|
736
|
-
attachments_category: util.joinStringsWithCommas(attachmentCategoryName)
|
|
737
|
-
attachment_url: input.attachmentUrl
|
|
1198
|
+
attachments_category: util.joinStringsWithCommas(attachmentCategoryName)
|
|
738
1199
|
};
|
|
739
1200
|
if (!urlParams.attachments_category_id?.length && !urlParams.attachments_category?.length) {
|
|
740
1201
|
throw new Error('attachmentCategoryId or attachmentCategoryName must be provided and not empty.');
|
|
741
1202
|
}
|
|
742
|
-
|
|
743
|
-
|
|
1203
|
+
const url = `/v2/${input.module}/${input.id}/${ZOHO_RECRUIT_ATTACHMENTS_MODULE}?${fetch.makeUrlSearchParams(urlParams).toString()}`;
|
|
1204
|
+
if (file != null) {
|
|
1205
|
+
const body = new FormData();
|
|
1206
|
+
body.append('file', file);
|
|
1207
|
+
// Clear the base Content-Type header (empty string removes it via mergeRequestHeaders) so fetch auto-detects multipart/form-data with the correct boundary from the FormData body.
|
|
1208
|
+
return context.fetch(url, { method: 'POST', headers: { 'Content-Type': '' }, body });
|
|
744
1209
|
}
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
response = context.fetch(url, { method: 'POST' });
|
|
749
|
-
}
|
|
750
|
-
else if (formData != null) {
|
|
751
|
-
throw new Error('unsupported currently. Use the attachmentUrl parameter instead.');
|
|
752
|
-
// There is something weird going on with sending requests this way and zoho's server is rejecting it.
|
|
753
|
-
/*
|
|
754
|
-
response = context.fetch(url, {
|
|
755
|
-
method: 'POST',
|
|
756
|
-
headers: {
|
|
757
|
-
'Content-Type': 'multipart/form-data',
|
|
758
|
-
'content-length': '210'
|
|
759
|
-
},
|
|
760
|
-
body: formData
|
|
761
|
-
});
|
|
762
|
-
*/
|
|
763
|
-
/*
|
|
764
|
-
const fullUrl = (context.config.apiUrl as string) + url;
|
|
765
|
-
const accessToken = await context.accessTokenStringFactory();
|
|
766
|
-
|
|
767
|
-
response = fetch(fullUrl, {
|
|
768
|
-
headers: {
|
|
769
|
-
Authorization: `Bearer ${accessToken}`
|
|
770
|
-
},
|
|
771
|
-
body: formData,
|
|
772
|
-
method: 'POST'
|
|
773
|
-
});
|
|
774
|
-
|
|
775
|
-
console.log({ response });
|
|
776
|
-
*/
|
|
1210
|
+
else if (attachmentUrl) {
|
|
1211
|
+
const urlWithAttachment = `${url}&${fetch.makeUrlSearchParams({ attachment_url: attachmentUrl }).toString()}`;
|
|
1212
|
+
return context.fetch(urlWithAttachment, { method: 'POST' });
|
|
777
1213
|
}
|
|
778
1214
|
else {
|
|
779
|
-
throw new Error('
|
|
1215
|
+
throw new Error('file or attachmentUrl must be provided.');
|
|
780
1216
|
}
|
|
781
|
-
return response;
|
|
782
1217
|
};
|
|
783
1218
|
}
|
|
784
1219
|
/**
|
|
785
|
-
*
|
|
1220
|
+
* Creates a {@link ZohoRecruitDownloadAttachmentForRecordFunction} bound to the given context.
|
|
786
1221
|
*
|
|
787
|
-
*
|
|
1222
|
+
* Downloads a specific attachment from a record. Returns a parsed
|
|
1223
|
+
* {@link FetchFileResponse} containing the file data and metadata extracted
|
|
1224
|
+
* from the response headers.
|
|
788
1225
|
*
|
|
789
|
-
* @param context
|
|
790
|
-
* @returns
|
|
1226
|
+
* @param context - Authenticated Zoho Recruit context providing fetch and rate limiting
|
|
1227
|
+
* @returns Function that downloads an attachment by record and attachment ID
|
|
1228
|
+
*
|
|
1229
|
+
* @example
|
|
1230
|
+
* ```typescript
|
|
1231
|
+
* const downloadAttachment = zohoRecruitDownloadAttachmentForRecord(context);
|
|
1232
|
+
*
|
|
1233
|
+
* const fileResponse = await downloadAttachment({
|
|
1234
|
+
* module: ZOHO_RECRUIT_CANDIDATES_MODULE,
|
|
1235
|
+
* id: candidateId,
|
|
1236
|
+
* attachment_id: attachmentId
|
|
1237
|
+
* });
|
|
1238
|
+
* ```
|
|
1239
|
+
*
|
|
1240
|
+
* @see https://www.zoho.com/recruit/developer-guide/apiv2/download-attachments.html
|
|
791
1241
|
*/
|
|
792
1242
|
function zohoRecruitDownloadAttachmentForRecord(context) {
|
|
793
1243
|
return (input) => context.fetch(`/v2/${input.module}/${input.id}/${ZOHO_RECRUIT_ATTACHMENTS_MODULE}/${input.attachment_id}`, { method: 'GET' }).then(fetch.parseFetchFileResponse);
|
|
794
1244
|
}
|
|
795
1245
|
/**
|
|
796
|
-
*
|
|
1246
|
+
* Creates a {@link ZohoRecruitDeleteAttachmentFromRecordFunction} bound to the given context.
|
|
797
1247
|
*
|
|
798
|
-
*
|
|
1248
|
+
* Deletes a specific attachment from a record by its attachment ID.
|
|
1249
|
+
* Returns the raw {@link Response}.
|
|
799
1250
|
*
|
|
800
|
-
* @param context
|
|
801
|
-
* @returns
|
|
1251
|
+
* @param context - Authenticated Zoho Recruit context providing fetch and rate limiting
|
|
1252
|
+
* @returns Function that deletes an attachment by record and attachment ID
|
|
1253
|
+
*
|
|
1254
|
+
* @example
|
|
1255
|
+
* ```typescript
|
|
1256
|
+
* const deleteAttachment = zohoRecruitDeleteAttachmentFromRecord(context);
|
|
1257
|
+
*
|
|
1258
|
+
* const response = await deleteAttachment({
|
|
1259
|
+
* module: ZOHO_RECRUIT_CANDIDATES_MODULE,
|
|
1260
|
+
* id: candidateId,
|
|
1261
|
+
* attachment_id: attachmentId
|
|
1262
|
+
* });
|
|
1263
|
+
* ```
|
|
1264
|
+
*
|
|
1265
|
+
* @see https://www.zoho.com/recruit/developer-guide/apiv2/delete-attachments.html
|
|
802
1266
|
*/
|
|
803
1267
|
function zohoRecruitDeleteAttachmentFromRecord(context) {
|
|
804
1268
|
return (input) => context.fetch(`/v2/${input.module}/${input.id}/${ZOHO_RECRUIT_ATTACHMENTS_MODULE}/${input.attachment_id}`, { method: 'DELETE' });
|
|
805
1269
|
}
|
|
1270
|
+
/**
|
|
1271
|
+
* Thrown when a Zoho Recruit serverless function execution fails, wrapping the error response with the API error code and message.
|
|
1272
|
+
*/
|
|
806
1273
|
class ZohoRecruitExecuteRestApiFunctionError extends makeError.BaseError {
|
|
807
1274
|
error;
|
|
808
1275
|
constructor(error) {
|
|
@@ -811,15 +1278,39 @@ class ZohoRecruitExecuteRestApiFunctionError extends makeError.BaseError {
|
|
|
811
1278
|
}
|
|
812
1279
|
}
|
|
813
1280
|
/**
|
|
814
|
-
* Creates a
|
|
1281
|
+
* Creates a function that executes Zoho Recruit serverless functions via the REST API.
|
|
1282
|
+
*
|
|
1283
|
+
* Supports both OAuth-based and API-key-based authentication. When using an API key, a custom target URL can be specified for cross-environment calls.
|
|
815
1284
|
*
|
|
816
1285
|
* OAuth Details:
|
|
817
1286
|
* - https://www.zoho.com/crm/developer/docs/functions/serverless-fn-oauth.html#OAuth2
|
|
818
1287
|
* - There is no documentation for ZohoRecruit specifically, but it seems to behave the same way
|
|
819
1288
|
* - You will need the following scopes: ZohoRecruit.functions.execute.READ,ZohoRecruit.functions.execute.CREATE
|
|
820
1289
|
*
|
|
821
|
-
* @param context
|
|
822
|
-
* @returns
|
|
1290
|
+
* @param context - Authenticated Zoho Recruit context providing fetch and rate limiting
|
|
1291
|
+
* @returns Function that executes serverless functions via the REST API
|
|
1292
|
+
* @throws {ZohoRecruitExecuteRestApiFunctionError} If the function execution fails
|
|
1293
|
+
*
|
|
1294
|
+
* @example
|
|
1295
|
+
* ```typescript
|
|
1296
|
+
* const executeFunction = zohoRecruitExecuteRestApiFunction(context);
|
|
1297
|
+
*
|
|
1298
|
+
* // Execute using OAuth credentials:
|
|
1299
|
+
* const result = await executeFunction({ functionName: 'my_function' });
|
|
1300
|
+
*
|
|
1301
|
+
* // Execute with parameters:
|
|
1302
|
+
* const paramResult = await executeFunction({
|
|
1303
|
+
* functionName: 'process_candidate',
|
|
1304
|
+
* params: { candidate_id: '12345', action: 'approve' }
|
|
1305
|
+
* });
|
|
1306
|
+
*
|
|
1307
|
+
* // Execute using an API key (cross-environment):
|
|
1308
|
+
* const apiResult = await executeFunction({
|
|
1309
|
+
* functionName: 'my_function',
|
|
1310
|
+
* apiKey: 'your-api-key',
|
|
1311
|
+
* apiUrl: 'production'
|
|
1312
|
+
* });
|
|
1313
|
+
* ```
|
|
823
1314
|
*/
|
|
824
1315
|
function zohoRecruitExecuteRestApiFunction(context) {
|
|
825
1316
|
return (input) => {
|
|
@@ -841,9 +1332,15 @@ function zohoRecruitExecuteRestApiFunction(context) {
|
|
|
841
1332
|
};
|
|
842
1333
|
}
|
|
843
1334
|
// MARK: Util
|
|
1335
|
+
/**
|
|
1336
|
+
* Builds URL search params from input objects, omitting the `module` key since it is used in the URL path rather than query string.
|
|
1337
|
+
*/
|
|
844
1338
|
function zohoRecruitUrlSearchParamsMinusModule(...input) {
|
|
845
1339
|
return fetch.makeUrlSearchParams(input, { omitKeys: 'module' });
|
|
846
1340
|
}
|
|
1341
|
+
/**
|
|
1342
|
+
* Builds URL search params from input objects, omitting both `id` and `module` keys since they are used in the URL path.
|
|
1343
|
+
*/
|
|
847
1344
|
function zohoRecruitUrlSearchParamsMinusIdAndModule(...input) {
|
|
848
1345
|
return fetch.makeUrlSearchParams(input, { omitKeys: ['id', 'module'] });
|
|
849
1346
|
}
|
|
@@ -851,6 +1348,9 @@ function zohoRecruitUrlSearchParamsMinusIdAndModule(...input) {
|
|
|
851
1348
|
* @deprecated use makeUrlSearchParams instead.
|
|
852
1349
|
*/
|
|
853
1350
|
const zohoRecruitUrlSearchParams = fetch.makeUrlSearchParams;
|
|
1351
|
+
/**
|
|
1352
|
+
* Constructs a standard {@link FetchJsonInput} for Zoho Recruit API calls with the given HTTP method and optional body.
|
|
1353
|
+
*/
|
|
854
1354
|
function zohoRecruitApiFetchJsonInput(method, body) {
|
|
855
1355
|
const result = {
|
|
856
1356
|
method,
|
|
@@ -858,6 +1358,28 @@ function zohoRecruitApiFetchJsonInput(method, body) {
|
|
|
858
1358
|
};
|
|
859
1359
|
return result;
|
|
860
1360
|
}
|
|
1361
|
+
/**
|
|
1362
|
+
* Separates a change response's entries into success and error arrays based on their status.
|
|
1363
|
+
*
|
|
1364
|
+
* Iterates over the `data` array from a Zoho Recruit change operation response and
|
|
1365
|
+
* partitions entries into `successItems` and `errorItems` based on their `status` field.
|
|
1366
|
+
* The original response is spread into the result, so all original fields remain accessible.
|
|
1367
|
+
*
|
|
1368
|
+
* Used internally by {@link zohoRecruitDeleteRecord} and similar functions to provide
|
|
1369
|
+
* convenient access to separated success/error results.
|
|
1370
|
+
*
|
|
1371
|
+
* @param response - Raw change operation response containing mixed success/error entries
|
|
1372
|
+
* @returns The response augmented with pre-separated `successItems` and `errorItems` arrays
|
|
1373
|
+
*
|
|
1374
|
+
* @example
|
|
1375
|
+
* ```typescript
|
|
1376
|
+
* const rawResponse = await context.fetchJson<ZohoRecruitChangeObjectLikeResponse>(...);
|
|
1377
|
+
* const result = zohoRecruitChangeObjectLikeResponseSuccessAndErrorPairs(rawResponse);
|
|
1378
|
+
*
|
|
1379
|
+
* result.successItems; // entries with status === 'success'
|
|
1380
|
+
* result.errorItems; // entries with non-success status
|
|
1381
|
+
* ```
|
|
1382
|
+
*/
|
|
861
1383
|
function zohoRecruitChangeObjectLikeResponseSuccessAndErrorPairs(response) {
|
|
862
1384
|
const { data } = response;
|
|
863
1385
|
const successItems = [];
|
|
@@ -877,6 +1399,20 @@ function zohoRecruitChangeObjectLikeResponseSuccessAndErrorPairs(response) {
|
|
|
877
1399
|
};
|
|
878
1400
|
return result;
|
|
879
1401
|
}
|
|
1402
|
+
/**
|
|
1403
|
+
* Pairs each input record with its corresponding API result and separates them into success and error arrays by status.
|
|
1404
|
+
*
|
|
1405
|
+
* Iterates over the `input` and `results` arrays in parallel, matching each input record
|
|
1406
|
+
* to its positional result. Entries are classified as success or error based on
|
|
1407
|
+
* the result's `status` field matching {@link ZOHO_SUCCESS_STATUS}.
|
|
1408
|
+
*
|
|
1409
|
+
* Used internally by {@link updateRecordLikeFunction} to pair input data with API outcomes
|
|
1410
|
+
* for insert, update, and upsert operations.
|
|
1411
|
+
*
|
|
1412
|
+
* @param input - Array of input records that were submitted to the API
|
|
1413
|
+
* @param results - Array of per-record results returned by the API, positionally aligned with `input`
|
|
1414
|
+
* @returns Object with `successItems` and `errorItems`, each containing paired `{ input, result }` entries
|
|
1415
|
+
*/
|
|
880
1416
|
function zohoRecruitMultiRecordResult(input, results) {
|
|
881
1417
|
const successItems = [];
|
|
882
1418
|
const errorItems = [];
|
|
@@ -973,10 +1509,9 @@ const executeRestApiFunction = zohoRecruitExecuteRestApiFunction;
|
|
|
973
1509
|
/**
|
|
974
1510
|
* Associates one or more candidates with one or more job openings.
|
|
975
1511
|
*
|
|
976
|
-
*
|
|
1512
|
+
* The result separates "already associated" errors from other errors, allowing callers to treat duplicate associations as non-fatal.
|
|
977
1513
|
*
|
|
978
|
-
*
|
|
979
|
-
* @returns
|
|
1514
|
+
* https://www.zoho.com/recruit/developer-guide/apiv2/associate-candidate.html
|
|
980
1515
|
*/
|
|
981
1516
|
function zohoRecruitAssociateCandidateRecordsWithJobOpenings(context) {
|
|
982
1517
|
return (input) => context.fetchJson(`/v2/${ZOHO_RECRUIT_CANDIDATES_MODULE}/actions/associate`, zohoRecruitApiFetchJsonInput('PUT', { data: util.asArray(input) })).then((x) => {
|
|
@@ -993,6 +1528,9 @@ function zohoRecruitAssociateCandidateRecordsWithJobOpenings(context) {
|
|
|
993
1528
|
};
|
|
994
1529
|
});
|
|
995
1530
|
}
|
|
1531
|
+
/**
|
|
1532
|
+
* Searches for records associated with a given candidate or job opening. Returns an empty page result when no records are found.
|
|
1533
|
+
*/
|
|
996
1534
|
function zohoRecruitSearchAssociatedRecords(context) {
|
|
997
1535
|
return (input) => {
|
|
998
1536
|
return context.fetchJson(`/v2/${input.module}/${input.id}/associate?${zohoRecruitUrlSearchParamsMinusIdAndModule(input).toString()}`, zohoRecruitApiFetchJsonInput('GET')).then((x) => {
|
|
@@ -1001,6 +1539,9 @@ function zohoRecruitSearchAssociatedRecords(context) {
|
|
|
1001
1539
|
});
|
|
1002
1540
|
};
|
|
1003
1541
|
}
|
|
1542
|
+
/**
|
|
1543
|
+
* Searches for job openings associated with a specific candidate.
|
|
1544
|
+
*/
|
|
1004
1545
|
function zohoRecruitSearchCandidateAssociatedJobOpeningRecords(context) {
|
|
1005
1546
|
const searchAssociatedRecordsFactory = zohoRecruitSearchAssociatedRecords(context);
|
|
1006
1547
|
return (input) => {
|
|
@@ -1010,9 +1551,15 @@ function zohoRecruitSearchCandidateAssociatedJobOpeningRecords(context) {
|
|
|
1010
1551
|
});
|
|
1011
1552
|
};
|
|
1012
1553
|
}
|
|
1554
|
+
/**
|
|
1555
|
+
* Creates a page factory for paginating over job openings associated with a candidate.
|
|
1556
|
+
*/
|
|
1013
1557
|
function zohoRecruitSearchCandidateAssociatedJobOpeningRecordsPageFactory(context) {
|
|
1014
1558
|
return zohoFetchPageFactory(zohoRecruitSearchCandidateAssociatedJobOpeningRecords(context));
|
|
1015
1559
|
}
|
|
1560
|
+
/**
|
|
1561
|
+
* Searches for candidates associated with a specific job opening.
|
|
1562
|
+
*/
|
|
1016
1563
|
function zohoRecruitSearchJobOpeningAssociatedCandidateRecords(context, jobOpeningModuleName = ZOHO_RECRUIT_JOB_OPENINGS_MODULE) {
|
|
1017
1564
|
const searchAssociatedRecordsFactory = zohoRecruitSearchAssociatedRecords(context);
|
|
1018
1565
|
return (input) => {
|
|
@@ -1022,26 +1569,124 @@ function zohoRecruitSearchJobOpeningAssociatedCandidateRecords(context, jobOpeni
|
|
|
1022
1569
|
});
|
|
1023
1570
|
};
|
|
1024
1571
|
}
|
|
1572
|
+
/**
|
|
1573
|
+
* Creates a page factory for paginating over candidates associated with a job opening.
|
|
1574
|
+
*/
|
|
1025
1575
|
function zohoRecruitSearchJobOpeningAssociatedCandidateRecordsPageFactory(context) {
|
|
1026
1576
|
return zohoFetchPageFactory(zohoRecruitSearchJobOpeningAssociatedCandidateRecords(context));
|
|
1027
1577
|
}
|
|
1028
1578
|
|
|
1579
|
+
/**
|
|
1580
|
+
* Creates a {@link ZohoRecruitCreateNotesFunction} bound to the given context.
|
|
1581
|
+
*
|
|
1582
|
+
* Creates one or more notes directly in the Notes module. Each note entry must include
|
|
1583
|
+
* `se_module` and `Parent_Id` to link the note to a record.
|
|
1584
|
+
*
|
|
1585
|
+
* Prefer {@link zohoRecruitCreateNotesForRecord} when creating notes linked to a specific
|
|
1586
|
+
* record, as it handles the module/parent linking automatically.
|
|
1587
|
+
*
|
|
1588
|
+
* @param context - Authenticated Zoho Recruit context providing fetch and rate limiting
|
|
1589
|
+
* @returns Function that creates notes in the Notes module
|
|
1590
|
+
*
|
|
1591
|
+
* @example
|
|
1592
|
+
* ```typescript
|
|
1593
|
+
* const createNotes = zohoRecruitCreateNotes(context);
|
|
1594
|
+
*
|
|
1595
|
+
* const result = await createNotes({
|
|
1596
|
+
* data: [{
|
|
1597
|
+
* Note_Title: 'Interview Notes',
|
|
1598
|
+
* Note_Content: 'Strong candidate',
|
|
1599
|
+
* se_module: ZOHO_RECRUIT_CANDIDATES_MODULE,
|
|
1600
|
+
* Parent_Id: candidateId
|
|
1601
|
+
* }]
|
|
1602
|
+
* });
|
|
1603
|
+
* ```
|
|
1604
|
+
*/
|
|
1029
1605
|
function zohoRecruitCreateNotes(context) {
|
|
1030
1606
|
return (input) => context.fetchJson(`/v2/${ZOHO_RECRUIT_NOTES_MODULE}`, zohoRecruitApiFetchJsonInput('POST', { data: input.data })).then((x) => {
|
|
1031
1607
|
return zohoRecruitMultiRecordResult(util.asArray(input.data), x.data);
|
|
1032
1608
|
});
|
|
1033
1609
|
}
|
|
1610
|
+
/**
|
|
1611
|
+
* Creates a {@link ZohoRecruitDeleteNotesFunction} bound to the given context.
|
|
1612
|
+
*
|
|
1613
|
+
* Deletes one or more notes by their IDs from the Notes module. Returns a paired
|
|
1614
|
+
* success/error result for each note ID.
|
|
1615
|
+
*
|
|
1616
|
+
* @param context - Authenticated Zoho Recruit context providing fetch and rate limiting
|
|
1617
|
+
* @returns Function that deletes notes by ID
|
|
1618
|
+
*
|
|
1619
|
+
* @example
|
|
1620
|
+
* ```typescript
|
|
1621
|
+
* const deleteNotes = zohoRecruitDeleteNotes(context);
|
|
1622
|
+
*
|
|
1623
|
+
* const result = await deleteNotes({ ids: [noteId1, noteId2] });
|
|
1624
|
+
* ```
|
|
1625
|
+
*/
|
|
1034
1626
|
function zohoRecruitDeleteNotes(context) {
|
|
1035
1627
|
return (input) => context.fetchJson(`/v2/${ZOHO_RECRUIT_NOTES_MODULE}?${fetch.makeUrlSearchParams({ ids: input.ids })}`, zohoRecruitApiFetchJsonInput('DELETE')).then((x) => {
|
|
1036
1628
|
return zohoRecruitMultiRecordResult(util.asArray(input.ids), x.data);
|
|
1037
1629
|
});
|
|
1038
1630
|
}
|
|
1631
|
+
/**
|
|
1632
|
+
* Creates a {@link ZohoRecruitGetNotesForRecordFunction} bound to the given context.
|
|
1633
|
+
*
|
|
1634
|
+
* Retrieves notes related to a specific record by targeting the Notes module
|
|
1635
|
+
* via the related records API. Returns an empty page result when no notes exist.
|
|
1636
|
+
*
|
|
1637
|
+
* @param context - Authenticated Zoho Recruit context providing fetch and rate limiting
|
|
1638
|
+
* @returns Function that retrieves notes for a record
|
|
1639
|
+
*
|
|
1640
|
+
* @example
|
|
1641
|
+
* ```typescript
|
|
1642
|
+
* const getNotesForRecord = zohoRecruitGetNotesForRecord(context);
|
|
1643
|
+
*
|
|
1644
|
+
* const result = await getNotesForRecord({
|
|
1645
|
+
* module: ZOHO_RECRUIT_CANDIDATES_MODULE,
|
|
1646
|
+
* id: candidateId
|
|
1647
|
+
* });
|
|
1648
|
+
* ```
|
|
1649
|
+
*/
|
|
1039
1650
|
function zohoRecruitGetNotesForRecord(context) {
|
|
1040
1651
|
return zohoRecruitGetRelatedRecordsFunctionFactory(context)({ targetModule: ZOHO_RECRUIT_NOTES_MODULE });
|
|
1041
1652
|
}
|
|
1653
|
+
/**
|
|
1654
|
+
* Creates a {@link ZohoRecruitGetNotesForRecordPageFactory} bound to the given context.
|
|
1655
|
+
*
|
|
1656
|
+
* Returns a page factory for iterating over notes related to a record across
|
|
1657
|
+
* multiple pages. Wraps {@link zohoRecruitGetNotesForRecord} with automatic
|
|
1658
|
+
* pagination handling via {@link zohoFetchPageFactory}.
|
|
1659
|
+
*
|
|
1660
|
+
* @param context - Authenticated Zoho Recruit context providing fetch and rate limiting
|
|
1661
|
+
* @returns Page factory for iterating over record notes
|
|
1662
|
+
*/
|
|
1042
1663
|
function zohoRecruitGetNotesForRecordPageFactory(context) {
|
|
1043
1664
|
return zohoFetchPageFactory(zohoRecruitGetNotesForRecord(context));
|
|
1044
1665
|
}
|
|
1666
|
+
/**
|
|
1667
|
+
* Creates a {@link ZohoRecruitCreateNotesForRecordFunction} bound to the given context.
|
|
1668
|
+
*
|
|
1669
|
+
* Creates one or more notes linked to a specific record. Automatically sets
|
|
1670
|
+
* `se_module` and `Parent_Id` on each note entry from the request's `module` and `id`,
|
|
1671
|
+
* then delegates to {@link zohoRecruitCreateNotes}.
|
|
1672
|
+
*
|
|
1673
|
+
* @param context - Authenticated Zoho Recruit context providing fetch and rate limiting
|
|
1674
|
+
* @returns Function that creates notes linked to a specific record
|
|
1675
|
+
*
|
|
1676
|
+
* @example
|
|
1677
|
+
* ```typescript
|
|
1678
|
+
* const createNotesForRecord = zohoRecruitCreateNotesForRecord(context);
|
|
1679
|
+
*
|
|
1680
|
+
* const result = await createNotesForRecord({
|
|
1681
|
+
* module: ZOHO_RECRUIT_CANDIDATES_MODULE,
|
|
1682
|
+
* id: candidateId,
|
|
1683
|
+
* notes: {
|
|
1684
|
+
* Note_Title: 'Interview Notes',
|
|
1685
|
+
* Note_Content: 'Strong candidate for the role.'
|
|
1686
|
+
* }
|
|
1687
|
+
* });
|
|
1688
|
+
* ```
|
|
1689
|
+
*/
|
|
1045
1690
|
function zohoRecruitCreateNotesForRecord(context) {
|
|
1046
1691
|
const createNotesInstance = zohoRecruitCreateNotes(context);
|
|
1047
1692
|
return (input) => {
|
|
@@ -1078,6 +1723,30 @@ const getNotesForRecordPageFactory = zohoRecruitGetNotesForRecordPageFactory;
|
|
|
1078
1723
|
*/
|
|
1079
1724
|
const createNotesForRecord = zohoRecruitCreateNotesForRecord;
|
|
1080
1725
|
|
|
1726
|
+
/**
|
|
1727
|
+
* Creates a {@link ZohoRecruitCreateTagsFunction} bound to the given context.
|
|
1728
|
+
*
|
|
1729
|
+
* Creates one or more tags for a module. The result separates duplicate tag errors
|
|
1730
|
+
* (which are often non-fatal) from other errors, making it easy to handle the common
|
|
1731
|
+
* case where a tag already exists.
|
|
1732
|
+
*
|
|
1733
|
+
* @param context - Authenticated Zoho Recruit context providing fetch and rate limiting
|
|
1734
|
+
* @returns Function that creates tags in the specified module
|
|
1735
|
+
*
|
|
1736
|
+
* @example
|
|
1737
|
+
* ```typescript
|
|
1738
|
+
* const createTags = zohoRecruitCreateTagsForModule(context);
|
|
1739
|
+
*
|
|
1740
|
+
* const result = await createTags({
|
|
1741
|
+
* module: ZOHO_RECRUIT_CANDIDATES_MODULE,
|
|
1742
|
+
* tags: [{ name: 'Interviewed' }, { name: 'Priority', color_code: '#FF0000' }]
|
|
1743
|
+
* });
|
|
1744
|
+
*
|
|
1745
|
+
* // Duplicate tags are separated for convenience:
|
|
1746
|
+
* result.duplicateErrorItems; // tags that already existed
|
|
1747
|
+
* result.errorItems; // other (real) errors
|
|
1748
|
+
* ```
|
|
1749
|
+
*/
|
|
1081
1750
|
function zohoRecruitCreateTagsForModule(context) {
|
|
1082
1751
|
return (input) => context.fetchJson(`/v2/settings/tags?${fetch.makeUrlSearchParams({ module: input.module })}`, zohoRecruitApiFetchJsonInput('POST', { tags: util.asArray(input.tags) })).then((x) => {
|
|
1083
1752
|
const result = zohoRecruitMultiRecordResult(util.asArray(input.tags), x.tags);
|
|
@@ -1093,12 +1762,24 @@ function zohoRecruitCreateTagsForModule(context) {
|
|
|
1093
1762
|
});
|
|
1094
1763
|
}
|
|
1095
1764
|
/**
|
|
1096
|
-
*
|
|
1765
|
+
* Creates a {@link ZohoRecruitGetTagsFunction} bound to the given context.
|
|
1097
1766
|
*
|
|
1098
|
-
*
|
|
1767
|
+
* Returns the list of tags within a module. Normalizes the non-standard API response
|
|
1768
|
+
* that returns data under a `tags` key instead of the standard `data` key.
|
|
1099
1769
|
*
|
|
1100
|
-
* @param context
|
|
1101
|
-
* @returns
|
|
1770
|
+
* @param context - Authenticated Zoho Recruit context providing fetch and rate limiting
|
|
1771
|
+
* @returns Function that retrieves tags for a module
|
|
1772
|
+
*
|
|
1773
|
+
* @example
|
|
1774
|
+
* ```typescript
|
|
1775
|
+
* const getTags = zohoRecruitGetTagsForModule(context);
|
|
1776
|
+
*
|
|
1777
|
+
* const result = await getTags({
|
|
1778
|
+
* module: ZOHO_RECRUIT_CANDIDATES_MODULE
|
|
1779
|
+
* });
|
|
1780
|
+
* ```
|
|
1781
|
+
*
|
|
1782
|
+
* @see https://www.zoho.com/recruit/developer-guide/apiv2/get-tag-list.html
|
|
1102
1783
|
*/
|
|
1103
1784
|
function zohoRecruitGetTagsForModule(context) {
|
|
1104
1785
|
return (input) => context.fetchJson(`/v2/settings/tags?${fetch.makeUrlSearchParams({ module: input.module, my_tags: input.my_tags })}`, zohoRecruitApiFetchJsonInput('GET')).then((x) => {
|
|
@@ -1109,21 +1790,45 @@ function zohoRecruitGetTagsForModule(context) {
|
|
|
1109
1790
|
};
|
|
1110
1791
|
});
|
|
1111
1792
|
}
|
|
1793
|
+
/**
|
|
1794
|
+
* Creates a {@link ZohoRecruitGetTagsForModulePageFactory} bound to the given context.
|
|
1795
|
+
*
|
|
1796
|
+
* Returns a page factory for iterating over tags in a module across multiple pages.
|
|
1797
|
+
* Wraps {@link zohoRecruitGetTagsForModule} with automatic pagination handling.
|
|
1798
|
+
*
|
|
1799
|
+
* @param context - Authenticated Zoho Recruit context providing fetch and rate limiting
|
|
1800
|
+
* @returns Page factory for iterating over module tags
|
|
1801
|
+
*/
|
|
1112
1802
|
function zohoRecruitGetTagsForModulePageFactory(context) {
|
|
1113
1803
|
return zohoFetchPageFactory(zohoRecruitGetTagsForModule(context));
|
|
1114
1804
|
}
|
|
1115
1805
|
// MARK: Add Tag To Record
|
|
1116
1806
|
/**
|
|
1117
|
-
*
|
|
1807
|
+
* Maximum number of record ids allowed when adding tags, enforced by the Zoho Recruit API.
|
|
1118
1808
|
*/
|
|
1119
1809
|
const ZOHO_RECRUIT_ADD_TAGS_TO_RECORDS_MAX_IDS_ALLOWED = 100;
|
|
1120
1810
|
/**
|
|
1121
|
-
*
|
|
1811
|
+
* Creates a {@link ZohoRecruitAddTagsToRecordsFunction} bound to the given context.
|
|
1122
1812
|
*
|
|
1123
|
-
*
|
|
1813
|
+
* Adds one or more tags to one or more records. Returns a paired success/error result
|
|
1814
|
+
* for each record. Maximum of {@link ZOHO_RECRUIT_ADD_TAGS_TO_RECORDS_MAX_IDS_ALLOWED} (100) record IDs per call.
|
|
1124
1815
|
*
|
|
1125
|
-
* @param context
|
|
1126
|
-
* @returns
|
|
1816
|
+
* @param context - Authenticated Zoho Recruit context providing fetch and rate limiting
|
|
1817
|
+
* @returns Function that adds tags to records
|
|
1818
|
+
* @throws {Error} If more than 100 record IDs are provided
|
|
1819
|
+
*
|
|
1820
|
+
* @example
|
|
1821
|
+
* ```typescript
|
|
1822
|
+
* const addTags = zohoRecruitAddTagsToRecords(context);
|
|
1823
|
+
*
|
|
1824
|
+
* const result = await addTags({
|
|
1825
|
+
* module: ZOHO_RECRUIT_CANDIDATES_MODULE,
|
|
1826
|
+
* tag_names: ['Interviewed', 'Priority'],
|
|
1827
|
+
* ids: [candidateId1, candidateId2]
|
|
1828
|
+
* });
|
|
1829
|
+
* ```
|
|
1830
|
+
*
|
|
1831
|
+
* @see https://www.zoho.com/recruit/developer-guide/apiv2/add-tags.html
|
|
1127
1832
|
*/
|
|
1128
1833
|
function zohoRecruitAddTagsToRecords(context) {
|
|
1129
1834
|
return (input) => {
|
|
@@ -1138,16 +1843,31 @@ function zohoRecruitAddTagsToRecords(context) {
|
|
|
1138
1843
|
}
|
|
1139
1844
|
// MARK: Remove Tag From Record
|
|
1140
1845
|
/**
|
|
1141
|
-
*
|
|
1846
|
+
* Maximum number of record ids allowed when removing tags, enforced by the Zoho Recruit API.
|
|
1142
1847
|
*/
|
|
1143
1848
|
const ZOHO_RECRUIT_REMOVE_TAGS_FROM_RECORDS_MAX_IDS_ALLOWED = 100;
|
|
1144
1849
|
/**
|
|
1145
|
-
*
|
|
1850
|
+
* Creates a {@link ZohoRecruitRemoveTagsFromRecordsFunction} bound to the given context.
|
|
1146
1851
|
*
|
|
1147
|
-
*
|
|
1852
|
+
* Removes one or more tags from one or more records. Returns a paired success/error result
|
|
1853
|
+
* for each record. Maximum of {@link ZOHO_RECRUIT_REMOVE_TAGS_FROM_RECORDS_MAX_IDS_ALLOWED} (100) record IDs per call.
|
|
1148
1854
|
*
|
|
1149
|
-
* @param context
|
|
1150
|
-
* @returns
|
|
1855
|
+
* @param context - Authenticated Zoho Recruit context providing fetch and rate limiting
|
|
1856
|
+
* @returns Function that removes tags from records
|
|
1857
|
+
* @throws {Error} If more than 100 record IDs are provided
|
|
1858
|
+
*
|
|
1859
|
+
* @example
|
|
1860
|
+
* ```typescript
|
|
1861
|
+
* const removeTags = zohoRecruitRemoveTagsFromRecords(context);
|
|
1862
|
+
*
|
|
1863
|
+
* const result = await removeTags({
|
|
1864
|
+
* module: ZOHO_RECRUIT_CANDIDATES_MODULE,
|
|
1865
|
+
* tag_names: 'Interviewed',
|
|
1866
|
+
* ids: candidateId
|
|
1867
|
+
* });
|
|
1868
|
+
* ```
|
|
1869
|
+
*
|
|
1870
|
+
* @see https://www.zoho.com/recruit/developer-guide/apiv2/remove-tags.html
|
|
1151
1871
|
*/
|
|
1152
1872
|
function zohoRecruitRemoveTagsFromRecords(context) {
|
|
1153
1873
|
return (input) => {
|
|
@@ -1250,16 +1970,39 @@ function zohoAccessTokenStringFactory(zohoAccessTokenFactory) {
|
|
|
1250
1970
|
};
|
|
1251
1971
|
}
|
|
1252
1972
|
|
|
1973
|
+
/**
|
|
1974
|
+
* Default handler that logs a warning to the console when the Zoho API rate limit is exceeded.
|
|
1975
|
+
*/
|
|
1253
1976
|
const DEFAULT_ZOHO_RATE_LIMITED_TOO_MANY_REQUETS_LOG_FUNCTION = (headers) => {
|
|
1254
1977
|
console.warn(`zohoRateLimitedFetchHandler(): Too many requests made. The limit is ${headers.limit} requests per reset period. Will be reset at ${headers.resetAt}.`);
|
|
1255
1978
|
};
|
|
1979
|
+
/**
|
|
1980
|
+
* Creates a {@link ZohoRateLimitedFetchHandler} that throttles outgoing requests based on
|
|
1981
|
+
* Zoho's rate limit headers (`X-RATELIMIT-LIMIT`, `X-RATELIMIT-REMAINING`, `X-RATELIMIT-RESET`).
|
|
1982
|
+
*
|
|
1983
|
+
* The handler uses an exponential backoff strategy with the following behavior:
|
|
1984
|
+
* - Rate limiting begins after 10% of the allowed requests have been made (`startLimitAt`)
|
|
1985
|
+
* - Wait times increase exponentially (rate 1.08) as more requests are made, capped at 10 seconds
|
|
1986
|
+
* - On each response, the limiter updates its remaining count and reset time from headers
|
|
1987
|
+
* - When the API returns a different limit than configured, the limiter dynamically adjusts
|
|
1988
|
+
* - On 429 responses, the request is automatically retried after the rate limiter delay
|
|
1989
|
+
* - The limiter is disabled when responses lack rate limit headers (e.g., error responses)
|
|
1990
|
+
*
|
|
1991
|
+
* @param config - Optional configuration for rate limit, reset period, and 429 handling
|
|
1992
|
+
* @returns A rate-limited fetch handler with the underlying rate limiter accessible via `_rateLimiter`
|
|
1993
|
+
*/
|
|
1256
1994
|
function zohoRateLimitedFetchHandler(config) {
|
|
1257
1995
|
const onTooManyRequests = config?.onTooManyRequests !== false ? (config?.onTooManyRequests ?? DEFAULT_ZOHO_RATE_LIMITED_TOO_MANY_REQUETS_LOG_FUNCTION) : undefined;
|
|
1258
1996
|
const defaultLimit = config?.maxRateLimit ?? DEFAULT_ZOHO_API_RATE_LIMIT;
|
|
1259
1997
|
const defaultResetPeriod = config?.resetPeriod ?? DEFAULT_ZOHO_API_RATE_LIMIT_RESET_PERIOD;
|
|
1998
|
+
/**
|
|
1999
|
+
* Builds a rate limiter config derived from the given limit.
|
|
2000
|
+
* Called once at initialization with `defaultLimit`, and again dynamically
|
|
2001
|
+
* when the API's `X-RATELIMIT-LIMIT` header reports a different value.
|
|
2002
|
+
*/
|
|
1260
2003
|
function configForLimit(limit, resetAt) {
|
|
1261
2004
|
return {
|
|
1262
|
-
limit
|
|
2005
|
+
limit,
|
|
1263
2006
|
startLimitAt: Math.ceil(limit / 10), // can do 10% of the requests of the limit before rate limiting begins
|
|
1264
2007
|
cooldownRate: 1.2 * (limit / (defaultResetPeriod / util.MS_IN_SECOND)),
|
|
1265
2008
|
exponentRate: 1.08,
|
|
@@ -1272,6 +2015,10 @@ function zohoRateLimitedFetchHandler(config) {
|
|
|
1272
2015
|
const rateLimiter = util.resetPeriodPromiseRateLimiter(defaultConfig);
|
|
1273
2016
|
return fetch.rateLimitedFetchHandler({
|
|
1274
2017
|
rateLimiter,
|
|
2018
|
+
/**
|
|
2019
|
+
* Inspects each response for Zoho rate limit headers and updates the limiter accordingly.
|
|
2020
|
+
* Returns `true` to signal a retry when a 429 status is received.
|
|
2021
|
+
*/
|
|
1275
2022
|
updateWithResponse: function (response, fetchResponseError) {
|
|
1276
2023
|
const hasLimitHeader = response.headers.has(ZOHO_RATE_LIMIT_REMAINING_HEADER);
|
|
1277
2024
|
let shouldRetry = false;
|
|
@@ -1280,7 +2027,7 @@ function zohoRateLimitedFetchHandler(config) {
|
|
|
1280
2027
|
const headerDetails = zohoRateLimitHeaderDetails(response.headers);
|
|
1281
2028
|
if (headerDetails) {
|
|
1282
2029
|
const { limit, resetAt, remaining } = headerDetails;
|
|
1283
|
-
if (limit !== defaultLimit) {
|
|
2030
|
+
if (limit && limit !== defaultLimit) {
|
|
1284
2031
|
const newConfig = configForLimit(limit, resetAt);
|
|
1285
2032
|
rateLimiter.setConfig(newConfig, false);
|
|
1286
2033
|
}
|
|
@@ -1303,6 +2050,31 @@ function zohoRateLimitedFetchHandler(config) {
|
|
|
1303
2050
|
});
|
|
1304
2051
|
}
|
|
1305
2052
|
|
|
2053
|
+
/**
|
|
2054
|
+
* Creates a {@link ZohoRecruitFactory} from the given configuration.
|
|
2055
|
+
*
|
|
2056
|
+
* The factory pre-initializes shared resources (access token provider, rate limiter)
|
|
2057
|
+
* once, then produces {@link ZohoRecruit} client instances for each {@link ZohoRecruitConfig}.
|
|
2058
|
+
* Each client handles OAuth token refresh on {@link ZohoInvalidTokenError}, rate limiting,
|
|
2059
|
+
* and Zoho Recruit's non-standard error responses (200 status with error body).
|
|
2060
|
+
*
|
|
2061
|
+
* @param factoryConfig - Configuration providing account credentials and optional overrides
|
|
2062
|
+
* @returns A factory function that creates authenticated Zoho Recruit clients
|
|
2063
|
+
*
|
|
2064
|
+
* @example
|
|
2065
|
+
* ```typescript
|
|
2066
|
+
* const factory = zohoRecruitFactory({
|
|
2067
|
+
* accountsContext: zohoAccountsApi.accountsContext
|
|
2068
|
+
* });
|
|
2069
|
+
*
|
|
2070
|
+
* const zohoRecruit = factory({
|
|
2071
|
+
* apiUrl: 'https://recruit.zoho.com'
|
|
2072
|
+
* });
|
|
2073
|
+
*
|
|
2074
|
+
* // Use the recruit context for API calls:
|
|
2075
|
+
* const { recruitContext } = zohoRecruit;
|
|
2076
|
+
* ```
|
|
2077
|
+
*/
|
|
1306
2078
|
function zohoRecruitFactory(factoryConfig) {
|
|
1307
2079
|
const { accountsContext } = factoryConfig;
|
|
1308
2080
|
const accessTokenStringFactory = zohoAccessTokenStringFactory(accountsContext.loadAccessToken);
|
|
@@ -1357,7 +2129,13 @@ function zohoRecruitFactory(factoryConfig) {
|
|
|
1357
2129
|
*/
|
|
1358
2130
|
const ZOHO_RECRUIT_TAG_NAME_MAX_LENGTH = 25;
|
|
1359
2131
|
|
|
2132
|
+
/**
|
|
2133
|
+
* Service identifier used for Zoho CRM API access token resolution and service routing.
|
|
2134
|
+
*/
|
|
1360
2135
|
const ZOHO_CRM_SERVICE_NAME = 'crm';
|
|
2136
|
+
/**
|
|
2137
|
+
* Resolves a CRM API URL input to its full base URL. Well-known keys ('sandbox', 'production') map to their respective Zoho CRM endpoints; custom URLs pass through unchanged.
|
|
2138
|
+
*/
|
|
1361
2139
|
function zohoCrmConfigApiUrl(input) {
|
|
1362
2140
|
switch (input) {
|
|
1363
2141
|
case 'sandbox':
|
|
@@ -1400,16 +2178,41 @@ const isZohoCrmValidUrl = util.isStandardInternetAccessibleWebsiteUrl;
|
|
|
1400
2178
|
const ZOHO_CRM_RECORD_ATTACHMENT_METADATA_ATTACH_TYPE_RESUME = 'Resume';
|
|
1401
2179
|
|
|
1402
2180
|
/**
|
|
1403
|
-
*
|
|
1404
|
-
*
|
|
1405
|
-
* https://www.zoho.com/crm/developer/docs/api/v8/search-records.html
|
|
2181
|
+
* Maximum number of criteria allowed per CRM search query (10).
|
|
1406
2182
|
*
|
|
1407
2183
|
* "You can search for a maximum of 10 criteria (with the same or different column) with equals and starts_with conditions."
|
|
2184
|
+
*
|
|
2185
|
+
* Re-exports {@link MAX_ZOHO_SEARCH_MODULE_RECORDS_CRITERIA}.
|
|
2186
|
+
*
|
|
2187
|
+
* @see https://www.zoho.com/crm/developer/docs/api/v8/search-records.html
|
|
1408
2188
|
*/
|
|
1409
2189
|
const MAX_ZOHO_CRM_SEARCH_MODULE_RECORDS_CRITERIA = MAX_ZOHO_SEARCH_MODULE_RECORDS_CRITERIA;
|
|
2190
|
+
/**
|
|
2191
|
+
* CRM-specific re-export of {@link zohoSearchRecordsCriteriaString}.
|
|
2192
|
+
*
|
|
2193
|
+
* Compiles a criteria tree element (entry array, nested tree, or raw string) into a
|
|
2194
|
+
* URL-ready criteria string. Returns `undefined` when the input is nullish or empty.
|
|
2195
|
+
*/
|
|
1410
2196
|
const zohoCrmSearchRecordsCriteriaString = zohoSearchRecordsCriteriaString;
|
|
2197
|
+
/**
|
|
2198
|
+
* CRM-specific re-export of {@link zohoSearchRecordsCriteriaStringForTree}.
|
|
2199
|
+
*
|
|
2200
|
+
* Compiles a {@link ZohoCrmSearchRecordsCriteriaTree} into a criteria string by
|
|
2201
|
+
* recursively resolving nested AND/OR groups.
|
|
2202
|
+
*/
|
|
1411
2203
|
const zohoCrmSearchRecordsCriteriaStringForTree = zohoSearchRecordsCriteriaStringForTree;
|
|
2204
|
+
/**
|
|
2205
|
+
* CRM-specific re-export of {@link escapeZohoFieldValueForCriteriaString}.
|
|
2206
|
+
*
|
|
2207
|
+
* Escapes parentheses and commas in a field value for use in a criteria string.
|
|
2208
|
+
*/
|
|
1412
2209
|
const escapeZohoCrmFieldValueForCriteriaString = escapeZohoFieldValueForCriteriaString;
|
|
2210
|
+
/**
|
|
2211
|
+
* CRM-specific re-export of {@link zohoSearchRecordsCriteriaEntryToCriteriaString}.
|
|
2212
|
+
*
|
|
2213
|
+
* Converts a single criteria entry into a parenthesized criteria string
|
|
2214
|
+
* in the format `(field:filter:escapedValue)`.
|
|
2215
|
+
*/
|
|
1413
2216
|
const zohoCrmSearchRecordsCriteriaEntryToCriteriaString = zohoSearchRecordsCriteriaEntryToCriteriaString;
|
|
1414
2217
|
|
|
1415
2218
|
/**
|
|
@@ -1430,19 +2233,37 @@ class ZohoCrmRecordNoContentError extends makeError.BaseError {
|
|
|
1430
2233
|
this.recordId = recordId;
|
|
1431
2234
|
}
|
|
1432
2235
|
}
|
|
2236
|
+
/**
|
|
2237
|
+
* Base error for Zoho CRM record CRUD operations. Wraps the server-provided error data including field-level details.
|
|
2238
|
+
*/
|
|
1433
2239
|
class ZohoCrmRecordCrudError extends ZohoServerError {
|
|
1434
2240
|
}
|
|
2241
|
+
/**
|
|
2242
|
+
* Thrown when a CRM create/update request is missing a required field defined in the module layout.
|
|
2243
|
+
*/
|
|
1435
2244
|
class ZohoCrmRecordCrudMandatoryFieldNotFoundError extends ZohoCrmRecordCrudError {
|
|
1436
2245
|
}
|
|
2246
|
+
/**
|
|
2247
|
+
* Thrown when a CRM create/update request would produce a duplicate record based on unique field constraints.
|
|
2248
|
+
*/
|
|
1437
2249
|
class ZohoCrmRecordCrudDuplicateDataError extends ZohoCrmRecordCrudError {
|
|
1438
2250
|
}
|
|
2251
|
+
/**
|
|
2252
|
+
* Thrown when a CRM request contains invalid field data. Provides access to the offending field details via {@link invalidFieldDetails}.
|
|
2253
|
+
*/
|
|
1439
2254
|
class ZohoCrmRecordCrudInvalidDataError extends ZohoCrmRecordCrudError {
|
|
1440
2255
|
get invalidFieldDetails() {
|
|
1441
2256
|
return this.error.details;
|
|
1442
2257
|
}
|
|
1443
2258
|
}
|
|
2259
|
+
/**
|
|
2260
|
+
* Thrown when an invalid data error targets the 'id' field, indicating the referenced record does not exist.
|
|
2261
|
+
*/
|
|
1444
2262
|
class ZohoCrmRecordCrudNoMatchingRecordError extends ZohoCrmRecordCrudInvalidDataError {
|
|
1445
2263
|
}
|
|
2264
|
+
/**
|
|
2265
|
+
* Maps a Zoho CRM server error to the appropriate typed {@link ZohoCrmRecordCrudError} subclass based on the error code and affected field.
|
|
2266
|
+
*/
|
|
1446
2267
|
function zohoCrmRecordCrudError(error) {
|
|
1447
2268
|
let result;
|
|
1448
2269
|
switch (error.code) {
|
|
@@ -1467,6 +2288,9 @@ function zohoCrmRecordCrudError(error) {
|
|
|
1467
2288
|
}
|
|
1468
2289
|
return result;
|
|
1469
2290
|
}
|
|
2291
|
+
/**
|
|
2292
|
+
* Returns an assertion function that throws {@link ZohoCrmRecordNoContentError} when a data array result is empty or null, typically indicating a missing record.
|
|
2293
|
+
*/
|
|
1470
2294
|
function assertZohoCrmRecordDataArrayResultHasContent(moduleName, recordId) {
|
|
1471
2295
|
return (x) => {
|
|
1472
2296
|
if (x == null || !x.data?.length) {
|
|
@@ -1477,7 +2301,13 @@ function assertZohoCrmRecordDataArrayResultHasContent(moduleName, recordId) {
|
|
|
1477
2301
|
}
|
|
1478
2302
|
};
|
|
1479
2303
|
}
|
|
2304
|
+
/**
|
|
2305
|
+
* Pre-configured console logger for Zoho CRM server errors. Data array errors are suppressed since they are handled separately by CRUD error classes.
|
|
2306
|
+
*/
|
|
1480
2307
|
const logZohoCrmErrorToConsole = logZohoServerErrorFunction('ZohoCrm', { logDataArrayErrors: false });
|
|
2308
|
+
/**
|
|
2309
|
+
* Parses a fetch response error into a typed Zoho CRM server error by extracting and interpreting the JSON error body.
|
|
2310
|
+
*/
|
|
1481
2311
|
async function parseZohoCrmError(responseError) {
|
|
1482
2312
|
const data = await responseError.response.json().catch((x) => undefined);
|
|
1483
2313
|
let result;
|
|
@@ -1486,6 +2316,9 @@ async function parseZohoCrmError(responseError) {
|
|
|
1486
2316
|
}
|
|
1487
2317
|
return result;
|
|
1488
2318
|
}
|
|
2319
|
+
/**
|
|
2320
|
+
* Parses a Zoho CRM error response body into a typed error. Delegates to CRM-specific error code handling before falling back to the generic Zoho error parser.
|
|
2321
|
+
*/
|
|
1489
2322
|
function parseZohoCrmServerErrorResponseData(errorResponseData, responseError) {
|
|
1490
2323
|
let result;
|
|
1491
2324
|
const error = tryFindZohoServerErrorData(errorResponseData, responseError);
|
|
@@ -1500,7 +2333,13 @@ function parseZohoCrmServerErrorResponseData(errorResponseData, responseError) {
|
|
|
1500
2333
|
}
|
|
1501
2334
|
return result;
|
|
1502
2335
|
}
|
|
2336
|
+
/**
|
|
2337
|
+
* Fetch response interceptor that detects Zoho CRM error payloads hidden within HTTP 200 responses and converts them into proper errors.
|
|
2338
|
+
*/
|
|
1503
2339
|
const interceptZohoCrm200StatusWithErrorResponse = interceptZohoErrorResponseFactory(parseZohoCrmServerErrorResponseData);
|
|
2340
|
+
/**
|
|
2341
|
+
* Wraps a fetch function with Zoho CRM error parsing and console logging, ensuring all CRM API errors are surfaced as typed exceptions.
|
|
2342
|
+
*/
|
|
1504
2343
|
const handleZohoCrmErrorFetch = handleZohoErrorFetchFactory(parseZohoCrmError, logZohoCrmErrorToConsole);
|
|
1505
2344
|
|
|
1506
2345
|
// MARK: Insert/Update/Upsert Response
|
|
@@ -1511,9 +2350,10 @@ const handleZohoCrmErrorFetch = handleZohoErrorFetchFactory(parseZohoCrmError, l
|
|
|
1511
2350
|
*/
|
|
1512
2351
|
const ZOHO_CRM_CRUD_FUNCTION_MAX_RECORDS_LIMIT = 100;
|
|
1513
2352
|
/**
|
|
1514
|
-
*
|
|
2353
|
+
* Shared implementation for the Insert, Upsert, and Update endpoints, which all share the same request/response structure.
|
|
1515
2354
|
*
|
|
1516
|
-
*
|
|
2355
|
+
* When a single record is provided, the function returns the change details directly or throws on error.
|
|
2356
|
+
* When multiple records are provided, it returns a paired success/error result.
|
|
1517
2357
|
*/
|
|
1518
2358
|
function updateRecordLikeFunction(context, fetchUrlPrefix, fetchMethod) {
|
|
1519
2359
|
return (({ data, module }) => context
|
|
@@ -1537,45 +2377,145 @@ function updateRecordLikeFunction(context, fetchUrlPrefix, fetchMethod) {
|
|
|
1537
2377
|
}));
|
|
1538
2378
|
}
|
|
1539
2379
|
/**
|
|
1540
|
-
*
|
|
2380
|
+
* Creates a {@link ZohoCrmInsertRecordFunction} bound to the given context.
|
|
1541
2381
|
*
|
|
1542
|
-
*
|
|
2382
|
+
* Inserts one or more records into a CRM module. When a single record is
|
|
2383
|
+
* provided, returns the {@link ZohoCrmChangeObjectDetails} directly or
|
|
2384
|
+
* throws on error. When multiple records are provided, returns a
|
|
2385
|
+
* {@link ZohoCrmUpdateRecordResult} with paired success/error arrays.
|
|
1543
2386
|
*
|
|
1544
|
-
* @
|
|
1545
|
-
*
|
|
2387
|
+
* Maximum of {@link ZOHO_CRM_CRUD_FUNCTION_MAX_RECORDS_LIMIT} records per call.
|
|
2388
|
+
*
|
|
2389
|
+
* @param context - Authenticated Zoho CRM context providing fetch and rate limiting
|
|
2390
|
+
* @returns Function that inserts records into the specified module
|
|
2391
|
+
*
|
|
2392
|
+
* @example
|
|
2393
|
+
* ```typescript
|
|
2394
|
+
* const insertRecord = zohoCrmInsertRecord(context);
|
|
2395
|
+
*
|
|
2396
|
+
* // Single record — returns details directly or throws on error:
|
|
2397
|
+
* const details = await insertRecord({
|
|
2398
|
+
* module: 'Contacts',
|
|
2399
|
+
* data: { First_Name: 'Jane', Last_Name: 'Doe', Email: 'jane@example.com' }
|
|
2400
|
+
* });
|
|
2401
|
+
*
|
|
2402
|
+
* // Multiple records — returns paired success/error arrays:
|
|
2403
|
+
* const result = await insertRecord({
|
|
2404
|
+
* module: 'Contacts',
|
|
2405
|
+
* data: [
|
|
2406
|
+
* { First_Name: 'Jane', Last_Name: 'Doe', Email: 'jane@example.com' },
|
|
2407
|
+
* { First_Name: 'John', Last_Name: 'Doe', Email: 'john@example.com' }
|
|
2408
|
+
* ]
|
|
2409
|
+
* });
|
|
2410
|
+
* ```
|
|
2411
|
+
*
|
|
2412
|
+
* @see https://www.zoho.com/crm/developer-guide/apiv2/insert-records.html
|
|
1546
2413
|
*/
|
|
1547
2414
|
function zohoCrmInsertRecord(context) {
|
|
1548
2415
|
return updateRecordLikeFunction(context, '', 'POST');
|
|
1549
2416
|
}
|
|
1550
2417
|
/**
|
|
1551
|
-
*
|
|
2418
|
+
* Creates a {@link ZohoCrmUpsertRecordFunction} bound to the given context.
|
|
1552
2419
|
*
|
|
1553
|
-
*
|
|
2420
|
+
* Inserts or updates one or more records in a CRM module based on whether
|
|
2421
|
+
* each record includes an `id`. Uses the `/upsert` endpoint. Single-record
|
|
2422
|
+
* calls return details directly or throw; multi-record calls return paired
|
|
2423
|
+
* success/error arrays.
|
|
1554
2424
|
*
|
|
1555
|
-
* @
|
|
1556
|
-
*
|
|
2425
|
+
* Maximum of {@link ZOHO_CRM_CRUD_FUNCTION_MAX_RECORDS_LIMIT} records per call.
|
|
2426
|
+
*
|
|
2427
|
+
* @param context - Authenticated Zoho CRM context providing fetch and rate limiting
|
|
2428
|
+
* @returns Function that upserts records in the specified module
|
|
2429
|
+
*
|
|
2430
|
+
* @example
|
|
2431
|
+
* ```typescript
|
|
2432
|
+
* const upsertRecord = zohoCrmUpsertRecord(context);
|
|
2433
|
+
*
|
|
2434
|
+
* // Create (no id) — returns details directly:
|
|
2435
|
+
* const created = await upsertRecord({
|
|
2436
|
+
* module: 'Contacts',
|
|
2437
|
+
* data: { Email: 'new@example.com', Last_Name: 'New' }
|
|
2438
|
+
* });
|
|
2439
|
+
*
|
|
2440
|
+
* // Update (with id) — returns details directly:
|
|
2441
|
+
* const updated = await upsertRecord({
|
|
2442
|
+
* module: 'Contacts',
|
|
2443
|
+
* data: { id: existingId, First_Name: 'Updated' }
|
|
2444
|
+
* });
|
|
2445
|
+
*
|
|
2446
|
+
* // Mixed create and update — returns paired arrays:
|
|
2447
|
+
* const result = await upsertRecord({
|
|
2448
|
+
* module: 'Contacts',
|
|
2449
|
+
* data: [
|
|
2450
|
+
* { Email: 'create@example.com', Last_Name: 'Create' },
|
|
2451
|
+
* { id: existingId, First_Name: 'Update' }
|
|
2452
|
+
* ]
|
|
2453
|
+
* });
|
|
2454
|
+
* ```
|
|
2455
|
+
*
|
|
2456
|
+
* @see https://www.zoho.com/crm/developer-guide/apiv2/upsert-records.html
|
|
1557
2457
|
*/
|
|
1558
2458
|
function zohoCrmUpsertRecord(context) {
|
|
1559
2459
|
return updateRecordLikeFunction(context, '/upsert', 'POST');
|
|
1560
2460
|
}
|
|
1561
2461
|
/**
|
|
1562
|
-
*
|
|
2462
|
+
* Creates a {@link ZohoCrmUpdateRecordFunction} bound to the given context.
|
|
1563
2463
|
*
|
|
1564
|
-
*
|
|
2464
|
+
* Updates one or more existing records in a CRM module. Each record must
|
|
2465
|
+
* include an `id` field. Single-record calls return details directly or throw;
|
|
2466
|
+
* multi-record calls return paired success/error arrays.
|
|
1565
2467
|
*
|
|
1566
|
-
* @
|
|
1567
|
-
*
|
|
2468
|
+
* Maximum of {@link ZOHO_CRM_CRUD_FUNCTION_MAX_RECORDS_LIMIT} records per call.
|
|
2469
|
+
*
|
|
2470
|
+
* @param context - Authenticated Zoho CRM context providing fetch and rate limiting
|
|
2471
|
+
* @returns Function that updates records in the specified module
|
|
2472
|
+
*
|
|
2473
|
+
* @example
|
|
2474
|
+
* ```typescript
|
|
2475
|
+
* const updateRecord = zohoCrmUpdateRecord(context);
|
|
2476
|
+
*
|
|
2477
|
+
* // Single record — returns details directly:
|
|
2478
|
+
* const details = await updateRecord({
|
|
2479
|
+
* module: 'Contacts',
|
|
2480
|
+
* data: { id: recordId, First_Name: 'Updated Name' }
|
|
2481
|
+
* });
|
|
2482
|
+
*
|
|
2483
|
+
* // Multiple records — returns paired arrays:
|
|
2484
|
+
* const result = await updateRecord({
|
|
2485
|
+
* module: 'Contacts',
|
|
2486
|
+
* data: [
|
|
2487
|
+
* { id: recordId1, First_Name: 'Updated 1' },
|
|
2488
|
+
* { id: recordId2, First_Name: 'Updated 2' }
|
|
2489
|
+
* ]
|
|
2490
|
+
* });
|
|
2491
|
+
* ```
|
|
2492
|
+
*
|
|
2493
|
+
* @see https://www.zoho.com/crm/developer-guide/apiv2/update-records.html
|
|
1568
2494
|
*/
|
|
1569
2495
|
function zohoCrmUpdateRecord(context) {
|
|
1570
2496
|
return updateRecordLikeFunction(context, '', 'PUT');
|
|
1571
2497
|
}
|
|
1572
2498
|
/**
|
|
1573
|
-
*
|
|
2499
|
+
* Creates a {@link ZohoCrmDeleteRecordFunction} bound to the given context.
|
|
1574
2500
|
*
|
|
1575
|
-
*
|
|
2501
|
+
* Deletes one or more records from a CRM module by their IDs. Supports
|
|
2502
|
+
* an optional `wf_trigger` flag to execute workflow rules on deletion. Returns
|
|
2503
|
+
* a response with separated success and error entries.
|
|
1576
2504
|
*
|
|
1577
|
-
* @param context
|
|
1578
|
-
* @returns
|
|
2505
|
+
* @param context - Authenticated Zoho CRM context providing fetch and rate limiting
|
|
2506
|
+
* @returns Function that deletes records from the specified module
|
|
2507
|
+
*
|
|
2508
|
+
* @example
|
|
2509
|
+
* ```typescript
|
|
2510
|
+
* const deleteRecord = zohoCrmDeleteRecord(context);
|
|
2511
|
+
*
|
|
2512
|
+
* const result = await deleteRecord({
|
|
2513
|
+
* module: 'Contacts',
|
|
2514
|
+
* ids: contactId
|
|
2515
|
+
* });
|
|
2516
|
+
* ```
|
|
2517
|
+
*
|
|
2518
|
+
* @see https://www.zoho.com/crm/developer-guide/apiv2/delete-records.html
|
|
1579
2519
|
*/
|
|
1580
2520
|
function zohoCrmDeleteRecord(context) {
|
|
1581
2521
|
return ({ ids, module, wf_trigger }) => {
|
|
@@ -1586,12 +2526,26 @@ function zohoCrmDeleteRecord(context) {
|
|
|
1586
2526
|
};
|
|
1587
2527
|
}
|
|
1588
2528
|
/**
|
|
1589
|
-
*
|
|
2529
|
+
* Creates a {@link ZohoCrmGetRecordByIdFunction} bound to the given context.
|
|
1590
2530
|
*
|
|
1591
|
-
*
|
|
2531
|
+
* Retrieves a single record from a CRM module by its ID. The response is
|
|
2532
|
+
* unwrapped from the standard data array, returning the record directly.
|
|
2533
|
+
* Throws if the record is not found.
|
|
1592
2534
|
*
|
|
1593
|
-
* @param context
|
|
1594
|
-
* @returns
|
|
2535
|
+
* @param context - Authenticated Zoho CRM context providing fetch and rate limiting
|
|
2536
|
+
* @returns Function that retrieves a record by module name and ID
|
|
2537
|
+
*
|
|
2538
|
+
* @example
|
|
2539
|
+
* ```typescript
|
|
2540
|
+
* const getRecordById = zohoCrmGetRecordById(context);
|
|
2541
|
+
*
|
|
2542
|
+
* const record = await getRecordById({
|
|
2543
|
+
* module: 'Contacts',
|
|
2544
|
+
* id: contactId
|
|
2545
|
+
* });
|
|
2546
|
+
* ```
|
|
2547
|
+
*
|
|
2548
|
+
* @see https://www.zoho.com/crm/developer-guide/apiv2/get-records.html
|
|
1595
2549
|
*/
|
|
1596
2550
|
function zohoCrmGetRecordById(context) {
|
|
1597
2551
|
return (input) => context
|
|
@@ -1600,23 +2554,62 @@ function zohoCrmGetRecordById(context) {
|
|
|
1600
2554
|
.then((x) => x.data[0]);
|
|
1601
2555
|
}
|
|
1602
2556
|
/**
|
|
1603
|
-
*
|
|
2557
|
+
* Creates a {@link ZohoCrmGetRecordsFunction} bound to the given context.
|
|
1604
2558
|
*
|
|
1605
|
-
*
|
|
2559
|
+
* Retrieves a paginated list of records from a CRM module. Supports field
|
|
2560
|
+
* selection, sorting, custom view filtering, territory filtering, and
|
|
2561
|
+
* conversion/approval status filters via {@link ZohoCrmGetRecordsInput}.
|
|
1606
2562
|
*
|
|
1607
|
-
* @param context
|
|
1608
|
-
* @returns
|
|
2563
|
+
* @param context - Authenticated Zoho CRM context providing fetch and rate limiting
|
|
2564
|
+
* @returns Function that retrieves paginated records from a module
|
|
2565
|
+
*
|
|
2566
|
+
* @example
|
|
2567
|
+
* ```typescript
|
|
2568
|
+
* const getRecords = zohoCrmGetRecords(context);
|
|
2569
|
+
*
|
|
2570
|
+
* const page = await getRecords({
|
|
2571
|
+
* module: 'Contacts',
|
|
2572
|
+
* fields: 'First_Name,Last_Name,Email',
|
|
2573
|
+
* per_page: 10
|
|
2574
|
+
* });
|
|
2575
|
+
* ```
|
|
2576
|
+
*
|
|
2577
|
+
* @see https://www.zoho.com/crm/developer-guide/apiv2/get-records.html
|
|
1609
2578
|
*/
|
|
1610
2579
|
function zohoCrmGetRecords(context) {
|
|
1611
2580
|
return ((input) => context.fetchJson(`/v8/${input.module}?${zohoCrmUrlSearchParamsMinusModule(input).toString()}`, zohoCrmApiFetchJsonInput('GET')));
|
|
1612
2581
|
}
|
|
1613
2582
|
/**
|
|
1614
|
-
*
|
|
2583
|
+
* Creates a {@link ZohoCrmSearchRecordsFunction} bound to the given context.
|
|
1615
2584
|
*
|
|
1616
|
-
*
|
|
2585
|
+
* Searches records in a CRM module using one of: criteria tree (compiled
|
|
2586
|
+
* via {@link zohoCrmSearchRecordsCriteriaString}), email, phone, cvid, or keyword.
|
|
2587
|
+
* At least one search parameter must be provided. Returns a paginated result,
|
|
2588
|
+
* defaulting to an empty data array when no matches are found.
|
|
1617
2589
|
*
|
|
1618
|
-
* @param context
|
|
1619
|
-
* @returns
|
|
2590
|
+
* @param context - Authenticated Zoho CRM context providing fetch and rate limiting
|
|
2591
|
+
* @returns Function that searches records in the specified module
|
|
2592
|
+
* @throws {Error} If none of `criteria`, `email`, `phone`, `cvid`, or `word` are provided
|
|
2593
|
+
*
|
|
2594
|
+
* @example
|
|
2595
|
+
* ```typescript
|
|
2596
|
+
* const searchRecords = zohoCrmSearchRecords(context);
|
|
2597
|
+
*
|
|
2598
|
+
* // Search by criteria:
|
|
2599
|
+
* const result = await searchRecords({
|
|
2600
|
+
* module: 'Contacts',
|
|
2601
|
+
* criteria: [{ field: 'Last_Name', filter: 'starts_with', value: 'Smith' }],
|
|
2602
|
+
* per_page: 10
|
|
2603
|
+
* });
|
|
2604
|
+
*
|
|
2605
|
+
* // Search by keyword:
|
|
2606
|
+
* const wordResult = await searchRecords({
|
|
2607
|
+
* module: 'Contacts',
|
|
2608
|
+
* word: 'engineer'
|
|
2609
|
+
* });
|
|
2610
|
+
* ```
|
|
2611
|
+
*
|
|
2612
|
+
* @see https://www.zoho.com/crm/developer-guide/apiv2/search-records.html
|
|
1620
2613
|
*/
|
|
1621
2614
|
function zohoCrmSearchRecords(context) {
|
|
1622
2615
|
function searchRecordsUrlSearchParams(input) {
|
|
@@ -1634,16 +2627,60 @@ function zohoCrmSearchRecords(context) {
|
|
|
1634
2627
|
}
|
|
1635
2628
|
return ((input) => context.fetchJson(`/v8/${input.module}/search?${searchRecordsUrlSearchParams(input).toString()}`, zohoCrmApiFetchJsonInput('GET')).then((x) => x ?? { data: [], info: { more_records: false } }));
|
|
1636
2629
|
}
|
|
2630
|
+
/**
|
|
2631
|
+
* Creates a {@link ZohoCrmSearchRecordsPageFactory} bound to the given context.
|
|
2632
|
+
*
|
|
2633
|
+
* Returns a page factory that automatically handles Zoho CRM's pagination,
|
|
2634
|
+
* making it easy to iterate through all search results across multiple pages.
|
|
2635
|
+
*
|
|
2636
|
+
* @param context - Authenticated Zoho CRM context providing fetch and rate limiting
|
|
2637
|
+
* @returns Page factory for iterating over search results
|
|
2638
|
+
*
|
|
2639
|
+
* @example
|
|
2640
|
+
* ```typescript
|
|
2641
|
+
* const pageFactory = zohoCrmSearchRecordsPageFactory(context);
|
|
2642
|
+
*
|
|
2643
|
+
* const fetchPage = pageFactory({
|
|
2644
|
+
* module: 'Contacts',
|
|
2645
|
+
* criteria: [{ field: 'Last_Name', filter: 'starts_with', value: 'Smith' }],
|
|
2646
|
+
* per_page: 5
|
|
2647
|
+
* });
|
|
2648
|
+
*
|
|
2649
|
+
* const firstPage = await fetchPage.fetchNext();
|
|
2650
|
+
* const secondPage = await firstPage.fetchNext();
|
|
2651
|
+
* ```
|
|
2652
|
+
*/
|
|
1637
2653
|
function zohoCrmSearchRecordsPageFactory(context) {
|
|
1638
2654
|
return zohoFetchPageFactory(zohoCrmSearchRecords(context));
|
|
1639
2655
|
}
|
|
1640
2656
|
/**
|
|
1641
|
-
* Creates a ZohoCrmGetRelatedRecordsFunctionFactory
|
|
2657
|
+
* Creates a {@link ZohoCrmGetRelatedRecordsFunctionFactory} bound to the given context.
|
|
2658
|
+
*
|
|
2659
|
+
* Returns a factory that produces typed functions for fetching related records
|
|
2660
|
+
* (e.g. Notes, Emails, Attachments) of a specific target module. The factory
|
|
2661
|
+
* accepts a {@link ZohoCrmGetRelatedRecordsFunctionConfig} to specify the
|
|
2662
|
+
* target module and empty-result behavior. By default, returns an empty page
|
|
2663
|
+
* result instead of null when no records are found.
|
|
2664
|
+
*
|
|
2665
|
+
* @param context - Authenticated Zoho CRM context providing fetch and rate limiting
|
|
2666
|
+
* @returns Factory that creates typed related-records retrieval functions
|
|
2667
|
+
*
|
|
2668
|
+
* @example
|
|
2669
|
+
* ```typescript
|
|
2670
|
+
* const factory = zohoCrmGetRelatedRecordsFunctionFactory(context);
|
|
1642
2671
|
*
|
|
1643
|
-
*
|
|
2672
|
+
* // Create a typed function for fetching related Notes:
|
|
2673
|
+
* const getNotesForRecord = factory<ZohoCrmRecordNote>({
|
|
2674
|
+
* targetModule: ZOHO_CRM_NOTES_MODULE
|
|
2675
|
+
* });
|
|
1644
2676
|
*
|
|
1645
|
-
*
|
|
1646
|
-
*
|
|
2677
|
+
* const notes = await getNotesForRecord({
|
|
2678
|
+
* module: 'Contacts',
|
|
2679
|
+
* id: contactId
|
|
2680
|
+
* });
|
|
2681
|
+
* ```
|
|
2682
|
+
*
|
|
2683
|
+
* @see https://www.zoho.com/crm/developer-guide/apiv2/get-related-records.html
|
|
1647
2684
|
*/
|
|
1648
2685
|
function zohoCrmGetRelatedRecordsFunctionFactory(context) {
|
|
1649
2686
|
return (config) => {
|
|
@@ -1651,6 +2688,29 @@ function zohoCrmGetRelatedRecordsFunctionFactory(context) {
|
|
|
1651
2688
|
return (input) => context.fetchJson(`/v8/${input.module}/${input.id}/${targetModule}?${zohoCrmUrlSearchParamsMinusIdAndModule(input, input.filter).toString()}`, zohoCrmApiFetchJsonInput('GET')).then((x) => x ?? (returnEmptyRecordsInsteadOfNull !== false ? emptyZohoPageResult() : x));
|
|
1652
2689
|
};
|
|
1653
2690
|
}
|
|
2691
|
+
/**
|
|
2692
|
+
* Creates a {@link ZohoCrmGetEmailsForRecordFunction} bound to the given context.
|
|
2693
|
+
*
|
|
2694
|
+
* Retrieves email metadata related to a specific record by targeting the
|
|
2695
|
+
* Emails module via the related records API. Normalizes the Zoho API response
|
|
2696
|
+
* which returns email data under an `Emails` key instead of the standard `data` key.
|
|
2697
|
+
* Returns a paginated result of {@link ZohoCrmRecordEmailMetadata} entries.
|
|
2698
|
+
*
|
|
2699
|
+
* @param context - Authenticated Zoho CRM context providing fetch and rate limiting
|
|
2700
|
+
* @returns Function that retrieves emails for a record
|
|
2701
|
+
*
|
|
2702
|
+
* @example
|
|
2703
|
+
* ```typescript
|
|
2704
|
+
* const getEmailsForRecord = zohoCrmGetEmailsForRecord(context);
|
|
2705
|
+
*
|
|
2706
|
+
* const result = await getEmailsForRecord({
|
|
2707
|
+
* id: contactId,
|
|
2708
|
+
* module: 'Contacts'
|
|
2709
|
+
* });
|
|
2710
|
+
* ```
|
|
2711
|
+
*
|
|
2712
|
+
* @see https://www.zoho.com/crm/developer/docs/api/v8/get-email-rel-list.html
|
|
2713
|
+
*/
|
|
1654
2714
|
function zohoCrmGetEmailsForRecord(context) {
|
|
1655
2715
|
const getEmailsFactory = zohoCrmGetRelatedRecordsFunctionFactory(context)({ targetModule: ZOHO_CRM_EMAILS_MODULE });
|
|
1656
2716
|
return (input) => getEmailsFactory(input).then((x) => {
|
|
@@ -1658,12 +2718,96 @@ function zohoCrmGetEmailsForRecord(context) {
|
|
|
1658
2718
|
return { ...x, data };
|
|
1659
2719
|
});
|
|
1660
2720
|
}
|
|
2721
|
+
/**
|
|
2722
|
+
* Creates a {@link ZohoCrmGetEmailsForRecordPageFactory} bound to the given context.
|
|
2723
|
+
*
|
|
2724
|
+
* Returns a page factory for iterating over emails related to a record across
|
|
2725
|
+
* multiple pages. Wraps {@link zohoCrmGetEmailsForRecord} with automatic
|
|
2726
|
+
* pagination handling via {@link zohoFetchPageFactory}.
|
|
2727
|
+
*
|
|
2728
|
+
* @param context - Authenticated Zoho CRM context providing fetch and rate limiting
|
|
2729
|
+
* @returns Page factory for iterating over record emails
|
|
2730
|
+
*
|
|
2731
|
+
* @example
|
|
2732
|
+
* ```typescript
|
|
2733
|
+
* const pageFactory = zohoCrmGetEmailsForRecordPageFactory(context);
|
|
2734
|
+
*
|
|
2735
|
+
* const fetchPage = pageFactory({
|
|
2736
|
+
* id: contactId,
|
|
2737
|
+
* module: 'Contacts',
|
|
2738
|
+
* per_page: 5
|
|
2739
|
+
* });
|
|
2740
|
+
*
|
|
2741
|
+
* const firstPage = await fetchPage.fetchNext();
|
|
2742
|
+
*
|
|
2743
|
+
* if (firstPage.result.info.more_records) {
|
|
2744
|
+
* const secondPage = await firstPage.fetchNext();
|
|
2745
|
+
* }
|
|
2746
|
+
* ```
|
|
2747
|
+
*
|
|
2748
|
+
* @see https://www.zoho.com/crm/developer/docs/api/v8/get-email-rel-list.html
|
|
2749
|
+
*/
|
|
1661
2750
|
function zohoCrmGetEmailsForRecordPageFactory(context) {
|
|
1662
2751
|
return zohoFetchPageFactory(zohoCrmGetEmailsForRecord(context));
|
|
1663
2752
|
}
|
|
2753
|
+
/**
|
|
2754
|
+
* Creates a {@link ZohoCrmGetAttachmentsForRecordFunction} bound to the given context.
|
|
2755
|
+
*
|
|
2756
|
+
* Retrieves attachment metadata related to a specific record by targeting the
|
|
2757
|
+
* Attachments module via the related records API. Returns a paginated result of
|
|
2758
|
+
* {@link ZohoCrmRecordAttachmentMetadata} entries including file names, sizes,
|
|
2759
|
+
* and category information. When no attachments exist for the record, the result
|
|
2760
|
+
* contains an empty data array rather than null.
|
|
2761
|
+
*
|
|
2762
|
+
* @param context - Authenticated Zoho CRM context providing fetch and rate limiting
|
|
2763
|
+
* @returns Function that retrieves attachments for a record
|
|
2764
|
+
*
|
|
2765
|
+
* @example
|
|
2766
|
+
* ```typescript
|
|
2767
|
+
* const getAttachmentsForRecord = zohoCrmGetAttachmentsForRecord(context);
|
|
2768
|
+
*
|
|
2769
|
+
* const result = await getAttachmentsForRecord({
|
|
2770
|
+
* id: contactId,
|
|
2771
|
+
* module: 'Contacts',
|
|
2772
|
+
* fields: 'File_Name,Size,Created_Time'
|
|
2773
|
+
* });
|
|
2774
|
+
* ```
|
|
2775
|
+
*
|
|
2776
|
+
* @see https://www.zoho.com/crm/developer-guide/apiv2/get-related-records.html
|
|
2777
|
+
*/
|
|
1664
2778
|
function zohoCrmGetAttachmentsForRecord(context) {
|
|
1665
2779
|
return zohoCrmGetRelatedRecordsFunctionFactory(context)({ targetModule: ZOHO_CRM_ATTACHMENTS_MODULE });
|
|
1666
2780
|
}
|
|
2781
|
+
/**
|
|
2782
|
+
* Creates a {@link ZohoCrmGetAttachmentsForRecordPageFactory} bound to the given context.
|
|
2783
|
+
*
|
|
2784
|
+
* Returns a page factory for iterating over attachments related to a record
|
|
2785
|
+
* across multiple pages. Wraps {@link zohoCrmGetAttachmentsForRecord} with
|
|
2786
|
+
* automatic pagination handling via {@link zohoFetchPageFactory}.
|
|
2787
|
+
*
|
|
2788
|
+
* @param context - Authenticated Zoho CRM context providing fetch and rate limiting
|
|
2789
|
+
* @returns Page factory for iterating over record attachments
|
|
2790
|
+
*
|
|
2791
|
+
* @example
|
|
2792
|
+
* ```typescript
|
|
2793
|
+
* const pageFactory = zohoCrmGetAttachmentsForRecordPageFactory(context);
|
|
2794
|
+
*
|
|
2795
|
+
* const fetchPage = pageFactory({
|
|
2796
|
+
* id: contactId,
|
|
2797
|
+
* module: 'Contacts',
|
|
2798
|
+
* fields: 'File_Name,Size',
|
|
2799
|
+
* per_page: 10
|
|
2800
|
+
* });
|
|
2801
|
+
*
|
|
2802
|
+
* const firstPage = await fetchPage.fetchNext();
|
|
2803
|
+
*
|
|
2804
|
+
* if (firstPage.result.info.more_records) {
|
|
2805
|
+
* const secondPage = await firstPage.fetchNext();
|
|
2806
|
+
* }
|
|
2807
|
+
* ```
|
|
2808
|
+
*
|
|
2809
|
+
* @see https://www.zoho.com/crm/developer-guide/apiv2/get-related-records.html
|
|
2810
|
+
*/
|
|
1667
2811
|
function zohoCrmGetAttachmentsForRecordPageFactory(context) {
|
|
1668
2812
|
return zohoFetchPageFactory(zohoCrmGetAttachmentsForRecord(context));
|
|
1669
2813
|
}
|
|
@@ -1674,88 +2818,101 @@ function zohoCrmGetAttachmentsForRecordPageFactory(context) {
|
|
|
1674
2818
|
*/
|
|
1675
2819
|
const ZOHO_CRM_ATTACHMENT_MAX_SIZE = 20 * 1024 * 1024;
|
|
1676
2820
|
/**
|
|
1677
|
-
*
|
|
2821
|
+
* Creates a {@link ZohoCrmUploadAttachmentForRecordFunction} bound to the given context.
|
|
1678
2822
|
*
|
|
1679
|
-
*
|
|
2823
|
+
* Uploads a file attachment to a specific record. The file is sent as
|
|
2824
|
+
* multipart/form-data. An attachment category must be specified by ID or name.
|
|
2825
|
+
* Maximum file size is {@link ZOHO_CRM_ATTACHMENT_MAX_SIZE} (20MB).
|
|
1680
2826
|
*
|
|
1681
|
-
* @param context
|
|
1682
|
-
* @returns
|
|
2827
|
+
* @param context - Authenticated Zoho CRM context providing fetch and rate limiting
|
|
2828
|
+
* @returns Function that uploads an attachment to a record
|
|
2829
|
+
* @throws {Error} If neither `attachmentCategoryId` nor `attachmentCategoryName` is provided
|
|
2830
|
+
*
|
|
2831
|
+
* @example
|
|
2832
|
+
* ```typescript
|
|
2833
|
+
* const uploadAttachment = zohoCrmUploadAttachmentForRecord(context);
|
|
2834
|
+
*
|
|
2835
|
+
* await uploadAttachment({
|
|
2836
|
+
* module: 'Contacts',
|
|
2837
|
+
* id: contactId,
|
|
2838
|
+
* file: new File(['content'], 'resume.pdf', { type: 'application/pdf' }),
|
|
2839
|
+
* attachmentCategoryName: 'Resume'
|
|
2840
|
+
* });
|
|
2841
|
+
* ```
|
|
2842
|
+
*
|
|
2843
|
+
* @see https://www.zoho.com/crm/developer/docs/api/v2.1/upload-attachment.html
|
|
1683
2844
|
*/
|
|
1684
2845
|
function zohoCrmUploadAttachmentForRecord(context) {
|
|
1685
2846
|
return (input) => {
|
|
1686
|
-
const { attachmentCategoryId, attachmentCategoryName,
|
|
2847
|
+
const { attachmentCategoryId, attachmentCategoryName, file } = input;
|
|
1687
2848
|
const urlParams = {
|
|
1688
2849
|
attachments_category_id: util.joinStringsWithCommas(attachmentCategoryId),
|
|
1689
|
-
attachments_category: util.joinStringsWithCommas(attachmentCategoryName)
|
|
1690
|
-
attachment_url: input.attachmentUrl
|
|
2850
|
+
attachments_category: util.joinStringsWithCommas(attachmentCategoryName)
|
|
1691
2851
|
};
|
|
1692
2852
|
if (!urlParams.attachments_category_id?.length && !urlParams.attachments_category?.length) {
|
|
1693
2853
|
throw new Error('attachmentCategoryId or attachmentCategoryName must be provided and not empty.');
|
|
1694
2854
|
}
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
if (urlParams.attachment_url) {
|
|
1701
|
-
response = context.fetch(url, { method: 'POST' });
|
|
1702
|
-
}
|
|
1703
|
-
else if (formData != null) {
|
|
1704
|
-
throw new Error('unsupported currently. Use the attachmentUrl parameter instead.');
|
|
1705
|
-
// There is something weird going on with sending requests this way and zoho's server is rejecting it.
|
|
1706
|
-
/*
|
|
1707
|
-
response = context.fetch(url, {
|
|
1708
|
-
method: 'POST',
|
|
1709
|
-
headers: {
|
|
1710
|
-
'Content-Type': 'multipart/form-data',
|
|
1711
|
-
'content-length': '210'
|
|
1712
|
-
},
|
|
1713
|
-
body: formData
|
|
1714
|
-
});
|
|
1715
|
-
*/
|
|
1716
|
-
/*
|
|
1717
|
-
const fullUrl = (context.config.apiUrl as string) + url;
|
|
1718
|
-
const accessToken = await context.accessTokenStringFactory();
|
|
1719
|
-
|
|
1720
|
-
response = fetch(fullUrl, {
|
|
1721
|
-
headers: {
|
|
1722
|
-
Authorization: `Bearer ${accessToken}`
|
|
1723
|
-
},
|
|
1724
|
-
body: formData,
|
|
1725
|
-
method: 'POST'
|
|
1726
|
-
});
|
|
1727
|
-
|
|
1728
|
-
console.log({ response });
|
|
1729
|
-
*/
|
|
1730
|
-
}
|
|
1731
|
-
else {
|
|
1732
|
-
throw new Error('body or attachmentUrl must be provided.');
|
|
1733
|
-
}
|
|
1734
|
-
return response;
|
|
2855
|
+
const url = `/v8/${input.module}/${input.id}/${ZOHO_CRM_ATTACHMENTS_MODULE}?${fetch.makeUrlSearchParams(urlParams).toString()}`;
|
|
2856
|
+
const body = new FormData();
|
|
2857
|
+
body.append('file', file);
|
|
2858
|
+
// Clear the base Content-Type header (empty string removes it via mergeRequestHeaders) so fetch auto-detects multipart/form-data with the correct boundary from the FormData body.
|
|
2859
|
+
return context.fetch(url, { method: 'POST', headers: { 'Content-Type': '' }, body });
|
|
1735
2860
|
};
|
|
1736
2861
|
}
|
|
1737
2862
|
/**
|
|
1738
|
-
*
|
|
2863
|
+
* Creates a {@link ZohoCrmDownloadAttachmentForRecordFunction} bound to the given context.
|
|
1739
2864
|
*
|
|
1740
|
-
*
|
|
2865
|
+
* Downloads a specific attachment from a record. Returns a parsed
|
|
2866
|
+
* {@link FetchFileResponse} containing the file data and metadata extracted
|
|
2867
|
+
* from the response headers.
|
|
1741
2868
|
*
|
|
1742
|
-
* @param context
|
|
1743
|
-
* @returns
|
|
2869
|
+
* @param context - Authenticated Zoho CRM context providing fetch and rate limiting
|
|
2870
|
+
* @returns Function that downloads an attachment by record and attachment ID
|
|
2871
|
+
*
|
|
2872
|
+
* @example
|
|
2873
|
+
* ```typescript
|
|
2874
|
+
* const downloadAttachment = zohoCrmDownloadAttachmentForRecord(context);
|
|
2875
|
+
*
|
|
2876
|
+
* const fileResponse = await downloadAttachment({
|
|
2877
|
+
* module: 'Contacts',
|
|
2878
|
+
* id: contactId,
|
|
2879
|
+
* attachment_id: attachmentId
|
|
2880
|
+
* });
|
|
2881
|
+
* ```
|
|
2882
|
+
*
|
|
2883
|
+
* @see https://www.zoho.com/crm/developer-guide/apiv2/download-attachments.html
|
|
1744
2884
|
*/
|
|
1745
2885
|
function zohoCrmDownloadAttachmentForRecord(context) {
|
|
1746
2886
|
return (input) => context.fetch(`/v8/${input.module}/${input.id}/${ZOHO_CRM_ATTACHMENTS_MODULE}/${input.attachment_id}`, { method: 'GET' }).then(fetch.parseFetchFileResponse);
|
|
1747
2887
|
}
|
|
1748
2888
|
/**
|
|
1749
|
-
*
|
|
2889
|
+
* Creates a {@link ZohoCrmDeleteAttachmentFromRecordFunction} bound to the given context.
|
|
1750
2890
|
*
|
|
1751
|
-
*
|
|
2891
|
+
* Deletes a specific attachment from a record by its attachment ID.
|
|
2892
|
+
* Returns the raw {@link Response}.
|
|
1752
2893
|
*
|
|
1753
|
-
* @param context
|
|
1754
|
-
* @returns
|
|
2894
|
+
* @param context - Authenticated Zoho CRM context providing fetch and rate limiting
|
|
2895
|
+
* @returns Function that deletes an attachment by record and attachment ID
|
|
2896
|
+
*
|
|
2897
|
+
* @example
|
|
2898
|
+
* ```typescript
|
|
2899
|
+
* const deleteAttachment = zohoCrmDeleteAttachmentFromRecord(context);
|
|
2900
|
+
*
|
|
2901
|
+
* const response = await deleteAttachment({
|
|
2902
|
+
* module: 'Contacts',
|
|
2903
|
+
* id: contactId,
|
|
2904
|
+
* attachment_id: attachmentId
|
|
2905
|
+
* });
|
|
2906
|
+
* ```
|
|
2907
|
+
*
|
|
2908
|
+
* @see https://www.zoho.com/crm/developer-guide/apiv2/delete-attachments.html
|
|
1755
2909
|
*/
|
|
1756
2910
|
function zohoCrmDeleteAttachmentFromRecord(context) {
|
|
1757
2911
|
return (input) => context.fetch(`/v8/${input.module}/${input.id}/${ZOHO_CRM_ATTACHMENTS_MODULE}/${input.attachment_id}`, { method: 'DELETE' });
|
|
1758
2912
|
}
|
|
2913
|
+
/**
|
|
2914
|
+
* Thrown when a Zoho CRM serverless function returns a non-success response code.
|
|
2915
|
+
*/
|
|
1759
2916
|
class ZohoCrmExecuteRestApiFunctionError extends makeError.BaseError {
|
|
1760
2917
|
error;
|
|
1761
2918
|
constructor(error) {
|
|
@@ -1764,15 +2921,41 @@ class ZohoCrmExecuteRestApiFunctionError extends makeError.BaseError {
|
|
|
1764
2921
|
}
|
|
1765
2922
|
}
|
|
1766
2923
|
/**
|
|
1767
|
-
* Creates a ZohoCrmExecuteRestApiFunctionFunction
|
|
2924
|
+
* Creates a {@link ZohoCrmExecuteRestApiFunctionFunction} bound to the given context.
|
|
2925
|
+
*
|
|
2926
|
+
* Executes Zoho CRM serverless functions via the REST API. Supports both
|
|
2927
|
+
* OAuth-based and API-key-based authentication. When using an API key, a custom
|
|
2928
|
+
* target URL can be specified for cross-environment calls.
|
|
1768
2929
|
*
|
|
1769
2930
|
* OAuth Details:
|
|
1770
2931
|
* - https://www.zoho.com/crm/developer/docs/functions/serverless-fn-oauth.html#OAuth2
|
|
1771
2932
|
* - There is no documentation for ZohoCrm specifically, but it seems to behave the same way
|
|
1772
2933
|
* - You will need the following scopes: ZohoCrm.functions.execute.READ,ZohoCrm.functions.execute.CREATE
|
|
1773
2934
|
*
|
|
1774
|
-
* @param context
|
|
1775
|
-
* @returns
|
|
2935
|
+
* @param context - Authenticated Zoho CRM context providing fetch and rate limiting
|
|
2936
|
+
* @returns Function that executes serverless functions via the REST API
|
|
2937
|
+
* @throws {ZohoCrmExecuteRestApiFunctionError} If the function execution fails
|
|
2938
|
+
*
|
|
2939
|
+
* @example
|
|
2940
|
+
* ```typescript
|
|
2941
|
+
* const executeFunction = zohoCrmExecuteRestApiFunction(context);
|
|
2942
|
+
*
|
|
2943
|
+
* // Execute using OAuth credentials:
|
|
2944
|
+
* const result = await executeFunction({ functionName: 'my_function' });
|
|
2945
|
+
*
|
|
2946
|
+
* // Execute with parameters:
|
|
2947
|
+
* const paramResult = await executeFunction({
|
|
2948
|
+
* functionName: 'process_contact',
|
|
2949
|
+
* params: { contact_id: '12345', action: 'approve' }
|
|
2950
|
+
* });
|
|
2951
|
+
*
|
|
2952
|
+
* // Execute using an API key (cross-environment):
|
|
2953
|
+
* const apiResult = await executeFunction({
|
|
2954
|
+
* functionName: 'my_function',
|
|
2955
|
+
* apiKey: 'your-api-key',
|
|
2956
|
+
* apiUrl: 'production'
|
|
2957
|
+
* });
|
|
2958
|
+
* ```
|
|
1776
2959
|
*/
|
|
1777
2960
|
function zohoCrmExecuteRestApiFunction(context) {
|
|
1778
2961
|
return (input) => {
|
|
@@ -1794,9 +2977,15 @@ function zohoCrmExecuteRestApiFunction(context) {
|
|
|
1794
2977
|
};
|
|
1795
2978
|
}
|
|
1796
2979
|
// MARK: Util
|
|
2980
|
+
/**
|
|
2981
|
+
* Builds URL search params from the input objects, omitting the `module` key since it is used in the URL path rather than as a query parameter.
|
|
2982
|
+
*/
|
|
1797
2983
|
function zohoCrmUrlSearchParamsMinusModule(...input) {
|
|
1798
2984
|
return fetch.makeUrlSearchParams(input, { omitKeys: 'module' });
|
|
1799
2985
|
}
|
|
2986
|
+
/**
|
|
2987
|
+
* Builds URL search params from the input objects, omitting both `id` and `module` keys since they are used in the URL path.
|
|
2988
|
+
*/
|
|
1800
2989
|
function zohoCrmUrlSearchParamsMinusIdAndModule(...input) {
|
|
1801
2990
|
return fetch.makeUrlSearchParams(input, { omitKeys: ['id', 'module'] });
|
|
1802
2991
|
}
|
|
@@ -1804,6 +2993,9 @@ function zohoCrmUrlSearchParamsMinusIdAndModule(...input) {
|
|
|
1804
2993
|
* @deprecated use makeUrlSearchParams instead.
|
|
1805
2994
|
*/
|
|
1806
2995
|
const zohoCrmUrlSearchParams = fetch.makeUrlSearchParams;
|
|
2996
|
+
/**
|
|
2997
|
+
* Constructs the standard FetchJsonInput used by CRM API calls, pairing the HTTP method with an optional body.
|
|
2998
|
+
*/
|
|
1807
2999
|
function zohoCrmApiFetchJsonInput(method, body) {
|
|
1808
3000
|
const result = {
|
|
1809
3001
|
method,
|
|
@@ -1829,6 +3021,28 @@ function zohoCrmCatchZohoCrmChangeObjectLikeResponseError(e) {
|
|
|
1829
3021
|
}
|
|
1830
3022
|
return result;
|
|
1831
3023
|
}
|
|
3024
|
+
/**
|
|
3025
|
+
* Separates a change response's entries into success and error arrays based on their status.
|
|
3026
|
+
*
|
|
3027
|
+
* Iterates over the `data` array from a Zoho CRM change operation response and
|
|
3028
|
+
* partitions entries into `successItems` and `errorItems` based on their `status` field.
|
|
3029
|
+
* The original response is spread into the result, so all original fields remain accessible.
|
|
3030
|
+
*
|
|
3031
|
+
* Used internally by {@link zohoCrmDeleteRecord} and similar functions to provide
|
|
3032
|
+
* convenient access to separated success/error results.
|
|
3033
|
+
*
|
|
3034
|
+
* @param response - Raw change operation response containing mixed success/error entries
|
|
3035
|
+
* @returns The response augmented with pre-separated `successItems` and `errorItems` arrays
|
|
3036
|
+
*
|
|
3037
|
+
* @example
|
|
3038
|
+
* ```typescript
|
|
3039
|
+
* const rawResponse = await context.fetchJson<ZohoCrmChangeObjectLikeResponse>(...);
|
|
3040
|
+
* const result = zohoCrmChangeObjectLikeResponseSuccessAndErrorPairs(rawResponse);
|
|
3041
|
+
*
|
|
3042
|
+
* result.successItems; // entries with status === 'success'
|
|
3043
|
+
* result.errorItems; // entries with non-success status
|
|
3044
|
+
* ```
|
|
3045
|
+
*/
|
|
1832
3046
|
function zohoCrmChangeObjectLikeResponseSuccessAndErrorPairs(response) {
|
|
1833
3047
|
const { data } = response;
|
|
1834
3048
|
const successItems = [];
|
|
@@ -1848,6 +3062,20 @@ function zohoCrmChangeObjectLikeResponseSuccessAndErrorPairs(response) {
|
|
|
1848
3062
|
};
|
|
1849
3063
|
return result;
|
|
1850
3064
|
}
|
|
3065
|
+
/**
|
|
3066
|
+
* Pairs each input record with its corresponding API result and separates them into success and error arrays by status.
|
|
3067
|
+
*
|
|
3068
|
+
* Iterates over the `input` and `results` arrays in parallel, matching each input record
|
|
3069
|
+
* to its positional result. Entries are classified as success or error based on
|
|
3070
|
+
* the result's `status` field matching {@link ZOHO_SUCCESS_STATUS}.
|
|
3071
|
+
*
|
|
3072
|
+
* Used internally by {@link updateRecordLikeFunction} to pair input data with API outcomes
|
|
3073
|
+
* for insert, update, and upsert operations.
|
|
3074
|
+
*
|
|
3075
|
+
* @param input - Array of input records that were submitted to the API
|
|
3076
|
+
* @param results - Array of per-record results returned by the API, positionally aligned with `input`
|
|
3077
|
+
* @returns Object with `successItems` and `errorItems`, each containing paired `{ input, result }` entries
|
|
3078
|
+
*/
|
|
1851
3079
|
function zohoCrmMultiRecordResult(input, results) {
|
|
1852
3080
|
const successItems = [];
|
|
1853
3081
|
const errorItems = [];
|
|
@@ -1873,27 +3101,40 @@ function zohoCrmMultiRecordResult(input, results) {
|
|
|
1873
3101
|
return result;
|
|
1874
3102
|
}
|
|
1875
3103
|
|
|
3104
|
+
/**
|
|
3105
|
+
* Creates notes directly in the CRM Notes module. Each note must include the parent record reference and module name.
|
|
3106
|
+
*
|
|
3107
|
+
* For creating notes associated with a specific record, prefer {@link zohoCrmCreateNotesForRecord} which handles parent binding automatically.
|
|
3108
|
+
*/
|
|
1876
3109
|
function zohoCrmCreateNotes(context) {
|
|
1877
3110
|
return (input) => context.fetchJson(`/v2/${ZOHO_CRM_NOTES_MODULE}`, zohoCrmApiFetchJsonInput('POST', { data: input.data })).then((x) => {
|
|
1878
3111
|
return zohoCrmMultiRecordResult(util.asArray(input.data), x.data);
|
|
1879
3112
|
});
|
|
1880
3113
|
}
|
|
3114
|
+
/**
|
|
3115
|
+
* Deletes one or more notes from the CRM Notes module by their IDs.
|
|
3116
|
+
*/
|
|
1881
3117
|
function zohoCrmDeleteNotes(context) {
|
|
1882
3118
|
return (input) => context.fetchJson(`/v2/${ZOHO_CRM_NOTES_MODULE}?${fetch.makeUrlSearchParams({ ids: input.ids })}`, zohoCrmApiFetchJsonInput('DELETE')).then((x) => {
|
|
1883
3119
|
return zohoCrmMultiRecordResult(util.asArray(input.ids), x.data);
|
|
1884
3120
|
});
|
|
1885
3121
|
}
|
|
3122
|
+
/**
|
|
3123
|
+
* Retrieves paginated notes associated with a specific CRM record using the related records API.
|
|
3124
|
+
*/
|
|
1886
3125
|
function zohoCrmGetNotesForRecord(context) {
|
|
1887
3126
|
return zohoCrmGetRelatedRecordsFunctionFactory(context)({ targetModule: ZOHO_CRM_NOTES_MODULE });
|
|
1888
3127
|
}
|
|
3128
|
+
/**
|
|
3129
|
+
* Creates a page factory for iterating through all notes for a record across multiple pages.
|
|
3130
|
+
*/
|
|
1889
3131
|
function zohoCrmGetNotesForRecordPageFactory(context) {
|
|
1890
3132
|
return zohoFetchPageFactory(zohoCrmGetNotesForRecord(context));
|
|
1891
3133
|
}
|
|
1892
3134
|
/**
|
|
1893
|
-
*
|
|
3135
|
+
* Creates notes for a specific record, automatically binding the parent module and record ID to each note entry.
|
|
1894
3136
|
*
|
|
1895
|
-
*
|
|
1896
|
-
* @returns
|
|
3137
|
+
* https://www.zoho.com/crm/developer/docs/api/v8/create-notes.html
|
|
1897
3138
|
*/
|
|
1898
3139
|
function zohoCrmCreateNotesForRecord(context) {
|
|
1899
3140
|
const createNotesInstance = zohoCrmCreateNotes(context);
|
|
@@ -1910,6 +3151,9 @@ function zohoCrmCreateNotesForRecord(context) {
|
|
|
1910
3151
|
};
|
|
1911
3152
|
}
|
|
1912
3153
|
|
|
3154
|
+
/**
|
|
3155
|
+
* Creates one or more tags for a CRM module. Duplicate tag errors are separated from other errors in the result to simplify idempotent workflows.
|
|
3156
|
+
*/
|
|
1913
3157
|
function zohoCrmCreateTagsForModule(context) {
|
|
1914
3158
|
return (input) => context
|
|
1915
3159
|
.fetchJson(`/v8/settings/tags?${fetch.makeUrlSearchParams({ module: input.module })}`, zohoCrmApiFetchJsonInput('POST', { tags: util.asArray(input.tags) }))
|
|
@@ -1971,12 +3215,15 @@ function zohoCrmGetTagsForModule(context) {
|
|
|
1971
3215
|
};
|
|
1972
3216
|
});
|
|
1973
3217
|
}
|
|
3218
|
+
/**
|
|
3219
|
+
* Creates a page factory for iterating through all tags in a module across multiple pages.
|
|
3220
|
+
*/
|
|
1974
3221
|
function zohoCrmGetTagsForModulePageFactory(context) {
|
|
1975
3222
|
return zohoFetchPageFactory(zohoCrmGetTagsForModule(context));
|
|
1976
3223
|
}
|
|
1977
3224
|
// MARK: Add Tag To Record
|
|
1978
3225
|
/**
|
|
1979
|
-
*
|
|
3226
|
+
* Maximum number of record IDs allowed per add-tags request, enforced by Zoho CRM.
|
|
1980
3227
|
*/
|
|
1981
3228
|
const ZOHO_CRM_ADD_TAGS_TO_RECORDS_MAX_IDS_ALLOWED = 100;
|
|
1982
3229
|
/**
|
|
@@ -1995,6 +3242,9 @@ function zohoCrmAddTagsToRecords(context) {
|
|
|
1995
3242
|
});
|
|
1996
3243
|
};
|
|
1997
3244
|
}
|
|
3245
|
+
/**
|
|
3246
|
+
* Builds the request body for the add/remove tags endpoints, merging `tag_names` into `tags` and enforcing the max ID limit.
|
|
3247
|
+
*/
|
|
1998
3248
|
function zohoCrmAddTagsToRecordsRequestBody(input) {
|
|
1999
3249
|
if (Array.isArray(input.ids) && input.ids.length > ZOHO_CRM_ADD_TAGS_TO_RECORDS_MAX_IDS_ALLOWED) {
|
|
2000
3250
|
throw new Error(`Cannot add/remove tags from more than ${ZOHO_CRM_ADD_TAGS_TO_RECORDS_MAX_IDS_ALLOWED} records at once.`);
|
|
@@ -2016,7 +3266,7 @@ function zohoCrmAddTagsToRecordsRequestBody(input) {
|
|
|
2016
3266
|
}
|
|
2017
3267
|
// MARK: Remove Tag From Record
|
|
2018
3268
|
/**
|
|
2019
|
-
*
|
|
3269
|
+
* Maximum number of record IDs allowed per remove-tags request, enforced by Zoho CRM.
|
|
2020
3270
|
*/
|
|
2021
3271
|
const ZOHO_CRM_REMOVE_TAGS_FROM_RECORDS_MAX_IDS_ALLOWED = 100;
|
|
2022
3272
|
/**
|
|
@@ -2036,6 +3286,31 @@ function zohoCrmRemoveTagsFromRecords(context) {
|
|
|
2036
3286
|
};
|
|
2037
3287
|
}
|
|
2038
3288
|
|
|
3289
|
+
/**
|
|
3290
|
+
* Creates a {@link ZohoCrmFactory} from the given configuration.
|
|
3291
|
+
*
|
|
3292
|
+
* The factory pre-initializes shared resources (access token provider, rate limiter)
|
|
3293
|
+
* once, then produces {@link ZohoCrm} client instances for each {@link ZohoCrmConfig}.
|
|
3294
|
+
* Each client handles OAuth token refresh on {@link ZohoInvalidTokenError}, rate limiting,
|
|
3295
|
+
* and Zoho CRM's non-standard error responses (200 status with error body).
|
|
3296
|
+
*
|
|
3297
|
+
* @param factoryConfig - Configuration providing account credentials and optional overrides
|
|
3298
|
+
* @returns A factory function that creates authenticated Zoho CRM clients
|
|
3299
|
+
*
|
|
3300
|
+
* @example
|
|
3301
|
+
* ```typescript
|
|
3302
|
+
* const factory = zohoCrmFactory({
|
|
3303
|
+
* accountsContext: zohoAccountsApi.accountsContext
|
|
3304
|
+
* });
|
|
3305
|
+
*
|
|
3306
|
+
* const zohoCrm = factory({
|
|
3307
|
+
* apiUrl: 'https://www.zohoapis.com/crm'
|
|
3308
|
+
* });
|
|
3309
|
+
*
|
|
3310
|
+
* // Use the CRM context for API calls:
|
|
3311
|
+
* const { crmContext } = zohoCrm;
|
|
3312
|
+
* ```
|
|
3313
|
+
*/
|
|
2039
3314
|
function zohoCrmFactory(factoryConfig) {
|
|
2040
3315
|
const { accountsContext } = factoryConfig;
|
|
2041
3316
|
const accessTokenStringFactory = zohoAccessTokenStringFactory(accountsContext.loadAccessToken);
|
|
@@ -2091,9 +3366,543 @@ function zohoCrmFactory(factoryConfig) {
|
|
|
2091
3366
|
const ZOHO_CRM_TAG_NAME_MAX_LENGTH = 25;
|
|
2092
3367
|
|
|
2093
3368
|
/**
|
|
2094
|
-
*
|
|
2095
|
-
*
|
|
2096
|
-
*
|
|
3369
|
+
* Creates a page factory that wraps a Zoho Sign fetch function with automatic offset-based pagination.
|
|
3370
|
+
*
|
|
3371
|
+
* The factory reads `page_context.has_more_rows` from each response to determine if additional
|
|
3372
|
+
* pages exist, and automatically advances `start_index` by `row_count` for subsequent requests.
|
|
3373
|
+
*
|
|
3374
|
+
* @param fetch - The Zoho Sign fetch function to paginate over
|
|
3375
|
+
* @param defaults - Optional default configuration for the page factory
|
|
3376
|
+
* @returns A page factory that produces iterable page fetchers
|
|
3377
|
+
*
|
|
3378
|
+
* @example
|
|
3379
|
+
* ```typescript
|
|
3380
|
+
* const pageFactory = zohoSignFetchPageFactory(zohoSignGetDocumentsList(context));
|
|
3381
|
+
*
|
|
3382
|
+
* const fetchPage = pageFactory({ row_count: 10 });
|
|
3383
|
+
* const firstPage = await fetchPage.fetchNext();
|
|
3384
|
+
*
|
|
3385
|
+
* if (firstPage.result.page_context.has_more_rows) {
|
|
3386
|
+
* const secondPage = await firstPage.fetchNext();
|
|
3387
|
+
* }
|
|
3388
|
+
* ```
|
|
3389
|
+
*/
|
|
3390
|
+
function zohoSignFetchPageFactory(fetch$1, defaults) {
|
|
3391
|
+
return fetch.fetchPageFactory({
|
|
3392
|
+
...defaults,
|
|
3393
|
+
fetch: fetch$1,
|
|
3394
|
+
readFetchPageResultInfo: function (result) {
|
|
3395
|
+
return {
|
|
3396
|
+
hasNext: result.page_context?.has_more_rows ?? false
|
|
3397
|
+
};
|
|
3398
|
+
},
|
|
3399
|
+
buildInputForNextPage: function (pageResult, input, options) {
|
|
3400
|
+
const previousResult = pageResult.result;
|
|
3401
|
+
const previousPageContext = previousResult?.page_context;
|
|
3402
|
+
const rowCount = options.maxItemsPerPage ?? input.row_count ?? previousPageContext?.row_count ?? 20;
|
|
3403
|
+
const nextStartIndex = (previousPageContext?.start_index ?? input.start_index ?? 1) + rowCount;
|
|
3404
|
+
return { ...input, start_index: nextStartIndex, row_count: rowCount };
|
|
3405
|
+
}
|
|
3406
|
+
});
|
|
3407
|
+
}
|
|
3408
|
+
|
|
3409
|
+
// MARK: Utility
|
|
3410
|
+
/**
|
|
3411
|
+
* Builds a {@link FetchJsonInput} for Zoho Sign API calls.
|
|
3412
|
+
*/
|
|
3413
|
+
function zohoSignApiFetchJsonInput(method, body) {
|
|
3414
|
+
return {
|
|
3415
|
+
method,
|
|
3416
|
+
body: body ?? undefined
|
|
3417
|
+
};
|
|
3418
|
+
}
|
|
3419
|
+
/**
|
|
3420
|
+
* Creates a {@link ZohoSignGetDocumentFunction} bound to the given context.
|
|
3421
|
+
*
|
|
3422
|
+
* Fetches the full details of a single Zoho Sign request (envelope) by its ID,
|
|
3423
|
+
* including actions, documents, and field data.
|
|
3424
|
+
*
|
|
3425
|
+
* @param context - Authenticated Zoho Sign context providing fetch and rate limiting
|
|
3426
|
+
* @returns Function that retrieves a document by request ID
|
|
3427
|
+
*
|
|
3428
|
+
* @example
|
|
3429
|
+
* ```typescript
|
|
3430
|
+
* const getDocument = zohoSignGetDocument(context);
|
|
3431
|
+
* const response = await getDocument({ requestId: '12345' });
|
|
3432
|
+
* ```
|
|
3433
|
+
*
|
|
3434
|
+
* @see https://www.zoho.com/sign/api/document-managment/get-details-of-a-particular-document.html
|
|
3435
|
+
*/
|
|
3436
|
+
function zohoSignGetDocument(context) {
|
|
3437
|
+
return ({ requestId }) => context.fetchJson(`/requests/${requestId}`, zohoSignApiFetchJsonInput('GET'));
|
|
3438
|
+
}
|
|
3439
|
+
/**
|
|
3440
|
+
* Creates a {@link ZohoSignGetDocumentsFunction} bound to the given context.
|
|
3441
|
+
*
|
|
3442
|
+
* Fetches a paginated list of Zoho Sign requests. Supports sorting via
|
|
3443
|
+
* {@link ZohoSignPageFilter} and filtering via {@link ZohoSignSearchColumns}.
|
|
3444
|
+
* Pagination parameters (`start_index`, `row_count`) are serialized as a
|
|
3445
|
+
* JSON `data` query parameter per the Zoho Sign API convention.
|
|
3446
|
+
*
|
|
3447
|
+
* @param context - Authenticated Zoho Sign context providing fetch and rate limiting
|
|
3448
|
+
* @returns Function that retrieves a paginated list of documents
|
|
3449
|
+
*
|
|
3450
|
+
* @example
|
|
3451
|
+
* ```typescript
|
|
3452
|
+
* const getDocuments = zohoSignGetDocuments(context);
|
|
3453
|
+
* const response = await getDocuments({ start_index: 1, row_count: 10 });
|
|
3454
|
+
* ```
|
|
3455
|
+
*
|
|
3456
|
+
* @see https://www.zoho.com/sign/api/document-managment/get-document-list.html
|
|
3457
|
+
*/
|
|
3458
|
+
function zohoSignGetDocuments(context) {
|
|
3459
|
+
return (input) => {
|
|
3460
|
+
const { search_columns, ...pageFilter } = input;
|
|
3461
|
+
const data = {
|
|
3462
|
+
page_context: {
|
|
3463
|
+
...pageFilter,
|
|
3464
|
+
...(search_columns ? { search_columns } : {})
|
|
3465
|
+
}
|
|
3466
|
+
};
|
|
3467
|
+
return context.fetchJson({
|
|
3468
|
+
url: `/requests`,
|
|
3469
|
+
queryParams: { data: JSON.stringify(data) }
|
|
3470
|
+
}, zohoSignApiFetchJsonInput('GET'));
|
|
3471
|
+
};
|
|
3472
|
+
}
|
|
3473
|
+
/**
|
|
3474
|
+
* Creates a {@link zohoSignFetchPageFactory} bound to the given context.
|
|
3475
|
+
*
|
|
3476
|
+
* Returns a page factory that automatically handles Zoho Sign's
|
|
3477
|
+
* `start_index`/`row_count` pagination, making it easy to iterate
|
|
3478
|
+
* through all documents across multiple pages.
|
|
3479
|
+
*
|
|
3480
|
+
* @param context - Authenticated Zoho Sign context providing fetch and rate limiting
|
|
3481
|
+
* @returns Page factory for iterating through documents
|
|
3482
|
+
*
|
|
3483
|
+
* @example
|
|
3484
|
+
* ```typescript
|
|
3485
|
+
* const pageFactory = zohoSignGetDocumentsPageFactory(context);
|
|
3486
|
+
* const fetchPage = pageFactory({ row_count: 5 });
|
|
3487
|
+
*
|
|
3488
|
+
* const firstPage = await fetchPage.fetchNext();
|
|
3489
|
+
* const secondPage = await firstPage.fetchNext();
|
|
3490
|
+
* ```
|
|
3491
|
+
*/
|
|
3492
|
+
function zohoSignGetDocumentsPageFactory(context) {
|
|
3493
|
+
const getDocuments = zohoSignGetDocuments(context);
|
|
3494
|
+
return zohoSignFetchPageFactory(getDocuments);
|
|
3495
|
+
}
|
|
3496
|
+
/**
|
|
3497
|
+
* Creates a {@link ZohoSignGetDocumentFormDataFunction} bound to the given context.
|
|
3498
|
+
*
|
|
3499
|
+
* Retrieves the filled form field values for a completed document. Only
|
|
3500
|
+
* returns data after all recipients have signed. The response includes
|
|
3501
|
+
* field labels and values grouped by recipient action.
|
|
3502
|
+
*
|
|
3503
|
+
* @param context - Authenticated Zoho Sign context providing fetch and rate limiting
|
|
3504
|
+
* @returns Function that retrieves form data for a completed document
|
|
3505
|
+
*
|
|
3506
|
+
* @example
|
|
3507
|
+
* ```typescript
|
|
3508
|
+
* const getFormData = zohoSignGetDocumentFormData(context);
|
|
3509
|
+
* const response = await getFormData({ requestId: '12345' });
|
|
3510
|
+
* ```
|
|
3511
|
+
*
|
|
3512
|
+
* @see https://www.zoho.com/sign/api/document-managment/get-document-form-data.html
|
|
3513
|
+
*/
|
|
3514
|
+
function zohoSignGetDocumentFormData(context) {
|
|
3515
|
+
return ({ requestId }) => context.fetchJson(`/requests/${requestId}/fielddata`, zohoSignApiFetchJsonInput('GET'));
|
|
3516
|
+
}
|
|
3517
|
+
/**
|
|
3518
|
+
* Creates a {@link ZohoSignRetrieveFieldTypesFunction} bound to the given context.
|
|
3519
|
+
*
|
|
3520
|
+
* Retrieves all available field types (e.g. Signature, Textfield, Checkbox)
|
|
3521
|
+
* that can be placed on documents. Useful for dynamically building document
|
|
3522
|
+
* templates or validating field configurations.
|
|
3523
|
+
*
|
|
3524
|
+
* @param context - Authenticated Zoho Sign context providing fetch and rate limiting
|
|
3525
|
+
* @returns Function that retrieves all available field types
|
|
3526
|
+
*
|
|
3527
|
+
* @example
|
|
3528
|
+
* ```typescript
|
|
3529
|
+
* const retrieveFieldTypes = zohoSignRetrieveFieldTypes(context);
|
|
3530
|
+
* const response = await retrieveFieldTypes();
|
|
3531
|
+
* ```
|
|
3532
|
+
*
|
|
3533
|
+
* @see https://www.zoho.com/sign/api/document-managment/retrieve-field-type.html
|
|
3534
|
+
*/
|
|
3535
|
+
function zohoSignRetrieveFieldTypes(context) {
|
|
3536
|
+
return () => context.fetchJson(`/fieldtypes`, zohoSignApiFetchJsonInput('GET'));
|
|
3537
|
+
}
|
|
3538
|
+
/**
|
|
3539
|
+
* Creates a {@link ZohoSignDownloadPdfFunction} bound to the given context.
|
|
3540
|
+
*
|
|
3541
|
+
* Downloads the signed PDF document(s) for a completed request. Returns a
|
|
3542
|
+
* raw {@link Response} containing either a PDF (single document) or a ZIP
|
|
3543
|
+
* archive (multiple documents). Supports optional parameters to include the
|
|
3544
|
+
* completion certificate, merge all documents, or provide a password for
|
|
3545
|
+
* protected files.
|
|
3546
|
+
*
|
|
3547
|
+
* @param context - Authenticated Zoho Sign context providing fetch and rate limiting
|
|
3548
|
+
* @returns Function that downloads signed PDFs by request ID
|
|
3549
|
+
*
|
|
3550
|
+
* @example
|
|
3551
|
+
* ```typescript
|
|
3552
|
+
* const downloadPdf = zohoSignDownloadPdf(context);
|
|
3553
|
+
*
|
|
3554
|
+
* // Download signed PDF with completion certificate merged in:
|
|
3555
|
+
* const response = await downloadPdf({
|
|
3556
|
+
* requestId: '12345',
|
|
3557
|
+
* with_coc: true,
|
|
3558
|
+
* merge: true
|
|
3559
|
+
* });
|
|
3560
|
+
*
|
|
3561
|
+
* const blob = await response.blob();
|
|
3562
|
+
* ```
|
|
3563
|
+
*
|
|
3564
|
+
* @see https://www.zoho.com/sign/api/document-managment/download-pdf.html
|
|
3565
|
+
*/
|
|
3566
|
+
function zohoSignDownloadPdf(context) {
|
|
3567
|
+
return ({ requestId, ...params }) => {
|
|
3568
|
+
const searchParams = fetch.makeUrlSearchParams(params);
|
|
3569
|
+
const queryString = searchParams.toString();
|
|
3570
|
+
const url = `/requests/${requestId}/pdf${queryString ? `?${queryString}` : ''}`;
|
|
3571
|
+
return context.fetch(url, { method: 'GET' });
|
|
3572
|
+
};
|
|
3573
|
+
}
|
|
3574
|
+
/**
|
|
3575
|
+
* Creates a {@link ZohoSignDownloadCompletionCertificateFunction} bound to the given context.
|
|
3576
|
+
*
|
|
3577
|
+
* Downloads the completion certificate PDF for a finished request. The
|
|
3578
|
+
* certificate contains an audit trail of the signing process including
|
|
3579
|
+
* timestamps and recipient details. Returns a raw {@link Response}.
|
|
3580
|
+
*
|
|
3581
|
+
* @param context - Authenticated Zoho Sign context providing fetch and rate limiting
|
|
3582
|
+
* @returns Function that downloads the completion certificate by request ID
|
|
3583
|
+
*
|
|
3584
|
+
* @example
|
|
3585
|
+
* ```typescript
|
|
3586
|
+
* const downloadCert = zohoSignDownloadCompletionCertificate(context);
|
|
3587
|
+
* const response = await downloadCert({ requestId: '12345' });
|
|
3588
|
+
* const blob = await response.blob();
|
|
3589
|
+
* ```
|
|
3590
|
+
*
|
|
3591
|
+
* @see https://www.zoho.com/sign/api/document-managment/download-completion-certificate.html
|
|
3592
|
+
*/
|
|
3593
|
+
function zohoSignDownloadCompletionCertificate(context) {
|
|
3594
|
+
return ({ requestId }) => context.fetch(`/requests/${requestId}/completioncertificate`, { method: 'GET' });
|
|
3595
|
+
}
|
|
3596
|
+
/**
|
|
3597
|
+
* Creates a {@link ZohoSignCreateDocumentFunction} bound to the given context.
|
|
3598
|
+
*
|
|
3599
|
+
* Creates a new document draft in Zoho Sign. The request data and file are
|
|
3600
|
+
* sent as multipart/form-data. The `Content-Type` header is intentionally
|
|
3601
|
+
* cleared so that `fetch` auto-detects the correct multipart boundary from
|
|
3602
|
+
* the {@link FormData} body.
|
|
3603
|
+
*
|
|
3604
|
+
* The created document starts in draft status and must be submitted
|
|
3605
|
+
* separately via {@link zohoSignSendDocumentForSignature}.
|
|
3606
|
+
*
|
|
3607
|
+
* @param context - Authenticated Zoho Sign context providing fetch and rate limiting
|
|
3608
|
+
* @returns Function that creates a new document draft
|
|
3609
|
+
*
|
|
3610
|
+
* @example
|
|
3611
|
+
* ```typescript
|
|
3612
|
+
* const createDocument = zohoSignCreateDocument(context);
|
|
3613
|
+
*
|
|
3614
|
+
* const response = await createDocument({
|
|
3615
|
+
* requestData: {
|
|
3616
|
+
* request_name: 'NDA Agreement',
|
|
3617
|
+
* is_sequential: true,
|
|
3618
|
+
* actions: [
|
|
3619
|
+
* {
|
|
3620
|
+
* action_type: 'SIGN',
|
|
3621
|
+
* recipient_name: 'Jane Doe',
|
|
3622
|
+
* recipient_email: 'jane@example.com'
|
|
3623
|
+
* }
|
|
3624
|
+
* ]
|
|
3625
|
+
* },
|
|
3626
|
+
* file: new File([pdfBuffer], 'nda.pdf', { type: 'application/pdf' })
|
|
3627
|
+
* });
|
|
3628
|
+
*
|
|
3629
|
+
* const draftId = response.requests.request_id;
|
|
3630
|
+
* ```
|
|
3631
|
+
*
|
|
3632
|
+
* @see https://www.zoho.com/sign/api/document-managment/create-document.html
|
|
3633
|
+
*/
|
|
3634
|
+
function zohoSignCreateDocument(context) {
|
|
3635
|
+
return ({ requestData, file }) => {
|
|
3636
|
+
const body = new FormData();
|
|
3637
|
+
body.append('data', JSON.stringify({ requests: requestData }));
|
|
3638
|
+
body.append('file', file);
|
|
3639
|
+
// Clear the base Content-Type header (empty string removes it via mergeRequestHeaders) so fetch auto-detects multipart/form-data with the correct boundary from the FormData body.
|
|
3640
|
+
return context.fetch(`/requests`, { method: 'POST', headers: { 'Content-Type': '' }, body }).then((response) => response.json());
|
|
3641
|
+
};
|
|
3642
|
+
}
|
|
3643
|
+
/**
|
|
3644
|
+
* Creates a {@link ZohoSignUpdateDocumentFunction} bound to the given context.
|
|
3645
|
+
*
|
|
3646
|
+
* Updates an existing document that is still in draft status. Accepts partial
|
|
3647
|
+
* {@link ZohoSignRequestData} to modify fields such as actions, notes, or
|
|
3648
|
+
* expiration settings. Cannot be used on documents that have already been
|
|
3649
|
+
* submitted for signature.
|
|
3650
|
+
*
|
|
3651
|
+
* @param context - Authenticated Zoho Sign context providing fetch and rate limiting
|
|
3652
|
+
* @returns Function that updates a draft document by request ID
|
|
3653
|
+
*
|
|
3654
|
+
* @example
|
|
3655
|
+
* ```typescript
|
|
3656
|
+
* const updateDocument = zohoSignUpdateDocument(context);
|
|
3657
|
+
*
|
|
3658
|
+
* await updateDocument({
|
|
3659
|
+
* requestId: '12345',
|
|
3660
|
+
* data: { notes: 'Updated notes for this document' }
|
|
3661
|
+
* });
|
|
3662
|
+
* ```
|
|
3663
|
+
*
|
|
3664
|
+
* @see https://www.zoho.com/sign/api/document-managment/update-document.html
|
|
3665
|
+
*/
|
|
3666
|
+
function zohoSignUpdateDocument(context) {
|
|
3667
|
+
return ({ requestId, data }) => context.fetchJson(`/requests/${requestId}`, zohoSignApiFetchJsonInput('PUT', { requests: data }));
|
|
3668
|
+
}
|
|
3669
|
+
/**
|
|
3670
|
+
* Creates a {@link ZohoSignSendDocumentForSignatureFunction} bound to the given context.
|
|
3671
|
+
*
|
|
3672
|
+
* Submits a draft document to its recipients for signing. Optionally accepts
|
|
3673
|
+
* partial {@link ZohoSignRequestData} to apply last-minute updates before
|
|
3674
|
+
* submission. Once submitted, the document transitions from draft to
|
|
3675
|
+
* "inprogress" status and recipients receive signing notifications.
|
|
3676
|
+
*
|
|
3677
|
+
* @param context - Authenticated Zoho Sign context providing fetch and rate limiting
|
|
3678
|
+
* @returns Function that sends a document for signature by request ID
|
|
3679
|
+
*
|
|
3680
|
+
* @example
|
|
3681
|
+
* ```typescript
|
|
3682
|
+
* const sendForSignature = zohoSignSendDocumentForSignature(context);
|
|
3683
|
+
*
|
|
3684
|
+
* // Submit a draft for signing:
|
|
3685
|
+
* await sendForSignature({ requestId: '12345' });
|
|
3686
|
+
*
|
|
3687
|
+
* // Submit with last-minute updates:
|
|
3688
|
+
* await sendForSignature({
|
|
3689
|
+
* requestId: '12345',
|
|
3690
|
+
* data: { expiration_days: 30 }
|
|
3691
|
+
* });
|
|
3692
|
+
* ```
|
|
3693
|
+
*
|
|
3694
|
+
* @see https://www.zoho.com/sign/api/document-managment/send-document-for-signature.html
|
|
3695
|
+
*/
|
|
3696
|
+
function zohoSignSendDocumentForSignature(context) {
|
|
3697
|
+
return ({ requestId, data }) => {
|
|
3698
|
+
const body = data ? { requests: data } : undefined;
|
|
3699
|
+
return context.fetchJson(`/requests/${requestId}/submit`, zohoSignApiFetchJsonInput('POST', body));
|
|
3700
|
+
};
|
|
3701
|
+
}
|
|
3702
|
+
/**
|
|
3703
|
+
* Creates a {@link ZohoSignExtendDocumentFunction} bound to the given context.
|
|
3704
|
+
*
|
|
3705
|
+
* Extends the expiration date of an in-progress document. The new date is
|
|
3706
|
+
* provided as a human-readable string (e.g. "30 November 2024"). Useful for
|
|
3707
|
+
* giving recipients additional time to complete signing.
|
|
3708
|
+
*
|
|
3709
|
+
* @param context - Authenticated Zoho Sign context providing fetch and rate limiting
|
|
3710
|
+
* @returns Function that extends a document's expiration by request ID
|
|
3711
|
+
*
|
|
3712
|
+
* @example
|
|
3713
|
+
* ```typescript
|
|
3714
|
+
* const extendDocument = zohoSignExtendDocument(context);
|
|
3715
|
+
* await extendDocument({ requestId: '12345', expire_by: '30 November 2024' });
|
|
3716
|
+
* ```
|
|
3717
|
+
*
|
|
3718
|
+
* @see https://www.zoho.com/sign/api/document-managment/extend-document.html
|
|
3719
|
+
*/
|
|
3720
|
+
function zohoSignExtendDocument(context) {
|
|
3721
|
+
return ({ requestId, expire_by }) => context.fetchJson(`/requests/${requestId}/extend`, zohoSignApiFetchJsonInput('PUT', { expire_by }));
|
|
3722
|
+
}
|
|
3723
|
+
/**
|
|
3724
|
+
* Creates a {@link ZohoSignDeleteDocumentFunction} bound to the given context.
|
|
3725
|
+
*
|
|
3726
|
+
* Deletes a document from Zoho Sign. For documents that are currently
|
|
3727
|
+
* in-progress, set `recall_inprogress` to `true` to recall the document
|
|
3728
|
+
* from recipients before deletion. An optional `reason` can be provided
|
|
3729
|
+
* to explain the recall. Sends the parameters as URL-encoded form data.
|
|
3730
|
+
*
|
|
3731
|
+
* @param context - Authenticated Zoho Sign context providing fetch and rate limiting
|
|
3732
|
+
* @returns Function that deletes a document by request ID
|
|
3733
|
+
*
|
|
3734
|
+
* @example
|
|
3735
|
+
* ```typescript
|
|
3736
|
+
* const deleteDocument = zohoSignDeleteDocument(context);
|
|
3737
|
+
*
|
|
3738
|
+
* // Delete a draft:
|
|
3739
|
+
* await deleteDocument({ requestId: '12345' });
|
|
3740
|
+
*
|
|
3741
|
+
* // Recall an in-progress document before deleting:
|
|
3742
|
+
* await deleteDocument({
|
|
3743
|
+
* requestId: '12345',
|
|
3744
|
+
* recall_inprogress: true,
|
|
3745
|
+
* reason: 'Contract terms changed'
|
|
3746
|
+
* });
|
|
3747
|
+
* ```
|
|
3748
|
+
*
|
|
3749
|
+
* @see https://www.zoho.com/sign/api/document-managment/delete-document.html
|
|
3750
|
+
*/
|
|
3751
|
+
function zohoSignDeleteDocument(context) {
|
|
3752
|
+
return ({ requestId, recall_inprogress, reason }) => {
|
|
3753
|
+
const params = {};
|
|
3754
|
+
if (recall_inprogress != null) {
|
|
3755
|
+
params['recall_inprogress'] = String(recall_inprogress);
|
|
3756
|
+
}
|
|
3757
|
+
if (reason != null) {
|
|
3758
|
+
params['reason'] = reason;
|
|
3759
|
+
}
|
|
3760
|
+
const form = fetch.makeUrlSearchParams(params);
|
|
3761
|
+
const hasForm = form.toString().length > 0;
|
|
3762
|
+
return context.fetchJson(`/requests/${requestId}/delete`, {
|
|
3763
|
+
method: 'PUT',
|
|
3764
|
+
...(hasForm ? { headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: form.toString() } : {})
|
|
3765
|
+
});
|
|
3766
|
+
};
|
|
3767
|
+
}
|
|
3768
|
+
|
|
3769
|
+
const ZOHO_SIGN_SERVICE_NAME = 'sign';
|
|
3770
|
+
function zohoSignConfigApiUrl(input) {
|
|
3771
|
+
switch (input) {
|
|
3772
|
+
case 'sandbox':
|
|
3773
|
+
return 'https://signsandbox.zoho.com/api/v1';
|
|
3774
|
+
case 'production':
|
|
3775
|
+
return 'https://sign.zoho.com/api/v1';
|
|
3776
|
+
default:
|
|
3777
|
+
return input;
|
|
3778
|
+
}
|
|
3779
|
+
}
|
|
3780
|
+
|
|
3781
|
+
const logZohoSignErrorToConsole = logZohoServerErrorFunction('ZohoSign', { logDataArrayErrors: false });
|
|
3782
|
+
async function parseZohoSignError(responseError) {
|
|
3783
|
+
const data = await responseError.response.json().catch(() => undefined);
|
|
3784
|
+
let result;
|
|
3785
|
+
if (data) {
|
|
3786
|
+
result = parseZohoSignServerErrorResponseData(data, responseError);
|
|
3787
|
+
}
|
|
3788
|
+
return result;
|
|
3789
|
+
}
|
|
3790
|
+
function parseZohoSignServerErrorResponseData(errorResponseData, responseError) {
|
|
3791
|
+
let result;
|
|
3792
|
+
const error = tryFindZohoServerErrorData(errorResponseData, responseError);
|
|
3793
|
+
if (error) {
|
|
3794
|
+
const errorData = zohoServerErrorData(error);
|
|
3795
|
+
switch (errorData.code) {
|
|
3796
|
+
// TODO: Add sign-specific error codes here.
|
|
3797
|
+
default:
|
|
3798
|
+
result = parseZohoServerErrorResponseData(errorResponseData, responseError);
|
|
3799
|
+
break;
|
|
3800
|
+
}
|
|
3801
|
+
}
|
|
3802
|
+
return result;
|
|
3803
|
+
}
|
|
3804
|
+
const interceptZohoSign200StatusWithErrorResponse = interceptZohoErrorResponseFactory(parseZohoSignServerErrorResponseData);
|
|
3805
|
+
const handleZohoSignErrorFetch = handleZohoErrorFetchFactory(parseZohoSignError, logZohoSignErrorToConsole);
|
|
3806
|
+
|
|
3807
|
+
/**
|
|
3808
|
+
* Creates a {@link ZohoSignFactory} from the given configuration.
|
|
3809
|
+
*
|
|
3810
|
+
* The factory pre-initializes shared resources (access token provider, rate limiter)
|
|
3811
|
+
* once, then produces {@link ZohoSign} client instances for each {@link ZohoSignConfig}.
|
|
3812
|
+
* Each client handles OAuth token refresh on {@link ZohoInvalidTokenError}, rate limiting,
|
|
3813
|
+
* and Zoho Sign's non-standard error responses (200 status with error body).
|
|
3814
|
+
*
|
|
3815
|
+
* @param factoryConfig - Configuration providing account credentials and optional overrides
|
|
3816
|
+
* @returns A factory function that creates authenticated Zoho Sign clients
|
|
3817
|
+
*
|
|
3818
|
+
* @example
|
|
3819
|
+
* ```typescript
|
|
3820
|
+
* const factory = zohoSignFactory({
|
|
3821
|
+
* accountsContext: zohoAccountsApi.accountsContext
|
|
3822
|
+
* });
|
|
3823
|
+
*
|
|
3824
|
+
* const zohoSign = factory({
|
|
3825
|
+
* apiUrl: 'https://sign.zoho.com'
|
|
3826
|
+
* });
|
|
3827
|
+
*
|
|
3828
|
+
* // Use the sign context for API calls:
|
|
3829
|
+
* const { signContext } = zohoSign;
|
|
3830
|
+
* ```
|
|
3831
|
+
*/
|
|
3832
|
+
function zohoSignFactory(factoryConfig) {
|
|
3833
|
+
const { accountsContext } = factoryConfig;
|
|
3834
|
+
const accessTokenStringFactory = zohoAccessTokenStringFactory(accountsContext.loadAccessToken);
|
|
3835
|
+
const fetchHandler = zohoRateLimitedFetchHandler(factoryConfig.rateLimiterConfig);
|
|
3836
|
+
const { logZohoServerErrorFunction, fetchFactory = (input) => fetch.fetchApiFetchService.makeFetch({
|
|
3837
|
+
baseUrl: input.apiUrl,
|
|
3838
|
+
baseRequest: async () => ({
|
|
3839
|
+
headers: {
|
|
3840
|
+
'Content-Type': 'application/json',
|
|
3841
|
+
Authorization: `Bearer ${await accessTokenStringFactory()}`
|
|
3842
|
+
}
|
|
3843
|
+
}),
|
|
3844
|
+
fetchHandler,
|
|
3845
|
+
timeout: 20 * 1000, // 20 second timeout
|
|
3846
|
+
requireOkResponse: true, // enforce ok response
|
|
3847
|
+
useTimeout: true // use timeout
|
|
3848
|
+
}) } = factoryConfig;
|
|
3849
|
+
return (config) => {
|
|
3850
|
+
if (!config.apiUrl) {
|
|
3851
|
+
throw new Error('ZohoConfig missing api url.');
|
|
3852
|
+
}
|
|
3853
|
+
const apiUrl = zohoSignConfigApiUrl(config.apiUrl);
|
|
3854
|
+
const baseFetch = fetchFactory({ apiUrl });
|
|
3855
|
+
const fetch$1 = handleZohoSignErrorFetch(baseFetch, logZohoServerErrorFunction, (x) => {
|
|
3856
|
+
if (x instanceof ZohoInvalidTokenError) {
|
|
3857
|
+
accountsContext.loadAccessToken.resetAccessToken();
|
|
3858
|
+
}
|
|
3859
|
+
});
|
|
3860
|
+
const fetchJson = fetch.fetchJsonFunction(fetch$1, {
|
|
3861
|
+
interceptJsonResponse: interceptZohoSign200StatusWithErrorResponse, // intercept errors that return status 200
|
|
3862
|
+
handleFetchJsonParseErrorFunction: fetch.returnNullHandleFetchJsonParseErrorFunction
|
|
3863
|
+
});
|
|
3864
|
+
const signContext = {
|
|
3865
|
+
fetch: fetch$1,
|
|
3866
|
+
fetchJson,
|
|
3867
|
+
accessTokenStringFactory,
|
|
3868
|
+
config: {
|
|
3869
|
+
...config,
|
|
3870
|
+
apiUrl
|
|
3871
|
+
},
|
|
3872
|
+
zohoRateLimiter: fetchHandler._rateLimiter
|
|
3873
|
+
};
|
|
3874
|
+
const zohoSign = {
|
|
3875
|
+
signContext
|
|
3876
|
+
};
|
|
3877
|
+
return zohoSign;
|
|
3878
|
+
};
|
|
3879
|
+
}
|
|
3880
|
+
|
|
3881
|
+
/**
|
|
3882
|
+
* Creates a function that exchanges a refresh token for a new short-lived access token
|
|
3883
|
+
* via the Zoho OAuth `/oauth/v2/token` endpoint.
|
|
3884
|
+
*
|
|
3885
|
+
* The returned function accepts optional overrides for client credentials and refresh token.
|
|
3886
|
+
* When omitted, values are read from the {@link ZohoAccountsContext}'s config.
|
|
3887
|
+
*
|
|
3888
|
+
* @param context - Authenticated Zoho Accounts context providing fetch and config
|
|
3889
|
+
* @returns Function that exchanges a refresh token for an access token
|
|
3890
|
+
*
|
|
3891
|
+
* @example
|
|
3892
|
+
* ```typescript
|
|
3893
|
+
* const getAccessToken = zohoAccountsAccessToken(accountsContext);
|
|
3894
|
+
*
|
|
3895
|
+
* // Using config defaults:
|
|
3896
|
+
* const { access_token, expires_in } = await getAccessToken();
|
|
3897
|
+
*
|
|
3898
|
+
* // With overrides:
|
|
3899
|
+
* const response = await getAccessToken({
|
|
3900
|
+
* client: { clientId: 'other-id', clientSecret: 'other-secret' },
|
|
3901
|
+
* refreshToken: 'other-refresh-token'
|
|
3902
|
+
* });
|
|
3903
|
+
* ```
|
|
3904
|
+
*
|
|
3905
|
+
* @see https://www.zoho.com/accounts/protocol/oauth/web-apps/access-token-expiry.html
|
|
2097
3906
|
*/
|
|
2098
3907
|
function zohoAccountsAccessToken(context) {
|
|
2099
3908
|
return (input) => {
|
|
@@ -2102,9 +3911,62 @@ function zohoAccountsAccessToken(context) {
|
|
|
2102
3911
|
const clientId = client?.clientId ?? configClientId;
|
|
2103
3912
|
const clientSecret = client?.clientSecret ?? configClientSecret;
|
|
2104
3913
|
const refreshToken = inputRefreshToken ?? configRefreshToken;
|
|
2105
|
-
|
|
3914
|
+
const params = fetch.makeUrlSearchParams({
|
|
3915
|
+
grant_type: 'refresh_token',
|
|
3916
|
+
client_id: clientId,
|
|
3917
|
+
client_secret: clientSecret,
|
|
3918
|
+
refresh_token: refreshToken
|
|
3919
|
+
});
|
|
3920
|
+
return context.fetchJson(`/oauth/v2/token?${params}`, zohoAccountsApiFetchJsonInput('POST'));
|
|
2106
3921
|
};
|
|
2107
3922
|
}
|
|
3923
|
+
/**
|
|
3924
|
+
* Creates a function that exchanges a single-use OAuth authorization code for
|
|
3925
|
+
* an access token and (optionally) a long-lived refresh token via the Zoho
|
|
3926
|
+
* `/oauth/v2/token` endpoint with `grant_type=authorization_code`.
|
|
3927
|
+
*
|
|
3928
|
+
* This is the second step of the Zoho OAuth 2.0 web server flow:
|
|
3929
|
+
* 1. Redirect the user to Zoho's consent page to obtain an authorization code
|
|
3930
|
+
* 2. Exchange the authorization code for tokens using this function
|
|
3931
|
+
* 3. Use the refresh token with {@link zohoAccountsAccessToken} to obtain new access tokens
|
|
3932
|
+
*
|
|
3933
|
+
* The refresh token is only returned if `access_type=offline` was passed during
|
|
3934
|
+
* the initial authorization request.
|
|
3935
|
+
*
|
|
3936
|
+
* @param context - Zoho Accounts context providing fetch and config (clientId/clientSecret)
|
|
3937
|
+
* @returns Function that exchanges an authorization code for tokens
|
|
3938
|
+
*
|
|
3939
|
+
* @example
|
|
3940
|
+
* ```typescript
|
|
3941
|
+
* const exchangeCode = zohoAccountsRefreshTokenFromAuthorizationCode(accountsContext);
|
|
3942
|
+
*
|
|
3943
|
+
* const { access_token, refresh_token } = await exchangeCode({
|
|
3944
|
+
* code: '1000.abc123.def456',
|
|
3945
|
+
* redirectUri: 'https://myapp.example.com/oauth/callback'
|
|
3946
|
+
* });
|
|
3947
|
+
* ```
|
|
3948
|
+
*
|
|
3949
|
+
* @see https://www.zoho.com/accounts/protocol/oauth/web-apps/access-token.html
|
|
3950
|
+
*/
|
|
3951
|
+
function zohoAccountsRefreshTokenFromAuthorizationCode(context) {
|
|
3952
|
+
return (input) => {
|
|
3953
|
+
const { clientId: configClientId, clientSecret: configClientSecret } = context.config;
|
|
3954
|
+
const { client, code, redirectUri } = input;
|
|
3955
|
+
const clientId = client?.clientId ?? configClientId;
|
|
3956
|
+
const clientSecret = client?.clientSecret ?? configClientSecret;
|
|
3957
|
+
const params = fetch.makeUrlSearchParams({
|
|
3958
|
+
grant_type: 'authorization_code',
|
|
3959
|
+
client_id: clientId,
|
|
3960
|
+
client_secret: clientSecret,
|
|
3961
|
+
code,
|
|
3962
|
+
redirect_uri: redirectUri
|
|
3963
|
+
});
|
|
3964
|
+
return context.fetchJson(`/oauth/v2/token?${params}`, zohoAccountsApiFetchJsonInput('POST'));
|
|
3965
|
+
};
|
|
3966
|
+
}
|
|
3967
|
+
/**
|
|
3968
|
+
* Constructs a standard {@link FetchJsonInput} for Zoho Accounts API calls with the given HTTP method and optional body.
|
|
3969
|
+
*/
|
|
2108
3970
|
function zohoAccountsApiFetchJsonInput(method, body) {
|
|
2109
3971
|
const result = {
|
|
2110
3972
|
method,
|
|
@@ -2126,6 +3988,38 @@ function zohoAccountsConfigApiUrl(input) {
|
|
|
2126
3988
|
}
|
|
2127
3989
|
}
|
|
2128
3990
|
|
|
3991
|
+
/**
|
|
3992
|
+
* Creates a {@link ZohoAccountsFactory} from the given configuration.
|
|
3993
|
+
*
|
|
3994
|
+
* The factory pre-initializes shared resources (rate limiter) once, then produces
|
|
3995
|
+
* {@link ZohoAccounts} client instances for each {@link ZohoAccountsConfig}. Each client
|
|
3996
|
+
* handles OAuth token refresh via the configured `refreshToken`, `clientId`, and `clientSecret`,
|
|
3997
|
+
* with in-memory caching and optional external {@link ZohoAccessTokenCache} support.
|
|
3998
|
+
*
|
|
3999
|
+
* The Accounts client is the foundation for CRM, Recruit, and Sign clients, as it provides
|
|
4000
|
+
* the {@link ZohoAccountsContext} needed for OAuth token management.
|
|
4001
|
+
*
|
|
4002
|
+
* @param factoryConfig - Configuration providing optional fetch and logging overrides
|
|
4003
|
+
* @returns A factory function that creates authenticated Zoho Accounts clients
|
|
4004
|
+
* @throws {Error} If `refreshToken`, `clientId`, or `clientSecret` are missing from the config
|
|
4005
|
+
*
|
|
4006
|
+
* @example
|
|
4007
|
+
* ```typescript
|
|
4008
|
+
* const factory = zohoAccountsFactory({});
|
|
4009
|
+
*
|
|
4010
|
+
* const zohoAccounts = factory({
|
|
4011
|
+
* refreshToken: 'your-refresh-token',
|
|
4012
|
+
* clientId: 'your-client-id',
|
|
4013
|
+
* clientSecret: 'your-client-secret',
|
|
4014
|
+
* apiUrl: 'us'
|
|
4015
|
+
* });
|
|
4016
|
+
*
|
|
4017
|
+
* // Pass the accounts context to CRM/Recruit/Sign factories:
|
|
4018
|
+
* const crmFactory = zohoCrmFactory({
|
|
4019
|
+
* accountsContext: zohoAccounts.accountsContext
|
|
4020
|
+
* });
|
|
4021
|
+
* ```
|
|
4022
|
+
*/
|
|
2129
4023
|
function zohoAccountsFactory(factoryConfig) {
|
|
2130
4024
|
const fetchHandler = zohoRateLimitedFetchHandler();
|
|
2131
4025
|
const { logZohoServerErrorFunction, fetchFactory = (input) => fetch.fetchApiFetchService.makeFetch({
|
|
@@ -2192,10 +4086,20 @@ function zohoAccountsFactory(factoryConfig) {
|
|
|
2192
4086
|
};
|
|
2193
4087
|
}
|
|
2194
4088
|
/**
|
|
2195
|
-
* Creates a
|
|
4089
|
+
* Creates a {@link ZohoAccessTokenFactory} that manages access token lifecycle with
|
|
4090
|
+
* in-memory caching, optional external cache support, and automatic refresh.
|
|
2196
4091
|
*
|
|
2197
|
-
*
|
|
2198
|
-
*
|
|
4092
|
+
* Token resolution order:
|
|
4093
|
+
* 1. Return the in-memory cached token if valid (not expired within the buffer)
|
|
4094
|
+
* 2. Load from the external {@link ZohoAccessTokenCache} if available
|
|
4095
|
+
* 3. Fetch a fresh token via the {@link ZohoAccessTokenRefresher}
|
|
4096
|
+
*
|
|
4097
|
+
* The returned function also exposes a `resetAccessToken()` method to invalidate
|
|
4098
|
+
* the current cached token, typically called on {@link ZohoInvalidTokenError}.
|
|
4099
|
+
*
|
|
4100
|
+
* @param config - Token refresh, caching, and expiration buffer configuration
|
|
4101
|
+
* @returns A token factory function with `resetAccessToken` for cache invalidation
|
|
4102
|
+
* @throws {ZohoAccountsAuthFailureError} If the token refresher fails
|
|
2199
4103
|
*/
|
|
2200
4104
|
function zohoAccountsZohoAccessTokenFactory(config) {
|
|
2201
4105
|
const { tokenRefresher, accessTokenCache, tokenExpirationBuffer: inputTokenExpirationBuffer } = config;
|
|
@@ -2250,10 +4154,17 @@ function safeZohoDateTimeString(date) {
|
|
|
2250
4154
|
return date != null ? zohoDateTimeString(date) : date;
|
|
2251
4155
|
}
|
|
2252
4156
|
/**
|
|
2253
|
-
* Converts
|
|
4157
|
+
* Converts a {@link Date} to a {@link ZohoDateTimeString} by stripping milliseconds
|
|
4158
|
+
* from the ISO 8601 representation.
|
|
2254
4159
|
*
|
|
2255
|
-
* @param date
|
|
2256
|
-
* @returns
|
|
4160
|
+
* @param date - Date to convert
|
|
4161
|
+
* @returns Zoho-formatted date-time string (e.g. `'2019-05-02T11:17:33Z'`)
|
|
4162
|
+
*
|
|
4163
|
+
* @example
|
|
4164
|
+
* ```typescript
|
|
4165
|
+
* zohoDateTimeString(new Date('2019-05-02T11:17:33.000Z'));
|
|
4166
|
+
* // => '2019-05-02T11:17:33Z'
|
|
4167
|
+
* ```
|
|
2257
4168
|
*/
|
|
2258
4169
|
function zohoDateTimeString(date) {
|
|
2259
4170
|
const isoDate = date.toISOString();
|
|
@@ -2308,6 +4219,7 @@ exports.ZOHO_RECRUIT_RECORD_ATTACHMENT_METADATA_ATTACH_TYPE_RESUME = ZOHO_RECRUI
|
|
|
2308
4219
|
exports.ZOHO_RECRUIT_REMOVE_TAGS_FROM_RECORDS_MAX_IDS_ALLOWED = ZOHO_RECRUIT_REMOVE_TAGS_FROM_RECORDS_MAX_IDS_ALLOWED;
|
|
2309
4220
|
exports.ZOHO_RECRUIT_SERVICE_NAME = ZOHO_RECRUIT_SERVICE_NAME;
|
|
2310
4221
|
exports.ZOHO_RECRUIT_TAG_NAME_MAX_LENGTH = ZOHO_RECRUIT_TAG_NAME_MAX_LENGTH;
|
|
4222
|
+
exports.ZOHO_SIGN_SERVICE_NAME = ZOHO_SIGN_SERVICE_NAME;
|
|
2311
4223
|
exports.ZOHO_SUCCESS_CODE = ZOHO_SUCCESS_CODE;
|
|
2312
4224
|
exports.ZOHO_SUCCESS_STATUS = ZOHO_SUCCESS_STATUS;
|
|
2313
4225
|
exports.ZOHO_TOO_MANY_REQUESTS_ERROR_CODE = ZOHO_TOO_MANY_REQUESTS_ERROR_CODE;
|
|
@@ -2365,11 +4277,13 @@ exports.handleZohoAccountsErrorFetch = handleZohoAccountsErrorFetch;
|
|
|
2365
4277
|
exports.handleZohoCrmErrorFetch = handleZohoCrmErrorFetch;
|
|
2366
4278
|
exports.handleZohoErrorFetchFactory = handleZohoErrorFetchFactory;
|
|
2367
4279
|
exports.handleZohoRecruitErrorFetch = handleZohoRecruitErrorFetch;
|
|
4280
|
+
exports.handleZohoSignErrorFetch = handleZohoSignErrorFetch;
|
|
2368
4281
|
exports.insertRecord = insertRecord;
|
|
2369
4282
|
exports.interceptZohoAccounts200StatusWithErrorResponse = interceptZohoAccounts200StatusWithErrorResponse;
|
|
2370
4283
|
exports.interceptZohoCrm200StatusWithErrorResponse = interceptZohoCrm200StatusWithErrorResponse;
|
|
2371
4284
|
exports.interceptZohoErrorResponseFactory = interceptZohoErrorResponseFactory;
|
|
2372
4285
|
exports.interceptZohoRecruit200StatusWithErrorResponse = interceptZohoRecruit200StatusWithErrorResponse;
|
|
4286
|
+
exports.interceptZohoSign200StatusWithErrorResponse = interceptZohoSign200StatusWithErrorResponse;
|
|
2373
4287
|
exports.isZohoCrmValidUrl = isZohoCrmValidUrl;
|
|
2374
4288
|
exports.isZohoRecruitValidUrl = isZohoRecruitValidUrl;
|
|
2375
4289
|
exports.isZohoServerErrorResponseDataArrayRef = isZohoServerErrorResponseDataArrayRef;
|
|
@@ -2377,6 +4291,7 @@ exports.logZohoAccountsErrorToConsole = logZohoAccountsErrorToConsole;
|
|
|
2377
4291
|
exports.logZohoCrmErrorToConsole = logZohoCrmErrorToConsole;
|
|
2378
4292
|
exports.logZohoRecruitErrorToConsole = logZohoRecruitErrorToConsole;
|
|
2379
4293
|
exports.logZohoServerErrorFunction = logZohoServerErrorFunction;
|
|
4294
|
+
exports.logZohoSignErrorToConsole = logZohoSignErrorToConsole;
|
|
2380
4295
|
exports.parseZohoAccountsError = parseZohoAccountsError;
|
|
2381
4296
|
exports.parseZohoAccountsServerErrorResponseData = parseZohoAccountsServerErrorResponseData;
|
|
2382
4297
|
exports.parseZohoCrmError = parseZohoCrmError;
|
|
@@ -2384,6 +4299,8 @@ exports.parseZohoCrmServerErrorResponseData = parseZohoCrmServerErrorResponseDat
|
|
|
2384
4299
|
exports.parseZohoRecruitError = parseZohoRecruitError;
|
|
2385
4300
|
exports.parseZohoRecruitServerErrorResponseData = parseZohoRecruitServerErrorResponseData;
|
|
2386
4301
|
exports.parseZohoServerErrorResponseData = parseZohoServerErrorResponseData;
|
|
4302
|
+
exports.parseZohoSignError = parseZohoSignError;
|
|
4303
|
+
exports.parseZohoSignServerErrorResponseData = parseZohoSignServerErrorResponseData;
|
|
2387
4304
|
exports.removeTagsFromRecords = removeTagsFromRecords;
|
|
2388
4305
|
exports.safeZohoDateTimeString = safeZohoDateTimeString;
|
|
2389
4306
|
exports.searchRecords = searchRecords;
|
|
@@ -2397,6 +4314,7 @@ exports.zohoAccountsAccessToken = zohoAccountsAccessToken;
|
|
|
2397
4314
|
exports.zohoAccountsApiFetchJsonInput = zohoAccountsApiFetchJsonInput;
|
|
2398
4315
|
exports.zohoAccountsConfigApiUrl = zohoAccountsConfigApiUrl;
|
|
2399
4316
|
exports.zohoAccountsFactory = zohoAccountsFactory;
|
|
4317
|
+
exports.zohoAccountsRefreshTokenFromAuthorizationCode = zohoAccountsRefreshTokenFromAuthorizationCode;
|
|
2400
4318
|
exports.zohoAccountsZohoAccessTokenFactory = zohoAccountsZohoAccessTokenFactory;
|
|
2401
4319
|
exports.zohoCrmAddTagsToRecords = zohoCrmAddTagsToRecords;
|
|
2402
4320
|
exports.zohoCrmAddTagsToRecordsRequestBody = zohoCrmAddTagsToRecordsRequestBody;
|
|
@@ -2490,3 +4408,18 @@ exports.zohoRecruitUrlSearchParams = zohoRecruitUrlSearchParams;
|
|
|
2490
4408
|
exports.zohoRecruitUrlSearchParamsMinusIdAndModule = zohoRecruitUrlSearchParamsMinusIdAndModule;
|
|
2491
4409
|
exports.zohoRecruitUrlSearchParamsMinusModule = zohoRecruitUrlSearchParamsMinusModule;
|
|
2492
4410
|
exports.zohoServerErrorData = zohoServerErrorData;
|
|
4411
|
+
exports.zohoSignConfigApiUrl = zohoSignConfigApiUrl;
|
|
4412
|
+
exports.zohoSignCreateDocument = zohoSignCreateDocument;
|
|
4413
|
+
exports.zohoSignDeleteDocument = zohoSignDeleteDocument;
|
|
4414
|
+
exports.zohoSignDownloadCompletionCertificate = zohoSignDownloadCompletionCertificate;
|
|
4415
|
+
exports.zohoSignDownloadPdf = zohoSignDownloadPdf;
|
|
4416
|
+
exports.zohoSignExtendDocument = zohoSignExtendDocument;
|
|
4417
|
+
exports.zohoSignFactory = zohoSignFactory;
|
|
4418
|
+
exports.zohoSignFetchPageFactory = zohoSignFetchPageFactory;
|
|
4419
|
+
exports.zohoSignGetDocument = zohoSignGetDocument;
|
|
4420
|
+
exports.zohoSignGetDocumentFormData = zohoSignGetDocumentFormData;
|
|
4421
|
+
exports.zohoSignGetDocuments = zohoSignGetDocuments;
|
|
4422
|
+
exports.zohoSignGetDocumentsPageFactory = zohoSignGetDocumentsPageFactory;
|
|
4423
|
+
exports.zohoSignRetrieveFieldTypes = zohoSignRetrieveFieldTypes;
|
|
4424
|
+
exports.zohoSignSendDocumentForSignature = zohoSignSendDocumentForSignature;
|
|
4425
|
+
exports.zohoSignUpdateDocument = zohoSignUpdateDocument;
|