@dereekb/zoho 13.0.0 → 13.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.cjs.default.js +1 -0
- package/index.cjs.js +1187 -1353
- package/index.cjs.mjs +2 -0
- package/index.esm.js +1187 -1353
- package/nestjs/index.cjs.default.js +1 -0
- package/nestjs/index.cjs.js +435 -420
- package/nestjs/index.cjs.mjs +2 -0
- package/nestjs/index.esm.js +435 -420
- package/nestjs/package.json +18 -15
- package/package.json +19 -24
package/index.esm.js
CHANGED
|
@@ -1,29 +1,20 @@
|
|
|
1
|
-
import 'core-js/modules/es.iterator.constructor.js';
|
|
2
|
-
import 'core-js/modules/es.iterator.filter.js';
|
|
3
|
-
import 'core-js/modules/es.iterator.for-each.js';
|
|
4
1
|
import { getNextPageNumber, isStandardInternetAccessibleWebsiteUrl, escapeStringCharactersFunction, filterMaybeArrayValues, asArray, MS_IN_MINUTE, joinStringsWithCommas, separateValues, resetPeriodPromiseRateLimiter, MS_IN_SECOND } from '@dereekb/util';
|
|
5
2
|
import { fetchPageFactory, FetchResponseError, makeUrlSearchParams, parseFetchFileResponse, FetchRequestFactoryError, rateLimitedFetchHandler, fetchApiFetchService, fetchJsonFunction, returnNullHandleFetchJsonParseErrorFunction } from '@dereekb/util/fetch';
|
|
6
|
-
import 'core-js/modules/es.iterator.flat-map.js';
|
|
7
|
-
import 'core-js/modules/es.iterator.map.js';
|
|
8
3
|
import { BaseError } from 'make-error';
|
|
9
|
-
import 'core-js/modules/es.set.difference.v2.js';
|
|
10
|
-
import 'core-js/modules/es.set.symmetric-difference.v2.js';
|
|
11
|
-
import 'core-js/modules/es.set.union.v2.js';
|
|
12
4
|
|
|
13
5
|
/**
|
|
14
6
|
* Returns an empty ZohoPageResult that is typed to R and has no more records/results.
|
|
15
7
|
*/
|
|
16
8
|
function emptyZohoPageResult() {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
};
|
|
9
|
+
return {
|
|
10
|
+
data: [],
|
|
11
|
+
info: {
|
|
12
|
+
page: 1,
|
|
13
|
+
per_page: 100, // default value
|
|
14
|
+
count: 0,
|
|
15
|
+
more_records: false
|
|
16
|
+
}
|
|
17
|
+
};
|
|
27
18
|
}
|
|
28
19
|
/**
|
|
29
20
|
* Creates a FetchPageFactory using the input ZohoFetchPageFetchFunction.
|
|
@@ -33,34 +24,30 @@ function emptyZohoPageResult() {
|
|
|
33
24
|
* @returns
|
|
34
25
|
*/
|
|
35
26
|
function zohoFetchPageFactory(fetch, defaults) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
per_page: options.maxItemsPerPage ?? input.per_page
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
});
|
|
27
|
+
return fetchPageFactory({
|
|
28
|
+
...defaults,
|
|
29
|
+
fetch,
|
|
30
|
+
readFetchPageResultInfo: function (result) {
|
|
31
|
+
return {
|
|
32
|
+
hasNext: result.info?.more_records ?? false // if no info is returned, assume something wrong and there are no more records
|
|
33
|
+
};
|
|
34
|
+
},
|
|
35
|
+
buildInputForNextPage: function (pageResult, input, options) {
|
|
36
|
+
return { ...input, page: getNextPageNumber(pageResult), per_page: options.maxItemsPerPage ?? input.per_page };
|
|
37
|
+
}
|
|
38
|
+
});
|
|
52
39
|
}
|
|
53
40
|
|
|
54
41
|
const ZOHO_RECRUIT_SERVICE_NAME = 'recruit';
|
|
55
42
|
function zohoRecruitConfigApiUrl(input) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
43
|
+
switch (input) {
|
|
44
|
+
case 'sandbox':
|
|
45
|
+
return 'https://recruitsandbox.zoho.com/recruit';
|
|
46
|
+
case 'production':
|
|
47
|
+
return 'https://recruit.zoho.com/recruit';
|
|
48
|
+
default:
|
|
49
|
+
return input;
|
|
50
|
+
}
|
|
64
51
|
}
|
|
65
52
|
|
|
66
53
|
/**
|
|
@@ -104,66 +91,67 @@ const MAX_ZOHO_SEARCH_MODULE_RECORDS_CRITERIA = 10;
|
|
|
104
91
|
* If the input tree is empty, returns undefined.
|
|
105
92
|
*/
|
|
106
93
|
function zohoSearchRecordsCriteriaString(input) {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
94
|
+
let result;
|
|
95
|
+
if (input != null) {
|
|
96
|
+
switch (typeof input) {
|
|
97
|
+
case 'string':
|
|
98
|
+
result = input;
|
|
99
|
+
break;
|
|
100
|
+
case 'object':
|
|
101
|
+
let tree;
|
|
102
|
+
if (Array.isArray(input)) {
|
|
103
|
+
tree = { and: [input] };
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
tree = input;
|
|
107
|
+
}
|
|
108
|
+
result = zohoSearchRecordsCriteriaStringForTree(tree);
|
|
109
|
+
break;
|
|
121
110
|
}
|
|
122
|
-
result = zohoSearchRecordsCriteriaStringForTree(tree);
|
|
123
|
-
break;
|
|
124
111
|
}
|
|
125
|
-
|
|
126
|
-
return result;
|
|
112
|
+
return result;
|
|
127
113
|
}
|
|
128
114
|
function zohoSearchRecordsCriteriaStringForTree(tree) {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
115
|
+
function convertToString(value) {
|
|
116
|
+
let result;
|
|
117
|
+
if (typeof value === 'object') {
|
|
118
|
+
// array of criteria entries
|
|
119
|
+
if (Array.isArray(value)) {
|
|
120
|
+
result = value.map(zohoSearchRecordsCriteriaEntryToCriteriaString);
|
|
121
|
+
}
|
|
122
|
+
else if (value) {
|
|
123
|
+
// criteria tree that first needs to be converted to a string
|
|
124
|
+
result = zohoSearchRecordsCriteriaStringForTree(value);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
result = value;
|
|
129
|
+
}
|
|
130
|
+
return result;
|
|
131
|
+
}
|
|
132
|
+
function mergeStringValues(values, type) {
|
|
133
|
+
return values.length > 1 ? `(${values.join(type)})` : values[0]; // wrap in and values
|
|
134
|
+
}
|
|
135
|
+
function mergeValues(values, type) {
|
|
136
|
+
const allStrings = filterMaybeArrayValues(values.map(convertToString)).flatMap(asArray);
|
|
137
|
+
return mergeStringValues(allStrings, type);
|
|
138
|
+
}
|
|
139
|
+
const orValues = tree.or ? mergeValues(tree.or, 'or') : undefined;
|
|
140
|
+
let result = orValues;
|
|
141
|
+
if (tree.and) {
|
|
142
|
+
result = mergeValues([mergeValues(tree.and, 'and'), orValues], 'and');
|
|
141
143
|
}
|
|
142
144
|
return result;
|
|
143
|
-
}
|
|
144
|
-
function mergeStringValues(values, type) {
|
|
145
|
-
return values.length > 1 ? `(${values.join(type)})` : values[0]; // wrap in and values
|
|
146
|
-
}
|
|
147
|
-
function mergeValues(values, type) {
|
|
148
|
-
const allStrings = filterMaybeArrayValues(values.map(convertToString)).flatMap(asArray);
|
|
149
|
-
return mergeStringValues(allStrings, type);
|
|
150
|
-
}
|
|
151
|
-
const orValues = tree.or ? mergeValues(tree.or, 'or') : undefined;
|
|
152
|
-
let result = orValues;
|
|
153
|
-
if (tree.and) {
|
|
154
|
-
result = mergeValues([mergeValues(tree.and, 'and'), orValues], 'and');
|
|
155
|
-
}
|
|
156
|
-
return result;
|
|
157
145
|
}
|
|
158
146
|
/**
|
|
159
147
|
* Escape used for ZohoSearchRecordsCriteriaString
|
|
160
148
|
*/
|
|
161
149
|
const escapeZohoFieldValueForCriteriaString = escapeStringCharactersFunction({
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
150
|
+
/**
|
|
151
|
+
* Parenthesis and commas must be escaped using a backslash
|
|
152
|
+
*/
|
|
153
|
+
escapeTargets: ['(', ')', ','],
|
|
154
|
+
escapeCharacter: (char) => `\\\\${char}`
|
|
167
155
|
});
|
|
168
156
|
/**
|
|
169
157
|
* Converts the input entry to a ZohoSearchRecordsCriteriaString. Properly escapes any parenthesis or commas.
|
|
@@ -172,8 +160,8 @@ const escapeZohoFieldValueForCriteriaString = escapeStringCharactersFunction({
|
|
|
172
160
|
* @returns
|
|
173
161
|
*/
|
|
174
162
|
function zohoSearchRecordsCriteriaEntryToCriteriaString(entry) {
|
|
175
|
-
|
|
176
|
-
|
|
163
|
+
const escapedValue = escapeZohoFieldValueForCriteriaString(entry.value);
|
|
164
|
+
return `(${entry.field}:${entry.filter}:${escapedValue})`;
|
|
177
165
|
}
|
|
178
166
|
|
|
179
167
|
/**
|
|
@@ -199,7 +187,7 @@ const ZOHO_DATA_ARRAY_BLANK_ERROR_CODE = '__internal_data_array_blank_error';
|
|
|
199
187
|
* @returns
|
|
200
188
|
*/
|
|
201
189
|
function isZohoServerErrorResponseDataArrayRef(value) {
|
|
202
|
-
|
|
190
|
+
return Array.isArray(value?.data);
|
|
203
191
|
}
|
|
204
192
|
/**
|
|
205
193
|
* A code used in some cases to denote success.
|
|
@@ -214,44 +202,45 @@ const ZOHO_SUCCESS_STATUS = 'success';
|
|
|
214
202
|
*/
|
|
215
203
|
const ZOHO_ERROR_STATUS = 'error';
|
|
216
204
|
function zohoServerErrorData(error) {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
205
|
+
const errorType = typeof error;
|
|
206
|
+
let errorData;
|
|
207
|
+
if (errorType === 'object') {
|
|
208
|
+
errorData = error;
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
errorData = {
|
|
212
|
+
code: error,
|
|
213
|
+
message: ''
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
return errorData;
|
|
228
217
|
}
|
|
229
218
|
/**
|
|
230
219
|
* Zoho Server Error
|
|
231
220
|
*/
|
|
232
221
|
class ZohoServerError extends BaseError {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
222
|
+
error;
|
|
223
|
+
get code() {
|
|
224
|
+
return this.error.code;
|
|
225
|
+
}
|
|
226
|
+
constructor(error) {
|
|
227
|
+
super(error.message);
|
|
228
|
+
this.error = error;
|
|
229
|
+
}
|
|
241
230
|
}
|
|
242
231
|
/**
|
|
243
232
|
* Zoho Server Error that includes the FetchResponseError
|
|
244
233
|
*/
|
|
245
234
|
class ZohoServerFetchResponseError extends ZohoServerError {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
235
|
+
data;
|
|
236
|
+
errorResponseData;
|
|
237
|
+
responseError;
|
|
238
|
+
constructor(data, errorResponseData, responseError) {
|
|
239
|
+
super(data);
|
|
240
|
+
this.data = data;
|
|
241
|
+
this.errorResponseData = errorResponseData;
|
|
242
|
+
this.responseError = responseError;
|
|
243
|
+
}
|
|
255
244
|
}
|
|
256
245
|
/**
|
|
257
246
|
* Used as a transient error for situations where there are potentially multiple errors within the data array returned.
|
|
@@ -260,15 +249,12 @@ class ZohoServerFetchResponseError extends ZohoServerError {
|
|
|
260
249
|
* the successful and errored results returned within the data.
|
|
261
250
|
*/
|
|
262
251
|
class ZohoServerFetchResponseDataArrayError extends ZohoServerFetchResponseError {
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
get errorDataArray() {
|
|
270
|
-
return this.errorResponseData.data;
|
|
271
|
-
}
|
|
252
|
+
constructor(errorResponseData, responseError) {
|
|
253
|
+
super({ code: ZOHO_DATA_ARRAY_BLANK_ERROR_CODE, message: 'Check data for individual errors.' }, errorResponseData, responseError);
|
|
254
|
+
}
|
|
255
|
+
get errorDataArray() {
|
|
256
|
+
return this.errorResponseData.data;
|
|
257
|
+
}
|
|
272
258
|
}
|
|
273
259
|
/**
|
|
274
260
|
* Creates a logZohoServerErrorFunction that logs the error to console.
|
|
@@ -277,25 +263,19 @@ class ZohoServerFetchResponseDataArrayError extends ZohoServerFetchResponseError
|
|
|
277
263
|
* @returns
|
|
278
264
|
*/
|
|
279
265
|
function logZohoServerErrorFunction(zohoApiNamePrefix, options) {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
error
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
} else {
|
|
294
|
-
console.log(`${zohoApiNamePrefix}Error(name:${error.name}): `, {
|
|
295
|
-
error
|
|
296
|
-
});
|
|
297
|
-
}
|
|
298
|
-
};
|
|
266
|
+
const { logDataArrayErrors = false } = options ?? {};
|
|
267
|
+
return (error) => {
|
|
268
|
+
if (error instanceof ZohoServerFetchResponseDataArrayError && !logDataArrayErrors) ;
|
|
269
|
+
else if (error instanceof ZohoServerFetchResponseError) {
|
|
270
|
+
console.log(`${zohoApiNamePrefix}Error(${error.responseError.response.status}): `, { error, errorData: error.data });
|
|
271
|
+
}
|
|
272
|
+
else if (error instanceof ZohoServerError) {
|
|
273
|
+
console.log(`${zohoApiNamePrefix}Error(code:${error.code}): `, { error });
|
|
274
|
+
}
|
|
275
|
+
else {
|
|
276
|
+
console.log(`${zohoApiNamePrefix}Error(name:${error.name}): `, { error });
|
|
277
|
+
}
|
|
278
|
+
};
|
|
299
279
|
}
|
|
300
280
|
/**
|
|
301
281
|
* Wraps a ConfiguredFetch to support handling errors returned by fetch.
|
|
@@ -304,41 +284,42 @@ function logZohoServerErrorFunction(zohoApiNamePrefix, options) {
|
|
|
304
284
|
* @returns
|
|
305
285
|
*/
|
|
306
286
|
function handleZohoErrorFetchFactory(parseZohoError, defaultLogError) {
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
287
|
+
return (fetch, logError = defaultLogError, onError) => {
|
|
288
|
+
return async (x, y) => {
|
|
289
|
+
try {
|
|
290
|
+
return await fetch(x, y); // await to catch thrown errors
|
|
291
|
+
}
|
|
292
|
+
catch (e) {
|
|
293
|
+
if (e instanceof FetchResponseError) {
|
|
294
|
+
const error = await parseZohoError(e);
|
|
295
|
+
if (error) {
|
|
296
|
+
logError(error); // log before throwing.
|
|
297
|
+
onError?.(error); // perform a task
|
|
298
|
+
throw error;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
throw e;
|
|
302
|
+
}
|
|
303
|
+
};
|
|
322
304
|
};
|
|
323
|
-
};
|
|
324
305
|
}
|
|
325
306
|
/**
|
|
326
307
|
* FetchJsonInterceptJsonResponseFunction that intercepts a 200 response that actually contains a ZohoServerError and throws a ZohoServerError for the error handling to catch.
|
|
327
308
|
*/
|
|
328
309
|
function interceptZohoErrorResponseFactory(parseZohoServerErrorResponseData) {
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
310
|
+
return (json, response) => {
|
|
311
|
+
const error = json?.error;
|
|
312
|
+
if (error != null) {
|
|
313
|
+
const responseError = new FetchResponseError(response);
|
|
314
|
+
if (responseError) {
|
|
315
|
+
const parsedError = parseZohoServerErrorResponseData(json, responseError);
|
|
316
|
+
if (parsedError) {
|
|
317
|
+
throw parsedError;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
337
320
|
}
|
|
338
|
-
|
|
339
|
-
}
|
|
340
|
-
return json;
|
|
341
|
-
};
|
|
321
|
+
return json;
|
|
322
|
+
};
|
|
342
323
|
}
|
|
343
324
|
// MARK: Parsed Errors
|
|
344
325
|
/**
|
|
@@ -350,7 +331,8 @@ const ZOHO_INTERNAL_ERROR_CODE = 'INTERNAL_ERROR';
|
|
|
350
331
|
* Error code for when an invalid oauth token is provided.
|
|
351
332
|
*/
|
|
352
333
|
const ZOHO_INVALID_TOKEN_ERROR_CODE = 'INVALID_TOKEN';
|
|
353
|
-
class ZohoInvalidTokenError extends ZohoServerFetchResponseError {
|
|
334
|
+
class ZohoInvalidTokenError extends ZohoServerFetchResponseError {
|
|
335
|
+
}
|
|
354
336
|
/**
|
|
355
337
|
* Error code for when a failure occured for the given action
|
|
356
338
|
*/
|
|
@@ -358,7 +340,8 @@ const ZOHO_FAILURE_ERROR_CODE = 'FAILURE';
|
|
|
358
340
|
/**
|
|
359
341
|
* Error when the Zoho API returns an internal error
|
|
360
342
|
*/
|
|
361
|
-
class ZohoInternalError extends ZohoServerFetchResponseError {
|
|
343
|
+
class ZohoInternalError extends ZohoServerFetchResponseError {
|
|
344
|
+
}
|
|
362
345
|
/**
|
|
363
346
|
* Error in the following cases:
|
|
364
347
|
* - Authorization is not properly constructed ("Invalid Ticket Id")
|
|
@@ -368,16 +351,17 @@ const ZOHO_INVALID_AUTHORIZATION_ERROR_CODE = '4834';
|
|
|
368
351
|
/**
|
|
369
352
|
* Error when the Zoho API returns an invalid authorization error code.
|
|
370
353
|
*/
|
|
371
|
-
class ZohoInvalidAuthorizationError extends ZohoServerFetchResponseError {
|
|
354
|
+
class ZohoInvalidAuthorizationError extends ZohoServerFetchResponseError {
|
|
355
|
+
}
|
|
372
356
|
/**
|
|
373
357
|
* Error in the following cases:
|
|
374
358
|
* - Search query is invalid
|
|
375
359
|
*/
|
|
376
360
|
const ZOHO_INVALID_QUERY_ERROR_CODE = 'INVALID_QUERY';
|
|
377
361
|
class ZohoInvalidQueryError extends ZohoServerFetchResponseError {
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
362
|
+
get details() {
|
|
363
|
+
return this.error.details;
|
|
364
|
+
}
|
|
381
365
|
}
|
|
382
366
|
/**
|
|
383
367
|
* Error when a mandatory field is missing.
|
|
@@ -405,28 +389,23 @@ const ZOHO_RATE_LIMIT_RESET_HEADER = 'X-RATELIMIT-RESET';
|
|
|
405
389
|
const DEFAULT_ZOHO_API_RATE_LIMIT = 100;
|
|
406
390
|
const DEFAULT_ZOHO_API_RATE_LIMIT_RESET_PERIOD = MS_IN_MINUTE;
|
|
407
391
|
function zohoRateLimitHeaderDetails(headers) {
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
reset,
|
|
421
|
-
resetAt
|
|
422
|
-
};
|
|
423
|
-
}
|
|
424
|
-
return result;
|
|
392
|
+
const limitHeader = headers.get(ZOHO_RATE_LIMIT_LIMIT_HEADER);
|
|
393
|
+
const remainingHeader = headers.get(ZOHO_RATE_LIMIT_REMAINING_HEADER);
|
|
394
|
+
const resetHeader = headers.get(ZOHO_RATE_LIMIT_RESET_HEADER);
|
|
395
|
+
let result = null;
|
|
396
|
+
if (limitHeader != null && remainingHeader != null && resetHeader != null) {
|
|
397
|
+
const limit = Number(limitHeader);
|
|
398
|
+
const remaining = Number(remainingHeader);
|
|
399
|
+
const reset = Number(resetHeader);
|
|
400
|
+
const resetAt = new Date(reset);
|
|
401
|
+
result = { limit, remaining, reset, resetAt };
|
|
402
|
+
}
|
|
403
|
+
return result;
|
|
425
404
|
}
|
|
426
405
|
class ZohoTooManyRequestsError extends ZohoServerFetchResponseError {
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
406
|
+
get headerDetails() {
|
|
407
|
+
return zohoRateLimitHeaderDetails(this.responseError.response.headers);
|
|
408
|
+
}
|
|
430
409
|
}
|
|
431
410
|
/**
|
|
432
411
|
* Function that parses/transforms a ZohoServerErrorResponseData into a general ZohoServerError or other known error type.
|
|
@@ -436,36 +415,37 @@ class ZohoTooManyRequestsError extends ZohoServerFetchResponseError {
|
|
|
436
415
|
* @returns
|
|
437
416
|
*/
|
|
438
417
|
function parseZohoServerErrorResponseData(errorResponseData, responseError) {
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
} else {
|
|
443
|
-
const error = tryFindZohoServerErrorData(errorResponseData, responseError);
|
|
444
|
-
if (error) {
|
|
445
|
-
const errorData = zohoServerErrorData(error);
|
|
446
|
-
switch (errorData.code) {
|
|
447
|
-
case ZOHO_INTERNAL_ERROR_CODE:
|
|
448
|
-
result = new ZohoInternalError(errorData, errorResponseData, responseError);
|
|
449
|
-
break;
|
|
450
|
-
case ZOHO_INVALID_TOKEN_ERROR_CODE:
|
|
451
|
-
result = new ZohoInvalidTokenError(errorData, errorResponseData, responseError);
|
|
452
|
-
break;
|
|
453
|
-
case ZOHO_INVALID_AUTHORIZATION_ERROR_CODE:
|
|
454
|
-
result = new ZohoInvalidAuthorizationError(errorData, errorResponseData, responseError);
|
|
455
|
-
break;
|
|
456
|
-
case ZOHO_INVALID_QUERY_ERROR_CODE:
|
|
457
|
-
result = new ZohoInvalidQueryError(errorData, errorResponseData, responseError);
|
|
458
|
-
break;
|
|
459
|
-
case ZOHO_TOO_MANY_REQUESTS_ERROR_CODE:
|
|
460
|
-
result = new ZohoTooManyRequestsError(errorData, errorResponseData, responseError);
|
|
461
|
-
break;
|
|
462
|
-
default:
|
|
463
|
-
result = new ZohoServerFetchResponseError(errorData, errorResponseData, responseError);
|
|
464
|
-
break;
|
|
465
|
-
}
|
|
418
|
+
let result;
|
|
419
|
+
if (isZohoServerErrorResponseDataArrayRef(errorResponseData)) {
|
|
420
|
+
result = new ZohoServerFetchResponseDataArrayError(errorResponseData, responseError);
|
|
466
421
|
}
|
|
467
|
-
|
|
468
|
-
|
|
422
|
+
else {
|
|
423
|
+
const error = tryFindZohoServerErrorData(errorResponseData, responseError);
|
|
424
|
+
if (error) {
|
|
425
|
+
const errorData = zohoServerErrorData(error);
|
|
426
|
+
switch (errorData.code) {
|
|
427
|
+
case ZOHO_INTERNAL_ERROR_CODE:
|
|
428
|
+
result = new ZohoInternalError(errorData, errorResponseData, responseError);
|
|
429
|
+
break;
|
|
430
|
+
case ZOHO_INVALID_TOKEN_ERROR_CODE:
|
|
431
|
+
result = new ZohoInvalidTokenError(errorData, errorResponseData, responseError);
|
|
432
|
+
break;
|
|
433
|
+
case ZOHO_INVALID_AUTHORIZATION_ERROR_CODE:
|
|
434
|
+
result = new ZohoInvalidAuthorizationError(errorData, errorResponseData, responseError);
|
|
435
|
+
break;
|
|
436
|
+
case ZOHO_INVALID_QUERY_ERROR_CODE:
|
|
437
|
+
result = new ZohoInvalidQueryError(errorData, errorResponseData, responseError);
|
|
438
|
+
break;
|
|
439
|
+
case ZOHO_TOO_MANY_REQUESTS_ERROR_CODE:
|
|
440
|
+
result = new ZohoTooManyRequestsError(errorData, errorResponseData, responseError);
|
|
441
|
+
break;
|
|
442
|
+
default:
|
|
443
|
+
result = new ZohoServerFetchResponseError(errorData, errorResponseData, responseError);
|
|
444
|
+
break;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
return result;
|
|
469
449
|
}
|
|
470
450
|
/**
|
|
471
451
|
* Attempts to retrieve an ZohoServerErrorResponseDataError from the input.
|
|
@@ -478,8 +458,8 @@ function parseZohoServerErrorResponseData(errorResponseData, responseError) {
|
|
|
478
458
|
* @returns
|
|
479
459
|
*/
|
|
480
460
|
function tryFindZohoServerErrorData(errorResponseData, responseError) {
|
|
481
|
-
|
|
482
|
-
|
|
461
|
+
const error = errorResponseData.error ?? errorResponseData.data?.[0] ?? (!responseError.response.ok ? errorResponseData : undefined);
|
|
462
|
+
return error;
|
|
483
463
|
}
|
|
484
464
|
|
|
485
465
|
/**
|
|
@@ -492,77 +472,83 @@ const ZOHO_RECRUIT_ALREADY_ASSOCIATED_ERROR_CODE = 'ALREADY_ASSOCIATED';
|
|
|
492
472
|
* Thrown when a record with the given id has no content. Typically also means it does not exist.
|
|
493
473
|
*/
|
|
494
474
|
class ZohoRecruitRecordNoContentError extends BaseError {
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
}
|
|
503
|
-
class ZohoRecruitRecordCrudError extends ZohoServerError {
|
|
504
|
-
|
|
505
|
-
class
|
|
475
|
+
moduleName;
|
|
476
|
+
recordId;
|
|
477
|
+
constructor(moduleName, recordId) {
|
|
478
|
+
super(`There was no content or matching records for the content. It may not exist.`);
|
|
479
|
+
this.moduleName = moduleName;
|
|
480
|
+
this.recordId = recordId;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
class ZohoRecruitRecordCrudError extends ZohoServerError {
|
|
484
|
+
}
|
|
485
|
+
class ZohoRecruitRecordCrudMandatoryFieldNotFoundError extends ZohoRecruitRecordCrudError {
|
|
486
|
+
}
|
|
487
|
+
class ZohoRecruitRecordCrudDuplicateDataError extends ZohoRecruitRecordCrudError {
|
|
488
|
+
}
|
|
506
489
|
class ZohoRecruitRecordCrudInvalidDataError extends ZohoRecruitRecordCrudError {
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
490
|
+
get invalidFieldDetails() {
|
|
491
|
+
return this.error.details;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
class ZohoRecruitRecordCrudNoMatchingRecordError extends ZohoRecruitRecordCrudInvalidDataError {
|
|
510
495
|
}
|
|
511
|
-
class ZohoRecruitRecordCrudNoMatchingRecordError extends ZohoRecruitRecordCrudInvalidDataError {}
|
|
512
496
|
function zohoRecruitRecordCrudError(error) {
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
497
|
+
let result;
|
|
498
|
+
switch (error.code) {
|
|
499
|
+
case ZOHO_INVALID_DATA_ERROR_CODE:
|
|
500
|
+
const invalidDataError = new ZohoRecruitRecordCrudInvalidDataError(error);
|
|
501
|
+
if (invalidDataError.invalidFieldDetails['id']) {
|
|
502
|
+
result = new ZohoRecruitRecordCrudNoMatchingRecordError(error);
|
|
503
|
+
}
|
|
504
|
+
else {
|
|
505
|
+
result = invalidDataError;
|
|
506
|
+
}
|
|
507
|
+
break;
|
|
508
|
+
case ZOHO_MANDATORY_NOT_FOUND_ERROR_CODE:
|
|
509
|
+
result = new ZohoRecruitRecordCrudMandatoryFieldNotFoundError(error);
|
|
510
|
+
break;
|
|
511
|
+
case ZOHO_DUPLICATE_DATA_ERROR_CODE:
|
|
512
|
+
result = new ZohoRecruitRecordCrudDuplicateDataError(error);
|
|
513
|
+
break;
|
|
514
|
+
default:
|
|
515
|
+
result = new ZohoRecruitRecordCrudError(error);
|
|
516
|
+
break;
|
|
517
|
+
}
|
|
518
|
+
return result;
|
|
534
519
|
}
|
|
535
520
|
function assertZohoRecruitRecordDataArrayResultHasContent(moduleName, recordId) {
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
521
|
+
return (x) => {
|
|
522
|
+
if (x == null || !x.data?.length) {
|
|
523
|
+
throw new ZohoRecruitRecordNoContentError(moduleName, recordId);
|
|
524
|
+
}
|
|
525
|
+
else {
|
|
526
|
+
return x;
|
|
527
|
+
}
|
|
528
|
+
};
|
|
543
529
|
}
|
|
544
530
|
const logZohoRecruitErrorToConsole = logZohoServerErrorFunction('ZohoRecruit');
|
|
545
531
|
async function parseZohoRecruitError(responseError) {
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
532
|
+
const data = await responseError.response.json().catch((x) => undefined);
|
|
533
|
+
let result;
|
|
534
|
+
if (data) {
|
|
535
|
+
result = parseZohoRecruitServerErrorResponseData(data, responseError);
|
|
536
|
+
}
|
|
537
|
+
return result;
|
|
552
538
|
}
|
|
553
539
|
function parseZohoRecruitServerErrorResponseData(errorResponseData, responseError) {
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
540
|
+
let result;
|
|
541
|
+
const error = tryFindZohoServerErrorData(errorResponseData, responseError);
|
|
542
|
+
if (error) {
|
|
543
|
+
const errorData = zohoServerErrorData(error);
|
|
544
|
+
switch (errorData.code) {
|
|
545
|
+
// TODO: Add recruit-specific error codes here.
|
|
546
|
+
default:
|
|
547
|
+
result = parseZohoServerErrorResponseData(errorResponseData, responseError);
|
|
548
|
+
break;
|
|
549
|
+
}
|
|
563
550
|
}
|
|
564
|
-
|
|
565
|
-
return result;
|
|
551
|
+
return result;
|
|
566
552
|
}
|
|
567
553
|
const interceptZohoRecruit200StatusWithErrorResponse = interceptZohoErrorResponseFactory(parseZohoRecruitServerErrorResponseData);
|
|
568
554
|
const handleZohoRecruitErrorFetch = handleZohoErrorFetchFactory(parseZohoRecruitError, logZohoRecruitErrorToConsole);
|
|
@@ -585,28 +571,22 @@ const ZOHO_RECRUIT_CRUD_FUNCTION_MAX_RECORDS_LIMIT = 100;
|
|
|
585
571
|
* @returns
|
|
586
572
|
*/
|
|
587
573
|
function updateRecordLikeFunction$1(context, fetchUrlPrefix, fetchMethod) {
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
throw zohoRecruitRecordCrudError(errorItems[0].result);
|
|
605
|
-
} else {
|
|
606
|
-
return successItems[0].result.details;
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
});
|
|
574
|
+
return (({ data, module }) => context.fetchJson(`/v2/${module}${fetchUrlPrefix}`, zohoRecruitApiFetchJsonInput(fetchMethod, { data: asArray(data) })).then((x) => {
|
|
575
|
+
const isInputMultipleItems = Array.isArray(data);
|
|
576
|
+
const result = zohoRecruitMultiRecordResult(asArray(data), x.data);
|
|
577
|
+
if (isInputMultipleItems) {
|
|
578
|
+
return result;
|
|
579
|
+
}
|
|
580
|
+
else {
|
|
581
|
+
const { successItems, errorItems } = result;
|
|
582
|
+
if (errorItems[0] != null) {
|
|
583
|
+
throw zohoRecruitRecordCrudError(errorItems[0].result);
|
|
584
|
+
}
|
|
585
|
+
else {
|
|
586
|
+
return successItems[0].result.details;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
}));
|
|
610
590
|
}
|
|
611
591
|
/**
|
|
612
592
|
* Inserts one or more records into Recruit.
|
|
@@ -617,7 +597,7 @@ function updateRecordLikeFunction$1(context, fetchUrlPrefix, fetchMethod) {
|
|
|
617
597
|
* @returns
|
|
618
598
|
*/
|
|
619
599
|
function zohoRecruitInsertRecord(context) {
|
|
620
|
-
|
|
600
|
+
return updateRecordLikeFunction$1(context, '', 'POST');
|
|
621
601
|
}
|
|
622
602
|
/**
|
|
623
603
|
* Updates or inserts one or more records in Recruit.
|
|
@@ -628,7 +608,7 @@ function zohoRecruitInsertRecord(context) {
|
|
|
628
608
|
* @returns
|
|
629
609
|
*/
|
|
630
610
|
function zohoRecruitUpsertRecord(context) {
|
|
631
|
-
|
|
611
|
+
return updateRecordLikeFunction$1(context, '/upsert', 'POST');
|
|
632
612
|
}
|
|
633
613
|
/**
|
|
634
614
|
* Updates one or more records in Recruit.
|
|
@@ -639,7 +619,7 @@ function zohoRecruitUpsertRecord(context) {
|
|
|
639
619
|
* @returns
|
|
640
620
|
*/
|
|
641
621
|
function zohoRecruitUpdateRecord(context) {
|
|
642
|
-
|
|
622
|
+
return updateRecordLikeFunction$1(context, '', 'PUT');
|
|
643
623
|
}
|
|
644
624
|
/**
|
|
645
625
|
* Deletes one or more records from the given module.
|
|
@@ -650,16 +630,9 @@ function zohoRecruitUpdateRecord(context) {
|
|
|
650
630
|
* @returns ZohoRecruitDeleteRecordFunction
|
|
651
631
|
*/
|
|
652
632
|
function zohoRecruitDeleteRecord(context) {
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
wf_trigger
|
|
657
|
-
}) => {
|
|
658
|
-
return context.fetchJson(`/v2/${module}?${makeUrlSearchParams({
|
|
659
|
-
ids,
|
|
660
|
-
wf_trigger
|
|
661
|
-
})}`, zohoRecruitApiFetchJsonInput('DELETE')).then(zohoRecruitChangeObjectLikeResponseSuccessAndErrorPairs);
|
|
662
|
-
};
|
|
633
|
+
return ({ ids, module, wf_trigger }) => {
|
|
634
|
+
return context.fetchJson(`/v2/${module}?${makeUrlSearchParams({ ids, wf_trigger })}`, zohoRecruitApiFetchJsonInput('DELETE')).then(zohoRecruitChangeObjectLikeResponseSuccessAndErrorPairs);
|
|
635
|
+
};
|
|
663
636
|
}
|
|
664
637
|
/**
|
|
665
638
|
* Retrieves a specific record from the given module.
|
|
@@ -670,7 +643,10 @@ function zohoRecruitDeleteRecord(context) {
|
|
|
670
643
|
* @returns
|
|
671
644
|
*/
|
|
672
645
|
function zohoRecruitGetRecordById(context) {
|
|
673
|
-
|
|
646
|
+
return (input) => context
|
|
647
|
+
.fetchJson(`/v2/${input.module}/${input.id}`, zohoRecruitApiFetchJsonInput('GET'))
|
|
648
|
+
.then(assertZohoRecruitRecordDataArrayResultHasContent(input.module))
|
|
649
|
+
.then((x) => x.data[0]);
|
|
674
650
|
}
|
|
675
651
|
/**
|
|
676
652
|
* Retrieves records from the given module. Used for paginating across all records.
|
|
@@ -681,7 +657,7 @@ function zohoRecruitGetRecordById(context) {
|
|
|
681
657
|
* @returns
|
|
682
658
|
*/
|
|
683
659
|
function zohoRecruitGetRecords(context) {
|
|
684
|
-
|
|
660
|
+
return ((input) => context.fetchJson(`/v2/${input.module}?${zohoRecruitUrlSearchParamsMinusModule(input).toString()}`, zohoRecruitApiFetchJsonInput('GET')));
|
|
685
661
|
}
|
|
686
662
|
/**
|
|
687
663
|
* Searches records from the given module.
|
|
@@ -692,30 +668,23 @@ function zohoRecruitGetRecords(context) {
|
|
|
692
668
|
* @returns
|
|
693
669
|
*/
|
|
694
670
|
function zohoRecruitSearchRecords(context) {
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
const urlParams = zohoRecruitUrlSearchParamsMinusModule(baseInput);
|
|
708
|
-
return urlParams;
|
|
709
|
-
}
|
|
710
|
-
return input => context.fetchJson(`/v2/${input.module}/search?${searchRecordsUrlSearchParams(input).toString()}`, zohoRecruitApiFetchJsonInput('GET')).then(x => x ?? {
|
|
711
|
-
data: [],
|
|
712
|
-
info: {
|
|
713
|
-
more_records: false
|
|
671
|
+
function searchRecordsUrlSearchParams(input) {
|
|
672
|
+
const baseInput = { ...input };
|
|
673
|
+
delete baseInput.criteria;
|
|
674
|
+
if (input.criteria != null) {
|
|
675
|
+
const criteriaString = zohoRecruitSearchRecordsCriteriaString(input.criteria);
|
|
676
|
+
baseInput.criteria = criteriaString;
|
|
677
|
+
}
|
|
678
|
+
if (!baseInput.word && !input.criteria && !input.email && !input.phone) {
|
|
679
|
+
throw new Error('At least one of word, criteria, email, or phone must be provided');
|
|
680
|
+
}
|
|
681
|
+
const urlParams = zohoRecruitUrlSearchParamsMinusModule(baseInput);
|
|
682
|
+
return urlParams;
|
|
714
683
|
}
|
|
715
|
-
|
|
684
|
+
return ((input) => context.fetchJson(`/v2/${input.module}/search?${searchRecordsUrlSearchParams(input).toString()}`, zohoRecruitApiFetchJsonInput('GET')).then((x) => x ?? { data: [], info: { more_records: false } }));
|
|
716
685
|
}
|
|
717
686
|
function zohoRecruitSearchRecordsPageFactory(context) {
|
|
718
|
-
|
|
687
|
+
return zohoFetchPageFactory(zohoRecruitSearchRecords(context));
|
|
719
688
|
}
|
|
720
689
|
/**
|
|
721
690
|
* Creates a ZohoRecruitGetRelatedRecordsFunctionFactory, which can be used to create ZohoRecruitGetRelatedRecordsFunction<T> that targets retrieving related records of a given type.
|
|
@@ -726,29 +695,22 @@ function zohoRecruitSearchRecordsPageFactory(context) {
|
|
|
726
695
|
* @returns a ZohoRecruitGetRelatedRecordsFunctionFactory
|
|
727
696
|
*/
|
|
728
697
|
function zohoRecruitGetRelatedRecordsFunctionFactory(context) {
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
} = config;
|
|
734
|
-
return input => context.fetchJson(`/v2/${input.module}/${input.id}/${targetModule}?${zohoRecruitUrlSearchParamsMinusIdAndModule(input, input.filter).toString()}`, zohoRecruitApiFetchJsonInput('GET')).then(x => x ?? (returnEmptyRecordsInsteadOfNull !== false ? emptyZohoPageResult() : x));
|
|
735
|
-
};
|
|
698
|
+
return (config) => {
|
|
699
|
+
const { targetModule, returnEmptyRecordsInsteadOfNull = true } = config;
|
|
700
|
+
return (input) => context.fetchJson(`/v2/${input.module}/${input.id}/${targetModule}?${zohoRecruitUrlSearchParamsMinusIdAndModule(input, input.filter).toString()}`, zohoRecruitApiFetchJsonInput('GET')).then((x) => x ?? (returnEmptyRecordsInsteadOfNull !== false ? emptyZohoPageResult() : x));
|
|
701
|
+
};
|
|
736
702
|
}
|
|
737
703
|
function zohoRecruitGetEmailsForRecord(context) {
|
|
738
|
-
|
|
739
|
-
targetModule: ZOHO_RECRUIT_EMAILS_MODULE
|
|
740
|
-
});
|
|
704
|
+
return zohoRecruitGetRelatedRecordsFunctionFactory(context)({ targetModule: ZOHO_RECRUIT_EMAILS_MODULE });
|
|
741
705
|
}
|
|
742
706
|
function zohoRecruitGetEmailsForRecordPageFactory(context) {
|
|
743
|
-
|
|
707
|
+
return zohoFetchPageFactory(zohoRecruitGetEmailsForRecord(context));
|
|
744
708
|
}
|
|
745
709
|
function zohoRecruitGetAttachmentsForRecord(context) {
|
|
746
|
-
|
|
747
|
-
targetModule: ZOHO_RECRUIT_ATTACHMENTS_MODULE
|
|
748
|
-
});
|
|
710
|
+
return zohoRecruitGetRelatedRecordsFunctionFactory(context)({ targetModule: ZOHO_RECRUIT_ATTACHMENTS_MODULE });
|
|
749
711
|
}
|
|
750
712
|
function zohoRecruitGetAttachmentsForRecordPageFactory(context) {
|
|
751
|
-
|
|
713
|
+
return zohoFetchPageFactory(zohoRecruitGetAttachmentsForRecord(context));
|
|
752
714
|
}
|
|
753
715
|
/**
|
|
754
716
|
* Maximum attachment size allowed by Zoho Recruit.
|
|
@@ -765,59 +727,57 @@ const ZOHO_RECRUIT_ATTACHMENT_MAX_SIZE = 20 * 1024 * 1024;
|
|
|
765
727
|
* @returns
|
|
766
728
|
*/
|
|
767
729
|
function zohoRecruitUploadAttachmentForRecord(context) {
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
730
|
+
return (input) => {
|
|
731
|
+
const { attachmentCategoryId, attachmentCategoryName, formData } = input;
|
|
732
|
+
const urlParams = {
|
|
733
|
+
attachments_category_id: joinStringsWithCommas(attachmentCategoryId),
|
|
734
|
+
attachments_category: joinStringsWithCommas(attachmentCategoryName),
|
|
735
|
+
attachment_url: input.attachmentUrl
|
|
736
|
+
};
|
|
737
|
+
if (!urlParams.attachments_category_id?.length && !urlParams.attachments_category?.length) {
|
|
738
|
+
throw new Error('attachmentCategoryId or attachmentCategoryName must be provided and not empty.');
|
|
739
|
+
}
|
|
740
|
+
if (formData != null) {
|
|
741
|
+
delete urlParams.attachment_url;
|
|
742
|
+
}
|
|
743
|
+
const url = `https://recruitsandbox.zoho.com/recruit/v2/${input.module}/${input.id}/${ZOHO_RECRUIT_ATTACHMENTS_MODULE}?${makeUrlSearchParams(urlParams).toString()}`;
|
|
744
|
+
let response;
|
|
745
|
+
if (urlParams.attachment_url) {
|
|
746
|
+
response = context.fetch(url, { method: 'POST' });
|
|
747
|
+
}
|
|
748
|
+
else if (formData != null) {
|
|
749
|
+
throw new Error('unsupported currently. Use the attachmentUrl parameter instead.');
|
|
750
|
+
// There is something weird going on with sending requests this way and zoho's server is rejecting it.
|
|
751
|
+
/*
|
|
752
|
+
response = context.fetch(url, {
|
|
753
|
+
method: 'POST',
|
|
754
|
+
headers: {
|
|
755
|
+
'Content-Type': 'multipart/form-data',
|
|
756
|
+
'content-length': '210'
|
|
757
|
+
},
|
|
758
|
+
body: formData
|
|
759
|
+
});
|
|
760
|
+
*/
|
|
761
|
+
/*
|
|
762
|
+
const fullUrl = (context.config.apiUrl as string) + url;
|
|
763
|
+
const accessToken = await context.accessTokenStringFactory();
|
|
764
|
+
|
|
765
|
+
response = fetch(fullUrl, {
|
|
766
|
+
headers: {
|
|
767
|
+
Authorization: `Bearer ${accessToken}`
|
|
768
|
+
},
|
|
769
|
+
body: formData,
|
|
770
|
+
method: 'POST'
|
|
771
|
+
});
|
|
772
|
+
|
|
773
|
+
console.log({ response });
|
|
774
|
+
*/
|
|
775
|
+
}
|
|
776
|
+
else {
|
|
777
|
+
throw new Error('body or attachmentUrl must be provided.');
|
|
778
|
+
}
|
|
779
|
+
return response;
|
|
778
780
|
};
|
|
779
|
-
if (!urlParams.attachments_category_id?.length && !urlParams.attachments_category?.length) {
|
|
780
|
-
throw new Error('attachmentCategoryId or attachmentCategoryName must be provided and not empty.');
|
|
781
|
-
}
|
|
782
|
-
if (formData != null) {
|
|
783
|
-
delete urlParams.attachment_url;
|
|
784
|
-
}
|
|
785
|
-
const url = `https://recruitsandbox.zoho.com/recruit/v2/${input.module}/${input.id}/${ZOHO_RECRUIT_ATTACHMENTS_MODULE}?${makeUrlSearchParams(urlParams).toString()}`;
|
|
786
|
-
let response;
|
|
787
|
-
if (urlParams.attachment_url) {
|
|
788
|
-
response = context.fetch(url, {
|
|
789
|
-
method: 'POST'
|
|
790
|
-
});
|
|
791
|
-
} else if (formData != null) {
|
|
792
|
-
throw new Error('unsupported currently. Use the attachmentUrl parameter instead.');
|
|
793
|
-
// There is something weird going on with sending requests this way and zoho's server is rejecting it.
|
|
794
|
-
/*
|
|
795
|
-
response = context.fetch(url, {
|
|
796
|
-
method: 'POST',
|
|
797
|
-
headers: {
|
|
798
|
-
'Content-Type': 'multipart/form-data',
|
|
799
|
-
'content-length': '210'
|
|
800
|
-
},
|
|
801
|
-
body: formData
|
|
802
|
-
});
|
|
803
|
-
*/
|
|
804
|
-
/*
|
|
805
|
-
const fullUrl = (context.config.apiUrl as string) + url;
|
|
806
|
-
const accessToken = await context.accessTokenStringFactory();
|
|
807
|
-
response = fetch(fullUrl, {
|
|
808
|
-
headers: {
|
|
809
|
-
Authorization: `Bearer ${accessToken}`
|
|
810
|
-
},
|
|
811
|
-
body: formData,
|
|
812
|
-
method: 'POST'
|
|
813
|
-
});
|
|
814
|
-
console.log({ response });
|
|
815
|
-
*/
|
|
816
|
-
} else {
|
|
817
|
-
throw new Error('body or attachmentUrl must be provided.');
|
|
818
|
-
}
|
|
819
|
-
return response;
|
|
820
|
-
};
|
|
821
781
|
}
|
|
822
782
|
/**
|
|
823
783
|
* Downloads an attachment from a record.
|
|
@@ -828,9 +788,7 @@ function zohoRecruitUploadAttachmentForRecord(context) {
|
|
|
828
788
|
* @returns
|
|
829
789
|
*/
|
|
830
790
|
function zohoRecruitDownloadAttachmentForRecord(context) {
|
|
831
|
-
|
|
832
|
-
method: 'GET'
|
|
833
|
-
}).then(parseFetchFileResponse);
|
|
791
|
+
return (input) => context.fetch(`/v2/${input.module}/${input.id}/${ZOHO_RECRUIT_ATTACHMENTS_MODULE}/${input.attachment_id}`, { method: 'GET' }).then(parseFetchFileResponse);
|
|
834
792
|
}
|
|
835
793
|
/**
|
|
836
794
|
* Deletes an attachment from a record.
|
|
@@ -841,16 +799,14 @@ function zohoRecruitDownloadAttachmentForRecord(context) {
|
|
|
841
799
|
* @returns
|
|
842
800
|
*/
|
|
843
801
|
function zohoRecruitDeleteAttachmentFromRecord(context) {
|
|
844
|
-
|
|
845
|
-
method: 'DELETE'
|
|
846
|
-
});
|
|
802
|
+
return (input) => context.fetch(`/v2/${input.module}/${input.id}/${ZOHO_RECRUIT_ATTACHMENTS_MODULE}/${input.attachment_id}`, { method: 'DELETE' });
|
|
847
803
|
}
|
|
848
804
|
class ZohoRecruitExecuteRestApiFunctionError extends BaseError {
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
805
|
+
error;
|
|
806
|
+
constructor(error) {
|
|
807
|
+
super(`An error occured during the execution of the function. Code: ${error.code}, Message: ${error.message}`);
|
|
808
|
+
this.error = error;
|
|
809
|
+
}
|
|
854
810
|
}
|
|
855
811
|
/**
|
|
856
812
|
* Creates a ZohoRecruitExecuteRestApiFunctionFunction
|
|
@@ -864,87 +820,84 @@ class ZohoRecruitExecuteRestApiFunctionError extends BaseError {
|
|
|
864
820
|
* @returns
|
|
865
821
|
*/
|
|
866
822
|
function zohoRecruitExecuteRestApiFunction(context) {
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
823
|
+
return (input) => {
|
|
824
|
+
const inputSearchParams = makeUrlSearchParams(input.params);
|
|
825
|
+
const inputSearchParamsString = inputSearchParams.toString();
|
|
826
|
+
const isSpecificRequest = Boolean(input.apiKey);
|
|
827
|
+
const urlParams = (isSpecificRequest ? `auth_type=apikey&zapikey=${input.apiKey}` : 'auth_type=oauth') + (inputSearchParamsString ? `&${inputSearchParamsString}` : '');
|
|
828
|
+
const relativeUrl = `/v2/functions/${input.functionName}/actions/execute?${urlParams}`;
|
|
829
|
+
const baseUrl = isSpecificRequest && input.apiUrl != null ? zohoRecruitConfigApiUrl(input.apiUrl) : '';
|
|
830
|
+
const url = `${baseUrl}${relativeUrl}`;
|
|
831
|
+
return context.fetchJson(url, zohoRecruitApiFetchJsonInput('POST')).then((x) => {
|
|
832
|
+
if (x.code === 'success') {
|
|
833
|
+
return x.details;
|
|
834
|
+
}
|
|
835
|
+
else {
|
|
836
|
+
throw new ZohoRecruitExecuteRestApiFunctionError(x);
|
|
837
|
+
}
|
|
838
|
+
});
|
|
839
|
+
};
|
|
883
840
|
}
|
|
884
841
|
// MARK: Util
|
|
885
842
|
function zohoRecruitUrlSearchParamsMinusModule(...input) {
|
|
886
|
-
|
|
887
|
-
omitKeys: 'module'
|
|
888
|
-
});
|
|
843
|
+
return makeUrlSearchParams(input, { omitKeys: 'module' });
|
|
889
844
|
}
|
|
890
845
|
function zohoRecruitUrlSearchParamsMinusIdAndModule(...input) {
|
|
891
|
-
|
|
892
|
-
omitKeys: ['id', 'module']
|
|
893
|
-
});
|
|
846
|
+
return makeUrlSearchParams(input, { omitKeys: ['id', 'module'] });
|
|
894
847
|
}
|
|
895
848
|
/**
|
|
896
849
|
* @deprecated use makeUrlSearchParams instead.
|
|
897
850
|
*/
|
|
898
851
|
const zohoRecruitUrlSearchParams = makeUrlSearchParams;
|
|
899
852
|
function zohoRecruitApiFetchJsonInput(method, body) {
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
853
|
+
const result = {
|
|
854
|
+
method,
|
|
855
|
+
body: body ?? undefined
|
|
856
|
+
};
|
|
857
|
+
return result;
|
|
905
858
|
}
|
|
906
859
|
function zohoRecruitChangeObjectLikeResponseSuccessAndErrorPairs(response) {
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
}
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
return result;
|
|
860
|
+
const { data } = response;
|
|
861
|
+
const successItems = [];
|
|
862
|
+
const errorItems = [];
|
|
863
|
+
data.forEach((x) => {
|
|
864
|
+
if (x.status === ZOHO_SUCCESS_STATUS) {
|
|
865
|
+
successItems.push(x);
|
|
866
|
+
}
|
|
867
|
+
else {
|
|
868
|
+
errorItems.push(x);
|
|
869
|
+
}
|
|
870
|
+
});
|
|
871
|
+
const result = {
|
|
872
|
+
...response,
|
|
873
|
+
successItems,
|
|
874
|
+
errorItems
|
|
875
|
+
};
|
|
876
|
+
return result;
|
|
925
877
|
}
|
|
926
878
|
function zohoRecruitMultiRecordResult(input, results) {
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
879
|
+
const successItems = [];
|
|
880
|
+
const errorItems = [];
|
|
881
|
+
input.forEach((x, i) => {
|
|
882
|
+
const result = results[i];
|
|
883
|
+
if (result.status === ZOHO_SUCCESS_STATUS) {
|
|
884
|
+
successItems.push({
|
|
885
|
+
input: x,
|
|
886
|
+
result: result
|
|
887
|
+
});
|
|
888
|
+
}
|
|
889
|
+
else {
|
|
890
|
+
errorItems.push({
|
|
891
|
+
input: x,
|
|
892
|
+
result: result
|
|
893
|
+
});
|
|
894
|
+
}
|
|
895
|
+
});
|
|
896
|
+
const result = {
|
|
897
|
+
successItems,
|
|
898
|
+
errorItems
|
|
899
|
+
};
|
|
900
|
+
return result;
|
|
948
901
|
}
|
|
949
902
|
/**
|
|
950
903
|
* @deprecated Use zohoRecruitInsertRecord instead.
|
|
@@ -1024,97 +977,82 @@ const executeRestApiFunction = zohoRecruitExecuteRestApiFunction;
|
|
|
1024
977
|
* @returns
|
|
1025
978
|
*/
|
|
1026
979
|
function zohoRecruitAssociateCandidateRecordsWithJobOpenings(context) {
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
980
|
+
return (input) => context.fetchJson(`/v2/${ZOHO_RECRUIT_CANDIDATES_MODULE}/actions/associate`, zohoRecruitApiFetchJsonInput('PUT', { data: asArray(input) })).then((x) => {
|
|
981
|
+
const resultInputMap = x.data.map(() => input); // assign "input" to each value for now
|
|
982
|
+
const result = zohoRecruitMultiRecordResult(resultInputMap, x.data);
|
|
983
|
+
const { included: alreadyAssociatedErrorItems, excluded: otherErrorItems } = separateValues(result.errorItems, (x) => {
|
|
984
|
+
return x.result.code === ZOHO_FAILURE_ERROR_CODE && x.result.details.error[0].code === ZOHO_RECRUIT_ALREADY_ASSOCIATED_ERROR_CODE;
|
|
985
|
+
});
|
|
986
|
+
return {
|
|
987
|
+
...result,
|
|
988
|
+
errorItems: otherErrorItems,
|
|
989
|
+
alreadyAssociatedErrorItems,
|
|
990
|
+
allErrorItems: result.errorItems
|
|
991
|
+
};
|
|
1037
992
|
});
|
|
1038
|
-
return {
|
|
1039
|
-
...result,
|
|
1040
|
-
errorItems: otherErrorItems,
|
|
1041
|
-
alreadyAssociatedErrorItems,
|
|
1042
|
-
allErrorItems: result.errorItems
|
|
1043
|
-
};
|
|
1044
|
-
});
|
|
1045
993
|
}
|
|
1046
994
|
function zohoRecruitSearchAssociatedRecords(context) {
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
995
|
+
return (input) => {
|
|
996
|
+
return context.fetchJson(`/v2/${input.module}/${input.id}/associate?${zohoRecruitUrlSearchParamsMinusIdAndModule(input).toString()}`, zohoRecruitApiFetchJsonInput('GET')).then((x) => {
|
|
997
|
+
const result = x ?? emptyZohoPageResult();
|
|
998
|
+
return result;
|
|
999
|
+
});
|
|
1000
|
+
};
|
|
1053
1001
|
}
|
|
1054
1002
|
function zohoRecruitSearchCandidateAssociatedJobOpeningRecords(context) {
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1003
|
+
const searchAssociatedRecordsFactory = zohoRecruitSearchAssociatedRecords(context);
|
|
1004
|
+
return (input) => {
|
|
1005
|
+
return searchAssociatedRecordsFactory({
|
|
1006
|
+
...input,
|
|
1007
|
+
module: ZOHO_RECRUIT_CANDIDATES_MODULE
|
|
1008
|
+
});
|
|
1009
|
+
};
|
|
1062
1010
|
}
|
|
1063
1011
|
function zohoRecruitSearchCandidateAssociatedJobOpeningRecordsPageFactory(context) {
|
|
1064
|
-
|
|
1012
|
+
return zohoFetchPageFactory(zohoRecruitSearchCandidateAssociatedJobOpeningRecords(context));
|
|
1065
1013
|
}
|
|
1066
1014
|
function zohoRecruitSearchJobOpeningAssociatedCandidateRecords(context, jobOpeningModuleName = ZOHO_RECRUIT_JOB_OPENINGS_MODULE) {
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1015
|
+
const searchAssociatedRecordsFactory = zohoRecruitSearchAssociatedRecords(context);
|
|
1016
|
+
return (input) => {
|
|
1017
|
+
return searchAssociatedRecordsFactory({
|
|
1018
|
+
...input,
|
|
1019
|
+
module: jobOpeningModuleName
|
|
1020
|
+
});
|
|
1021
|
+
};
|
|
1074
1022
|
}
|
|
1075
1023
|
function zohoRecruitSearchJobOpeningAssociatedCandidateRecordsPageFactory(context) {
|
|
1076
|
-
|
|
1024
|
+
return zohoFetchPageFactory(zohoRecruitSearchJobOpeningAssociatedCandidateRecords(context));
|
|
1077
1025
|
}
|
|
1078
1026
|
|
|
1079
1027
|
function zohoRecruitCreateNotes(context) {
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
return zohoRecruitMultiRecordResult(asArray(input.data), x.data);
|
|
1084
|
-
});
|
|
1028
|
+
return (input) => context.fetchJson(`/v2/${ZOHO_RECRUIT_NOTES_MODULE}`, zohoRecruitApiFetchJsonInput('POST', { data: input.data })).then((x) => {
|
|
1029
|
+
return zohoRecruitMultiRecordResult(asArray(input.data), x.data);
|
|
1030
|
+
});
|
|
1085
1031
|
}
|
|
1086
1032
|
function zohoRecruitDeleteNotes(context) {
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
return zohoRecruitMultiRecordResult(asArray(input.ids), x.data);
|
|
1091
|
-
});
|
|
1033
|
+
return (input) => context.fetchJson(`/v2/${ZOHO_RECRUIT_NOTES_MODULE}?${makeUrlSearchParams({ ids: input.ids })}`, zohoRecruitApiFetchJsonInput('DELETE')).then((x) => {
|
|
1034
|
+
return zohoRecruitMultiRecordResult(asArray(input.ids), x.data);
|
|
1035
|
+
});
|
|
1092
1036
|
}
|
|
1093
1037
|
function zohoRecruitGetNotesForRecord(context) {
|
|
1094
|
-
|
|
1095
|
-
targetModule: ZOHO_RECRUIT_NOTES_MODULE
|
|
1096
|
-
});
|
|
1038
|
+
return zohoRecruitGetRelatedRecordsFunctionFactory(context)({ targetModule: ZOHO_RECRUIT_NOTES_MODULE });
|
|
1097
1039
|
}
|
|
1098
1040
|
function zohoRecruitGetNotesForRecordPageFactory(context) {
|
|
1099
|
-
|
|
1041
|
+
return zohoFetchPageFactory(zohoRecruitGetNotesForRecord(context));
|
|
1100
1042
|
}
|
|
1101
1043
|
function zohoRecruitCreateNotesForRecord(context) {
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
Parent_Id
|
|
1114
|
-
}))
|
|
1044
|
+
const createNotesInstance = zohoRecruitCreateNotes(context);
|
|
1045
|
+
return (input) => {
|
|
1046
|
+
const { module: se_module, id: Parent_Id, notes } = input;
|
|
1047
|
+
const createNotesRequest = {
|
|
1048
|
+
data: asArray(notes).map((x) => ({
|
|
1049
|
+
...x,
|
|
1050
|
+
se_module,
|
|
1051
|
+
Parent_Id
|
|
1052
|
+
}))
|
|
1053
|
+
};
|
|
1054
|
+
return createNotesInstance(createNotesRequest);
|
|
1115
1055
|
};
|
|
1116
|
-
return createNotesInstance(createNotesRequest);
|
|
1117
|
-
};
|
|
1118
1056
|
}
|
|
1119
1057
|
// MARK: Compat
|
|
1120
1058
|
/**
|
|
@@ -1139,25 +1077,18 @@ const getNotesForRecordPageFactory = zohoRecruitGetNotesForRecordPageFactory;
|
|
|
1139
1077
|
const createNotesForRecord = zohoRecruitCreateNotesForRecord;
|
|
1140
1078
|
|
|
1141
1079
|
function zohoRecruitCreateTagsForModule(context) {
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1080
|
+
return (input) => context.fetchJson(`/v2/settings/tags?${makeUrlSearchParams({ module: input.module })}`, zohoRecruitApiFetchJsonInput('POST', { tags: asArray(input.tags) })).then((x) => {
|
|
1081
|
+
const result = zohoRecruitMultiRecordResult(asArray(input.tags), x.tags);
|
|
1082
|
+
const { included: duplicateErrorItems, excluded: otherErrorItems } = separateValues(result.errorItems, (x) => {
|
|
1083
|
+
return x.result.code === ZOHO_DUPLICATE_DATA_ERROR_CODE;
|
|
1084
|
+
});
|
|
1085
|
+
return {
|
|
1086
|
+
...result,
|
|
1087
|
+
errorItems: otherErrorItems,
|
|
1088
|
+
duplicateErrorItems,
|
|
1089
|
+
allErrorItems: result.errorItems
|
|
1090
|
+
};
|
|
1153
1091
|
});
|
|
1154
|
-
return {
|
|
1155
|
-
...result,
|
|
1156
|
-
errorItems: otherErrorItems,
|
|
1157
|
-
duplicateErrorItems,
|
|
1158
|
-
allErrorItems: result.errorItems
|
|
1159
|
-
};
|
|
1160
|
-
});
|
|
1161
1092
|
}
|
|
1162
1093
|
/**
|
|
1163
1094
|
* Returns the list of tags within a module.
|
|
@@ -1168,19 +1099,16 @@ function zohoRecruitCreateTagsForModule(context) {
|
|
|
1168
1099
|
* @returns
|
|
1169
1100
|
*/
|
|
1170
1101
|
function zohoRecruitGetTagsForModule(context) {
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
data: x.tags
|
|
1179
|
-
};
|
|
1180
|
-
});
|
|
1102
|
+
return (input) => context.fetchJson(`/v2/settings/tags?${makeUrlSearchParams({ module: input.module, my_tags: input.my_tags })}`, zohoRecruitApiFetchJsonInput('GET')).then((x) => {
|
|
1103
|
+
// NOTE: This doesn't follow the api documentation, and instead is a normal page result except it has "tags" instead of "data".
|
|
1104
|
+
return {
|
|
1105
|
+
...x,
|
|
1106
|
+
data: x.tags
|
|
1107
|
+
};
|
|
1108
|
+
});
|
|
1181
1109
|
}
|
|
1182
1110
|
function zohoRecruitGetTagsForModulePageFactory(context) {
|
|
1183
|
-
|
|
1111
|
+
return zohoFetchPageFactory(zohoRecruitGetTagsForModule(context));
|
|
1184
1112
|
}
|
|
1185
1113
|
// MARK: Add Tag To Record
|
|
1186
1114
|
/**
|
|
@@ -1196,18 +1124,15 @@ const ZOHO_RECRUIT_ADD_TAGS_TO_RECORDS_MAX_IDS_ALLOWED = 100;
|
|
|
1196
1124
|
* @returns
|
|
1197
1125
|
*/
|
|
1198
1126
|
function zohoRecruitAddTagsToRecords(context) {
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
return zohoRecruitMultiRecordResult(resultInputMap, x.data);
|
|
1209
|
-
});
|
|
1210
|
-
};
|
|
1127
|
+
return (input) => {
|
|
1128
|
+
if (Array.isArray(input.ids) && input.ids.length > ZOHO_RECRUIT_ADD_TAGS_TO_RECORDS_MAX_IDS_ALLOWED) {
|
|
1129
|
+
throw new Error(`Cannot add tags to more than ${ZOHO_RECRUIT_ADD_TAGS_TO_RECORDS_MAX_IDS_ALLOWED} records at once.`);
|
|
1130
|
+
}
|
|
1131
|
+
return context.fetchJson(`/v2/${ZOHO_RECRUIT_CANDIDATES_MODULE}/actions/add_tags?${makeUrlSearchParams({ tag_names: input.tag_names, ids: input.ids })}`, zohoRecruitApiFetchJsonInput('POST')).then((x) => {
|
|
1132
|
+
const resultInputMap = x.data.map(() => input); // assign "input" to each value for now
|
|
1133
|
+
return zohoRecruitMultiRecordResult(resultInputMap, x.data);
|
|
1134
|
+
});
|
|
1135
|
+
};
|
|
1211
1136
|
}
|
|
1212
1137
|
// MARK: Remove Tag From Record
|
|
1213
1138
|
/**
|
|
@@ -1223,18 +1148,15 @@ const ZOHO_RECRUIT_REMOVE_TAGS_FROM_RECORDS_MAX_IDS_ALLOWED = 100;
|
|
|
1223
1148
|
* @returns
|
|
1224
1149
|
*/
|
|
1225
1150
|
function zohoRecruitRemoveTagsFromRecords(context) {
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
return zohoRecruitMultiRecordResult(resultInputMap, x.data);
|
|
1236
|
-
});
|
|
1237
|
-
};
|
|
1151
|
+
return (input) => {
|
|
1152
|
+
if (Array.isArray(input.ids) && input.ids.length > ZOHO_RECRUIT_REMOVE_TAGS_FROM_RECORDS_MAX_IDS_ALLOWED) {
|
|
1153
|
+
throw new Error(`Cannot remove tags from more than ${ZOHO_RECRUIT_REMOVE_TAGS_FROM_RECORDS_MAX_IDS_ALLOWED} records at once.`);
|
|
1154
|
+
}
|
|
1155
|
+
return context.fetchJson(`/v2/${ZOHO_RECRUIT_CANDIDATES_MODULE}/actions/remove_tags?${makeUrlSearchParams({ tag_names: input.tag_names, ids: input.ids })}`, zohoRecruitApiFetchJsonInput('POST')).then((x) => {
|
|
1156
|
+
const resultInputMap = x.data.map(() => input); // assign "input" to each value for now
|
|
1157
|
+
return zohoRecruitMultiRecordResult(resultInputMap, x.data);
|
|
1158
|
+
});
|
|
1159
|
+
};
|
|
1238
1160
|
}
|
|
1239
1161
|
// MARK: Compat
|
|
1240
1162
|
/**
|
|
@@ -1268,47 +1190,47 @@ const ZOHO_ACCOUNTS_INVALID_CLIENT_ERROR_CODE = 'invalid_client';
|
|
|
1268
1190
|
* Thrown if the call to the Zoho API creating an access token using a refresh token fails.
|
|
1269
1191
|
*/
|
|
1270
1192
|
class ZohoAccountsAccessTokenError extends FetchRequestFactoryError {
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1193
|
+
errorCode;
|
|
1194
|
+
constructor(errorCode) {
|
|
1195
|
+
super(`ZohoAccountsAccessTokenError: ${errorCode}`);
|
|
1196
|
+
this.errorCode = errorCode;
|
|
1197
|
+
}
|
|
1276
1198
|
}
|
|
1277
1199
|
/**
|
|
1278
1200
|
* Thrown if a valid ZohoAccessToken cannot be retrieved successfully.
|
|
1279
1201
|
*/
|
|
1280
1202
|
class ZohoAccountsAuthFailureError extends FetchRequestFactoryError {
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1203
|
+
reason;
|
|
1204
|
+
constructor(reason) {
|
|
1205
|
+
super(`Failed to retrieve proper authentication for the API call. Reason: ${reason}`);
|
|
1206
|
+
this.reason = reason;
|
|
1207
|
+
}
|
|
1286
1208
|
}
|
|
1287
1209
|
const logZohoAccountsErrorToConsole = logZohoServerErrorFunction('ZohoAccounts');
|
|
1288
1210
|
async function parseZohoAccountsError(responseError) {
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1211
|
+
const data = await responseError.response.json().catch((x) => undefined);
|
|
1212
|
+
let result;
|
|
1213
|
+
if (data) {
|
|
1214
|
+
result = parseZohoAccountsServerErrorResponseData(data, responseError);
|
|
1215
|
+
}
|
|
1216
|
+
return result;
|
|
1295
1217
|
}
|
|
1296
1218
|
function parseZohoAccountsServerErrorResponseData(errorResponseData, responseError) {
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1219
|
+
let result;
|
|
1220
|
+
const error = errorResponseData.error;
|
|
1221
|
+
if (error) {
|
|
1222
|
+
const errorData = zohoServerErrorData(error);
|
|
1223
|
+
switch (errorData.code) {
|
|
1224
|
+
case ZOHO_ACCOUNTS_INVALID_CODE_ERROR_CODE:
|
|
1225
|
+
case ZOHO_ACCOUNTS_INVALID_CLIENT_ERROR_CODE:
|
|
1226
|
+
result = new ZohoAccountsAccessTokenError(errorData.code);
|
|
1227
|
+
break;
|
|
1228
|
+
default:
|
|
1229
|
+
result = parseZohoServerErrorResponseData(errorResponseData, responseError);
|
|
1230
|
+
break;
|
|
1231
|
+
}
|
|
1309
1232
|
}
|
|
1310
|
-
|
|
1311
|
-
return result;
|
|
1233
|
+
return result;
|
|
1312
1234
|
}
|
|
1313
1235
|
const interceptZohoAccounts200StatusWithErrorResponse = interceptZohoErrorResponseFactory(parseZohoAccountsServerErrorResponseData);
|
|
1314
1236
|
const handleZohoAccountsErrorFetch = handleZohoErrorFetchFactory(parseZohoAccountsError, logZohoAccountsErrorToConsole);
|
|
@@ -1317,129 +1239,115 @@ const handleZohoAccountsErrorFetch = handleZohoErrorFetchFactory(parseZohoAccoun
|
|
|
1317
1239
|
* Generates a new ZohoAccessTokenStringFactory.
|
|
1318
1240
|
*/
|
|
1319
1241
|
function zohoAccessTokenStringFactory(zohoAccessTokenFactory) {
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1242
|
+
return async () => {
|
|
1243
|
+
const token = await zohoAccessTokenFactory();
|
|
1244
|
+
if (!token?.accessToken) {
|
|
1245
|
+
throw new ZohoAccountsAuthFailureError();
|
|
1246
|
+
}
|
|
1247
|
+
return token.accessToken;
|
|
1248
|
+
};
|
|
1327
1249
|
}
|
|
1328
1250
|
|
|
1329
|
-
const DEFAULT_ZOHO_RATE_LIMITED_TOO_MANY_REQUETS_LOG_FUNCTION = headers => {
|
|
1330
|
-
|
|
1251
|
+
const DEFAULT_ZOHO_RATE_LIMITED_TOO_MANY_REQUETS_LOG_FUNCTION = (headers) => {
|
|
1252
|
+
console.warn(`zohoRateLimitedFetchHandler(): Too many requests made. The limit is ${headers.limit} requests per reset period. Will be reset at ${headers.resetAt}.`);
|
|
1331
1253
|
};
|
|
1332
1254
|
function zohoRateLimitedFetchHandler(config) {
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
};
|
|
1347
|
-
}
|
|
1348
|
-
const defaultConfig = configForLimit(defaultLimit);
|
|
1349
|
-
const rateLimiter = resetPeriodPromiseRateLimiter(defaultConfig);
|
|
1350
|
-
return rateLimitedFetchHandler({
|
|
1351
|
-
rateLimiter,
|
|
1352
|
-
updateWithResponse: function (response, fetchResponseError) {
|
|
1353
|
-
const hasLimitHeader = response.headers.has(ZOHO_RATE_LIMIT_REMAINING_HEADER);
|
|
1354
|
-
let shouldRetry = false;
|
|
1355
|
-
let enabled = false;
|
|
1356
|
-
if (hasLimitHeader) {
|
|
1357
|
-
const headerDetails = zohoRateLimitHeaderDetails(response.headers);
|
|
1358
|
-
if (headerDetails) {
|
|
1359
|
-
const {
|
|
1360
|
-
limit,
|
|
1361
|
-
resetAt,
|
|
1362
|
-
remaining
|
|
1363
|
-
} = headerDetails;
|
|
1364
|
-
if (limit !== defaultLimit) {
|
|
1365
|
-
const newConfig = configForLimit(limit, resetAt);
|
|
1366
|
-
rateLimiter.setConfig(newConfig, false);
|
|
1367
|
-
}
|
|
1368
|
-
rateLimiter.setRemainingLimit(remaining);
|
|
1369
|
-
rateLimiter.setNextResetAt(resetAt);
|
|
1370
|
-
enabled = true;
|
|
1371
|
-
// only retry if it's a TOO MANY REQUESTS error
|
|
1372
|
-
if (response.status === ZOHO_TOO_MANY_REQUESTS_HTTP_STATUS_CODE) {
|
|
1373
|
-
shouldRetry = true;
|
|
1374
|
-
try {
|
|
1375
|
-
onTooManyRequests?.(headerDetails, response, fetchResponseError);
|
|
1376
|
-
} catch (e) {}
|
|
1377
|
-
}
|
|
1378
|
-
}
|
|
1379
|
-
}
|
|
1380
|
-
rateLimiter.setEnabled(enabled);
|
|
1381
|
-
return shouldRetry;
|
|
1255
|
+
const onTooManyRequests = config?.onTooManyRequests !== false ? (config?.onTooManyRequests ?? DEFAULT_ZOHO_RATE_LIMITED_TOO_MANY_REQUETS_LOG_FUNCTION) : undefined;
|
|
1256
|
+
const defaultLimit = config?.maxRateLimit ?? DEFAULT_ZOHO_API_RATE_LIMIT;
|
|
1257
|
+
const defaultResetPeriod = config?.resetPeriod ?? DEFAULT_ZOHO_API_RATE_LIMIT_RESET_PERIOD;
|
|
1258
|
+
function configForLimit(limit, resetAt) {
|
|
1259
|
+
return {
|
|
1260
|
+
limit: defaultLimit,
|
|
1261
|
+
startLimitAt: Math.ceil(limit / 10), // can do 10% of the requests of the limit before rate limiting begins
|
|
1262
|
+
cooldownRate: 1.2 * (limit / (defaultResetPeriod / MS_IN_SECOND)),
|
|
1263
|
+
exponentRate: 1.08,
|
|
1264
|
+
maxWaitTime: MS_IN_SECOND * 10,
|
|
1265
|
+
resetPeriod: defaultResetPeriod,
|
|
1266
|
+
resetAt
|
|
1267
|
+
};
|
|
1382
1268
|
}
|
|
1383
|
-
|
|
1269
|
+
const defaultConfig = configForLimit(defaultLimit);
|
|
1270
|
+
const rateLimiter = resetPeriodPromiseRateLimiter(defaultConfig);
|
|
1271
|
+
return rateLimitedFetchHandler({
|
|
1272
|
+
rateLimiter,
|
|
1273
|
+
updateWithResponse: function (response, fetchResponseError) {
|
|
1274
|
+
const hasLimitHeader = response.headers.has(ZOHO_RATE_LIMIT_REMAINING_HEADER);
|
|
1275
|
+
let shouldRetry = false;
|
|
1276
|
+
let enabled = false;
|
|
1277
|
+
if (hasLimitHeader) {
|
|
1278
|
+
const headerDetails = zohoRateLimitHeaderDetails(response.headers);
|
|
1279
|
+
if (headerDetails) {
|
|
1280
|
+
const { limit, resetAt, remaining } = headerDetails;
|
|
1281
|
+
if (limit !== defaultLimit) {
|
|
1282
|
+
const newConfig = configForLimit(limit, resetAt);
|
|
1283
|
+
rateLimiter.setConfig(newConfig, false);
|
|
1284
|
+
}
|
|
1285
|
+
rateLimiter.setRemainingLimit(remaining);
|
|
1286
|
+
rateLimiter.setNextResetAt(resetAt);
|
|
1287
|
+
enabled = true;
|
|
1288
|
+
// only retry if it's a TOO MANY REQUESTS error
|
|
1289
|
+
if (response.status === ZOHO_TOO_MANY_REQUESTS_HTTP_STATUS_CODE) {
|
|
1290
|
+
shouldRetry = true;
|
|
1291
|
+
try {
|
|
1292
|
+
onTooManyRequests?.(headerDetails, response, fetchResponseError);
|
|
1293
|
+
}
|
|
1294
|
+
catch (e) { }
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
rateLimiter.setEnabled(enabled);
|
|
1299
|
+
return shouldRetry;
|
|
1300
|
+
}
|
|
1301
|
+
});
|
|
1384
1302
|
}
|
|
1385
1303
|
|
|
1386
1304
|
function zohoRecruitFactory(factoryConfig) {
|
|
1387
|
-
|
|
1388
|
-
accountsContext
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1305
|
+
const { accountsContext } = factoryConfig;
|
|
1306
|
+
const accessTokenStringFactory = zohoAccessTokenStringFactory(accountsContext.loadAccessToken);
|
|
1307
|
+
const fetchHandler = zohoRateLimitedFetchHandler(factoryConfig.rateLimiterConfig);
|
|
1308
|
+
const { logZohoServerErrorFunction, fetchFactory = (input) => fetchApiFetchService.makeFetch({
|
|
1309
|
+
baseUrl: input.apiUrl,
|
|
1310
|
+
baseRequest: async () => ({
|
|
1311
|
+
headers: {
|
|
1312
|
+
'Content-Type': 'application/json',
|
|
1313
|
+
Authorization: `Bearer ${await accessTokenStringFactory()}`
|
|
1314
|
+
}
|
|
1315
|
+
}),
|
|
1316
|
+
fetchHandler,
|
|
1317
|
+
timeout: 20 * 1000, // 20 second timeout
|
|
1318
|
+
requireOkResponse: true, // enforce ok response
|
|
1319
|
+
useTimeout: true // use timeout
|
|
1320
|
+
}) } = factoryConfig;
|
|
1321
|
+
return (config) => {
|
|
1322
|
+
if (!config.apiUrl) {
|
|
1323
|
+
throw new Error('ZohoConfig missing api url.');
|
|
1400
1324
|
}
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
handleFetchJsonParseErrorFunction: returnNullHandleFetchJsonParseErrorFunction
|
|
1427
|
-
});
|
|
1428
|
-
const recruitContext = {
|
|
1429
|
-
fetch,
|
|
1430
|
-
fetchJson,
|
|
1431
|
-
accessTokenStringFactory,
|
|
1432
|
-
config: {
|
|
1433
|
-
...config,
|
|
1434
|
-
apiUrl
|
|
1435
|
-
},
|
|
1436
|
-
zohoRateLimiter: fetchHandler._rateLimiter
|
|
1437
|
-
};
|
|
1438
|
-
const zohoRecruit = {
|
|
1439
|
-
recruitContext
|
|
1325
|
+
const apiUrl = zohoRecruitConfigApiUrl(config.apiUrl);
|
|
1326
|
+
const baseFetch = fetchFactory({ apiUrl });
|
|
1327
|
+
const fetch = handleZohoRecruitErrorFetch(baseFetch, logZohoServerErrorFunction, (x) => {
|
|
1328
|
+
if (x instanceof ZohoInvalidTokenError) {
|
|
1329
|
+
accountsContext.loadAccessToken.resetAccessToken();
|
|
1330
|
+
}
|
|
1331
|
+
});
|
|
1332
|
+
const fetchJson = fetchJsonFunction(fetch, {
|
|
1333
|
+
interceptJsonResponse: interceptZohoRecruit200StatusWithErrorResponse, // intercept errors that return status 200
|
|
1334
|
+
handleFetchJsonParseErrorFunction: returnNullHandleFetchJsonParseErrorFunction
|
|
1335
|
+
});
|
|
1336
|
+
const recruitContext = {
|
|
1337
|
+
fetch,
|
|
1338
|
+
fetchJson,
|
|
1339
|
+
accessTokenStringFactory,
|
|
1340
|
+
config: {
|
|
1341
|
+
...config,
|
|
1342
|
+
apiUrl
|
|
1343
|
+
},
|
|
1344
|
+
zohoRateLimiter: fetchHandler._rateLimiter
|
|
1345
|
+
};
|
|
1346
|
+
const zohoRecruit = {
|
|
1347
|
+
recruitContext
|
|
1348
|
+
};
|
|
1349
|
+
return zohoRecruit;
|
|
1440
1350
|
};
|
|
1441
|
-
return zohoRecruit;
|
|
1442
|
-
};
|
|
1443
1351
|
}
|
|
1444
1352
|
|
|
1445
1353
|
/**
|
|
@@ -1449,14 +1357,14 @@ const ZOHO_RECRUIT_TAG_NAME_MAX_LENGTH = 25;
|
|
|
1449
1357
|
|
|
1450
1358
|
const ZOHO_CRM_SERVICE_NAME = 'crm';
|
|
1451
1359
|
function zohoCrmConfigApiUrl(input) {
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1360
|
+
switch (input) {
|
|
1361
|
+
case 'sandbox':
|
|
1362
|
+
return 'https://crmsandbox.zoho.com/crm';
|
|
1363
|
+
case 'production':
|
|
1364
|
+
return 'https://www.zohoapis.com/crm';
|
|
1365
|
+
default:
|
|
1366
|
+
return input;
|
|
1367
|
+
}
|
|
1460
1368
|
}
|
|
1461
1369
|
|
|
1462
1370
|
/**
|
|
@@ -1512,79 +1420,83 @@ const ZOHO_CRM_ALREADY_ASSOCIATED_ERROR_CODE = 'ALREADY_ASSOCIATED';
|
|
|
1512
1420
|
* Thrown when a record with the given id has no content. Typically also means it does not exist.
|
|
1513
1421
|
*/
|
|
1514
1422
|
class ZohoCrmRecordNoContentError extends BaseError {
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
}
|
|
1523
|
-
class ZohoCrmRecordCrudError extends ZohoServerError {
|
|
1524
|
-
|
|
1525
|
-
class
|
|
1423
|
+
moduleName;
|
|
1424
|
+
recordId;
|
|
1425
|
+
constructor(moduleName, recordId) {
|
|
1426
|
+
super(`There was no content or matching records for the content. It may not exist.`);
|
|
1427
|
+
this.moduleName = moduleName;
|
|
1428
|
+
this.recordId = recordId;
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
class ZohoCrmRecordCrudError extends ZohoServerError {
|
|
1432
|
+
}
|
|
1433
|
+
class ZohoCrmRecordCrudMandatoryFieldNotFoundError extends ZohoCrmRecordCrudError {
|
|
1434
|
+
}
|
|
1435
|
+
class ZohoCrmRecordCrudDuplicateDataError extends ZohoCrmRecordCrudError {
|
|
1436
|
+
}
|
|
1526
1437
|
class ZohoCrmRecordCrudInvalidDataError extends ZohoCrmRecordCrudError {
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1438
|
+
get invalidFieldDetails() {
|
|
1439
|
+
return this.error.details;
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
class ZohoCrmRecordCrudNoMatchingRecordError extends ZohoCrmRecordCrudInvalidDataError {
|
|
1530
1443
|
}
|
|
1531
|
-
class ZohoCrmRecordCrudNoMatchingRecordError extends ZohoCrmRecordCrudInvalidDataError {}
|
|
1532
1444
|
function zohoCrmRecordCrudError(error) {
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1445
|
+
let result;
|
|
1446
|
+
switch (error.code) {
|
|
1447
|
+
case ZOHO_INVALID_DATA_ERROR_CODE:
|
|
1448
|
+
const invalidDataError = new ZohoCrmRecordCrudInvalidDataError(error);
|
|
1449
|
+
if (invalidDataError.invalidFieldDetails.api_name === 'id') {
|
|
1450
|
+
result = new ZohoCrmRecordCrudNoMatchingRecordError(error);
|
|
1451
|
+
}
|
|
1452
|
+
else {
|
|
1453
|
+
result = invalidDataError;
|
|
1454
|
+
}
|
|
1455
|
+
break;
|
|
1456
|
+
case ZOHO_MANDATORY_NOT_FOUND_ERROR_CODE:
|
|
1457
|
+
result = new ZohoCrmRecordCrudMandatoryFieldNotFoundError(error);
|
|
1458
|
+
break;
|
|
1459
|
+
case ZOHO_DUPLICATE_DATA_ERROR_CODE:
|
|
1460
|
+
result = new ZohoCrmRecordCrudDuplicateDataError(error);
|
|
1461
|
+
break;
|
|
1462
|
+
default:
|
|
1463
|
+
result = new ZohoCrmRecordCrudError(error);
|
|
1464
|
+
break;
|
|
1465
|
+
}
|
|
1466
|
+
return result;
|
|
1554
1467
|
}
|
|
1555
1468
|
function assertZohoCrmRecordDataArrayResultHasContent(moduleName, recordId) {
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1469
|
+
return (x) => {
|
|
1470
|
+
if (x == null || !x.data?.length) {
|
|
1471
|
+
throw new ZohoCrmRecordNoContentError(moduleName, recordId);
|
|
1472
|
+
}
|
|
1473
|
+
else {
|
|
1474
|
+
return x;
|
|
1475
|
+
}
|
|
1476
|
+
};
|
|
1563
1477
|
}
|
|
1564
|
-
const logZohoCrmErrorToConsole = logZohoServerErrorFunction('ZohoCrm', {
|
|
1565
|
-
logDataArrayErrors: false
|
|
1566
|
-
});
|
|
1478
|
+
const logZohoCrmErrorToConsole = logZohoServerErrorFunction('ZohoCrm', { logDataArrayErrors: false });
|
|
1567
1479
|
async function parseZohoCrmError(responseError) {
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1480
|
+
const data = await responseError.response.json().catch((x) => undefined);
|
|
1481
|
+
let result;
|
|
1482
|
+
if (data) {
|
|
1483
|
+
result = parseZohoCrmServerErrorResponseData(data, responseError);
|
|
1484
|
+
}
|
|
1485
|
+
return result;
|
|
1574
1486
|
}
|
|
1575
1487
|
function parseZohoCrmServerErrorResponseData(errorResponseData, responseError) {
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1488
|
+
let result;
|
|
1489
|
+
const error = tryFindZohoServerErrorData(errorResponseData, responseError);
|
|
1490
|
+
if (error) {
|
|
1491
|
+
const errorData = zohoServerErrorData(error);
|
|
1492
|
+
switch (errorData.code) {
|
|
1493
|
+
// TODO: Add crm-specific error codes here.
|
|
1494
|
+
default:
|
|
1495
|
+
result = parseZohoServerErrorResponseData(errorResponseData, responseError);
|
|
1496
|
+
break;
|
|
1497
|
+
}
|
|
1585
1498
|
}
|
|
1586
|
-
|
|
1587
|
-
return result;
|
|
1499
|
+
return result;
|
|
1588
1500
|
}
|
|
1589
1501
|
const interceptZohoCrm200StatusWithErrorResponse = interceptZohoErrorResponseFactory(parseZohoCrmServerErrorResponseData);
|
|
1590
1502
|
const handleZohoCrmErrorFetch = handleZohoErrorFetchFactory(parseZohoCrmError, logZohoCrmErrorToConsole);
|
|
@@ -1602,28 +1514,25 @@ const ZOHO_CRM_CRUD_FUNCTION_MAX_RECORDS_LIMIT = 100;
|
|
|
1602
1514
|
* @returns
|
|
1603
1515
|
*/
|
|
1604
1516
|
function updateRecordLikeFunction(context, fetchUrlPrefix, fetchMethod) {
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
}
|
|
1625
|
-
}
|
|
1626
|
-
});
|
|
1517
|
+
return (({ data, module }) => context
|
|
1518
|
+
.fetchJson(`/v8/${module}${fetchUrlPrefix}`, zohoCrmApiFetchJsonInput(fetchMethod, { data: asArray(data) }))
|
|
1519
|
+
.catch(zohoCrmCatchZohoCrmChangeObjectLikeResponseError)
|
|
1520
|
+
.then((x) => {
|
|
1521
|
+
const isInputMultipleItems = Array.isArray(data);
|
|
1522
|
+
const result = zohoCrmMultiRecordResult(asArray(data), x.data);
|
|
1523
|
+
if (isInputMultipleItems) {
|
|
1524
|
+
return result;
|
|
1525
|
+
}
|
|
1526
|
+
else {
|
|
1527
|
+
const { successItems, errorItems } = result;
|
|
1528
|
+
if (errorItems[0] != null) {
|
|
1529
|
+
throw zohoCrmRecordCrudError(errorItems[0].result);
|
|
1530
|
+
}
|
|
1531
|
+
else {
|
|
1532
|
+
return successItems[0].result.details;
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
}));
|
|
1627
1536
|
}
|
|
1628
1537
|
/**
|
|
1629
1538
|
* Inserts one or more records into Crm.
|
|
@@ -1634,7 +1543,7 @@ function updateRecordLikeFunction(context, fetchUrlPrefix, fetchMethod) {
|
|
|
1634
1543
|
* @returns
|
|
1635
1544
|
*/
|
|
1636
1545
|
function zohoCrmInsertRecord(context) {
|
|
1637
|
-
|
|
1546
|
+
return updateRecordLikeFunction(context, '', 'POST');
|
|
1638
1547
|
}
|
|
1639
1548
|
/**
|
|
1640
1549
|
* Updates or inserts one or more records in Crm.
|
|
@@ -1645,7 +1554,7 @@ function zohoCrmInsertRecord(context) {
|
|
|
1645
1554
|
* @returns
|
|
1646
1555
|
*/
|
|
1647
1556
|
function zohoCrmUpsertRecord(context) {
|
|
1648
|
-
|
|
1557
|
+
return updateRecordLikeFunction(context, '/upsert', 'POST');
|
|
1649
1558
|
}
|
|
1650
1559
|
/**
|
|
1651
1560
|
* Updates one or more records in Crm.
|
|
@@ -1656,7 +1565,7 @@ function zohoCrmUpsertRecord(context) {
|
|
|
1656
1565
|
* @returns
|
|
1657
1566
|
*/
|
|
1658
1567
|
function zohoCrmUpdateRecord(context) {
|
|
1659
|
-
|
|
1568
|
+
return updateRecordLikeFunction(context, '', 'PUT');
|
|
1660
1569
|
}
|
|
1661
1570
|
/**
|
|
1662
1571
|
* Deletes one or more records from the given module.
|
|
@@ -1667,16 +1576,12 @@ function zohoCrmUpdateRecord(context) {
|
|
|
1667
1576
|
* @returns ZohoCrmDeleteRecordFunction
|
|
1668
1577
|
*/
|
|
1669
1578
|
function zohoCrmDeleteRecord(context) {
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
ids,
|
|
1677
|
-
wf_trigger
|
|
1678
|
-
})}`, zohoCrmApiFetchJsonInput('DELETE')).catch(zohoCrmCatchZohoCrmChangeObjectLikeResponseError).then(zohoCrmChangeObjectLikeResponseSuccessAndErrorPairs);
|
|
1679
|
-
};
|
|
1579
|
+
return ({ ids, module, wf_trigger }) => {
|
|
1580
|
+
return context
|
|
1581
|
+
.fetchJson(`/v8/${module}?${makeUrlSearchParams({ ids, wf_trigger })}`, zohoCrmApiFetchJsonInput('DELETE'))
|
|
1582
|
+
.catch(zohoCrmCatchZohoCrmChangeObjectLikeResponseError)
|
|
1583
|
+
.then(zohoCrmChangeObjectLikeResponseSuccessAndErrorPairs);
|
|
1584
|
+
};
|
|
1680
1585
|
}
|
|
1681
1586
|
/**
|
|
1682
1587
|
* Retrieves a specific record from the given module.
|
|
@@ -1687,7 +1592,10 @@ function zohoCrmDeleteRecord(context) {
|
|
|
1687
1592
|
* @returns
|
|
1688
1593
|
*/
|
|
1689
1594
|
function zohoCrmGetRecordById(context) {
|
|
1690
|
-
|
|
1595
|
+
return (input) => context
|
|
1596
|
+
.fetchJson(`/v8/${input.module}/${input.id}`, zohoCrmApiFetchJsonInput('GET'))
|
|
1597
|
+
.then(assertZohoCrmRecordDataArrayResultHasContent(input.module))
|
|
1598
|
+
.then((x) => x.data[0]);
|
|
1691
1599
|
}
|
|
1692
1600
|
/**
|
|
1693
1601
|
* Retrieves records from the given module. Used for paginating across all records.
|
|
@@ -1698,7 +1606,7 @@ function zohoCrmGetRecordById(context) {
|
|
|
1698
1606
|
* @returns
|
|
1699
1607
|
*/
|
|
1700
1608
|
function zohoCrmGetRecords(context) {
|
|
1701
|
-
|
|
1609
|
+
return ((input) => context.fetchJson(`/v8/${input.module}?${zohoCrmUrlSearchParamsMinusModule(input).toString()}`, zohoCrmApiFetchJsonInput('GET')));
|
|
1702
1610
|
}
|
|
1703
1611
|
/**
|
|
1704
1612
|
* Searches records from the given module.
|
|
@@ -1709,30 +1617,23 @@ function zohoCrmGetRecords(context) {
|
|
|
1709
1617
|
* @returns
|
|
1710
1618
|
*/
|
|
1711
1619
|
function zohoCrmSearchRecords(context) {
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
const urlParams = zohoCrmUrlSearchParamsMinusModule(baseInput);
|
|
1725
|
-
return urlParams;
|
|
1726
|
-
}
|
|
1727
|
-
return input => context.fetchJson(`/v8/${input.module}/search?${searchRecordsUrlSearchParams(input).toString()}`, zohoCrmApiFetchJsonInput('GET')).then(x => x ?? {
|
|
1728
|
-
data: [],
|
|
1729
|
-
info: {
|
|
1730
|
-
more_records: false
|
|
1620
|
+
function searchRecordsUrlSearchParams(input) {
|
|
1621
|
+
const baseInput = { ...input };
|
|
1622
|
+
delete baseInput.criteria;
|
|
1623
|
+
if (input.criteria != null) {
|
|
1624
|
+
const criteriaString = zohoCrmSearchRecordsCriteriaString(input.criteria);
|
|
1625
|
+
baseInput.criteria = criteriaString;
|
|
1626
|
+
}
|
|
1627
|
+
if (!baseInput.word && !input.cvid && !input.criteria && !input.email && !input.phone) {
|
|
1628
|
+
throw new Error('At least one of word, cvid, criteria, email, or phone must be provided');
|
|
1629
|
+
}
|
|
1630
|
+
const urlParams = zohoCrmUrlSearchParamsMinusModule(baseInput);
|
|
1631
|
+
return urlParams;
|
|
1731
1632
|
}
|
|
1732
|
-
|
|
1633
|
+
return ((input) => context.fetchJson(`/v8/${input.module}/search?${searchRecordsUrlSearchParams(input).toString()}`, zohoCrmApiFetchJsonInput('GET')).then((x) => x ?? { data: [], info: { more_records: false } }));
|
|
1733
1634
|
}
|
|
1734
1635
|
function zohoCrmSearchRecordsPageFactory(context) {
|
|
1735
|
-
|
|
1636
|
+
return zohoFetchPageFactory(zohoCrmSearchRecords(context));
|
|
1736
1637
|
}
|
|
1737
1638
|
/**
|
|
1738
1639
|
* Creates a ZohoCrmGetRelatedRecordsFunctionFactory, which can be used to create ZohoCrmGetRelatedRecordsFunction<T> that targets retrieving related records of a given type.
|
|
@@ -1743,36 +1644,26 @@ function zohoCrmSearchRecordsPageFactory(context) {
|
|
|
1743
1644
|
* @returns a ZohoCrmGetRelatedRecordsFunctionFactory
|
|
1744
1645
|
*/
|
|
1745
1646
|
function zohoCrmGetRelatedRecordsFunctionFactory(context) {
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
} = config;
|
|
1751
|
-
return input => context.fetchJson(`/v8/${input.module}/${input.id}/${targetModule}?${zohoCrmUrlSearchParamsMinusIdAndModule(input, input.filter).toString()}`, zohoCrmApiFetchJsonInput('GET')).then(x => x ?? (returnEmptyRecordsInsteadOfNull !== false ? emptyZohoPageResult() : x));
|
|
1752
|
-
};
|
|
1647
|
+
return (config) => {
|
|
1648
|
+
const { targetModule, returnEmptyRecordsInsteadOfNull = true } = config;
|
|
1649
|
+
return (input) => context.fetchJson(`/v8/${input.module}/${input.id}/${targetModule}?${zohoCrmUrlSearchParamsMinusIdAndModule(input, input.filter).toString()}`, zohoCrmApiFetchJsonInput('GET')).then((x) => x ?? (returnEmptyRecordsInsteadOfNull !== false ? emptyZohoPageResult() : x));
|
|
1650
|
+
};
|
|
1753
1651
|
}
|
|
1754
1652
|
function zohoCrmGetEmailsForRecord(context) {
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
return {
|
|
1761
|
-
...x,
|
|
1762
|
-
data
|
|
1763
|
-
};
|
|
1764
|
-
});
|
|
1653
|
+
const getEmailsFactory = zohoCrmGetRelatedRecordsFunctionFactory(context)({ targetModule: ZOHO_CRM_EMAILS_MODULE });
|
|
1654
|
+
return (input) => getEmailsFactory(input).then((x) => {
|
|
1655
|
+
const data = x.data ?? x.Emails;
|
|
1656
|
+
return { ...x, data };
|
|
1657
|
+
});
|
|
1765
1658
|
}
|
|
1766
1659
|
function zohoCrmGetEmailsForRecordPageFactory(context) {
|
|
1767
|
-
|
|
1660
|
+
return zohoFetchPageFactory(zohoCrmGetEmailsForRecord(context));
|
|
1768
1661
|
}
|
|
1769
1662
|
function zohoCrmGetAttachmentsForRecord(context) {
|
|
1770
|
-
|
|
1771
|
-
targetModule: ZOHO_CRM_ATTACHMENTS_MODULE
|
|
1772
|
-
});
|
|
1663
|
+
return zohoCrmGetRelatedRecordsFunctionFactory(context)({ targetModule: ZOHO_CRM_ATTACHMENTS_MODULE });
|
|
1773
1664
|
}
|
|
1774
1665
|
function zohoCrmGetAttachmentsForRecordPageFactory(context) {
|
|
1775
|
-
|
|
1666
|
+
return zohoFetchPageFactory(zohoCrmGetAttachmentsForRecord(context));
|
|
1776
1667
|
}
|
|
1777
1668
|
/**
|
|
1778
1669
|
* Maximum attachment size allowed by Zoho Crm.
|
|
@@ -1789,59 +1680,57 @@ const ZOHO_CRM_ATTACHMENT_MAX_SIZE = 20 * 1024 * 1024;
|
|
|
1789
1680
|
* @returns
|
|
1790
1681
|
*/
|
|
1791
1682
|
function zohoCrmUploadAttachmentForRecord(context) {
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1683
|
+
return (input) => {
|
|
1684
|
+
const { attachmentCategoryId, attachmentCategoryName, formData } = input;
|
|
1685
|
+
const urlParams = {
|
|
1686
|
+
attachments_category_id: joinStringsWithCommas(attachmentCategoryId),
|
|
1687
|
+
attachments_category: joinStringsWithCommas(attachmentCategoryName),
|
|
1688
|
+
attachment_url: input.attachmentUrl
|
|
1689
|
+
};
|
|
1690
|
+
if (!urlParams.attachments_category_id?.length && !urlParams.attachments_category?.length) {
|
|
1691
|
+
throw new Error('attachmentCategoryId or attachmentCategoryName must be provided and not empty.');
|
|
1692
|
+
}
|
|
1693
|
+
if (formData != null) {
|
|
1694
|
+
delete urlParams.attachment_url;
|
|
1695
|
+
}
|
|
1696
|
+
const url = `https://crmsandbox.zoho.com/crm/v8/${input.module}/${input.id}/${ZOHO_CRM_ATTACHMENTS_MODULE}?${makeUrlSearchParams(urlParams).toString()}`;
|
|
1697
|
+
let response;
|
|
1698
|
+
if (urlParams.attachment_url) {
|
|
1699
|
+
response = context.fetch(url, { method: 'POST' });
|
|
1700
|
+
}
|
|
1701
|
+
else if (formData != null) {
|
|
1702
|
+
throw new Error('unsupported currently. Use the attachmentUrl parameter instead.');
|
|
1703
|
+
// There is something weird going on with sending requests this way and zoho's server is rejecting it.
|
|
1704
|
+
/*
|
|
1705
|
+
response = context.fetch(url, {
|
|
1706
|
+
method: 'POST',
|
|
1707
|
+
headers: {
|
|
1708
|
+
'Content-Type': 'multipart/form-data',
|
|
1709
|
+
'content-length': '210'
|
|
1710
|
+
},
|
|
1711
|
+
body: formData
|
|
1712
|
+
});
|
|
1713
|
+
*/
|
|
1714
|
+
/*
|
|
1715
|
+
const fullUrl = (context.config.apiUrl as string) + url;
|
|
1716
|
+
const accessToken = await context.accessTokenStringFactory();
|
|
1717
|
+
|
|
1718
|
+
response = fetch(fullUrl, {
|
|
1719
|
+
headers: {
|
|
1720
|
+
Authorization: `Bearer ${accessToken}`
|
|
1721
|
+
},
|
|
1722
|
+
body: formData,
|
|
1723
|
+
method: 'POST'
|
|
1724
|
+
});
|
|
1725
|
+
|
|
1726
|
+
console.log({ response });
|
|
1727
|
+
*/
|
|
1728
|
+
}
|
|
1729
|
+
else {
|
|
1730
|
+
throw new Error('body or attachmentUrl must be provided.');
|
|
1731
|
+
}
|
|
1732
|
+
return response;
|
|
1802
1733
|
};
|
|
1803
|
-
if (!urlParams.attachments_category_id?.length && !urlParams.attachments_category?.length) {
|
|
1804
|
-
throw new Error('attachmentCategoryId or attachmentCategoryName must be provided and not empty.');
|
|
1805
|
-
}
|
|
1806
|
-
if (formData != null) {
|
|
1807
|
-
delete urlParams.attachment_url;
|
|
1808
|
-
}
|
|
1809
|
-
const url = `https://crmsandbox.zoho.com/crm/v8/${input.module}/${input.id}/${ZOHO_CRM_ATTACHMENTS_MODULE}?${makeUrlSearchParams(urlParams).toString()}`;
|
|
1810
|
-
let response;
|
|
1811
|
-
if (urlParams.attachment_url) {
|
|
1812
|
-
response = context.fetch(url, {
|
|
1813
|
-
method: 'POST'
|
|
1814
|
-
});
|
|
1815
|
-
} else if (formData != null) {
|
|
1816
|
-
throw new Error('unsupported currently. Use the attachmentUrl parameter instead.');
|
|
1817
|
-
// There is something weird going on with sending requests this way and zoho's server is rejecting it.
|
|
1818
|
-
/*
|
|
1819
|
-
response = context.fetch(url, {
|
|
1820
|
-
method: 'POST',
|
|
1821
|
-
headers: {
|
|
1822
|
-
'Content-Type': 'multipart/form-data',
|
|
1823
|
-
'content-length': '210'
|
|
1824
|
-
},
|
|
1825
|
-
body: formData
|
|
1826
|
-
});
|
|
1827
|
-
*/
|
|
1828
|
-
/*
|
|
1829
|
-
const fullUrl = (context.config.apiUrl as string) + url;
|
|
1830
|
-
const accessToken = await context.accessTokenStringFactory();
|
|
1831
|
-
response = fetch(fullUrl, {
|
|
1832
|
-
headers: {
|
|
1833
|
-
Authorization: `Bearer ${accessToken}`
|
|
1834
|
-
},
|
|
1835
|
-
body: formData,
|
|
1836
|
-
method: 'POST'
|
|
1837
|
-
});
|
|
1838
|
-
console.log({ response });
|
|
1839
|
-
*/
|
|
1840
|
-
} else {
|
|
1841
|
-
throw new Error('body or attachmentUrl must be provided.');
|
|
1842
|
-
}
|
|
1843
|
-
return response;
|
|
1844
|
-
};
|
|
1845
1734
|
}
|
|
1846
1735
|
/**
|
|
1847
1736
|
* Downloads an attachment from a record.
|
|
@@ -1852,9 +1741,7 @@ function zohoCrmUploadAttachmentForRecord(context) {
|
|
|
1852
1741
|
* @returns
|
|
1853
1742
|
*/
|
|
1854
1743
|
function zohoCrmDownloadAttachmentForRecord(context) {
|
|
1855
|
-
|
|
1856
|
-
method: 'GET'
|
|
1857
|
-
}).then(parseFetchFileResponse);
|
|
1744
|
+
return (input) => context.fetch(`/v8/${input.module}/${input.id}/${ZOHO_CRM_ATTACHMENTS_MODULE}/${input.attachment_id}`, { method: 'GET' }).then(parseFetchFileResponse);
|
|
1858
1745
|
}
|
|
1859
1746
|
/**
|
|
1860
1747
|
* Deletes an attachment from a record.
|
|
@@ -1865,16 +1752,14 @@ function zohoCrmDownloadAttachmentForRecord(context) {
|
|
|
1865
1752
|
* @returns
|
|
1866
1753
|
*/
|
|
1867
1754
|
function zohoCrmDeleteAttachmentFromRecord(context) {
|
|
1868
|
-
|
|
1869
|
-
method: 'DELETE'
|
|
1870
|
-
});
|
|
1755
|
+
return (input) => context.fetch(`/v8/${input.module}/${input.id}/${ZOHO_CRM_ATTACHMENTS_MODULE}/${input.attachment_id}`, { method: 'DELETE' });
|
|
1871
1756
|
}
|
|
1872
1757
|
class ZohoCrmExecuteRestApiFunctionError extends BaseError {
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1758
|
+
error;
|
|
1759
|
+
constructor(error) {
|
|
1760
|
+
super(`An error occured during the execution of the function. Code: ${error.code}, Message: ${error.message}`);
|
|
1761
|
+
this.error = error;
|
|
1762
|
+
}
|
|
1878
1763
|
}
|
|
1879
1764
|
/**
|
|
1880
1765
|
* Creates a ZohoCrmExecuteRestApiFunctionFunction
|
|
@@ -1888,44 +1773,41 @@ class ZohoCrmExecuteRestApiFunctionError extends BaseError {
|
|
|
1888
1773
|
* @returns
|
|
1889
1774
|
*/
|
|
1890
1775
|
function zohoCrmExecuteRestApiFunction(context) {
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1776
|
+
return (input) => {
|
|
1777
|
+
const inputSearchParams = makeUrlSearchParams(input.params);
|
|
1778
|
+
const inputSearchParamsString = inputSearchParams.toString();
|
|
1779
|
+
const isSpecificRequest = Boolean(input.apiKey);
|
|
1780
|
+
const urlParams = (isSpecificRequest ? `auth_type=apikey&zapikey=${input.apiKey}` : 'auth_type=oauth') + (inputSearchParamsString ? `&${inputSearchParamsString}` : '');
|
|
1781
|
+
const relativeUrl = `/v8/functions/${input.functionName}/actions/execute?${urlParams}`;
|
|
1782
|
+
const baseUrl = isSpecificRequest && input.apiUrl != null ? zohoCrmConfigApiUrl(input.apiUrl) : '';
|
|
1783
|
+
const url = `${baseUrl}${relativeUrl}`;
|
|
1784
|
+
return context.fetchJson(url, zohoCrmApiFetchJsonInput('POST')).then((x) => {
|
|
1785
|
+
if (x.code === 'success') {
|
|
1786
|
+
return x.details;
|
|
1787
|
+
}
|
|
1788
|
+
else {
|
|
1789
|
+
throw new ZohoCrmExecuteRestApiFunctionError(x);
|
|
1790
|
+
}
|
|
1791
|
+
});
|
|
1792
|
+
};
|
|
1907
1793
|
}
|
|
1908
1794
|
// MARK: Util
|
|
1909
1795
|
function zohoCrmUrlSearchParamsMinusModule(...input) {
|
|
1910
|
-
|
|
1911
|
-
omitKeys: 'module'
|
|
1912
|
-
});
|
|
1796
|
+
return makeUrlSearchParams(input, { omitKeys: 'module' });
|
|
1913
1797
|
}
|
|
1914
1798
|
function zohoCrmUrlSearchParamsMinusIdAndModule(...input) {
|
|
1915
|
-
|
|
1916
|
-
omitKeys: ['id', 'module']
|
|
1917
|
-
});
|
|
1799
|
+
return makeUrlSearchParams(input, { omitKeys: ['id', 'module'] });
|
|
1918
1800
|
}
|
|
1919
1801
|
/**
|
|
1920
1802
|
* @deprecated use makeUrlSearchParams instead.
|
|
1921
1803
|
*/
|
|
1922
1804
|
const zohoCrmUrlSearchParams = makeUrlSearchParams;
|
|
1923
1805
|
function zohoCrmApiFetchJsonInput(method, body) {
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1806
|
+
const result = {
|
|
1807
|
+
method,
|
|
1808
|
+
body: body ?? undefined
|
|
1809
|
+
};
|
|
1810
|
+
return result;
|
|
1929
1811
|
}
|
|
1930
1812
|
// MARK: Results
|
|
1931
1813
|
/**
|
|
@@ -1934,81 +1816,76 @@ function zohoCrmApiFetchJsonInput(method, body) {
|
|
|
1934
1816
|
* Use to catch errors from functions that return ZohoCrmChangeObjectLikeResponse and pass the result to zohoCrmChangeObjectLikeResponseSuccessAndErrorPairs.
|
|
1935
1817
|
*/
|
|
1936
1818
|
function zohoCrmCatchZohoCrmChangeObjectLikeResponseError(e) {
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1819
|
+
let result;
|
|
1820
|
+
if (e instanceof ZohoServerFetchResponseDataArrayError) {
|
|
1821
|
+
result = {
|
|
1822
|
+
data: e.errorDataArray
|
|
1823
|
+
};
|
|
1824
|
+
}
|
|
1825
|
+
else {
|
|
1826
|
+
throw e;
|
|
1827
|
+
}
|
|
1828
|
+
return result;
|
|
1946
1829
|
}
|
|
1947
1830
|
function zohoCrmChangeObjectLikeResponseSuccessAndErrorPairs(response) {
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
}
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
return result;
|
|
1831
|
+
const { data } = response;
|
|
1832
|
+
const successItems = [];
|
|
1833
|
+
const errorItems = [];
|
|
1834
|
+
data.forEach((x) => {
|
|
1835
|
+
if (x.status === ZOHO_SUCCESS_STATUS) {
|
|
1836
|
+
successItems.push(x);
|
|
1837
|
+
}
|
|
1838
|
+
else {
|
|
1839
|
+
errorItems.push(x);
|
|
1840
|
+
}
|
|
1841
|
+
});
|
|
1842
|
+
const result = {
|
|
1843
|
+
...response,
|
|
1844
|
+
successItems,
|
|
1845
|
+
errorItems
|
|
1846
|
+
};
|
|
1847
|
+
return result;
|
|
1966
1848
|
}
|
|
1967
1849
|
function zohoCrmMultiRecordResult(input, results) {
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1850
|
+
const successItems = [];
|
|
1851
|
+
const errorItems = [];
|
|
1852
|
+
input.forEach((x, i) => {
|
|
1853
|
+
const result = results[i];
|
|
1854
|
+
if (result.status === ZOHO_SUCCESS_STATUS) {
|
|
1855
|
+
successItems.push({
|
|
1856
|
+
input: x,
|
|
1857
|
+
result: result
|
|
1858
|
+
});
|
|
1859
|
+
}
|
|
1860
|
+
else {
|
|
1861
|
+
errorItems.push({
|
|
1862
|
+
input: x,
|
|
1863
|
+
result: result
|
|
1864
|
+
});
|
|
1865
|
+
}
|
|
1866
|
+
});
|
|
1867
|
+
const result = {
|
|
1868
|
+
successItems,
|
|
1869
|
+
errorItems
|
|
1870
|
+
};
|
|
1871
|
+
return result;
|
|
1989
1872
|
}
|
|
1990
1873
|
|
|
1991
1874
|
function zohoCrmCreateNotes(context) {
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
return zohoCrmMultiRecordResult(asArray(input.data), x.data);
|
|
1996
|
-
});
|
|
1875
|
+
return (input) => context.fetchJson(`/v2/${ZOHO_CRM_NOTES_MODULE}`, zohoCrmApiFetchJsonInput('POST', { data: input.data })).then((x) => {
|
|
1876
|
+
return zohoCrmMultiRecordResult(asArray(input.data), x.data);
|
|
1877
|
+
});
|
|
1997
1878
|
}
|
|
1998
1879
|
function zohoCrmDeleteNotes(context) {
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
return zohoCrmMultiRecordResult(asArray(input.ids), x.data);
|
|
2003
|
-
});
|
|
1880
|
+
return (input) => context.fetchJson(`/v2/${ZOHO_CRM_NOTES_MODULE}?${makeUrlSearchParams({ ids: input.ids })}`, zohoCrmApiFetchJsonInput('DELETE')).then((x) => {
|
|
1881
|
+
return zohoCrmMultiRecordResult(asArray(input.ids), x.data);
|
|
1882
|
+
});
|
|
2004
1883
|
}
|
|
2005
1884
|
function zohoCrmGetNotesForRecord(context) {
|
|
2006
|
-
|
|
2007
|
-
targetModule: ZOHO_CRM_NOTES_MODULE
|
|
2008
|
-
});
|
|
1885
|
+
return zohoCrmGetRelatedRecordsFunctionFactory(context)({ targetModule: ZOHO_CRM_NOTES_MODULE });
|
|
2009
1886
|
}
|
|
2010
1887
|
function zohoCrmGetNotesForRecordPageFactory(context) {
|
|
2011
|
-
|
|
1888
|
+
return zohoFetchPageFactory(zohoCrmGetNotesForRecord(context));
|
|
2012
1889
|
}
|
|
2013
1890
|
/**
|
|
2014
1891
|
* https://www.zoho.com/crm/developer/docs/api/v8/create-notes.html
|
|
@@ -2017,58 +1894,50 @@ function zohoCrmGetNotesForRecordPageFactory(context) {
|
|
|
2017
1894
|
* @returns
|
|
2018
1895
|
*/
|
|
2019
1896
|
function zohoCrmCreateNotesForRecord(context) {
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
Parent_Id
|
|
2032
|
-
}))
|
|
1897
|
+
const createNotesInstance = zohoCrmCreateNotes(context);
|
|
1898
|
+
return (input) => {
|
|
1899
|
+
const { module: se_module, id: Parent_Id, notes } = input;
|
|
1900
|
+
const createNotesRequest = {
|
|
1901
|
+
data: asArray(notes).map((x) => ({
|
|
1902
|
+
...x,
|
|
1903
|
+
se_module,
|
|
1904
|
+
Parent_Id
|
|
1905
|
+
}))
|
|
1906
|
+
};
|
|
1907
|
+
return createNotesInstance(createNotesRequest);
|
|
2033
1908
|
};
|
|
2034
|
-
return createNotesInstance(createNotesRequest);
|
|
2035
|
-
};
|
|
2036
1909
|
}
|
|
2037
1910
|
|
|
2038
1911
|
function zohoCrmCreateTagsForModule(context) {
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
1912
|
+
return (input) => context
|
|
1913
|
+
.fetchJson(`/v8/settings/tags?${makeUrlSearchParams({ module: input.module })}`, zohoCrmApiFetchJsonInput('POST', { tags: asArray(input.tags) }))
|
|
1914
|
+
.catch((e) => {
|
|
1915
|
+
let result;
|
|
1916
|
+
if (e instanceof ZohoServerFetchResponseError) {
|
|
1917
|
+
const tags = e.data?.tags;
|
|
1918
|
+
if (Array.isArray(tags)) {
|
|
1919
|
+
result = {
|
|
1920
|
+
tags
|
|
1921
|
+
};
|
|
1922
|
+
}
|
|
1923
|
+
}
|
|
1924
|
+
if (!result) {
|
|
1925
|
+
throw e;
|
|
1926
|
+
}
|
|
1927
|
+
return result;
|
|
1928
|
+
})
|
|
1929
|
+
.then((x) => {
|
|
1930
|
+
const result = zohoCrmMultiRecordResult(asArray(input.tags), x.tags);
|
|
1931
|
+
const { included: duplicateErrorItems, excluded: otherErrorItems } = separateValues(result.errorItems, (x) => {
|
|
1932
|
+
return x.result.code === ZOHO_DUPLICATE_DATA_ERROR_CODE;
|
|
1933
|
+
});
|
|
1934
|
+
return {
|
|
1935
|
+
...result,
|
|
1936
|
+
errorItems: otherErrorItems,
|
|
1937
|
+
duplicateErrorItems,
|
|
1938
|
+
allErrorItems: result.errorItems
|
|
2050
1939
|
};
|
|
2051
|
-
}
|
|
2052
|
-
}
|
|
2053
|
-
if (!result) {
|
|
2054
|
-
throw e;
|
|
2055
|
-
}
|
|
2056
|
-
return result;
|
|
2057
|
-
}).then(x => {
|
|
2058
|
-
const result = zohoCrmMultiRecordResult(asArray(input.tags), x.tags);
|
|
2059
|
-
const {
|
|
2060
|
-
included: duplicateErrorItems,
|
|
2061
|
-
excluded: otherErrorItems
|
|
2062
|
-
} = separateValues(result.errorItems, x => {
|
|
2063
|
-
return x.result.code === ZOHO_DUPLICATE_DATA_ERROR_CODE;
|
|
2064
1940
|
});
|
|
2065
|
-
return {
|
|
2066
|
-
...result,
|
|
2067
|
-
errorItems: otherErrorItems,
|
|
2068
|
-
duplicateErrorItems,
|
|
2069
|
-
allErrorItems: result.errorItems
|
|
2070
|
-
};
|
|
2071
|
-
});
|
|
2072
1941
|
}
|
|
2073
1942
|
/**
|
|
2074
1943
|
* Deletes a single tag by ID.
|
|
@@ -2081,7 +1950,7 @@ function zohoCrmCreateTagsForModule(context) {
|
|
|
2081
1950
|
* @returns
|
|
2082
1951
|
*/
|
|
2083
1952
|
function zohoCrmDeleteTag(context) {
|
|
2084
|
-
|
|
1953
|
+
return (input) => context.fetchJson(`/v8/settings/tags/${input.id}`, zohoCrmApiFetchJsonInput('DELETE')).then((x) => x.tags);
|
|
2085
1954
|
}
|
|
2086
1955
|
/**
|
|
2087
1956
|
* Returns the list of tags within a module.
|
|
@@ -2092,19 +1961,16 @@ function zohoCrmDeleteTag(context) {
|
|
|
2092
1961
|
* @returns
|
|
2093
1962
|
*/
|
|
2094
1963
|
function zohoCrmGetTagsForModule(context) {
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
data: x.tags
|
|
2103
|
-
};
|
|
2104
|
-
});
|
|
1964
|
+
return (input) => context.fetchJson(`/v8/settings/tags?${makeUrlSearchParams({ module: input.module, my_tags: input.my_tags })}`, zohoCrmApiFetchJsonInput('GET')).then((x) => {
|
|
1965
|
+
// NOTE: This doesn't follow the api documentation, and instead is a normal page result except it has "tags" instead of "data".
|
|
1966
|
+
return {
|
|
1967
|
+
...x,
|
|
1968
|
+
data: x.tags
|
|
1969
|
+
};
|
|
1970
|
+
});
|
|
2105
1971
|
}
|
|
2106
1972
|
function zohoCrmGetTagsForModulePageFactory(context) {
|
|
2107
|
-
|
|
1973
|
+
return zohoFetchPageFactory(zohoCrmGetTagsForModule(context));
|
|
2108
1974
|
}
|
|
2109
1975
|
// MARK: Add Tag To Record
|
|
2110
1976
|
/**
|
|
@@ -2120,33 +1986,31 @@ const ZOHO_CRM_ADD_TAGS_TO_RECORDS_MAX_IDS_ALLOWED = 100;
|
|
|
2120
1986
|
* @returns
|
|
2121
1987
|
*/
|
|
2122
1988
|
function zohoCrmAddTagsToRecords(context) {
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
1989
|
+
return (input) => {
|
|
1990
|
+
return context.fetchJson(`/v8/${input.module}/actions/add_tags`, zohoCrmApiFetchJsonInput('POST', zohoCrmAddTagsToRecordsRequestBody(input))).then((x) => {
|
|
1991
|
+
const resultInputMap = x.data.map(() => input); // assign "input" to each value for now
|
|
1992
|
+
return zohoCrmMultiRecordResult(resultInputMap, x.data);
|
|
1993
|
+
});
|
|
1994
|
+
};
|
|
2129
1995
|
}
|
|
2130
1996
|
function zohoCrmAddTagsToRecordsRequestBody(input) {
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
1997
|
+
if (Array.isArray(input.ids) && input.ids.length > ZOHO_CRM_ADD_TAGS_TO_RECORDS_MAX_IDS_ALLOWED) {
|
|
1998
|
+
throw new Error(`Cannot add/remove tags from more than ${ZOHO_CRM_ADD_TAGS_TO_RECORDS_MAX_IDS_ALLOWED} records at once.`);
|
|
1999
|
+
}
|
|
2000
|
+
const tags = [...asArray(input.tags)];
|
|
2001
|
+
const tagNames = new Set(tags.map((x) => x.name));
|
|
2002
|
+
if (input.tag_names) {
|
|
2003
|
+
asArray(input.tag_names).forEach((x) => {
|
|
2004
|
+
if (!tagNames.has(x)) {
|
|
2005
|
+
tags.push({ name: x });
|
|
2006
|
+
tagNames.add(x);
|
|
2007
|
+
}
|
|
2141
2008
|
});
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
tags,
|
|
2148
|
-
ids: asArray(input.ids)
|
|
2149
|
-
};
|
|
2009
|
+
}
|
|
2010
|
+
return {
|
|
2011
|
+
tags,
|
|
2012
|
+
ids: asArray(input.ids)
|
|
2013
|
+
};
|
|
2150
2014
|
}
|
|
2151
2015
|
// MARK: Remove Tag From Record
|
|
2152
2016
|
/**
|
|
@@ -2162,71 +2026,61 @@ const ZOHO_CRM_REMOVE_TAGS_FROM_RECORDS_MAX_IDS_ALLOWED = 100;
|
|
|
2162
2026
|
* @returns
|
|
2163
2027
|
*/
|
|
2164
2028
|
function zohoCrmRemoveTagsFromRecords(context) {
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2029
|
+
return (input) => {
|
|
2030
|
+
return context.fetchJson(`/v8/${input.module}/actions/remove_tags`, zohoCrmApiFetchJsonInput('POST', zohoCrmAddTagsToRecordsRequestBody(input))).then((x) => {
|
|
2031
|
+
const resultInputMap = x.data.map(() => input); // assign "input" to each value for now
|
|
2032
|
+
return zohoCrmMultiRecordResult(resultInputMap, x.data);
|
|
2033
|
+
});
|
|
2034
|
+
};
|
|
2171
2035
|
}
|
|
2172
2036
|
|
|
2173
2037
|
function zohoCrmFactory(factoryConfig) {
|
|
2174
|
-
|
|
2175
|
-
accountsContext
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2038
|
+
const { accountsContext } = factoryConfig;
|
|
2039
|
+
const accessTokenStringFactory = zohoAccessTokenStringFactory(accountsContext.loadAccessToken);
|
|
2040
|
+
const fetchHandler = zohoRateLimitedFetchHandler(factoryConfig.rateLimiterConfig);
|
|
2041
|
+
const { logZohoServerErrorFunction, fetchFactory = (input) => fetchApiFetchService.makeFetch({
|
|
2042
|
+
baseUrl: input.apiUrl,
|
|
2043
|
+
baseRequest: async () => ({
|
|
2044
|
+
headers: {
|
|
2045
|
+
'Content-Type': 'application/json',
|
|
2046
|
+
Authorization: `Bearer ${await accessTokenStringFactory()}`
|
|
2047
|
+
}
|
|
2048
|
+
}),
|
|
2049
|
+
fetchHandler,
|
|
2050
|
+
timeout: 20 * 1000, // 20 second timeout
|
|
2051
|
+
requireOkResponse: true, // enforce ok response
|
|
2052
|
+
useTimeout: true // use timeout
|
|
2053
|
+
}) } = factoryConfig;
|
|
2054
|
+
return (config) => {
|
|
2055
|
+
if (!config.apiUrl) {
|
|
2056
|
+
throw new Error('ZohoConfig missing api url.');
|
|
2187
2057
|
}
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
handleFetchJsonParseErrorFunction: returnNullHandleFetchJsonParseErrorFunction
|
|
2214
|
-
});
|
|
2215
|
-
const crmContext = {
|
|
2216
|
-
fetch,
|
|
2217
|
-
fetchJson,
|
|
2218
|
-
accessTokenStringFactory,
|
|
2219
|
-
config: {
|
|
2220
|
-
...config,
|
|
2221
|
-
apiUrl
|
|
2222
|
-
},
|
|
2223
|
-
zohoRateLimiter: fetchHandler._rateLimiter
|
|
2224
|
-
};
|
|
2225
|
-
const zohoCrm = {
|
|
2226
|
-
crmContext
|
|
2058
|
+
const apiUrl = zohoCrmConfigApiUrl(config.apiUrl);
|
|
2059
|
+
const baseFetch = fetchFactory({ apiUrl });
|
|
2060
|
+
const fetch = handleZohoCrmErrorFetch(baseFetch, logZohoServerErrorFunction, (x) => {
|
|
2061
|
+
if (x instanceof ZohoInvalidTokenError) {
|
|
2062
|
+
accountsContext.loadAccessToken.resetAccessToken();
|
|
2063
|
+
}
|
|
2064
|
+
});
|
|
2065
|
+
const fetchJson = fetchJsonFunction(fetch, {
|
|
2066
|
+
interceptJsonResponse: interceptZohoCrm200StatusWithErrorResponse, // intercept errors that return status 200
|
|
2067
|
+
handleFetchJsonParseErrorFunction: returnNullHandleFetchJsonParseErrorFunction
|
|
2068
|
+
});
|
|
2069
|
+
const crmContext = {
|
|
2070
|
+
fetch,
|
|
2071
|
+
fetchJson,
|
|
2072
|
+
accessTokenStringFactory,
|
|
2073
|
+
config: {
|
|
2074
|
+
...config,
|
|
2075
|
+
apiUrl
|
|
2076
|
+
},
|
|
2077
|
+
zohoRateLimiter: fetchHandler._rateLimiter
|
|
2078
|
+
};
|
|
2079
|
+
const zohoCrm = {
|
|
2080
|
+
crmContext
|
|
2081
|
+
};
|
|
2082
|
+
return zohoCrm;
|
|
2227
2083
|
};
|
|
2228
|
-
return zohoCrm;
|
|
2229
|
-
};
|
|
2230
2084
|
}
|
|
2231
2085
|
|
|
2232
2086
|
/**
|
|
@@ -2240,28 +2094,21 @@ const ZOHO_CRM_TAG_NAME_MAX_LENGTH = 25;
|
|
|
2240
2094
|
* @returns
|
|
2241
2095
|
*/
|
|
2242
2096
|
function zohoAccountsAccessToken(context) {
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
refreshToken: inputRefreshToken
|
|
2252
|
-
} = input ?? {};
|
|
2253
|
-
const clientId = client?.clientId ?? configClientId;
|
|
2254
|
-
const clientSecret = client?.clientSecret ?? configClientSecret;
|
|
2255
|
-
const refreshToken = inputRefreshToken ?? configRefreshToken;
|
|
2256
|
-
return context.fetchJson(`/oauth/v2/token?grant_type=refresh_token&client_id=${clientId}&client_secret=${clientSecret}&refresh_token=${refreshToken}`, zohoAccountsApiFetchJsonInput('POST'));
|
|
2257
|
-
};
|
|
2097
|
+
return (input) => {
|
|
2098
|
+
const { clientId: configClientId, clientSecret: configClientSecret, refreshToken: configRefreshToken } = context.config;
|
|
2099
|
+
const { client, refreshToken: inputRefreshToken } = input ?? {};
|
|
2100
|
+
const clientId = client?.clientId ?? configClientId;
|
|
2101
|
+
const clientSecret = client?.clientSecret ?? configClientSecret;
|
|
2102
|
+
const refreshToken = inputRefreshToken ?? configRefreshToken;
|
|
2103
|
+
return context.fetchJson(`/oauth/v2/token?grant_type=refresh_token&client_id=${clientId}&client_secret=${clientSecret}&refresh_token=${refreshToken}`, zohoAccountsApiFetchJsonInput('POST'));
|
|
2104
|
+
};
|
|
2258
2105
|
}
|
|
2259
2106
|
function zohoAccountsApiFetchJsonInput(method, body) {
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2107
|
+
const result = {
|
|
2108
|
+
method,
|
|
2109
|
+
body
|
|
2110
|
+
};
|
|
2111
|
+
return result;
|
|
2265
2112
|
}
|
|
2266
2113
|
|
|
2267
2114
|
/**
|
|
@@ -2269,89 +2116,78 @@ function zohoAccountsApiFetchJsonInput(method, body) {
|
|
|
2269
2116
|
*/
|
|
2270
2117
|
const ZOHO_ACCOUNTS_US_API_URL = 'https://accounts.zoho.com';
|
|
2271
2118
|
function zohoAccountsConfigApiUrl(input) {
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2119
|
+
switch (input) {
|
|
2120
|
+
case 'us':
|
|
2121
|
+
return ZOHO_ACCOUNTS_US_API_URL;
|
|
2122
|
+
default:
|
|
2123
|
+
return input;
|
|
2124
|
+
}
|
|
2278
2125
|
}
|
|
2279
2126
|
|
|
2280
2127
|
function zohoAccountsFactory(factoryConfig) {
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2128
|
+
const fetchHandler = zohoRateLimitedFetchHandler();
|
|
2129
|
+
const { logZohoServerErrorFunction, fetchFactory = (input) => fetchApiFetchService.makeFetch({
|
|
2130
|
+
baseUrl: input.apiUrl,
|
|
2131
|
+
baseRequest: {
|
|
2132
|
+
headers: {
|
|
2133
|
+
'Content-Type': 'application/json'
|
|
2134
|
+
}
|
|
2135
|
+
},
|
|
2136
|
+
fetchHandler,
|
|
2137
|
+
timeout: 20 * 1000, // 20 second timeout
|
|
2138
|
+
requireOkResponse: true, // enforce ok response
|
|
2139
|
+
useTimeout: true // use timeout
|
|
2140
|
+
}) } = factoryConfig;
|
|
2141
|
+
return (config) => {
|
|
2142
|
+
if (!config.refreshToken) {
|
|
2143
|
+
throw new Error('ZohoAccountsConfig missing refreshToken.');
|
|
2289
2144
|
}
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
return config.accessTokenCache?.clearCachedToken();
|
|
2336
|
-
};
|
|
2337
|
-
const loadAccessToken = zohoAccountsZohoAccessTokenFactory({
|
|
2338
|
-
tokenRefresher,
|
|
2339
|
-
accessTokenCache: config.accessTokenCache
|
|
2340
|
-
});
|
|
2341
|
-
const accountsContext = {
|
|
2342
|
-
fetch,
|
|
2343
|
-
fetchJson,
|
|
2344
|
-
loadAccessToken,
|
|
2345
|
-
config: {
|
|
2346
|
-
...config,
|
|
2347
|
-
apiUrl
|
|
2348
|
-
}
|
|
2349
|
-
};
|
|
2350
|
-
const zohoAccounts = {
|
|
2351
|
-
accountsContext
|
|
2145
|
+
else if (!config.clientId) {
|
|
2146
|
+
throw new Error('ZohoAccountsConfig missing clientId.');
|
|
2147
|
+
}
|
|
2148
|
+
else if (!config.clientSecret) {
|
|
2149
|
+
throw new Error('ZohoAccountsConfig missing clientSecret.');
|
|
2150
|
+
}
|
|
2151
|
+
const apiUrl = zohoAccountsConfigApiUrl(config.apiUrl ?? 'us');
|
|
2152
|
+
const baseFetch = fetchFactory({ apiUrl });
|
|
2153
|
+
const fetch = handleZohoAccountsErrorFetch(baseFetch, logZohoServerErrorFunction);
|
|
2154
|
+
const fetchJson = fetchJsonFunction(fetch, {
|
|
2155
|
+
interceptJsonResponse: interceptZohoAccounts200StatusWithErrorResponse, // intercept errors that return status 200
|
|
2156
|
+
handleFetchJsonParseErrorFunction: returnNullHandleFetchJsonParseErrorFunction
|
|
2157
|
+
});
|
|
2158
|
+
const tokenRefresher = async () => {
|
|
2159
|
+
const createdAt = new Date().getTime();
|
|
2160
|
+
const { access_token, api_domain, scope, expires_in } = await zohoAccountsAccessToken(accountsContext)();
|
|
2161
|
+
const result = {
|
|
2162
|
+
accessToken: access_token,
|
|
2163
|
+
apiDomain: api_domain,
|
|
2164
|
+
expiresIn: expires_in,
|
|
2165
|
+
expiresAt: new Date(createdAt + expires_in * MS_IN_SECOND),
|
|
2166
|
+
scope
|
|
2167
|
+
};
|
|
2168
|
+
return result;
|
|
2169
|
+
};
|
|
2170
|
+
tokenRefresher.resetAccessToken = async () => {
|
|
2171
|
+
return config.accessTokenCache?.clearCachedToken();
|
|
2172
|
+
};
|
|
2173
|
+
const loadAccessToken = zohoAccountsZohoAccessTokenFactory({
|
|
2174
|
+
tokenRefresher,
|
|
2175
|
+
accessTokenCache: config.accessTokenCache
|
|
2176
|
+
});
|
|
2177
|
+
const accountsContext = {
|
|
2178
|
+
fetch,
|
|
2179
|
+
fetchJson,
|
|
2180
|
+
loadAccessToken,
|
|
2181
|
+
config: {
|
|
2182
|
+
...config,
|
|
2183
|
+
apiUrl
|
|
2184
|
+
}
|
|
2185
|
+
};
|
|
2186
|
+
const zohoAccounts = {
|
|
2187
|
+
accountsContext
|
|
2188
|
+
};
|
|
2189
|
+
return zohoAccounts;
|
|
2352
2190
|
};
|
|
2353
|
-
return zohoAccounts;
|
|
2354
|
-
};
|
|
2355
2191
|
}
|
|
2356
2192
|
/**
|
|
2357
2193
|
* Creates a ZohoAccountsZohoAccessTokenFactoryConfig
|
|
@@ -2360,58 +2196,56 @@ function zohoAccountsFactory(factoryConfig) {
|
|
|
2360
2196
|
* @returns
|
|
2361
2197
|
*/
|
|
2362
2198
|
function zohoAccountsZohoAccessTokenFactory(config) {
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
* Caches the token internally here until it expires.
|
|
2371
|
-
*/
|
|
2372
|
-
let currentToken = null;
|
|
2373
|
-
const resetAccessToken = async () => {
|
|
2374
|
-
currentToken = null;
|
|
2375
|
-
};
|
|
2376
|
-
const fn = async () => {
|
|
2377
|
-
// load from cache
|
|
2378
|
-
if (!currentToken) {
|
|
2379
|
-
const cachedToken = await accessTokenCache?.loadCachedToken();
|
|
2380
|
-
if (cachedToken) {
|
|
2381
|
-
currentToken = cachedToken;
|
|
2382
|
-
}
|
|
2383
|
-
}
|
|
2384
|
-
// check expiration
|
|
2385
|
-
if (currentToken != null) {
|
|
2386
|
-
const isExpired = new Date().getTime() + tokenExpirationBuffer >= currentToken.expiresAt.getTime();
|
|
2387
|
-
if (isExpired) {
|
|
2199
|
+
const { tokenRefresher, accessTokenCache, tokenExpirationBuffer: inputTokenExpirationBuffer } = config;
|
|
2200
|
+
const tokenExpirationBuffer = inputTokenExpirationBuffer ?? MS_IN_MINUTE;
|
|
2201
|
+
/**
|
|
2202
|
+
* Caches the token internally here until it expires.
|
|
2203
|
+
*/
|
|
2204
|
+
let currentToken = null;
|
|
2205
|
+
const resetAccessToken = async () => {
|
|
2388
2206
|
currentToken = null;
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
throw new ZohoAccountsAuthFailureError('Token Refresh Failed');
|
|
2398
|
-
}
|
|
2399
|
-
if (currentToken) {
|
|
2400
|
-
try {
|
|
2401
|
-
await accessTokenCache?.updateCachedToken(currentToken);
|
|
2402
|
-
} catch (e) {
|
|
2403
|
-
// do nothing
|
|
2207
|
+
};
|
|
2208
|
+
const fn = async () => {
|
|
2209
|
+
// load from cache
|
|
2210
|
+
if (!currentToken) {
|
|
2211
|
+
const cachedToken = await accessTokenCache?.loadCachedToken();
|
|
2212
|
+
if (cachedToken) {
|
|
2213
|
+
currentToken = cachedToken;
|
|
2214
|
+
}
|
|
2404
2215
|
}
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2216
|
+
// check expiration
|
|
2217
|
+
if (currentToken != null) {
|
|
2218
|
+
const isExpired = new Date().getTime() + tokenExpirationBuffer >= currentToken.expiresAt.getTime();
|
|
2219
|
+
if (isExpired) {
|
|
2220
|
+
currentToken = null;
|
|
2221
|
+
}
|
|
2222
|
+
}
|
|
2223
|
+
// load from source
|
|
2224
|
+
if (!currentToken) {
|
|
2225
|
+
try {
|
|
2226
|
+
currentToken = await tokenRefresher();
|
|
2227
|
+
}
|
|
2228
|
+
catch (e) {
|
|
2229
|
+
console.error(`zohoAccountsZohoAccessTokenFactory(): Failed retrieving new token from tokenRefresher: `, e);
|
|
2230
|
+
throw new ZohoAccountsAuthFailureError('Token Refresh Failed');
|
|
2231
|
+
}
|
|
2232
|
+
if (currentToken) {
|
|
2233
|
+
try {
|
|
2234
|
+
await accessTokenCache?.updateCachedToken(currentToken);
|
|
2235
|
+
}
|
|
2236
|
+
catch (e) {
|
|
2237
|
+
// do nothing
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
2240
|
+
}
|
|
2241
|
+
return currentToken;
|
|
2242
|
+
};
|
|
2243
|
+
fn.resetAccessToken = resetAccessToken;
|
|
2244
|
+
return fn;
|
|
2411
2245
|
}
|
|
2412
2246
|
|
|
2413
2247
|
function safeZohoDateTimeString(date) {
|
|
2414
|
-
|
|
2248
|
+
return date != null ? zohoDateTimeString(date) : date;
|
|
2415
2249
|
}
|
|
2416
2250
|
/**
|
|
2417
2251
|
* Converts the input date to a Zoho date.
|
|
@@ -2420,8 +2254,8 @@ function safeZohoDateTimeString(date) {
|
|
|
2420
2254
|
* @returns
|
|
2421
2255
|
*/
|
|
2422
2256
|
function zohoDateTimeString(date) {
|
|
2423
|
-
|
|
2424
|
-
|
|
2257
|
+
const isoDate = date.toISOString();
|
|
2258
|
+
return isoDate.substring(0, isoDate.length - 5) + 'Z';
|
|
2425
2259
|
}
|
|
2426
2260
|
|
|
2427
2261
|
export { DEFAULT_ZOHO_API_RATE_LIMIT, DEFAULT_ZOHO_API_RATE_LIMIT_RESET_PERIOD, DEFAULT_ZOHO_RATE_LIMITED_TOO_MANY_REQUETS_LOG_FUNCTION, MAX_ZOHO_CRM_SEARCH_MODULE_RECORDS_CRITERIA, MAX_ZOHO_RECRUIT_SEARCH_MODULE_RECORDS_CRITERIA, ZOHO_ACCOUNTS_INVALID_CLIENT_ERROR_CODE, ZOHO_ACCOUNTS_INVALID_CODE_ERROR_CODE, ZOHO_ACCOUNTS_US_API_URL, ZOHO_CRM_ADD_TAGS_TO_RECORDS_MAX_IDS_ALLOWED, ZOHO_CRM_ALREADY_ASSOCIATED_ERROR_CODE, ZOHO_CRM_ATTACHMENTS_MODULE, ZOHO_CRM_ATTACHMENT_MAX_SIZE, ZOHO_CRM_CONTACTS_MODULE, ZOHO_CRM_CRUD_FUNCTION_MAX_RECORDS_LIMIT, ZOHO_CRM_EMAILS_MODULE, ZOHO_CRM_LEADS_MODULE, ZOHO_CRM_NOTES_MODULE, ZOHO_CRM_RECORD_ATTACHMENT_METADATA_ATTACH_TYPE_RESUME, ZOHO_CRM_REMOVE_TAGS_FROM_RECORDS_MAX_IDS_ALLOWED, ZOHO_CRM_SERVICE_NAME, ZOHO_CRM_TAG_NAME_MAX_LENGTH, ZOHO_CRM_TASKS_MODULE, ZOHO_DATA_ARRAY_BLANK_ERROR_CODE, ZOHO_DUPLICATE_DATA_ERROR_CODE, ZOHO_ERROR_STATUS, ZOHO_FAILURE_ERROR_CODE, ZOHO_INTERNAL_ERROR_CODE, ZOHO_INVALID_AUTHORIZATION_ERROR_CODE, ZOHO_INVALID_DATA_ERROR_CODE, ZOHO_INVALID_QUERY_ERROR_CODE, ZOHO_INVALID_TOKEN_ERROR_CODE, ZOHO_MANDATORY_NOT_FOUND_ERROR_CODE, ZOHO_RATE_LIMIT_LIMIT_HEADER, ZOHO_RATE_LIMIT_REMAINING_HEADER, ZOHO_RATE_LIMIT_RESET_HEADER, ZOHO_RECRUIT_ADD_TAGS_TO_RECORDS_MAX_IDS_ALLOWED, ZOHO_RECRUIT_ALREADY_ASSOCIATED_ERROR_CODE, ZOHO_RECRUIT_ATTACHMENTS_MODULE, ZOHO_RECRUIT_ATTACHMENT_MAX_SIZE, ZOHO_RECRUIT_CANDIDATES_MODULE, ZOHO_RECRUIT_CRUD_FUNCTION_MAX_RECORDS_LIMIT, ZOHO_RECRUIT_EMAILS_MODULE, ZOHO_RECRUIT_JOB_OPENINGS_MODULE, ZOHO_RECRUIT_NOTES_MODULE, ZOHO_RECRUIT_RECORD_ATTACHMENT_METADATA_ATTACH_TYPE_RESUME, ZOHO_RECRUIT_REMOVE_TAGS_FROM_RECORDS_MAX_IDS_ALLOWED, ZOHO_RECRUIT_SERVICE_NAME, ZOHO_RECRUIT_TAG_NAME_MAX_LENGTH, ZOHO_SUCCESS_CODE, ZOHO_SUCCESS_STATUS, ZOHO_TOO_MANY_REQUESTS_ERROR_CODE, ZOHO_TOO_MANY_REQUESTS_HTTP_STATUS_CODE, ZohoAccountsAccessTokenError, ZohoAccountsAuthFailureError, ZohoCrmExecuteRestApiFunctionError, ZohoCrmRecordCrudDuplicateDataError, ZohoCrmRecordCrudError, ZohoCrmRecordCrudInvalidDataError, ZohoCrmRecordCrudMandatoryFieldNotFoundError, ZohoCrmRecordCrudNoMatchingRecordError, ZohoCrmRecordNoContentError, ZohoInternalError, ZohoInvalidAuthorizationError, ZohoInvalidQueryError, ZohoInvalidTokenError, ZohoRecruitExecuteRestApiFunctionError, ZohoRecruitRecordCrudDuplicateDataError, ZohoRecruitRecordCrudError, ZohoRecruitRecordCrudInvalidDataError, ZohoRecruitRecordCrudMandatoryFieldNotFoundError, ZohoRecruitRecordCrudNoMatchingRecordError, ZohoRecruitRecordNoContentError, ZohoServerError, ZohoServerFetchResponseDataArrayError, ZohoServerFetchResponseError, ZohoTooManyRequestsError, addTagsToRecords, assertRecordDataArrayResultHasContent, assertZohoCrmRecordDataArrayResultHasContent, assertZohoRecruitRecordDataArrayResultHasContent, createNotes, createNotesForRecord, createTagsForModule, deleteAttachmentFromRecord, deleteNotes, deleteRecord, downloadAttachmentForRecord, emptyZohoPageResult, escapeZohoCrmFieldValueForCriteriaString, executeRestApiFunction, getAttachmentsForRecord, getAttachmentsForRecordPageFactory, getEmailsForRecord, getEmailsForRecordPageFactory, getNotesForRecord, getNotesForRecordPageFactory, getRecordById, getRecords, getRelatedRecordsFunctionFactory, getTagsForModule, getTagsForModulePageFactory, handleZohoAccountsErrorFetch, handleZohoCrmErrorFetch, handleZohoErrorFetchFactory, handleZohoRecruitErrorFetch, insertRecord, interceptZohoAccounts200StatusWithErrorResponse, interceptZohoCrm200StatusWithErrorResponse, interceptZohoErrorResponseFactory, interceptZohoRecruit200StatusWithErrorResponse, isZohoCrmValidUrl, isZohoRecruitValidUrl, isZohoServerErrorResponseDataArrayRef, logZohoAccountsErrorToConsole, logZohoCrmErrorToConsole, logZohoRecruitErrorToConsole, logZohoServerErrorFunction, parseZohoAccountsError, parseZohoAccountsServerErrorResponseData, parseZohoCrmError, parseZohoCrmServerErrorResponseData, parseZohoRecruitError, parseZohoRecruitServerErrorResponseData, parseZohoServerErrorResponseData, removeTagsFromRecords, safeZohoDateTimeString, searchRecords, searchRecordsPageFactory, tryFindZohoServerErrorData, updateRecord, uploadAttachmentForRecord, upsertRecord, zohoAccessTokenStringFactory, zohoAccountsAccessToken, zohoAccountsApiFetchJsonInput, zohoAccountsConfigApiUrl, zohoAccountsFactory, zohoAccountsZohoAccessTokenFactory, zohoCrmAddTagsToRecords, zohoCrmAddTagsToRecordsRequestBody, zohoCrmApiFetchJsonInput, zohoCrmCatchZohoCrmChangeObjectLikeResponseError, zohoCrmChangeObjectLikeResponseSuccessAndErrorPairs, zohoCrmConfigApiUrl, zohoCrmCreateNotes, zohoCrmCreateNotesForRecord, zohoCrmCreateTagsForModule, zohoCrmDeleteAttachmentFromRecord, zohoCrmDeleteNotes, zohoCrmDeleteRecord, zohoCrmDeleteTag, zohoCrmDownloadAttachmentForRecord, zohoCrmExecuteRestApiFunction, zohoCrmFactory, zohoCrmGetAttachmentsForRecord, zohoCrmGetAttachmentsForRecordPageFactory, zohoCrmGetEmailsForRecord, zohoCrmGetEmailsForRecordPageFactory, zohoCrmGetNotesForRecord, zohoCrmGetNotesForRecordPageFactory, zohoCrmGetRecordById, zohoCrmGetRecords, zohoCrmGetRelatedRecordsFunctionFactory, zohoCrmGetTagsForModule, zohoCrmGetTagsForModulePageFactory, zohoCrmInsertRecord, zohoCrmMultiRecordResult, zohoCrmRecordCrudError, zohoCrmRemoveTagsFromRecords, zohoCrmSearchRecords, zohoCrmSearchRecordsCriteriaEntryToCriteriaString, zohoCrmSearchRecordsCriteriaString, zohoCrmSearchRecordsCriteriaStringForTree, zohoCrmSearchRecordsPageFactory, zohoCrmUpdateRecord, zohoCrmUploadAttachmentForRecord, zohoCrmUpsertRecord, zohoCrmUrlSearchParams, zohoCrmUrlSearchParamsMinusIdAndModule, zohoCrmUrlSearchParamsMinusModule, zohoDateTimeString, zohoFetchPageFactory, zohoRateLimitHeaderDetails, zohoRateLimitedFetchHandler, zohoRecruitAddTagsToRecords, zohoRecruitApiFetchJsonInput, zohoRecruitAssociateCandidateRecordsWithJobOpenings, zohoRecruitChangeObjectLikeResponseSuccessAndErrorPairs, zohoRecruitConfigApiUrl, zohoRecruitCreateNotes, zohoRecruitCreateNotesForRecord, zohoRecruitCreateTagsForModule, zohoRecruitDeleteAttachmentFromRecord, zohoRecruitDeleteNotes, zohoRecruitDeleteRecord, zohoRecruitDownloadAttachmentForRecord, zohoRecruitExecuteRestApiFunction, zohoRecruitFactory, zohoRecruitGetAttachmentsForRecord, zohoRecruitGetAttachmentsForRecordPageFactory, zohoRecruitGetEmailsForRecord, zohoRecruitGetEmailsForRecordPageFactory, zohoRecruitGetNotesForRecord, zohoRecruitGetNotesForRecordPageFactory, zohoRecruitGetRecordById, zohoRecruitGetRecords, zohoRecruitGetRelatedRecordsFunctionFactory, zohoRecruitGetTagsForModule, zohoRecruitGetTagsForModulePageFactory, zohoRecruitInsertRecord, zohoRecruitMultiRecordResult, zohoRecruitRecordCrudError, zohoRecruitRemoveTagsFromRecords, zohoRecruitSearchAssociatedRecords, zohoRecruitSearchCandidateAssociatedJobOpeningRecords, zohoRecruitSearchCandidateAssociatedJobOpeningRecordsPageFactory, zohoRecruitSearchJobOpeningAssociatedCandidateRecords, zohoRecruitSearchJobOpeningAssociatedCandidateRecordsPageFactory, zohoRecruitSearchRecords, zohoRecruitSearchRecordsCriteriaEntryToCriteriaString, zohoRecruitSearchRecordsCriteriaString, zohoRecruitSearchRecordsCriteriaStringForTree, zohoRecruitSearchRecordsPageFactory, zohoRecruitUpdateRecord, zohoRecruitUploadAttachmentForRecord, zohoRecruitUpsertRecord, zohoRecruitUrlSearchParams, zohoRecruitUrlSearchParamsMinusIdAndModule, zohoRecruitUrlSearchParamsMinusModule, zohoServerErrorData };
|