@devrev/ts-adaas 1.2.4 → 1.2.6
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/README.md +191 -305
- package/dist/deprecated/uploader/index.js +3 -3
- package/dist/repo/repo.d.ts +1 -0
- package/dist/repo/repo.js +6 -3
- package/dist/repo/repo.test.js +32 -1
- package/dist/types/workers.d.ts +2 -0
- package/dist/uploader/uploader.d.ts +5 -5
- package/dist/uploader/uploader.interfaces.d.ts +0 -1
- package/dist/uploader/uploader.js +24 -20
- package/dist/workers/default-workers/attachments-extraction.js +3 -1
- package/dist/workers/default-workers/data-extraction.js +0 -1
- package/dist/workers/default-workers/load-attachments.js +0 -1
- package/dist/workers/default-workers/load-data.js +0 -1
- package/dist/workers/worker-adapter.js +21 -15
- package/package.json +1 -1
- package/dist/http/axios-devrev-client.d.ts +0 -3
- package/dist/http/axios-devrev-client.js +0 -37
package/README.md
CHANGED
|
@@ -1,235 +1,124 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
## Release Notes
|
|
4
|
-
|
|
5
|
-
### v1.2.4
|
|
6
|
-
|
|
7
|
-
- Do not fail the extraction of attachments if streaming of single attachment fails.
|
|
8
|
-
|
|
9
|
-
### v1.2.3
|
|
10
|
-
|
|
11
|
-
- Add `local` flag to use for local development of the ADaaS snap-ins.
|
|
12
|
-
- Send library version, snap-in version and snap-in slug in headers while emitting.
|
|
13
|
-
- Make `actor_id` field optional for `SsorAttachment` interface.
|
|
14
|
-
- Fix bugs related to event handling, error logging.
|
|
15
|
-
|
|
16
|
-
### v1.2.2
|
|
17
|
-
|
|
18
|
-
- Add library version as a part of control protocol.
|
|
19
|
-
- Improve axios client and adapter logging.
|
|
20
|
-
- Fix bugs related to state handling.
|
|
21
|
-
|
|
22
|
-
### v1.2.1
|
|
23
|
-
|
|
24
|
-
- Reduced the `delayFactor` to minimize unnecessary delays.
|
|
25
|
-
- Correct the setting of the `lastSyncStarted` timestamp.
|
|
26
|
-
- Improve logging for attachment extraction and loading.
|
|
27
|
-
- Fix several bugs related to the control protocol.
|
|
28
|
-
|
|
29
|
-
### v1.2.0
|
|
30
|
-
|
|
31
|
-
- Add support for loading attachments from DevRev to external system.
|
|
32
|
-
|
|
33
|
-
### v1.1.6
|
|
34
|
-
|
|
35
|
-
- Add exponential retry and handle rate-limiting towards DevRev.
|
|
36
|
-
- Gracefully handle failure to upload extracted attachments.
|
|
37
|
-
|
|
38
|
-
### v1.1.5
|
|
39
|
-
|
|
40
|
-
- Increase `delayFactor` and number of retries for the exponential backoff retry mechanism for HTTP requests.
|
|
41
|
-
- Provide an inject function for streaming attachments.
|
|
42
|
-
- Fix the attachments streaming bug.
|
|
43
|
-
|
|
44
|
-
### v1.1.4
|
|
45
|
-
|
|
46
|
-
- Provide log lines and stack traces for runtime worker errors.
|
|
47
|
-
|
|
48
|
-
### v1.1.3
|
|
49
|
-
|
|
50
|
-
- Export `axios` and `axiosClient` with the exponential backoff retry mechanism for HTTP requests and omit Authorization headers from Axios errors.
|
|
51
|
-
- Resolve circular structure logging issues.
|
|
52
|
-
- Fix the attachments metadata normalization bug.
|
|
53
|
-
- Improve repository logging.
|
|
54
|
-
|
|
55
|
-
### v1.1.2
|
|
56
|
-
|
|
57
|
-
- Unify incoming and outgoing event context.
|
|
58
|
-
- Add `dev_oid` to logger tags.
|
|
59
|
-
|
|
60
|
-
### v1.1.1
|
|
61
|
-
|
|
62
|
-
- Add default workers for loading deletion events.
|
|
63
|
-
|
|
64
|
-
### v1.1.0
|
|
65
|
-
|
|
66
|
-
- Support sync from DevRev to the external system. (Known limitations: no support for loading attachments.)
|
|
67
|
-
|
|
68
|
-
### v1.0.4
|
|
69
|
-
|
|
70
|
-
- Fix logging from worker threads.
|
|
71
|
-
|
|
72
|
-
### v1.0.3
|
|
73
|
-
|
|
74
|
-
- Add release notes.
|
|
75
|
-
|
|
76
|
-
### v1.0.2
|
|
77
|
-
|
|
78
|
-
- Fix bugs and improve local development.
|
|
79
|
-
- Expose `formatAxiosError` function for error handling.
|
|
80
|
-
|
|
81
|
-
### v1.0.1
|
|
82
|
-
|
|
83
|
-
- Fix bugs and improve logging.
|
|
84
|
-
|
|
85
|
-
### v1.0.0
|
|
86
|
-
|
|
87
|
-
- Enable extractions to use the full lambda runtime and gracefully handle execution context timeout.
|
|
88
|
-
- Simplify metadata and data normalization and uploading with the repo implementation.
|
|
89
|
-
- Provide default handling of the attachment extraction phase in the ADaaS SDK library.
|
|
90
|
-
- Reduce file size and streamline processes with gzip compression.
|
|
91
|
-
- Fix bugs and improve error handling.
|
|
92
|
-
|
|
93
|
-
### v0.0.3
|
|
94
|
-
|
|
95
|
-
- Support new recipe management.
|
|
96
|
-
|
|
97
|
-
### v0.0.2
|
|
98
|
-
|
|
99
|
-
- Support the State API.
|
|
100
|
-
- Provide an HTTP client for API requests.
|
|
101
|
-
- Create local artifact files in the local development environment.
|
|
102
|
-
- Improve logging.
|
|
103
|
-
|
|
104
|
-
### v0.0.1
|
|
105
|
-
|
|
106
|
-
- Implement a demo of the ADaaS snap-in.
|
|
107
|
-
- Add an adapter for the ADaaS control protocol with helper functions.
|
|
108
|
-
- Provide an uploader for uploading artifacts.
|
|
109
|
-
|
|
110
|
-
# Overview
|
|
1
|
+
# Airdrop SDK
|
|
111
2
|
|
|
112
3
|
[](https://coveralls.io/github/devrev/adaas-sdk?branch=main)
|
|
113
4
|
|
|
114
|
-
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The Airdrop SDK for TypeScript helps developers build snap-ins that integrate with DevRev’s Airdrop platform.
|
|
8
|
+
This SDK simplifies the workflow for handling data extraction and loading, event-driven actions, state management, and artifact handling.
|
|
115
9
|
|
|
116
10
|
It provides features such as:
|
|
117
11
|
|
|
118
|
-
- Type Definitions: Structured types for
|
|
12
|
+
- Type Definitions: Structured types for Airdrop control protocol
|
|
119
13
|
- Event Management: Easily emit events for different extraction or loading phases
|
|
120
14
|
- State Handling: Update and access state in real-time within tasks
|
|
121
15
|
- Artifact Management: Supports batched storage of artifacts
|
|
122
16
|
- Error & Timeout Support: Error handling and timeout management for long-running tasks
|
|
123
17
|
|
|
124
|
-
|
|
18
|
+
## Installation
|
|
125
19
|
|
|
126
20
|
```bash
|
|
127
21
|
npm install @devrev/ts-adaas
|
|
128
22
|
```
|
|
129
23
|
|
|
130
|
-
|
|
24
|
+
## Reference
|
|
131
25
|
|
|
132
|
-
|
|
26
|
+
### `spawn` function
|
|
133
27
|
|
|
134
|
-
|
|
28
|
+
This function initializes a new worker thread and oversees its lifecycle.
|
|
29
|
+
It should be invoked when the snap-in receives a message from the Airdrop platform.
|
|
30
|
+
The worker script provided then handles the event accordingly.
|
|
135
31
|
|
|
136
|
-
|
|
137
|
-
- Metadata Extraction
|
|
138
|
-
- Data Extraction
|
|
139
|
-
- Attachments Extraction
|
|
32
|
+
#### Usage
|
|
140
33
|
|
|
141
|
-
|
|
34
|
+
```typescript
|
|
35
|
+
spawn({ event, initialState, workerPath, options })
|
|
36
|
+
```
|
|
142
37
|
|
|
143
|
-
|
|
38
|
+
#### Parameters
|
|
144
39
|
|
|
145
|
-
|
|
40
|
+
* _event_
|
|
41
|
+
|
|
42
|
+
Required. An object of type __AirdropEvent__ that is received from the Airdrop platform.
|
|
146
43
|
|
|
147
|
-
|
|
44
|
+
* _initialState_
|
|
148
45
|
|
|
149
|
-
|
|
46
|
+
Required. Object of __any__ type that represents the initial state of the snap-in.
|
|
150
47
|
|
|
151
|
-
|
|
48
|
+
* _workerPath_
|
|
152
49
|
|
|
153
|
-
|
|
154
|
-
import { AirdropEvent, EventType, spawn } from '@devrev/ts-adaas';
|
|
50
|
+
Required. A __string__ that represents the path to the worker file.
|
|
155
51
|
|
|
156
|
-
|
|
157
|
-
issues: { completed: boolean };
|
|
158
|
-
users: { completed: boolean };
|
|
159
|
-
attachments: { completed: boolean };
|
|
160
|
-
}
|
|
52
|
+
* _options_
|
|
161
53
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
54
|
+
Optional. An object of type **WorkerAdapterOptions**, which will be passed to the newly created worker. This worker will then initialize a `WorkerAdapter` by invoking the `processTask` function. The options include:
|
|
55
|
+
|
|
56
|
+
* `isLocalDevelopment`
|
|
57
|
+
|
|
58
|
+
A __boolean__ flag. If set to `true`, intermediary files containing extracted data will be stored on the local machine, which is useful during development. The default value is `false`.
|
|
167
59
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
path = __dirname + '/workers/metadata-extraction';
|
|
176
|
-
break;
|
|
177
|
-
case EventType.ExtractionDataStart:
|
|
178
|
-
case EventType.ExtractionDataContinue:
|
|
179
|
-
path = __dirname + '/workers/data-extraction';
|
|
180
|
-
break;
|
|
181
|
-
}
|
|
182
|
-
return path;
|
|
183
|
-
}
|
|
60
|
+
* `timeout`
|
|
61
|
+
|
|
62
|
+
A __number__ that specifies the timeout duration for the lambda function, in milliseconds. The default is 10 minutes (10 * 60 * 1000 milliseconds), with a maximum allowable duration of 13 minutes (13 * 60 * 1000 milliseconds).
|
|
63
|
+
|
|
64
|
+
* `batchSize`
|
|
65
|
+
|
|
66
|
+
A __number__ that determines the maximum number of items to be processed and saved to an intermediary file before being sent to the Airdrop platform. The default batch size is 2,000.
|
|
184
67
|
|
|
68
|
+
#### Return value
|
|
69
|
+
|
|
70
|
+
A __promise__ that resolves once the worker has completed processing.
|
|
71
|
+
|
|
72
|
+
#### Example
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
185
75
|
const run = async (events: AirdropEvent[]) => {
|
|
186
76
|
for (const event of events) {
|
|
187
77
|
const file = getWorkerPerExtractionPhase(event);
|
|
188
|
-
await spawn<
|
|
78
|
+
await spawn<ExtractorState>({
|
|
189
79
|
event,
|
|
190
80
|
initialState,
|
|
191
81
|
workerPath: file,
|
|
192
|
-
options: {
|
|
193
|
-
isLocalDevelopment: true,
|
|
194
|
-
},
|
|
195
82
|
});
|
|
196
83
|
}
|
|
197
84
|
};
|
|
198
|
-
|
|
199
|
-
export default run;
|
|
200
85
|
```
|
|
201
86
|
|
|
202
|
-
|
|
87
|
+
### `processTask` function
|
|
203
88
|
|
|
204
|
-
The
|
|
89
|
+
The `processTask` function retrieves the current state from the Airdrop platform and initializes a new `WorkerAdapter`.
|
|
90
|
+
It executes the code specified in the `task` parameter, which contains the worker's functionality.
|
|
91
|
+
If a timeout occurs, the function handles it by executing the `onTimeout` callback, ensuring the worker exits gracefully.
|
|
92
|
+
Both functions receive an `adapter` parameter, representing the initialized `WorkerAdapter` object.
|
|
205
93
|
|
|
206
|
-
The ADaaS library provides a repository management system to handle artifacts in batches. The `initializeRepos` function initializes the repositories, and the `push` function uploads the artifacts to the repositories. The `postState` function is used to post the state of the extraction task.
|
|
207
94
|
|
|
208
|
-
|
|
95
|
+
#### Usage
|
|
96
|
+
```typescript
|
|
97
|
+
processTask({ task, onTimeout })
|
|
98
|
+
```
|
|
209
99
|
|
|
210
|
-
|
|
100
|
+
#### Parameters
|
|
211
101
|
|
|
212
|
-
|
|
102
|
+
* _task_
|
|
103
|
+
|
|
104
|
+
Required. A __function__ that defines the logic associated with the given event type.
|
|
213
105
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
ExtractorEventType,
|
|
218
|
-
processTask,
|
|
219
|
-
} from '@devrev/ts-adaas';
|
|
106
|
+
* _onTimeout_
|
|
107
|
+
|
|
108
|
+
Required. A __function__ managing the timeout of the lambda invocation, including saving any necessary progress at the time of timeout.
|
|
220
109
|
|
|
221
|
-
|
|
222
|
-
{
|
|
223
|
-
id: 'devrev',
|
|
224
|
-
name: 'devrev',
|
|
225
|
-
description: 'Demo external sync unit',
|
|
226
|
-
item_count: 2,
|
|
227
|
-
item_type: 'issues',
|
|
228
|
-
},
|
|
229
|
-
];
|
|
110
|
+
#### Example
|
|
230
111
|
|
|
112
|
+
````typescript
|
|
113
|
+
// External sync units extraction
|
|
231
114
|
processTask({
|
|
232
115
|
task: async ({ adapter }) => {
|
|
116
|
+
const httpClient = new HttpClient(adapter.event);
|
|
117
|
+
|
|
118
|
+
const todoLists = await httpClient.getTodoLists();
|
|
119
|
+
|
|
120
|
+
const externalSyncUnits: ExternalSyncUnit[] = todoLists.map((todoList) => normalizeTodoList(todoList));
|
|
121
|
+
|
|
233
122
|
await adapter.emit(ExtractorEventType.ExtractionExternalSyncUnitsDone, {
|
|
234
123
|
external_sync_units: externalSyncUnits,
|
|
235
124
|
});
|
|
@@ -242,170 +131,167 @@ processTask({
|
|
|
242
131
|
});
|
|
243
132
|
},
|
|
244
133
|
});
|
|
245
|
-
|
|
134
|
+
````
|
|
135
|
+
|
|
136
|
+
### `WorkerAdapter` class
|
|
246
137
|
|
|
247
|
-
|
|
138
|
+
Used to interact with Airdrop platform.
|
|
139
|
+
Provides utilities to emit events to the Airdrop platform, update the state of the snap-in and upload artifacts (files with data) to the platform.
|
|
248
140
|
|
|
249
|
-
|
|
141
|
+
### Usage
|
|
250
142
|
|
|
251
143
|
```typescript
|
|
252
|
-
|
|
253
|
-
|
|
144
|
+
new WorkerAdapter({
|
|
145
|
+
event,
|
|
146
|
+
adapterState,
|
|
147
|
+
options,
|
|
148
|
+
});
|
|
149
|
+
```
|
|
254
150
|
|
|
255
|
-
|
|
151
|
+
#### Parameters
|
|
256
152
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
153
|
+
* _event_
|
|
154
|
+
|
|
155
|
+
Required. An object of type __AirdropEvent__ that is received from the Airdrop platform.
|
|
156
|
+
|
|
157
|
+
* _adapterState_
|
|
158
|
+
|
|
159
|
+
Required. An object of type __State__, which represents the initial state of the adapter.
|
|
160
|
+
|
|
161
|
+
* _options_
|
|
162
|
+
|
|
163
|
+
Optional. An object of type __WorkerAdapterOptions__ that specifies additional configuration options for the `WorkerAdapter`. This object is passed via the `spawn` function.
|
|
164
|
+
|
|
165
|
+
#### Example
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
const adapter = new WorkerAdapter<ConnectorState>({
|
|
169
|
+
event,
|
|
170
|
+
adapterState,
|
|
171
|
+
options,
|
|
270
172
|
});
|
|
271
173
|
```
|
|
272
174
|
|
|
273
|
-
###
|
|
175
|
+
### `WorkerAdapter.state` property
|
|
274
176
|
|
|
275
|
-
|
|
177
|
+
Getter and setter methods for working with the adapter state.
|
|
178
|
+
|
|
179
|
+
### Usage
|
|
276
180
|
|
|
277
181
|
```typescript
|
|
278
|
-
|
|
279
|
-
|
|
182
|
+
// get state
|
|
183
|
+
const adapterState = adapter.state;
|
|
280
184
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
];
|
|
185
|
+
// set state
|
|
186
|
+
adapter.state = newAdapterState;
|
|
187
|
+
```
|
|
285
188
|
|
|
286
|
-
|
|
287
|
-
{ id: 'user-1', created_date: '1999-12-25T01:00:03+01:00', ... },
|
|
288
|
-
{ id: 'user-2', created_date: '1999-12-27T15:31:34+01:00', ... },
|
|
289
|
-
];
|
|
189
|
+
#### Example
|
|
290
190
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
191
|
+
```typescript
|
|
192
|
+
export const initialState: ExtractorState = {
|
|
193
|
+
users: { completed: false },
|
|
194
|
+
tasks: { completed: false },
|
|
195
|
+
attachments: { completed: false },
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
adapter.state = initialState;
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### `WorkerAdapter.initializeRepos` method
|
|
202
|
+
|
|
203
|
+
Initializes a `Repo` object for each item provided.
|
|
204
|
+
|
|
205
|
+
### Usage
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
adapter.initializeRepos(repos);
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
#### Parameters
|
|
212
|
+
|
|
213
|
+
* _repos_
|
|
214
|
+
|
|
215
|
+
Required. An array of objects of type `RepoInterface`.
|
|
216
|
+
|
|
217
|
+
#### Example
|
|
218
|
+
|
|
219
|
+
This should typically be called within the function passed as a parameter to the `processTask` function in the data extraction phase.
|
|
295
220
|
|
|
221
|
+
```typescript
|
|
296
222
|
const repos = [
|
|
297
|
-
{
|
|
298
|
-
|
|
299
|
-
|
|
223
|
+
{
|
|
224
|
+
itemType: 'tasks',
|
|
225
|
+
normalize: normalizeTask,
|
|
226
|
+
}
|
|
300
227
|
];
|
|
301
228
|
|
|
302
|
-
|
|
303
|
-
task: async ({ adapter }) => {
|
|
304
|
-
adapter.initializeRepos(repos);
|
|
305
|
-
|
|
306
|
-
if (adapter.event.payload.event_type === EventType.ExtractionDataStart) {
|
|
307
|
-
await adapter.getRepo('issues')?.push(issues);
|
|
308
|
-
await adapter.emit(ExtractorEventType.ExtractionDataProgress, { progress: 50 });
|
|
309
|
-
} else {
|
|
310
|
-
await adapter.getRepo('users')?.push(users);
|
|
311
|
-
await adapter.getRepo('attachments')?.push(attachments);
|
|
312
|
-
await adapter.emit(ExtractorEventType.ExtractionDataDone, { progress: 100 });
|
|
313
|
-
}
|
|
314
|
-
},
|
|
315
|
-
onTimeout: async ({ adapter }) => {
|
|
316
|
-
await adapter.postState();
|
|
317
|
-
await adapter.emit(ExtractorEventType.ExtractionDataProgress, { progress: 50 });
|
|
318
|
-
},
|
|
319
|
-
});
|
|
229
|
+
adapter.initializeRepos(repos);
|
|
320
230
|
```
|
|
321
231
|
|
|
322
|
-
###
|
|
232
|
+
### `WorkerAdapter.getRepo` method
|
|
323
233
|
|
|
324
|
-
|
|
234
|
+
Finds a Repo from the initialized repos.
|
|
325
235
|
|
|
326
|
-
|
|
236
|
+
### Usage
|
|
327
237
|
|
|
328
238
|
```typescript
|
|
329
|
-
|
|
330
|
-
url: string;
|
|
331
|
-
id: string;
|
|
332
|
-
file_name: string;
|
|
333
|
-
author_id: string;
|
|
334
|
-
parent_id: string;
|
|
335
|
-
}
|
|
239
|
+
adapter.getRepo(itemType);
|
|
336
240
|
```
|
|
337
241
|
|
|
338
|
-
|
|
242
|
+
#### Parameters
|
|
339
243
|
|
|
340
|
-
|
|
244
|
+
* _itemType_
|
|
245
|
+
|
|
246
|
+
Required. A __string__ that represents the itemType property for the searched repo.
|
|
341
247
|
|
|
342
|
-
|
|
248
|
+
#### Return value
|
|
343
249
|
|
|
344
|
-
|
|
250
|
+
An object of type __Repo__ if the repo is found, otherwise __undefined__.
|
|
251
|
+
|
|
252
|
+
#### Example
|
|
253
|
+
|
|
254
|
+
This should typically be called within the function passed as a parameter to the `processTask` function.
|
|
345
255
|
|
|
346
256
|
```typescript
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
update: updateConversation,
|
|
360
|
-
},
|
|
361
|
-
],
|
|
362
|
-
});
|
|
363
|
-
|
|
364
|
-
await adapter.emit(LoaderEventType.DataLoadingDone, {
|
|
365
|
-
reports,
|
|
366
|
-
processed_files,
|
|
367
|
-
});
|
|
368
|
-
},
|
|
369
|
-
onTimeout: async ({ adapter }) => {
|
|
370
|
-
await adapter.emit(LoaderEventType.DataLoadingProgress, {
|
|
371
|
-
reports: adapter.reports,
|
|
372
|
-
processed_files: adapter.processedFiles,
|
|
373
|
-
});
|
|
374
|
-
});
|
|
257
|
+
// Push users to the repository designated for 'users' data.
|
|
258
|
+
await adapter.getRepo('users')?.push(users);
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### `WorkerAdapter.emit` method
|
|
262
|
+
|
|
263
|
+
Emits an event to the Airdrop platform.
|
|
264
|
+
|
|
265
|
+
### Usage
|
|
266
|
+
|
|
267
|
+
```typescript
|
|
268
|
+
adapter.emit( newEventType, data ):
|
|
375
269
|
```
|
|
376
270
|
|
|
377
|
-
|
|
271
|
+
#### Parameters
|
|
378
272
|
|
|
379
|
-
|
|
273
|
+
* _newEventType_
|
|
274
|
+
|
|
275
|
+
Required. The event type to be emitted, of type __ExtractorEventType__ or __LoaderEventType__.
|
|
380
276
|
|
|
381
|
-
|
|
277
|
+
* _data_
|
|
278
|
+
|
|
279
|
+
Optional. An object of type __EventData__ which represents the data to be sent with the event.
|
|
382
280
|
|
|
383
|
-
|
|
281
|
+
#### Return value
|
|
384
282
|
|
|
385
|
-
|
|
283
|
+
A __promise__, which resolves to undefined after the emit function completes its execution or rejects with an error.
|
|
284
|
+
|
|
285
|
+
#### Example
|
|
286
|
+
|
|
287
|
+
This should typically be called within the function passed as a parameter to the `processTask` function.
|
|
386
288
|
|
|
387
289
|
```typescript
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
const { reports, processed_files } = await adapter.loadAttachments({
|
|
391
|
-
create,
|
|
392
|
-
});
|
|
290
|
+
// Emitting successfully finished data extraction.
|
|
291
|
+
await adapter.emit(ExtractorEventType.ExtractionDataDone);
|
|
393
292
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
});
|
|
398
|
-
},
|
|
399
|
-
onTimeout: async ({ adapter }) => {
|
|
400
|
-
await adapter.postState();
|
|
401
|
-
await adapter.emit(LoaderEventType.AttachmentLoadingProgress, {
|
|
402
|
-
reports: adapter.reports,
|
|
403
|
-
processed_files: adapter.processedFiles,
|
|
404
|
-
});
|
|
405
|
-
},
|
|
293
|
+
// Emitting a delay in attachments extraction phase.
|
|
294
|
+
await adapter.emit(ExtractorEventType.ExtractionAttachmentsDelay, {
|
|
295
|
+
delay: 10,
|
|
406
296
|
});
|
|
407
297
|
```
|
|
408
|
-
|
|
409
|
-
The loading function `create` provides loading to the external system, to make API calls to the external system to create the attachments and handle errors and external system's rate limiting.
|
|
410
|
-
|
|
411
|
-
Functions return an ID and modified date of the record in the external system, specify rate-liming back-off, or log errors, if the attachment could not be created.
|
|
@@ -34,7 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.Uploader = void 0;
|
|
37
|
-
const
|
|
37
|
+
const axios_client_1 = require("../../http/axios-client");
|
|
38
38
|
const typescript_sdk_1 = require("@devrev/typescript-sdk");
|
|
39
39
|
const fs_1 = __importStar(require("fs"));
|
|
40
40
|
const helpers_1 = require("../common/helpers");
|
|
@@ -118,9 +118,9 @@ class Uploader {
|
|
|
118
118
|
) {
|
|
119
119
|
const formData = (0, helpers_1.createFormData)(preparedArtifact, fetchedObjects);
|
|
120
120
|
try {
|
|
121
|
-
const response = await
|
|
121
|
+
const response = await axios_client_1.axiosClient.post(preparedArtifact.url, formData, {
|
|
122
122
|
headers: {
|
|
123
|
-
'Content-Type': 'multipart/form',
|
|
123
|
+
'Content-Type': 'multipart/form-data',
|
|
124
124
|
},
|
|
125
125
|
});
|
|
126
126
|
return response;
|
package/dist/repo/repo.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export declare class Repo {
|
|
|
7
7
|
private normalize?;
|
|
8
8
|
private uploader;
|
|
9
9
|
private onUpload;
|
|
10
|
+
private options?;
|
|
10
11
|
constructor({ event, itemType, normalize, onUpload, options, }: RepoFactoryInterface);
|
|
11
12
|
getItems(): (NormalizedItem | NormalizedAttachment | Item)[];
|
|
12
13
|
upload(batch?: (NormalizedItem | NormalizedAttachment | Item)[]): Promise<void | ErrorRecord>;
|
package/dist/repo/repo.js
CHANGED
|
@@ -10,6 +10,7 @@ class Repo {
|
|
|
10
10
|
this.normalize = normalize;
|
|
11
11
|
this.onUpload = onUpload;
|
|
12
12
|
this.uploader = new uploader_1.Uploader({ event, options });
|
|
13
|
+
this.options = options;
|
|
13
14
|
}
|
|
14
15
|
getItems() {
|
|
15
16
|
return this.items;
|
|
@@ -35,6 +36,7 @@ class Repo {
|
|
|
35
36
|
}
|
|
36
37
|
}
|
|
37
38
|
async push(items) {
|
|
39
|
+
var _a;
|
|
38
40
|
let recordsToPush;
|
|
39
41
|
if (!items || items.length === 0) {
|
|
40
42
|
console.log(`No items to push for type ${this.itemType}. Skipping push.`);
|
|
@@ -52,9 +54,10 @@ class Repo {
|
|
|
52
54
|
// Add the new records to the items array
|
|
53
55
|
this.items.push(...recordsToPush);
|
|
54
56
|
// Upload in batches while the number of items exceeds the batch size
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
57
|
+
const batchSize = ((_a = this.options) === null || _a === void 0 ? void 0 : _a.batchSize) || constants_1.ARTIFACT_BATCH_SIZE;
|
|
58
|
+
while (this.items.length >= batchSize) {
|
|
59
|
+
// Slice out a batch of batchSize items to upload
|
|
60
|
+
const batch = this.items.splice(0, batchSize);
|
|
58
61
|
try {
|
|
59
62
|
// Upload the batch
|
|
60
63
|
await this.upload(batch);
|
package/dist/repo/repo.test.js
CHANGED
|
@@ -15,7 +15,6 @@ describe('Repo class push method', () => {
|
|
|
15
15
|
itemType: 'test_item_type',
|
|
16
16
|
normalize,
|
|
17
17
|
onUpload: jest.fn(),
|
|
18
|
-
options: {},
|
|
19
18
|
});
|
|
20
19
|
});
|
|
21
20
|
afterEach(() => {
|
|
@@ -83,4 +82,36 @@ describe('Repo class push method', () => {
|
|
|
83
82
|
expect(uploadSpy).toHaveBeenCalledTimes(2); // Check that upload was called twice
|
|
84
83
|
uploadSpy.mockRestore();
|
|
85
84
|
});
|
|
85
|
+
describe('should take batch size into account', () => {
|
|
86
|
+
beforeEach(() => {
|
|
87
|
+
repo = new repo_1.Repo({
|
|
88
|
+
event: (0, test_helpers_1.createEvent)({ eventType: types_1.EventType.ExtractionDataStart }),
|
|
89
|
+
itemType: 'test_item_type',
|
|
90
|
+
normalize,
|
|
91
|
+
onUpload: jest.fn(),
|
|
92
|
+
options: {
|
|
93
|
+
batchSize: 50,
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
it('should empty the items array after pushing 50 items with batch size of 50', async () => {
|
|
98
|
+
const items = (0, test_helpers_1.createItems)(50);
|
|
99
|
+
await repo.push(items);
|
|
100
|
+
expect(repo.getItems()).toEqual([]);
|
|
101
|
+
});
|
|
102
|
+
it('should leave 5 items in the items array after pushing 205 items with batch size of 50', async () => {
|
|
103
|
+
const items = (0, test_helpers_1.createItems)(205);
|
|
104
|
+
await repo.push(items);
|
|
105
|
+
expect(repo.getItems().length).toBe(5);
|
|
106
|
+
});
|
|
107
|
+
it('should upload 4 batches of 50 and leave 5 items in the items array after pushing 205 items with batch size of 50', async () => {
|
|
108
|
+
const uploadSpy = jest.spyOn(repo, 'upload');
|
|
109
|
+
const items = (0, test_helpers_1.createItems)(205);
|
|
110
|
+
await repo.push(items);
|
|
111
|
+
expect(normalize).toHaveBeenCalledTimes(205);
|
|
112
|
+
expect(repo.getItems().length).toBe(5);
|
|
113
|
+
expect(uploadSpy).toHaveBeenCalledTimes(4);
|
|
114
|
+
uploadSpy.mockRestore();
|
|
115
|
+
});
|
|
116
|
+
});
|
|
86
117
|
});
|
package/dist/types/workers.d.ts
CHANGED
|
@@ -22,10 +22,12 @@ export interface WorkerAdapterInterface<ConnectorState> {
|
|
|
22
22
|
* @constructor
|
|
23
23
|
* @param {boolean=} isLocalDevelopment - A flag to indicate if the adapter is being used in local development
|
|
24
24
|
* @param {number=} timeout - The timeout for the worker thread
|
|
25
|
+
* @param {number=} batchSize - Maximum number of extracted items in a batch
|
|
25
26
|
*/
|
|
26
27
|
export interface WorkerAdapterOptions {
|
|
27
28
|
isLocalDevelopment?: boolean;
|
|
28
29
|
timeout?: number;
|
|
30
|
+
batchSize?: number;
|
|
29
31
|
}
|
|
30
32
|
/**
|
|
31
33
|
* SpawnInterface is an interface for Spawn class.
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { betaSDK } from '@devrev/typescript-sdk';
|
|
2
1
|
import { NormalizedAttachment } from '../repo/repo.interfaces';
|
|
3
|
-
import { UploadResponse, UploaderFactoryInterface } from './uploader.interfaces';
|
|
2
|
+
import { ArtifactsPrepareResponse, UploadResponse, UploaderFactoryInterface } from './uploader.interfaces';
|
|
4
3
|
import { AxiosResponse } from 'axios';
|
|
5
4
|
export declare class Uploader {
|
|
6
5
|
private event;
|
|
7
|
-
private betaDevrevSdk;
|
|
8
6
|
private isLocalDevelopment?;
|
|
7
|
+
private devrevApiEndpoint;
|
|
8
|
+
private devrevApiToken;
|
|
9
9
|
constructor({ event, options }: UploaderFactoryInterface);
|
|
10
10
|
/**
|
|
11
11
|
* Uploads the fetched objects to the DevRev platform.
|
|
@@ -17,9 +17,9 @@ export declare class Uploader {
|
|
|
17
17
|
* or error information if there was an error
|
|
18
18
|
*/
|
|
19
19
|
upload(itemType: string, fetchedObjects: object[] | object): Promise<UploadResponse>;
|
|
20
|
-
prepareArtifact(filename: string, fileType: string): Promise<
|
|
20
|
+
prepareArtifact(filename: string, fileType: string): Promise<ArtifactsPrepareResponse | void>;
|
|
21
21
|
private uploadToArtifact;
|
|
22
|
-
streamToArtifact(preparedArtifact:
|
|
22
|
+
streamToArtifact(preparedArtifact: ArtifactsPrepareResponse, fileStreamResponse: any): Promise<AxiosResponse | void>;
|
|
23
23
|
getAttachmentsFromArtifactId({ artifact, }: {
|
|
24
24
|
artifact: string;
|
|
25
25
|
}): Promise<{
|
|
@@ -42,16 +42,13 @@ const axios_client_1 = require("../http/axios-client");
|
|
|
42
42
|
const zlib_1 = __importDefault(require("zlib"));
|
|
43
43
|
const js_jsonl_1 = require("js-jsonl");
|
|
44
44
|
const form_data_1 = __importDefault(require("form-data"));
|
|
45
|
-
const typescript_sdk_1 = require("@devrev/typescript-sdk");
|
|
46
45
|
const constants_1 = require("../common/constants");
|
|
47
46
|
const logger_1 = require("../logger/logger");
|
|
48
47
|
class Uploader {
|
|
49
48
|
constructor({ event, options }) {
|
|
50
49
|
this.event = event;
|
|
51
|
-
this.
|
|
52
|
-
|
|
53
|
-
token: event.context.secrets.service_account_token,
|
|
54
|
-
});
|
|
50
|
+
this.devrevApiEndpoint = event.execution_metadata.devrev_endpoint;
|
|
51
|
+
this.devrevApiToken = event.context.secrets.service_account_token;
|
|
55
52
|
this.isLocalDevelopment = options === null || options === void 0 ? void 0 : options.isLocalDevelopment;
|
|
56
53
|
}
|
|
57
54
|
/**
|
|
@@ -101,9 +98,13 @@ class Uploader {
|
|
|
101
98
|
}
|
|
102
99
|
async prepareArtifact(filename, fileType) {
|
|
103
100
|
try {
|
|
104
|
-
const response = await
|
|
101
|
+
const response = await axios_client_1.axiosClient.post(`${this.devrevApiEndpoint}/artifacts.prepare`, {
|
|
105
102
|
file_name: filename,
|
|
106
103
|
file_type: fileType,
|
|
104
|
+
}, {
|
|
105
|
+
headers: {
|
|
106
|
+
Authorization: `Bearer ${this.devrevApiToken}`,
|
|
107
|
+
},
|
|
107
108
|
});
|
|
108
109
|
return response.data;
|
|
109
110
|
}
|
|
@@ -116,9 +117,7 @@ class Uploader {
|
|
|
116
117
|
}
|
|
117
118
|
}
|
|
118
119
|
}
|
|
119
|
-
async uploadToArtifact(preparedArtifact, file
|
|
120
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
121
|
-
) {
|
|
120
|
+
async uploadToArtifact(preparedArtifact, file) {
|
|
122
121
|
const formData = new form_data_1.default();
|
|
123
122
|
for (const field of preparedArtifact.form_data) {
|
|
124
123
|
formData.append(field.key, field.value);
|
|
@@ -139,22 +138,23 @@ class Uploader {
|
|
|
139
138
|
}
|
|
140
139
|
}
|
|
141
140
|
}
|
|
142
|
-
async streamToArtifact(preparedArtifact,
|
|
143
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
144
|
-
fileStreamResponse) {
|
|
141
|
+
async streamToArtifact(preparedArtifact, fileStreamResponse) {
|
|
145
142
|
const formData = new form_data_1.default();
|
|
146
143
|
for (const field of preparedArtifact.form_data) {
|
|
147
144
|
formData.append(field.key, field.value);
|
|
148
145
|
}
|
|
149
146
|
formData.append('file', fileStreamResponse.data);
|
|
150
147
|
if (fileStreamResponse.headers['content-length'] > constants_1.MAX_DEVREV_ARTIFACT_SIZE) {
|
|
148
|
+
console.warn(`File size exceeds the maximum limit of ${constants_1.MAX_DEVREV_ARTIFACT_SIZE} bytes.`);
|
|
151
149
|
return;
|
|
152
150
|
}
|
|
153
151
|
try {
|
|
154
152
|
const response = await axios_client_1.axiosClient.post(preparedArtifact.url, formData, {
|
|
155
|
-
headers: Object.assign(Object.assign({}, formData.getHeaders()), (!fileStreamResponse.headers['content-length']
|
|
156
|
-
|
|
157
|
-
|
|
153
|
+
headers: Object.assign(Object.assign({}, formData.getHeaders()), (!fileStreamResponse.headers['content-length']
|
|
154
|
+
? {
|
|
155
|
+
'Content-Length': constants_1.MAX_DEVREV_ARTIFACT_SIZE,
|
|
156
|
+
}
|
|
157
|
+
: {})),
|
|
158
158
|
});
|
|
159
159
|
return response;
|
|
160
160
|
}
|
|
@@ -169,28 +169,28 @@ class Uploader {
|
|
|
169
169
|
}
|
|
170
170
|
}
|
|
171
171
|
async getAttachmentsFromArtifactId({ artifact, }) {
|
|
172
|
-
//
|
|
172
|
+
// Get the URL of the attachments metadata artifact
|
|
173
173
|
const artifactUrl = await this.getArtifactDownloadUrl(artifact);
|
|
174
174
|
if (!artifactUrl) {
|
|
175
175
|
return {
|
|
176
176
|
error: { message: 'Error while getting artifact download URL.' },
|
|
177
177
|
};
|
|
178
178
|
}
|
|
179
|
-
//
|
|
179
|
+
// Download artifact from the URL
|
|
180
180
|
const gzippedJsonlObject = await this.downloadArtifact(artifactUrl);
|
|
181
181
|
if (!gzippedJsonlObject) {
|
|
182
182
|
return {
|
|
183
183
|
error: { message: 'Error while downloading gzipped jsonl object.' },
|
|
184
184
|
};
|
|
185
185
|
}
|
|
186
|
-
//
|
|
186
|
+
// Decompress the gzipped jsonl object
|
|
187
187
|
const jsonlObject = this.decompressGzip(gzippedJsonlObject);
|
|
188
188
|
if (!jsonlObject) {
|
|
189
189
|
return {
|
|
190
190
|
error: { message: 'Error while decompressing gzipped jsonl object.' },
|
|
191
191
|
};
|
|
192
192
|
}
|
|
193
|
-
//
|
|
193
|
+
// Parse the jsonl object to get the attachment metadata
|
|
194
194
|
const jsonObject = this.parseJsonl(jsonlObject);
|
|
195
195
|
if (!jsonObject) {
|
|
196
196
|
return {
|
|
@@ -201,8 +201,12 @@ class Uploader {
|
|
|
201
201
|
}
|
|
202
202
|
async getArtifactDownloadUrl(artifactId) {
|
|
203
203
|
try {
|
|
204
|
-
const response = await
|
|
204
|
+
const response = await axios_client_1.axiosClient.post(`${this.devrevApiEndpoint}/artifacts.locate`, {
|
|
205
205
|
id: artifactId,
|
|
206
|
+
}, {
|
|
207
|
+
headers: {
|
|
208
|
+
Authorization: `Bearer ${this.devrevApiToken}`,
|
|
209
|
+
},
|
|
206
210
|
});
|
|
207
211
|
return response.data.url;
|
|
208
212
|
}
|
|
@@ -7,6 +7,9 @@ const getAttachmentStream = async ({ item, }) => {
|
|
|
7
7
|
try {
|
|
8
8
|
const fileStreamResponse = await axios_client_1.axiosClient.get(url, {
|
|
9
9
|
responseType: 'stream',
|
|
10
|
+
headers: {
|
|
11
|
+
'Accept-Encoding': 'identity',
|
|
12
|
+
},
|
|
10
13
|
});
|
|
11
14
|
return { httpStream: fileStreamResponse };
|
|
12
15
|
}
|
|
@@ -51,7 +54,6 @@ const getAttachmentStream = async ({ item, }) => {
|
|
|
51
54
|
}
|
|
52
55
|
},
|
|
53
56
|
onTimeout: async ({ adapter }) => {
|
|
54
|
-
await adapter.postState();
|
|
55
57
|
await adapter.emit(index_1.ExtractorEventType.ExtractionAttachmentsProgress, {
|
|
56
58
|
progress: 50,
|
|
57
59
|
});
|
|
@@ -11,7 +11,6 @@ const types_1 = require("../../types");
|
|
|
11
11
|
});
|
|
12
12
|
},
|
|
13
13
|
onTimeout: async ({ adapter }) => {
|
|
14
|
-
await adapter.postState();
|
|
15
14
|
await adapter.emit(types_1.LoaderEventType.AttachmentLoadingError, {
|
|
16
15
|
reports: adapter.reports,
|
|
17
16
|
processed_files: adapter.processedFiles,
|
|
@@ -10,7 +10,6 @@ const loading_1 = require("../../types/loading");
|
|
|
10
10
|
});
|
|
11
11
|
},
|
|
12
12
|
onTimeout: async ({ adapter }) => {
|
|
13
|
-
await adapter.postState();
|
|
14
13
|
await adapter.emit(loading_1.LoaderEventType.DataLoadingError, {
|
|
15
14
|
reports: adapter.reports,
|
|
16
15
|
processed_files: adapter.processedFiles,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.WorkerAdapter = void 0;
|
|
4
4
|
exports.createWorkerAdapter = createWorkerAdapter;
|
|
5
|
-
const
|
|
5
|
+
const axios_client_1 = require("../http/axios-client");
|
|
6
6
|
const extraction_1 = require("../types/extraction");
|
|
7
7
|
const loading_1 = require("../types/loading");
|
|
8
8
|
const constants_1 = require("../common/constants");
|
|
@@ -153,7 +153,7 @@ class WorkerAdapter {
|
|
|
153
153
|
node_worker_threads_1.parentPort === null || node_worker_threads_1.parentPort === void 0 ? void 0 : node_worker_threads_1.parentPort.postMessage(message);
|
|
154
154
|
}
|
|
155
155
|
catch (error) {
|
|
156
|
-
if (
|
|
156
|
+
if (axios_client_1.axios.isAxiosError(error)) {
|
|
157
157
|
console.error(`Error while emitting event with event type: ${newEventType}`, (0, logger_1.serializeAxiosError)(error));
|
|
158
158
|
}
|
|
159
159
|
else {
|
|
@@ -374,7 +374,7 @@ class WorkerAdapter {
|
|
|
374
374
|
});
|
|
375
375
|
}
|
|
376
376
|
catch (error) {
|
|
377
|
-
if (
|
|
377
|
+
if (axios_client_1.axios.isAxiosError(error)) {
|
|
378
378
|
console.warn('Failed to update sync mapper record', (0, logger_1.serializeAxiosError)(error));
|
|
379
379
|
return {
|
|
380
380
|
error: {
|
|
@@ -419,7 +419,7 @@ class WorkerAdapter {
|
|
|
419
419
|
// TODO: Update mapper (optional)
|
|
420
420
|
}
|
|
421
421
|
catch (error) {
|
|
422
|
-
if (
|
|
422
|
+
if (axios_client_1.axios.isAxiosError(error)) {
|
|
423
423
|
if (((_a = error.response) === null || _a === void 0 ? void 0 : _a.status) === 404) {
|
|
424
424
|
// Create item in external system if mapper record not found
|
|
425
425
|
const { id, delay, error } = await itemTypeToLoad.create({
|
|
@@ -444,7 +444,7 @@ class WorkerAdapter {
|
|
|
444
444
|
};
|
|
445
445
|
}
|
|
446
446
|
catch (error) {
|
|
447
|
-
if (
|
|
447
|
+
if (axios_client_1.axios.isAxiosError(error)) {
|
|
448
448
|
console.warn('Failed to create sync mapper record', (0, logger_1.serializeAxiosError)(error));
|
|
449
449
|
return {
|
|
450
450
|
error: {
|
|
@@ -511,14 +511,12 @@ class WorkerAdapter {
|
|
|
511
511
|
const fileType = ((_a = httpStream.headers) === null || _a === void 0 ? void 0 : _a['content-type']) || 'application/octet-stream';
|
|
512
512
|
const preparedArtifact = await this.uploader.prepareArtifact(attachment.file_name, fileType);
|
|
513
513
|
if (!preparedArtifact) {
|
|
514
|
-
console.warn(
|
|
515
|
-
attachment.id +
|
|
516
|
-
'. Skipping attachment');
|
|
514
|
+
console.warn(`Error while preparing artifact for attachment ID ${attachment.id}. Skipping attachment.`);
|
|
517
515
|
return;
|
|
518
516
|
}
|
|
519
517
|
const uploadedArtifact = await this.uploader.streamToArtifact(preparedArtifact, httpStream);
|
|
520
518
|
if (!uploadedArtifact) {
|
|
521
|
-
console.warn(
|
|
519
|
+
console.warn(`Error while streaming to artifact for attachment ID ${attachment.id}. Skipping attachment.`);
|
|
522
520
|
return;
|
|
523
521
|
}
|
|
524
522
|
const ssorAttachment = {
|
|
@@ -579,17 +577,24 @@ class WorkerAdapter {
|
|
|
579
577
|
* or error information if there was an error
|
|
580
578
|
*/
|
|
581
579
|
async streamAttachments({ stream, processors, }) {
|
|
582
|
-
var _a, _b, _c;
|
|
580
|
+
var _a, _b, _c, _d;
|
|
583
581
|
const repos = [
|
|
584
582
|
{
|
|
585
583
|
itemType: 'ssor_attachment',
|
|
586
584
|
},
|
|
587
585
|
];
|
|
588
586
|
this.initializeRepos(repos);
|
|
589
|
-
const
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
console.log(`
|
|
587
|
+
const attachmentsMetadataArtifactIds = (_b = (_a = this.state.toDevRev) === null || _a === void 0 ? void 0 : _a.attachmentsMetadata) === null || _b === void 0 ? void 0 : _b.artifactIds;
|
|
588
|
+
if (!attachmentsMetadataArtifactIds ||
|
|
589
|
+
attachmentsMetadataArtifactIds.length === 0) {
|
|
590
|
+
console.log(`No attachments metadata artifact IDs found in state.`);
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
else {
|
|
594
|
+
console.log(`Found ${attachmentsMetadataArtifactIds.length} attachments metadata artifact IDs in state.`);
|
|
595
|
+
}
|
|
596
|
+
for (const attachmentsMetadataArtifactId of attachmentsMetadataArtifactIds) {
|
|
597
|
+
console.log(`Started processing attachments for attachments metadata artifact ID: ${attachmentsMetadataArtifactId}.`);
|
|
593
598
|
const { attachments, error } = await this.uploader.getAttachmentsFromArtifactId({
|
|
594
599
|
artifact: attachmentsMetadataArtifactId,
|
|
595
600
|
});
|
|
@@ -601,6 +606,7 @@ class WorkerAdapter {
|
|
|
601
606
|
console.warn(`No attachments found for artifact ID: ${attachmentsMetadataArtifactId}.`);
|
|
602
607
|
continue;
|
|
603
608
|
}
|
|
609
|
+
console.log(`Found ${attachments.length} attachments for artifact ID: ${attachmentsMetadataArtifactId}.`);
|
|
604
610
|
if (processors) {
|
|
605
611
|
console.log(`Using custom processors for attachments.`);
|
|
606
612
|
const { reducer, iterator } = processors;
|
|
@@ -616,7 +622,7 @@ class WorkerAdapter {
|
|
|
616
622
|
}
|
|
617
623
|
else {
|
|
618
624
|
console.log(`Using default processors for attachments.`);
|
|
619
|
-
const attachmentsToProcess = attachments.slice((
|
|
625
|
+
const attachmentsToProcess = attachments.slice((_d = (_c = this.state.toDevRev) === null || _c === void 0 ? void 0 : _c.attachmentsMetadata) === null || _d === void 0 ? void 0 : _d.lastProcessed, attachments.length);
|
|
620
626
|
for (const attachment of attachmentsToProcess) {
|
|
621
627
|
const response = await this.processAttachment(attachment, stream);
|
|
622
628
|
if (response === null || response === void 0 ? void 0 : response.delay) {
|
package/package.json
CHANGED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.axiosDevRevClient = exports.axios = void 0;
|
|
7
|
-
const axios_1 = __importDefault(require("axios"));
|
|
8
|
-
exports.axios = axios_1.default;
|
|
9
|
-
const axios_retry_1 = __importDefault(require("axios-retry"));
|
|
10
|
-
const axiosDevRevClient = axios_1.default.create();
|
|
11
|
-
exports.axiosDevRevClient = axiosDevRevClient;
|
|
12
|
-
(0, axios_retry_1.default)(axiosDevRevClient, {
|
|
13
|
-
retries: 5,
|
|
14
|
-
retryDelay: (retryCount, error) => {
|
|
15
|
-
var _a, _b;
|
|
16
|
-
console.warn('Retry attempt: ' + retryCount + ' to url: ' + ((_a = error.config) === null || _a === void 0 ? void 0 : _a.url) + '.');
|
|
17
|
-
if (error.response) {
|
|
18
|
-
const retry_after = (_b = error.response) === null || _b === void 0 ? void 0 : _b.headers['retry-after'];
|
|
19
|
-
if (retry_after) {
|
|
20
|
-
return retry_after;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
// Exponential backoff algorithm: 1 * 2 ^ retryCount * 1000ms
|
|
24
|
-
return axios_retry_1.default.exponentialDelay(retryCount, error, 1000);
|
|
25
|
-
},
|
|
26
|
-
retryCondition: (error) => {
|
|
27
|
-
var _a;
|
|
28
|
-
return (axios_retry_1.default.isNetworkOrIdempotentRequestError(error) ||
|
|
29
|
-
((_a = error.response) === null || _a === void 0 ? void 0 : _a.status) === 429);
|
|
30
|
-
},
|
|
31
|
-
onMaxRetryTimesExceeded(error, retryCount) {
|
|
32
|
-
var _a;
|
|
33
|
-
console.log(`Max retries attempted: ${retryCount}`);
|
|
34
|
-
(_a = error.config) === null || _a === void 0 ? true : delete _a.headers.Authorization;
|
|
35
|
-
delete error.request._header;
|
|
36
|
-
},
|
|
37
|
-
});
|