@or-sdk/contacts 3.2.2-beta.1774.0 → 3.2.2-beta.1778.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 -166
- 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 -93
- 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 -4
- 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 -130
- package/src/apiError.ts +11 -0
- package/src/constants.ts +8 -2
- package/src/types.ts +11 -10
- 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,47 +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, notifyEventProxy = false): 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
|
-
notifyEventProxy,
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Initiates bulk of deleting all contacts from a certain book.
|
|
119
|
-
* The only first batch is going to be executed and the only - its
|
|
120
|
-
* process id is returned in the response.
|
|
121
|
-
* Tracking of the batch status is possible with trackBatchProcess
|
|
122
|
-
* from BatchProcessApi
|
|
123
|
-
*
|
|
124
|
-
* @see batchProcessApi.trackBatchProcess
|
|
125
|
-
*/
|
|
126
|
-
async initDeleteBookContactsBulk(bookId: string, notifyEventProxy = false) {
|
|
127
|
-
const { contactsCount } = await this.bookServiceApi.getContactBook(bookId);
|
|
128
|
-
const contacts = await this.listContact({
|
|
129
|
-
contact_book: bookId,
|
|
130
|
-
size: CONTACTS_DELETE_MAX_AMOUNT,
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
const batchProcess = await this.runSingleDeleteContactsBulk({
|
|
134
|
-
ids: contacts.items.map(({ id }) => id),
|
|
135
|
-
contact_book: bookId,
|
|
136
|
-
notifyEventProxy,
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
return {
|
|
140
|
-
batchId: batchProcess.id,
|
|
141
|
-
totalBatches: Math.floor(contactsCount / 50),
|
|
142
|
-
firstBatchSize: CONTACTS_DELETE_MAX_AMOUNT,
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
|
|
146
104
|
/**
|
|
147
105
|
* @description Create Contact
|
|
148
106
|
* @param data
|
|
@@ -157,18 +115,61 @@ export default class ContactApi extends BaseApi {
|
|
|
157
115
|
|
|
158
116
|
/**
|
|
159
117
|
* @description Create Contacts either in single or in multi batch(es), depending on payload size
|
|
160
|
-
* @param data
|
|
161
118
|
*/
|
|
162
|
-
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);
|
|
163
122
|
const { contacts, ...rest } = data;
|
|
164
123
|
const contactsMaxSize = REQUEST_PAYLOAD_MAX_BYTES - getObjectSizeInBytes({ ...rest });
|
|
165
124
|
const contactsChunks = chunkArrByMaxSize(contacts, contactsMaxSize);
|
|
166
125
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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]), []);
|
|
170
137
|
}
|
|
171
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> {
|
|
152
|
+
const { contacts, ...rest } = data;
|
|
153
|
+
const contactsMaxSize = REQUEST_PAYLOAD_MAX_BYTES - getObjectSizeInBytes({ ...rest });
|
|
154
|
+
const contactsChunks = chunkArrByMaxSize(contacts, contactsMaxSize);
|
|
155
|
+
|
|
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
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
|
|
172
173
|
/**
|
|
173
174
|
* @description Merge two Contacts into one
|
|
174
175
|
* @param id Contact id TO which the data will be merged
|
|
@@ -232,103 +233,99 @@ export default class ContactApi extends BaseApi {
|
|
|
232
233
|
});
|
|
233
234
|
}
|
|
234
235
|
|
|
235
|
-
private async
|
|
236
|
-
contactIds: string[],
|
|
237
|
-
bookId: string | undefined,
|
|
238
|
-
repeats?: number
|
|
239
|
-
): Promise<List<ContactResponseDto>> {
|
|
240
|
-
const contacts = await this.listContact({
|
|
241
|
-
contactIds,
|
|
242
|
-
...(bookId && { contact_book: bookId }),
|
|
243
|
-
}).catch((e) => {
|
|
244
|
-
repeats = repeats || 0;
|
|
245
|
-
if (repeats < FAILED_REQUEST_REPEATS) {
|
|
246
|
-
return this.getSafelyContactsList(contactIds, bookId, repeats + 1);
|
|
247
|
-
}
|
|
248
|
-
throw new Error(e);
|
|
249
|
-
});
|
|
250
|
-
return contacts;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
private async bulkDeleteContacts(data: DeleteContactsData): Promise<void> {
|
|
254
|
-
const { ids, contact_book, all, ...rest } = data;
|
|
255
|
-
if (all) {
|
|
256
|
-
if (!contact_book) {
|
|
257
|
-
throw new ApiError(400, 'contact_book should be provided in case if "all" is true');
|
|
258
|
-
}
|
|
259
|
-
return await this.deleteContactsByBook(contact_book);
|
|
260
|
-
}
|
|
261
|
-
if (!ids) {
|
|
262
|
-
throw new ApiError(400, 'Provide either "contact_book" or "all"');
|
|
263
|
-
}
|
|
264
|
-
const batchSize = CONTACTS_DELETE_MAX_AMOUNT;
|
|
265
|
-
for (let i = 0;i < ids.length;i += batchSize) {
|
|
266
|
-
const chunk = ids.slice(i, i + batchSize);
|
|
267
|
-
await this.runSingleDeleteContactsBulk({
|
|
268
|
-
ids: chunk,
|
|
269
|
-
contact_book,
|
|
270
|
-
...rest,
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
private async runSingleDeleteContactsBulk(
|
|
276
|
-
data: DeleteContactMultiParamsDto,
|
|
277
|
-
withPoling = true
|
|
278
|
-
): Promise<BatchProcessResponseDto> {
|
|
236
|
+
private async bulkDeleteContacts(data: DeleteContactMultiParamsDto): Promise<void> {
|
|
279
237
|
const batchProcess = await this.apiCall<BatchProcessResponseDto>({
|
|
280
238
|
method: 'DELETE',
|
|
281
239
|
route: `${this.apiBasePath}/bulk`,
|
|
282
240
|
data,
|
|
283
241
|
});
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
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 = 2;
|
|
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');
|
|
287
274
|
}
|
|
288
|
-
|
|
275
|
+
|
|
276
|
+
return results;
|
|
289
277
|
}
|
|
290
278
|
|
|
291
|
-
private async createContactsInSingleBatch(data: CreateMultipleContactsDto): Promise<
|
|
279
|
+
private async createContactsInSingleBatch(data: CreateMultipleContactsDto): Promise<CreateContactsBatchResults> {
|
|
280
|
+
const s = performance.now();
|
|
292
281
|
const batchProcess = await this.apiCall<BatchProcessResponseDto>({
|
|
293
282
|
method: 'POST',
|
|
294
283
|
route: `${this.apiBasePath}/bulk`,
|
|
295
284
|
data,
|
|
296
285
|
});
|
|
297
286
|
const batchProcessResult = await this.polling(batchProcess.id);
|
|
298
|
-
const
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
data: Omit<CreateMultipleContactsDto, 'contacts'>
|
|
306
|
-
): Promise<ContactResponseDto[]> {
|
|
307
|
-
const batchPromises = contactsChunks.map((chunkContacts) => async () => this.createContactsInSingleBatch({
|
|
308
|
-
contacts: chunkContacts,
|
|
309
|
-
...data,
|
|
310
|
-
}));
|
|
311
|
-
|
|
312
|
-
const results: ContactResponseDto[] = [];
|
|
313
|
-
for (const fn of batchPromises) {
|
|
314
|
-
const singleBatchResults = await fn();
|
|
315
|
-
results.push(...singleBatchResults);
|
|
316
|
-
}
|
|
317
|
-
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
|
+
};
|
|
318
294
|
}
|
|
319
295
|
|
|
320
296
|
/**
|
|
321
297
|
* Pols specific batch process until status of it isn't turned from 'pending to something else.
|
|
322
298
|
*/
|
|
323
|
-
private async polling(batchId: string, repeats
|
|
324
|
-
|
|
325
|
-
|
|
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) {
|
|
326
311
|
if (repeats < FAILED_REQUEST_REPEATS) {
|
|
327
312
|
return this.polling(batchId, repeats + 1);
|
|
328
313
|
}
|
|
329
|
-
throw new
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
|
|
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;
|
|
333
328
|
}
|
|
334
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,16 +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 OptionalBy<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
|
|
13
|
-
|
|
14
6
|
//re-export types
|
|
15
7
|
export { OrderDirection, PaginationOptions, OrderOptions } from '@or-sdk/base';
|
|
16
8
|
|
|
@@ -39,7 +31,16 @@ export interface ContactBookParams extends AdaptedListParams<ContactBookParamsDt
|
|
|
39
31
|
export type AdaptedListParams<T extends ListApiParams & OrderParams> =
|
|
40
32
|
Omit<T, 'order' | 'skip' | 'take'> & Partial<PaginationOptions & OrderOptions>;
|
|
41
33
|
|
|
42
|
-
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
|
+
};
|
|
43
44
|
|
|
44
45
|
export type TrackBatchProcessResponse = {
|
|
45
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
|
+
|