@or-sdk/contacts 3.2.2-beta.1771.0 → 3.2.2-beta.1775.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/Contacts.js +1 -1
- package/dist/cjs/Contacts.js.map +1 -1
- package/dist/cjs/api/batchProcessApi.js +6 -0
- package/dist/cjs/api/batchProcessApi.js.map +1 -1
- package/dist/cjs/api/contactApi.js +143 -165
- package/dist/cjs/api/contactApi.js.map +1 -1
- package/dist/cjs/apiError.js +13 -1
- package/dist/cjs/apiError.js.map +1 -1
- package/dist/cjs/constants.js +2 -2
- package/dist/cjs/constants.js.map +1 -1
- package/dist/cjs/types.js.map +1 -1
- package/dist/cjs/utils.js +11 -1
- package/dist/cjs/utils.js.map +1 -1
- package/dist/esm/Contacts.js +1 -1
- package/dist/esm/Contacts.js.map +1 -1
- package/dist/esm/api/batchProcessApi.js +7 -1
- package/dist/esm/api/batchProcessApi.js.map +1 -1
- package/dist/esm/api/contactApi.js +82 -94
- package/dist/esm/api/contactApi.js.map +1 -1
- package/dist/esm/apiError.js +8 -0
- package/dist/esm/apiError.js.map +1 -1
- package/dist/esm/constants.js +2 -2
- package/dist/esm/constants.js.map +1 -1
- package/dist/esm/types.js.map +1 -1
- package/dist/esm/utils.js +9 -0
- package/dist/esm/utils.js.map +1 -1
- package/dist/types/api/batchProcessApi.d.ts +2 -1
- package/dist/types/api/batchProcessApi.d.ts.map +1 -1
- package/dist/types/api/contactApi.d.ts +7 -16
- package/dist/types/api/contactApi.d.ts.map +1 -1
- package/dist/types/apiError.d.ts +6 -0
- package/dist/types/apiError.d.ts.map +1 -1
- package/dist/types/constants.d.ts +2 -2
- package/dist/types/constants.d.ts.map +1 -1
- package/dist/types/types.d.ts +9 -5
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/utils.d.ts +1 -0
- package/dist/types/utils.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/Contacts.ts +1 -1
- package/src/api/batchProcessApi.ts +14 -5
- package/src/api/contactApi.ts +127 -127
- package/src/apiError.ts +11 -0
- package/src/constants.ts +8 -2
- package/src/types.ts +11 -11
- package/src/utils.ts +16 -0
package/src/api/contactApi.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
BatchProcessResponseDto,
|
|
3
|
+
BatchProcessStatus,
|
|
3
4
|
ContactParamsDto,
|
|
4
5
|
ContactRequestDto,
|
|
5
6
|
ContactResponseDto,
|
|
@@ -18,17 +19,15 @@ import {
|
|
|
18
19
|
import { CalApiParams, List } from '@or-sdk/base';
|
|
19
20
|
import { BaseApi } from './baseApi';
|
|
20
21
|
import BatchProcessApi from './batchProcessApi';
|
|
21
|
-
import
|
|
22
|
-
import {
|
|
22
|
+
import { getObjectSizeInBytes, chunkArrByMaxSize, debouncePromise } from '../utils';
|
|
23
|
+
import { InitCreateBatchResponse, CreateContactsBatchResults } from '../types';
|
|
23
24
|
import { REQUEST_PAYLOAD_MAX_BYTES, FAILED_REQUEST_REPEATS, CONTACTS_DELETE_MAX_AMOUNT } from '../constants';
|
|
24
|
-
import {
|
|
25
|
-
import { DeleteContactsData } from '../types';
|
|
25
|
+
import { CreateContactsBatchError } from '../apiError';
|
|
26
26
|
|
|
27
27
|
export default class ContactApi extends BaseApi {
|
|
28
28
|
constructor(
|
|
29
29
|
protected readonly apiCall: <T>(params: CalApiParams) => Promise<T>,
|
|
30
|
-
private batchProcessApi: BatchProcessApi
|
|
31
|
-
private bookServiceApi: ContactBookApi
|
|
30
|
+
private batchProcessApi: BatchProcessApi
|
|
32
31
|
) {
|
|
33
32
|
super(apiCall);
|
|
34
33
|
}
|
|
@@ -91,8 +90,8 @@ export default class ContactApi extends BaseApi {
|
|
|
91
90
|
* @description Delete Contact
|
|
92
91
|
* @param data
|
|
93
92
|
*/
|
|
94
|
-
deleteMulti(data:
|
|
95
|
-
if ((Array.isArray(data.ids) && data.ids.length
|
|
93
|
+
deleteMulti(data: DeleteContactMultiParamsDto): Promise<void> {
|
|
94
|
+
if ((Array.isArray(data.ids) && data.ids.length > CONTACTS_DELETE_MAX_AMOUNT) || data.all) {
|
|
96
95
|
return this.bulkDeleteContacts(data);
|
|
97
96
|
}
|
|
98
97
|
return this.apiCall({
|
|
@@ -102,45 +101,6 @@ export default class ContactApi extends BaseApi {
|
|
|
102
101
|
});
|
|
103
102
|
}
|
|
104
103
|
|
|
105
|
-
/**
|
|
106
|
-
* @description Delete all contacts from a book
|
|
107
|
-
*/
|
|
108
|
-
async deleteContactsByBook(bookId: string): Promise<void> {
|
|
109
|
-
const contacts = await this.listContact({ contact_book: bookId });
|
|
110
|
-
return this.bulkDeleteContacts({
|
|
111
|
-
ids: contacts.items.map(({ id }) => id),
|
|
112
|
-
contact_book: bookId,
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Initiates bulk of deleting all contacts from a certain book.
|
|
118
|
-
* The only first batch is going to be executed and the only - its
|
|
119
|
-
* process id is returned in the response.
|
|
120
|
-
* Tracking of the batch status is possible with trackBatchProcess
|
|
121
|
-
* from BatchProcessApi
|
|
122
|
-
*
|
|
123
|
-
* @see batchProcessApi.trackBatchProcess
|
|
124
|
-
*/
|
|
125
|
-
async initDeleteBookContactsBulk(bookId: string) {
|
|
126
|
-
const { contactsCount } = await this.bookServiceApi.getContactBook(bookId);
|
|
127
|
-
const contacts = await this.listContact({
|
|
128
|
-
contact_book: bookId,
|
|
129
|
-
size: CONTACTS_DELETE_MAX_AMOUNT,
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
const batchProcess = await this.runSingleDeleteContactsBulk({
|
|
133
|
-
ids: contacts.items.map(({ id }) => id),
|
|
134
|
-
contact_book: bookId,
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
return {
|
|
138
|
-
batchId: batchProcess.id,
|
|
139
|
-
totalBatches: Math.floor(contactsCount / 50),
|
|
140
|
-
firstBatchSize: CONTACTS_DELETE_MAX_AMOUNT,
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
|
|
144
104
|
/**
|
|
145
105
|
* @description Create Contact
|
|
146
106
|
* @param data
|
|
@@ -155,18 +115,61 @@ export default class ContactApi extends BaseApi {
|
|
|
155
115
|
|
|
156
116
|
/**
|
|
157
117
|
* @description Create Contacts either in single or in multi batch(es), depending on payload size
|
|
158
|
-
* @param data
|
|
159
118
|
*/
|
|
160
|
-
async bulkCreateContacts(data: CreateMultipleContactsDto): Promise<
|
|
119
|
+
async bulkCreateContacts(data: CreateMultipleContactsDto): Promise<string[]> {
|
|
120
|
+
// eslint-disable-next-line no-console
|
|
121
|
+
console.log('contacts: ', data.contacts.length);
|
|
122
|
+
const { contacts, ...rest } = data;
|
|
123
|
+
const contactsMaxSize = REQUEST_PAYLOAD_MAX_BYTES - getObjectSizeInBytes({ ...rest });
|
|
124
|
+
const contactsChunks = chunkArrByMaxSize(contacts, contactsMaxSize);
|
|
125
|
+
|
|
126
|
+
contactsChunks.forEach((chunk, idx) => {
|
|
127
|
+
// eslint-disable-next-line no-console
|
|
128
|
+
console.log('size of ', idx, ' chunk', ' is ', getObjectSizeInBytes(chunk));
|
|
129
|
+
});
|
|
130
|
+
// eslint-disable-next-line no-console
|
|
131
|
+
console.log('chunks: ', contactsChunks.length);
|
|
132
|
+
const results = contactsChunks.length === 1
|
|
133
|
+
? [await this.createContactsInSingleBatch(data)]
|
|
134
|
+
: await this.createContactsInMultiBatches(contactsChunks, rest);
|
|
135
|
+
|
|
136
|
+
return results.reduce<string[]>((acc, { contactsIds }) => ([...acc, ...contactsIds]), []);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Breaks the contacts to chunks of maximum allowed size (200 KB) and launches create batch
|
|
141
|
+
* only for the first chunk.
|
|
142
|
+
* Returns batch id in response by which the batch can be tracked using batch process API.
|
|
143
|
+
* Note: since create contact batch is a very expensive operations from terms of consuming
|
|
144
|
+
* DB connections, it is highly recommended to check amount of pending (running) batches
|
|
145
|
+
* prior to this method execution, and if the amount is greater of 2 the batch initiation
|
|
146
|
+
* should be deferred.
|
|
147
|
+
*
|
|
148
|
+
* @see batchProcessApi.trackBatchProcess
|
|
149
|
+
* @see batchProcessApi.getPendingBatchProcesses
|
|
150
|
+
*/
|
|
151
|
+
async initCreateBatch(data: CreateMultipleContactsDto): Promise<InitCreateBatchResponse> {
|
|
161
152
|
const { contacts, ...rest } = data;
|
|
162
153
|
const contactsMaxSize = REQUEST_PAYLOAD_MAX_BYTES - getObjectSizeInBytes({ ...rest });
|
|
163
154
|
const contactsChunks = chunkArrByMaxSize(contacts, contactsMaxSize);
|
|
164
155
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
: this.
|
|
156
|
+
const batchProcess = await this.apiCall<BatchProcessResponseDto>({
|
|
157
|
+
method: 'POST',
|
|
158
|
+
route: `${this.apiBasePath}/bulk`,
|
|
159
|
+
data: {
|
|
160
|
+
contacts: contactsChunks[0],
|
|
161
|
+
...rest,
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
totalChunks: contactsChunks.length,
|
|
167
|
+
firstChunkSize: contactsChunks[0].length,
|
|
168
|
+
batchId: batchProcess.id,
|
|
169
|
+
};
|
|
168
170
|
}
|
|
169
171
|
|
|
172
|
+
|
|
170
173
|
/**
|
|
171
174
|
* @description Merge two Contacts into one
|
|
172
175
|
* @param id Contact id TO which the data will be merged
|
|
@@ -230,102 +233,99 @@ export default class ContactApi extends BaseApi {
|
|
|
230
233
|
});
|
|
231
234
|
}
|
|
232
235
|
|
|
233
|
-
private async
|
|
234
|
-
contactIds: string[],
|
|
235
|
-
bookId: string | undefined,
|
|
236
|
-
repeats?: number
|
|
237
|
-
): Promise<List<ContactResponseDto>> {
|
|
238
|
-
const contacts = await this.listContact({
|
|
239
|
-
contactIds,
|
|
240
|
-
...(bookId && { contact_book: bookId }),
|
|
241
|
-
}).catch((e) => {
|
|
242
|
-
repeats = repeats || 0;
|
|
243
|
-
if (repeats < FAILED_REQUEST_REPEATS) {
|
|
244
|
-
return this.getSafelyContactsList(contactIds, bookId, repeats + 1);
|
|
245
|
-
}
|
|
246
|
-
throw new Error(e);
|
|
247
|
-
});
|
|
248
|
-
return contacts;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
private async bulkDeleteContacts(data: DeleteContactsData): Promise<void> {
|
|
252
|
-
const { ids, contact_book, all } = data;
|
|
253
|
-
if (all) {
|
|
254
|
-
if (!contact_book) {
|
|
255
|
-
throw new ApiError(400, 'contact_book should be provided in case if "all" is true');
|
|
256
|
-
}
|
|
257
|
-
return await this.deleteContactsByBook(contact_book);
|
|
258
|
-
}
|
|
259
|
-
if (!ids) {
|
|
260
|
-
throw new ApiError(400, 'Provide either "contact_book" or "all"');
|
|
261
|
-
}
|
|
262
|
-
const batchSize = CONTACTS_DELETE_MAX_AMOUNT;
|
|
263
|
-
for (let i = 0;i < ids.length;i += batchSize) {
|
|
264
|
-
const chunk = ids.slice(i, i + batchSize);
|
|
265
|
-
await this.runSingleDeleteContactsBulk({
|
|
266
|
-
ids: chunk,
|
|
267
|
-
contact_book,
|
|
268
|
-
});
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
private async runSingleDeleteContactsBulk(
|
|
273
|
-
data: DeleteContactMultiParamsDto,
|
|
274
|
-
withPoling = true
|
|
275
|
-
): Promise<BatchProcessResponseDto> {
|
|
236
|
+
private async bulkDeleteContacts(data: DeleteContactMultiParamsDto): Promise<void> {
|
|
276
237
|
const batchProcess = await this.apiCall<BatchProcessResponseDto>({
|
|
277
238
|
method: 'DELETE',
|
|
278
239
|
route: `${this.apiBasePath}/bulk`,
|
|
279
240
|
data,
|
|
280
241
|
});
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
242
|
+
await this.polling(batchProcess.id);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
private async createContactsInMultiBatches(
|
|
246
|
+
contactsChunks: ContactRequestDto[][],
|
|
247
|
+
data: Omit<CreateMultipleContactsDto, 'contacts'>
|
|
248
|
+
): Promise<CreateContactsBatchResults[]> {
|
|
249
|
+
const batchPromises = contactsChunks
|
|
250
|
+
.map((chunkContacts) => async () => this.createContactsInSingleBatch({
|
|
251
|
+
contacts: chunkContacts,
|
|
252
|
+
...data,
|
|
253
|
+
}));
|
|
254
|
+
|
|
255
|
+
const promisesBatchSize = 1;
|
|
256
|
+
const results: CreateContactsBatchResults[] = [];
|
|
257
|
+
|
|
258
|
+
for (let i = 0;i < batchPromises.length;i += promisesBatchSize) {
|
|
259
|
+
const s = performance.now();
|
|
260
|
+
try {
|
|
261
|
+
const chunk = batchPromises.slice(i, i + promisesBatchSize);
|
|
262
|
+
const chunkResults = await Promise.all(chunk.map((p) => p()));
|
|
263
|
+
results.push(...chunkResults);
|
|
264
|
+
} catch (e) {
|
|
265
|
+
if (e instanceof CreateContactsBatchError) {
|
|
266
|
+
e.processedBatchIds = results.map(({ batchId }) => batchId);
|
|
267
|
+
}
|
|
268
|
+
throw e;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const e = performance.now();
|
|
272
|
+
// eslint-disable-next-line no-console
|
|
273
|
+
console.log('batch id = ', i, '; awaiting ', promisesBatchSize, ' batches: ', (e - s) / 1000, 's');
|
|
284
274
|
}
|
|
285
|
-
|
|
275
|
+
|
|
276
|
+
return results;
|
|
286
277
|
}
|
|
287
278
|
|
|
288
|
-
private async createContactsInSingleBatch(data: CreateMultipleContactsDto): Promise<
|
|
279
|
+
private async createContactsInSingleBatch(data: CreateMultipleContactsDto): Promise<CreateContactsBatchResults> {
|
|
280
|
+
const s = performance.now();
|
|
289
281
|
const batchProcess = await this.apiCall<BatchProcessResponseDto>({
|
|
290
282
|
method: 'POST',
|
|
291
283
|
route: `${this.apiBasePath}/bulk`,
|
|
292
284
|
data,
|
|
293
285
|
});
|
|
294
286
|
const batchProcessResult = await this.polling(batchProcess.id);
|
|
295
|
-
const
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
data: Omit<CreateMultipleContactsDto, 'contacts'>
|
|
303
|
-
): Promise<ContactResponseDto[]> {
|
|
304
|
-
const batchPromises = contactsChunks.map((chunkContacts) => async () => this.createContactsInSingleBatch({
|
|
305
|
-
contacts: chunkContacts,
|
|
306
|
-
...data,
|
|
307
|
-
}));
|
|
308
|
-
|
|
309
|
-
const results: ContactResponseDto[] = [];
|
|
310
|
-
for (const fn of batchPromises) {
|
|
311
|
-
const singleBatchResults = await fn();
|
|
312
|
-
results.push(...singleBatchResults);
|
|
313
|
-
}
|
|
314
|
-
return results;
|
|
287
|
+
const e = performance.now();
|
|
288
|
+
// eslint-disable-next-line no-console
|
|
289
|
+
console.log('single batch time: ', (e - s) / 1000, 's');
|
|
290
|
+
return {
|
|
291
|
+
batchId: batchProcess.id,
|
|
292
|
+
contactsIds: batchProcessResult.results.flatMap<string>(i => JSON.parse(i)),
|
|
293
|
+
};
|
|
315
294
|
}
|
|
316
295
|
|
|
317
296
|
/**
|
|
318
297
|
* Pols specific batch process until status of it isn't turned from 'pending to something else.
|
|
319
298
|
*/
|
|
320
|
-
private async polling(batchId: string, repeats
|
|
321
|
-
|
|
322
|
-
|
|
299
|
+
private async polling(batchId: string, repeats = 0): Promise<BatchProcessResponseDto> {
|
|
300
|
+
|
|
301
|
+
let batchProcess: BatchProcessResponseDto;
|
|
302
|
+
try {
|
|
303
|
+
batchProcess = await debouncePromise(() => this.batchProcessApi.getBatchProcess(batchId), 1000);
|
|
304
|
+
// eslint-disable-next-line no-console
|
|
305
|
+
const counter = batchCounters[batchId] || 0;
|
|
306
|
+
// eslint-disable-next-line no-console
|
|
307
|
+
console.log('polling counter of ', batchId, ' = ', counter, ' | status = ', batchProcess.status);
|
|
308
|
+
batchCounters[batchId] = counter + 1;
|
|
309
|
+
|
|
310
|
+
} catch (e) {
|
|
323
311
|
if (repeats < FAILED_REQUEST_REPEATS) {
|
|
324
312
|
return this.polling(batchId, repeats + 1);
|
|
325
313
|
}
|
|
326
|
-
throw new
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
|
|
314
|
+
throw new CreateContactsBatchError((e as unknown as Error).message, batchId);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (batchProcess.status === BatchProcessStatus.failed) {
|
|
318
|
+
throw new CreateContactsBatchError(
|
|
319
|
+
'Could not complete batch process',
|
|
320
|
+
batchId,
|
|
321
|
+
batchProcess.messages
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
return batchProcess.status === BatchProcessStatus.pending
|
|
326
|
+
? this.polling(batchId)
|
|
327
|
+
: batchProcess;
|
|
330
328
|
}
|
|
331
329
|
}
|
|
330
|
+
|
|
331
|
+
const batchCounters: Record<string, number> = {};
|
package/src/apiError.ts
CHANGED
|
@@ -11,3 +11,14 @@ export class ApiError extends Error {
|
|
|
11
11
|
super(message);
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
|
+
|
|
15
|
+
export class CreateContactsBatchError extends Error {
|
|
16
|
+
constructor(
|
|
17
|
+
message: string,
|
|
18
|
+
public readonly failedBatchId: string,
|
|
19
|
+
public readonly batchMessages?: string[],
|
|
20
|
+
public processedBatchIds?: string[],
|
|
21
|
+
) {
|
|
22
|
+
super(message);
|
|
23
|
+
}
|
|
24
|
+
}
|
package/src/constants.ts
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
export const CONTACTS_SERVICE_KEY = 'contacts-api';
|
|
2
2
|
|
|
3
|
-
export const REQUEST_PAYLOAD_MAX_BYTES =
|
|
3
|
+
export const REQUEST_PAYLOAD_MAX_BYTES = 150000;
|
|
4
4
|
|
|
5
5
|
export const FAILED_REQUEST_REPEATS = 3;
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
/**
|
|
8
|
+
* 5 selected experimentally
|
|
9
|
+
* Since contacts deleting assumes cascading on related field values and deleting
|
|
10
|
+
* relations in contact_book_contact table, it results in huge amount of queries
|
|
11
|
+
* which sent simultaneously might kill connection to the DB.
|
|
12
|
+
*/
|
|
13
|
+
export const CONTACTS_DELETE_MAX_AMOUNT = 5;
|
package/src/types.ts
CHANGED
|
@@ -1,17 +1,8 @@
|
|
|
1
1
|
import { Token } from '@or-sdk/base';
|
|
2
|
-
import {
|
|
3
|
-
ContactBookParamsDto,
|
|
4
|
-
ListApiParams,
|
|
5
|
-
OrderParams,
|
|
6
|
-
DeleteContactMultiParamsDto,
|
|
7
|
-
BatchProcessStatus,
|
|
8
|
-
} from '@onereach/types-contacts-api';
|
|
2
|
+
import { BatchProcessStatus, ContactBookParamsDto, ListApiParams, OrderParams } from '@onereach/types-contacts-api';
|
|
9
3
|
import { OrderOptions, PaginationOptions } from '@or-sdk/base';
|
|
10
4
|
export * from '@onereach/types-contacts-api';
|
|
11
5
|
|
|
12
|
-
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
|
|
13
|
-
type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
|
|
14
|
-
|
|
15
6
|
//re-export types
|
|
16
7
|
export { OrderDirection, PaginationOptions, OrderOptions } from '@or-sdk/base';
|
|
17
8
|
|
|
@@ -40,7 +31,16 @@ export interface ContactBookParams extends AdaptedListParams<ContactBookParamsDt
|
|
|
40
31
|
export type AdaptedListParams<T extends ListApiParams & OrderParams> =
|
|
41
32
|
Omit<T, 'order' | 'skip' | 'take'> & Partial<PaginationOptions & OrderOptions>;
|
|
42
33
|
|
|
43
|
-
export type
|
|
34
|
+
export type CreateContactsBatchResults ={
|
|
35
|
+
batchId: string;
|
|
36
|
+
contactsIds: string[];
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export type InitCreateBatchResponse = {
|
|
40
|
+
totalChunks: number;
|
|
41
|
+
firstChunkSize: number;
|
|
42
|
+
batchId: string;
|
|
43
|
+
};
|
|
44
44
|
|
|
45
45
|
export type TrackBatchProcessResponse = {
|
|
46
46
|
status: BatchProcessStatus;
|
package/src/utils.ts
CHANGED
|
@@ -43,3 +43,19 @@ export const chunkArrByMaxSize = <T>(arr: T[], maxSize: number): T[][] => {
|
|
|
43
43
|
...chunkArrByMaxSize(arr.slice(mid), maxSize),
|
|
44
44
|
];
|
|
45
45
|
};
|
|
46
|
+
|
|
47
|
+
export function debouncePromise<T>(
|
|
48
|
+
caller: () => Promise<T>,
|
|
49
|
+
delay: number,
|
|
50
|
+
reject?: (value: string | T | PromiseLike<T>) => void
|
|
51
|
+
): Promise<T> {
|
|
52
|
+
let timeout: NodeJS.Timeout | null = null;
|
|
53
|
+
|
|
54
|
+
return new Promise<T>((res) => {
|
|
55
|
+
if (timeout) {
|
|
56
|
+
clearTimeout(timeout);
|
|
57
|
+
}
|
|
58
|
+
timeout = setTimeout(() => caller().then((x) => res(x)).catch((err) => reject && reject(err)), delay);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|