@devrev/airsync-sdk 2.0.0-beta.2 → 2.0.0-beta.3

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.
Files changed (89) hide show
  1. package/dist/airsync-sdk.d.ts +414 -157
  2. package/dist/attachments-streaming/attachments-streaming-pool.d.ts +2 -1
  3. package/dist/attachments-streaming/attachments-streaming-pool.d.ts.map +1 -1
  4. package/dist/attachments-streaming/attachments-streaming-pool.interfaces.d.ts +8 -2
  5. package/dist/attachments-streaming/attachments-streaming-pool.interfaces.d.ts.map +1 -1
  6. package/dist/attachments-streaming/attachments-streaming-pool.js +14 -14
  7. package/dist/attachments-streaming/attachments-streaming-pool.test.js +47 -23
  8. package/dist/common/time-value-resolver.d.ts +2 -2
  9. package/dist/common/time-value-resolver.d.ts.map +1 -1
  10. package/dist/index.d.ts +3 -2
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +7 -4
  13. package/dist/multithreading/adapters/base-adapter.d.ts +56 -0
  14. package/dist/multithreading/adapters/base-adapter.d.ts.map +1 -0
  15. package/dist/multithreading/adapters/base-adapter.emit.test.d.ts +2 -0
  16. package/dist/multithreading/adapters/base-adapter.emit.test.d.ts.map +1 -0
  17. package/dist/multithreading/{worker-adapter/worker-adapter.emit.test.js → adapters/base-adapter.emit.test.js} +66 -53
  18. package/dist/multithreading/adapters/base-adapter.js +105 -0
  19. package/dist/multithreading/adapters/extraction-adapter.artifacts.test.d.ts +2 -0
  20. package/dist/multithreading/adapters/extraction-adapter.artifacts.test.d.ts.map +1 -0
  21. package/dist/multithreading/{worker-adapter/worker-adapter.artifacts.test.js → adapters/extraction-adapter.artifacts.test.js} +6 -6
  22. package/dist/multithreading/adapters/extraction-adapter.d.ts +58 -0
  23. package/dist/multithreading/adapters/extraction-adapter.d.ts.map +1 -0
  24. package/dist/multithreading/adapters/extraction-adapter.js +325 -0
  25. package/dist/multithreading/adapters/extraction-adapter.test.d.ts +2 -0
  26. package/dist/multithreading/adapters/extraction-adapter.test.d.ts.map +1 -0
  27. package/dist/multithreading/{worker-adapter/worker-adapter.extraction.test.js → adapters/extraction-adapter.test.js} +41 -51
  28. package/dist/multithreading/adapters/loading-adapter.d.ts +44 -0
  29. package/dist/multithreading/adapters/loading-adapter.d.ts.map +1 -0
  30. package/dist/multithreading/{worker-adapter/worker-adapter.helpers.d.ts → adapters/loading-adapter.helpers.d.ts} +1 -1
  31. package/dist/multithreading/adapters/loading-adapter.helpers.d.ts.map +1 -0
  32. package/dist/multithreading/adapters/loading-adapter.helpers.test.d.ts +2 -0
  33. package/dist/multithreading/adapters/loading-adapter.helpers.test.d.ts.map +1 -0
  34. package/dist/multithreading/{worker-adapter/worker-adapter.helpers.test.js → adapters/loading-adapter.helpers.test.js} +26 -26
  35. package/dist/multithreading/adapters/loading-adapter.js +461 -0
  36. package/dist/multithreading/adapters/loading-adapter.test.d.ts +2 -0
  37. package/dist/multithreading/adapters/loading-adapter.test.d.ts.map +1 -0
  38. package/dist/multithreading/{worker-adapter/worker-adapter.loading.test.js → adapters/loading-adapter.test.js} +19 -26
  39. package/dist/multithreading/adapters/serialization.test.d.ts +2 -0
  40. package/dist/multithreading/adapters/serialization.test.d.ts.map +1 -0
  41. package/dist/multithreading/process-task.d.ts +16 -1
  42. package/dist/multithreading/process-task.d.ts.map +1 -1
  43. package/dist/multithreading/process-task.js +97 -49
  44. package/dist/multithreading/process-task.test.js +15 -15
  45. package/dist/state/base-state.d.ts +75 -0
  46. package/dist/state/base-state.d.ts.map +1 -0
  47. package/dist/state/base-state.js +261 -0
  48. package/dist/state/extraction-state.d.ts +29 -0
  49. package/dist/state/extraction-state.d.ts.map +1 -0
  50. package/dist/state/extraction-state.js +122 -0
  51. package/dist/state/loading-state.d.ts +19 -0
  52. package/dist/state/loading-state.d.ts.map +1 -0
  53. package/dist/state/loading-state.js +40 -0
  54. package/dist/state/state.extract-window.test.js +7 -7
  55. package/dist/state/state.interfaces.d.ts +32 -25
  56. package/dist/state/state.interfaces.d.ts.map +1 -1
  57. package/dist/state/state.interfaces.js +15 -4
  58. package/dist/state/state.pending-boundaries.test.js +15 -15
  59. package/dist/state/state.post-state.test.js +9 -8
  60. package/dist/state/state.test.js +22 -19
  61. package/dist/state/state.time-value-resolution.test.js +8 -8
  62. package/dist/types/extraction.d.ts +3 -3
  63. package/dist/types/extraction.d.ts.map +1 -1
  64. package/dist/types/index.d.ts +0 -1
  65. package/dist/types/index.d.ts.map +1 -1
  66. package/dist/types/workers.d.ts +16 -15
  67. package/dist/types/workers.d.ts.map +1 -1
  68. package/package.json +1 -1
  69. package/dist/multithreading/worker-adapter/worker-adapter.artifacts.test.d.ts +0 -2
  70. package/dist/multithreading/worker-adapter/worker-adapter.artifacts.test.d.ts.map +0 -1
  71. package/dist/multithreading/worker-adapter/worker-adapter.d.ts +0 -97
  72. package/dist/multithreading/worker-adapter/worker-adapter.d.ts.map +0 -1
  73. package/dist/multithreading/worker-adapter/worker-adapter.emit.test.d.ts +0 -2
  74. package/dist/multithreading/worker-adapter/worker-adapter.emit.test.d.ts.map +0 -1
  75. package/dist/multithreading/worker-adapter/worker-adapter.extraction.test.d.ts +0 -2
  76. package/dist/multithreading/worker-adapter/worker-adapter.extraction.test.d.ts.map +0 -1
  77. package/dist/multithreading/worker-adapter/worker-adapter.helpers.d.ts.map +0 -1
  78. package/dist/multithreading/worker-adapter/worker-adapter.helpers.test.d.ts +0 -2
  79. package/dist/multithreading/worker-adapter/worker-adapter.helpers.test.d.ts.map +0 -1
  80. package/dist/multithreading/worker-adapter/worker-adapter.js +0 -846
  81. package/dist/multithreading/worker-adapter/worker-adapter.loading.test.d.ts +0 -2
  82. package/dist/multithreading/worker-adapter/worker-adapter.loading.test.d.ts.map +0 -1
  83. package/dist/multithreading/worker-adapter/worker-adapter.serialization.test.d.ts +0 -2
  84. package/dist/multithreading/worker-adapter/worker-adapter.serialization.test.d.ts.map +0 -1
  85. package/dist/state/state.d.ts +0 -36
  86. package/dist/state/state.d.ts.map +0 -1
  87. package/dist/state/state.js +0 -265
  88. /package/dist/multithreading/{worker-adapter/worker-adapter.helpers.js → adapters/loading-adapter.helpers.js} +0 -0
  89. /package/dist/multithreading/{worker-adapter/worker-adapter.serialization.test.js → adapters/serialization.test.js} +0 -0
@@ -0,0 +1,325 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ExtractionAdapter = void 0;
4
+ const attachments_streaming_pool_1 = require("../../attachments-streaming/attachments-streaming-pool");
5
+ const constants_1 = require("../../common/constants");
6
+ const logger_1 = require("../../logger/logger");
7
+ const logger_context_1 = require("../../logger/logger.context");
8
+ const repo_1 = require("../../repo/repo");
9
+ const extraction_1 = require("../../types/extraction");
10
+ const base_adapter_1 = require("./base-adapter");
11
+ /**
12
+ * ExtractionAdapter is the adapter passed to extraction tasks. It exposes the
13
+ * extraction surface (repos, artifacts, attachment streaming) and updates the
14
+ * extraction boundaries before emitting.
15
+ *
16
+ * @public
17
+ */
18
+ class ExtractionAdapter extends base_adapter_1.BaseAdapter {
19
+ constructor(params) {
20
+ super(params);
21
+ this.repos = [];
22
+ this.currentEventDataLength = 0;
23
+ this._artifacts = [];
24
+ }
25
+ get extractionScope() {
26
+ return this.adapterState.extractionScope;
27
+ }
28
+ /**
29
+ * Returns whether the given item type should be extracted.
30
+ * Defaults to true if the scope is empty or the item type is not listed.
31
+ */
32
+ shouldExtract(itemType) {
33
+ const scope = this.extractionScope;
34
+ if (Object.keys(scope).length === 0)
35
+ return true;
36
+ if (!(itemType in scope))
37
+ return true;
38
+ return scope[itemType].extract;
39
+ }
40
+ initializeRepos(repos) {
41
+ this.repos = repos.map((repo) => {
42
+ const shouldNormalize = repo.itemType !== constants_1.AirSyncDefaultItemTypes.EXTERNAL_DOMAIN_METADATA &&
43
+ repo.itemType !== constants_1.SSOR_ATTACHMENT;
44
+ return new repo_1.Repo(Object.assign(Object.assign({ event: this.event, itemType: repo.itemType }, (shouldNormalize && { normalize: repo.normalize })), { onUpload: (artifact) => {
45
+ var _a;
46
+ // We need to store artifacts ids in state for later use when streaming attachments
47
+ if (repo.itemType === constants_1.AirSyncDefaultItemTypes.ATTACHMENTS) {
48
+ (_a = this.adapterState.sdkState.toDevRev) === null || _a === void 0 ? void 0 : _a.attachmentsMetadata.artifactIds.push(artifact.id);
49
+ }
50
+ // Calculate size of the entire artifact object that goes in the SQS message
51
+ this.currentEventDataLength += Buffer.byteLength(JSON.stringify(artifact), 'utf8');
52
+ if (this.currentEventDataLength > constants_1.EVENT_SIZE_THRESHOLD_BYTES &&
53
+ !this.isTimeout) {
54
+ this.isTimeout = true;
55
+ }
56
+ }, options: Object.assign(Object.assign({}, this.options), repo.overridenOptions) }));
57
+ });
58
+ }
59
+ getRepo(itemType) {
60
+ return (0, logger_context_1.runWithSdkLogContext)(() => {
61
+ const repo = this.repos.find((repo) => repo.itemType === itemType);
62
+ if (!repo) {
63
+ console.error(`Repo for item type ${itemType} not found.`);
64
+ return;
65
+ }
66
+ return repo;
67
+ });
68
+ }
69
+ get artifacts() {
70
+ return this._artifacts;
71
+ }
72
+ set artifacts(artifacts) {
73
+ this._artifacts = this._artifacts
74
+ .concat(artifacts)
75
+ .filter((value, index, self) => self.indexOf(value) === index);
76
+ }
77
+ async beforeEmit(newEventType) {
78
+ // Upload all repos before emitting the event
79
+ console.log(`Uploading all repos before emitting event with event type: ${newEventType}.`);
80
+ await this.uploadAllRepos();
81
+ if (newEventType === extraction_1.ExtractorEventType.AttachmentExtractionDone) {
82
+ const sdkState = this.adapterState.sdkState;
83
+ // Clear pending extraction boundaries now that the cycle is complete
84
+ sdkState.pendingWorkersOldest = '';
85
+ sdkState.pendingWorkersNewest = '';
86
+ // Update workersOldest and workersNewest boundaries from resolved extraction timestamps.
87
+ // Expand boundaries: workersOldest gets the earliest timestamp, workersNewest gets the latest.
88
+ const extractionStart = this.event.payload.event_context.extract_from;
89
+ const extractionEnd = this.event.payload.event_context.extract_to;
90
+ if (extractionStart &&
91
+ (!sdkState.workersOldest || extractionStart < sdkState.workersOldest)) {
92
+ console.log(`Updating workersOldest from '${sdkState.workersOldest}' to '${extractionStart}'.`);
93
+ sdkState.workersOldest = extractionStart;
94
+ }
95
+ if (extractionEnd &&
96
+ (!sdkState.workersNewest || extractionEnd > sdkState.workersNewest)) {
97
+ console.log(`Updating workersNewest from '${sdkState.workersNewest}' to '${extractionEnd}'.`);
98
+ sdkState.workersNewest = extractionEnd;
99
+ }
100
+ }
101
+ }
102
+ buildEmitPayload(newEventType) {
103
+ const isExtractionEvent = Object.values(extraction_1.ExtractorEventType).includes(newEventType);
104
+ return isExtractionEvent ? { artifacts: this.artifacts } : {};
105
+ }
106
+ afterEmit() {
107
+ this.artifacts = [];
108
+ }
109
+ async uploadAllRepos() {
110
+ for (const repo of this.repos) {
111
+ const error = await repo.upload();
112
+ this.artifacts.push(...repo.uploadedArtifacts);
113
+ if (error) {
114
+ throw error;
115
+ }
116
+ }
117
+ }
118
+ async processAttachment(attachment, stream) {
119
+ return (0, logger_context_1.runWithSdkLogContext)(async () => {
120
+ var _a, _b, _c;
121
+ const { httpStream, delay, error } = await (0, logger_context_1.runWithUserLogContext)(async () => stream({
122
+ item: attachment,
123
+ event: this.event,
124
+ }));
125
+ if (error) {
126
+ return { error };
127
+ }
128
+ else if (delay) {
129
+ return { delay };
130
+ }
131
+ if (httpStream) {
132
+ const fileType = attachment.content_type ||
133
+ ((_a = httpStream.headers['content-type']) === null || _a === void 0 ? void 0 : _a.toString()) ||
134
+ 'application/octet-stream';
135
+ const contentLength = (_b = httpStream.headers['content-length']) === null || _b === void 0 ? void 0 : _b.toString();
136
+ const fileSize = contentLength ? parseInt(contentLength) : undefined;
137
+ // Get upload URL
138
+ const { error: artifactUrlError, response: artifactUrlResponse } = await this.uploader.getArtifactUploadUrl(attachment.file_name, fileType, fileSize);
139
+ if (artifactUrlError) {
140
+ this.destroyHttpStream(httpStream);
141
+ return {
142
+ error: {
143
+ message: `Error while preparing artifact for attachment ID ${attachment.id}. Skipping attachment. ${(0, logger_1.serializeError)(artifactUrlError)}`,
144
+ fileSize: fileSize,
145
+ },
146
+ };
147
+ }
148
+ // Stream attachment
149
+ const { error: uploadedArtifactError } = await this.uploader.streamArtifact(artifactUrlResponse, httpStream);
150
+ if (uploadedArtifactError) {
151
+ this.destroyHttpStream(httpStream);
152
+ return {
153
+ error: {
154
+ message: `Error while streaming to artifact for attachment ID ${attachment.id}. Skipping attachment. ` +
155
+ (0, logger_1.serializeError)(uploadedArtifactError),
156
+ fileSize: fileSize,
157
+ },
158
+ };
159
+ }
160
+ // Confirm attachment upload
161
+ const { error: confirmArtifactUploadError } = await this.uploader.confirmArtifactUpload(artifactUrlResponse.artifact_id);
162
+ if (confirmArtifactUploadError) {
163
+ return {
164
+ error: {
165
+ message: `Error while confirming upload for attachment ID ${attachment.id}. ` +
166
+ (0, logger_1.serializeError)(confirmArtifactUploadError),
167
+ fileSize: fileSize,
168
+ },
169
+ };
170
+ }
171
+ const ssorAttachment = {
172
+ id: {
173
+ devrev: artifactUrlResponse.artifact_id,
174
+ external: attachment.id,
175
+ },
176
+ parent_id: {
177
+ external: attachment.parent_id,
178
+ },
179
+ };
180
+ if (attachment.author_id) {
181
+ ssorAttachment.actor_id = {
182
+ external: attachment.author_id,
183
+ };
184
+ }
185
+ // This will set inline flag in ssor_attachment only if it is explicity
186
+ // set in the attachment object.
187
+ if (attachment.inline === true) {
188
+ ssorAttachment.inline = true;
189
+ }
190
+ else if (attachment.inline === false) {
191
+ ssorAttachment.inline = false;
192
+ }
193
+ await ((_c = this.getRepo('ssor_attachment')) === null || _c === void 0 ? void 0 : _c.push([ssorAttachment]));
194
+ return;
195
+ }
196
+ return {
197
+ error: {
198
+ message: `Error while opening attachment stream. Skipping attachment.`,
199
+ },
200
+ };
201
+ });
202
+ }
203
+ /**
204
+ * Destroys a stream to prevent memory leaks.
205
+ * @param httpStream - The axios response stream to destroy
206
+ */
207
+ destroyHttpStream(httpStream) {
208
+ try {
209
+ if (httpStream && httpStream.data) {
210
+ if (typeof httpStream.data.destroy === 'function') {
211
+ httpStream.data.destroy();
212
+ }
213
+ else if (typeof httpStream.data.close === 'function') {
214
+ httpStream.data.close();
215
+ }
216
+ }
217
+ }
218
+ catch (error) {
219
+ console.warn('Error while destroying HTTP stream:', error);
220
+ }
221
+ }
222
+ /**
223
+ * Streams the attachments to the DevRev platform.
224
+ * The attachments are streamed to the platform and the artifact information is returned.
225
+ * @param params - The parameters to stream the attachments
226
+ * @returns The response object containing the ssorAttachment artifact information
227
+ * or error information if there was an error
228
+ */
229
+ async streamAttachments({ stream, processors, batchSize = 1, // By default, we want to stream one attachment at a time
230
+ }) {
231
+ return (0, logger_context_1.runWithSdkLogContext)(async () => {
232
+ var _a, _b;
233
+ if (batchSize <= 0) {
234
+ console.warn(`The specified batch size (${batchSize}) is invalid. Using 1 instead.`);
235
+ batchSize = 1;
236
+ }
237
+ if (batchSize > 50) {
238
+ console.warn(`The specified batch size (${batchSize}) is too large. Using 50 instead.`);
239
+ batchSize = 50;
240
+ }
241
+ const repos = [
242
+ {
243
+ itemType: 'ssor_attachment',
244
+ },
245
+ ];
246
+ this.initializeRepos(repos);
247
+ const attachmentsMetadata = (_a = this.adapterState.sdkState.toDevRev) === null || _a === void 0 ? void 0 : _a.attachmentsMetadata;
248
+ // If there are no attachments metadata artifact IDs in state, finish here
249
+ if (!((_b = attachmentsMetadata === null || attachmentsMetadata === void 0 ? void 0 : attachmentsMetadata.artifactIds) === null || _b === void 0 ? void 0 : _b.length)) {
250
+ console.log(`No attachments metadata artifact IDs found in state.`);
251
+ return;
252
+ }
253
+ else {
254
+ console.log(`Found ${attachmentsMetadata.artifactIds.length} attachments metadata artifact IDs in state.`);
255
+ }
256
+ // Loop through the attachments metadata artifact IDs
257
+ while (attachmentsMetadata.artifactIds.length > 0) {
258
+ const attachmentsMetadataArtifactId = attachmentsMetadata.artifactIds[0];
259
+ console.log(`Started processing attachments for attachments metadata artifact ID: ${attachmentsMetadataArtifactId}.`);
260
+ const { attachments, error } = await this.uploader.getAttachmentsFromArtifactId({
261
+ artifact: attachmentsMetadataArtifactId,
262
+ });
263
+ if (error) {
264
+ console.error(`Failed to get attachments for artifact ID: ${attachmentsMetadataArtifactId}.`);
265
+ return { error };
266
+ }
267
+ if (!attachments || attachments.length === 0) {
268
+ console.warn(`No attachments found for artifact ID: ${attachmentsMetadataArtifactId}.`);
269
+ // Remove empty artifact and reset lastProcessed
270
+ attachmentsMetadata.artifactIds.shift();
271
+ attachmentsMetadata.lastProcessed = 0;
272
+ continue;
273
+ }
274
+ console.log(`Found ${attachments.length} attachments for artifact ID: ${attachmentsMetadataArtifactId}.`);
275
+ let response;
276
+ if (processors) {
277
+ console.log(`Using custom processors for attachments.`);
278
+ const reducer = processors.reducer;
279
+ const iterator = processors.iterator;
280
+ const reducedAttachments = (0, logger_context_1.runWithUserLogContext)(() => reducer({
281
+ attachments,
282
+ adapter: this,
283
+ batchSize,
284
+ }));
285
+ response = await (0, logger_context_1.runWithUserLogContext)(async () => {
286
+ return await iterator({
287
+ reducedAttachments,
288
+ adapter: this,
289
+ stream,
290
+ });
291
+ });
292
+ }
293
+ else {
294
+ console.log(`Using attachments streaming pool for attachments streaming.`);
295
+ const attachmentsPool = new attachments_streaming_pool_1.AttachmentsStreamingPool({
296
+ adapter: this,
297
+ attachments,
298
+ attachmentsMetadata,
299
+ batchSize,
300
+ stream,
301
+ });
302
+ response = await attachmentsPool.streamAll();
303
+ }
304
+ if ((response === null || response === void 0 ? void 0 : response.delay) || (response === null || response === void 0 ? void 0 : response.error)) {
305
+ return response;
306
+ }
307
+ // On timeout, emit progress and exit to allow continuation.
308
+ if (this.isTimeout) {
309
+ console.log(`Timeout detected after processing attachments for artifact ID: ${attachmentsMetadataArtifactId}. Emitting progress to allow continuation.`);
310
+ await this.emit(extraction_1.ExtractorEventType.AttachmentExtractionProgress);
311
+ process.exit(0);
312
+ return;
313
+ }
314
+ console.log(`Finished processing all attachments for artifact ID: ${attachmentsMetadataArtifactId}.`);
315
+ attachmentsMetadata.artifactIds.shift();
316
+ attachmentsMetadata.lastProcessed = 0;
317
+ if (attachmentsMetadata.lastProcessedAttachmentsIdsList) {
318
+ attachmentsMetadata.lastProcessedAttachmentsIdsList.length = 0;
319
+ }
320
+ }
321
+ return;
322
+ });
323
+ }
324
+ }
325
+ exports.ExtractionAdapter = ExtractionAdapter;
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=extraction-adapter.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extraction-adapter.test.d.ts","sourceRoot":"","sources":["../../../src/multithreading/adapters/extraction-adapter.test.ts"],"names":[],"mappings":""}
@@ -1,11 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const attachments_streaming_pool_1 = require("../../attachments-streaming/attachments-streaming-pool");
4
- const state_1 = require("../../state/state");
4
+ const extraction_state_1 = require("../../state/extraction-state");
5
5
  const jest_setup_1 = require("../../tests/jest.setup");
6
6
  const test_utils_1 = require("../../common/test-utils");
7
7
  const types_1 = require("../../types");
8
- const worker_adapter_1 = require("./worker-adapter");
8
+ const extraction_adapter_1 = require("./extraction-adapter");
9
9
  /* eslint-disable @typescript-eslint/no-require-imports */
10
10
  jest.mock('../../common/control-protocol', () => ({
11
11
  emit: jest.fn().mockResolvedValue({}),
@@ -27,29 +27,30 @@ function makeAdapter(eventType = types_1.EventType.StartExtractingData) {
27
27
  });
28
28
  const initialState = {
29
29
  attachments: { completed: false },
30
- snapInVersionId: '',
31
- toDevRev: {
32
- attachmentsMetadata: {
33
- artifactIds: [],
34
- lastProcessed: 0,
35
- lastProcessedAttachmentsIdsList: [],
36
- },
30
+ };
31
+ const adapterState = new extraction_state_1.ExtractionState({ event, initialState });
32
+ adapterState.sdkState.snapInVersionId = '';
33
+ adapterState.sdkState.toDevRev = {
34
+ attachmentsMetadata: {
35
+ artifactIds: [],
36
+ lastProcessed: 0,
37
+ lastProcessedAttachmentsIdsList: [],
37
38
  },
38
39
  };
39
- const adapterState = new state_1.State({ event, initialState });
40
- const adapter = new worker_adapter_1.WorkerAdapter({ event, adapterState });
40
+ const adapter = new extraction_adapter_1.ExtractionAdapter({ event, adapterState });
41
41
  return { adapter, event, adapterState };
42
42
  }
43
- describe(`${worker_adapter_1.WorkerAdapter.name}.streamAttachments`, () => {
43
+ describe(`${extraction_adapter_1.ExtractionAdapter.name}.streamAttachments`, () => {
44
44
  let adapter;
45
+ let adapterState;
45
46
  beforeEach(() => {
46
47
  jest.clearAllMocks();
47
- ({ adapter } = makeAdapter());
48
+ ({ adapter, adapterState } = makeAdapter());
48
49
  });
49
50
  it('should process all artifact batches successfully', async () => {
50
51
  // Arrange
51
52
  const mockStream = jest.fn();
52
- adapter.state.toDevRev = {
53
+ adapterState.sdkState.toDevRev = {
53
54
  attachmentsMetadata: {
54
55
  artifactIds: ['artifact1', 'artifact2'],
55
56
  lastProcessed: 0,
@@ -95,14 +96,14 @@ describe(`${worker_adapter_1.WorkerAdapter.name}.streamAttachments`, () => {
95
96
  ]);
96
97
  expect(adapter.initializeRepos).toHaveBeenCalledTimes(1);
97
98
  expect(adapter['uploader'].getAttachmentsFromArtifactId).toHaveBeenCalledTimes(2);
98
- expect(adapter.state.toDevRev.attachmentsMetadata.artifactIds).toEqual([]);
99
- expect(adapter.state.toDevRev.attachmentsMetadata.lastProcessed).toBe(0);
99
+ expect(adapterState.sdkState.toDevRev.attachmentsMetadata.artifactIds).toEqual([]);
100
+ expect(adapterState.sdkState.toDevRev.attachmentsMetadata.lastProcessed).toBe(0);
100
101
  expect(result).toBeUndefined();
101
102
  });
102
103
  it('[edge] should handle invalid batch size by using 1 instead', async () => {
103
104
  // Arrange
104
105
  const mockStream = jest.fn();
105
- adapter.state.toDevRev = {
106
+ adapterState.sdkState.toDevRev = {
106
107
  attachmentsMetadata: {
107
108
  artifactIds: ['artifact1'],
108
109
  lastProcessed: 0,
@@ -133,7 +134,7 @@ describe(`${worker_adapter_1.WorkerAdapter.name}.streamAttachments`, () => {
133
134
  it('[edge] should cap batch size to 50 when batchSize is greater than 50', async () => {
134
135
  // Arrange
135
136
  const mockStream = jest.fn();
136
- adapter.state.toDevRev = {
137
+ adapterState.sdkState.toDevRev = {
137
138
  attachmentsMetadata: {
138
139
  artifactIds: ['artifact1'],
139
140
  lastProcessed: 0,
@@ -164,7 +165,7 @@ describe(`${worker_adapter_1.WorkerAdapter.name}.streamAttachments`, () => {
164
165
  it('[edge] should handle empty attachments metadata artifact IDs', async () => {
165
166
  // Arrange
166
167
  const mockStream = jest.fn();
167
- adapter.state.toDevRev = {
168
+ adapterState.sdkState.toDevRev = {
168
169
  attachmentsMetadata: {
169
170
  artifactIds: [],
170
171
  lastProcessed: 0,
@@ -180,7 +181,7 @@ describe(`${worker_adapter_1.WorkerAdapter.name}.streamAttachments`, () => {
180
181
  it('[edge] should handle errors when getting attachments', async () => {
181
182
  // Arrange
182
183
  const mockStream = jest.fn();
183
- adapter.state.toDevRev = {
184
+ adapterState.sdkState.toDevRev = {
184
185
  attachmentsMetadata: {
185
186
  artifactIds: ['artifact1'],
186
187
  lastProcessed: 0,
@@ -206,7 +207,7 @@ describe(`${worker_adapter_1.WorkerAdapter.name}.streamAttachments`, () => {
206
207
  it('[edge] should handle empty attachments array from artifact', async () => {
207
208
  // Arrange
208
209
  const mockStream = jest.fn();
209
- adapter.state.toDevRev = {
210
+ adapterState.sdkState.toDevRev = {
210
211
  attachmentsMetadata: {
211
212
  artifactIds: ['artifact1'],
212
213
  lastProcessed: 0,
@@ -224,7 +225,7 @@ describe(`${worker_adapter_1.WorkerAdapter.name}.streamAttachments`, () => {
224
225
  stream: mockStream,
225
226
  });
226
227
  // Assert
227
- expect(adapter.state.toDevRev.attachmentsMetadata.artifactIds).toEqual([]);
228
+ expect(adapterState.sdkState.toDevRev.attachmentsMetadata.artifactIds).toEqual([]);
228
229
  expect(result).toBeUndefined();
229
230
  });
230
231
  it('should use custom processors when provided', async () => {
@@ -232,7 +233,7 @@ describe(`${worker_adapter_1.WorkerAdapter.name}.streamAttachments`, () => {
232
233
  const mockStream = jest.fn();
233
234
  const mockReducer = jest.fn().mockReturnValue(['custom-reduced']);
234
235
  const mockIterator = jest.fn().mockResolvedValue({});
235
- adapter.state.toDevRev = {
236
+ adapterState.sdkState.toDevRev = {
236
237
  attachmentsMetadata: {
237
238
  artifactIds: ['artifact1'],
238
239
  lastProcessed: 0,
@@ -272,7 +273,7 @@ describe(`${worker_adapter_1.WorkerAdapter.name}.streamAttachments`, () => {
272
273
  attachments_streaming_pool_1.AttachmentsStreamingPool.mockImplementationOnce(() => ({
273
274
  streamAll: jest.fn().mockResolvedValue({ delay: 30 }),
274
275
  }));
275
- adapter.state.toDevRev = {
276
+ adapterState.sdkState.toDevRev = {
276
277
  attachmentsMetadata: {
277
278
  artifactIds: ['artifact1'],
278
279
  lastProcessed: 0,
@@ -291,9 +292,7 @@ describe(`${worker_adapter_1.WorkerAdapter.name}.streamAttachments`, () => {
291
292
  });
292
293
  // Assert
293
294
  expect(result).toEqual({ delay: 30 });
294
- expect(adapter.state.toDevRev.attachmentsMetadata.artifactIds).toEqual([
295
- 'artifact1',
296
- ]);
295
+ expect(adapterState.sdkState.toDevRev.attachmentsMetadata.artifactIds).toEqual(['artifact1']);
297
296
  });
298
297
  it('should handle error from iterator', async () => {
299
298
  // Arrange
@@ -303,7 +302,7 @@ describe(`${worker_adapter_1.WorkerAdapter.name}.streamAttachments`, () => {
303
302
  error: 'Mock error',
304
303
  }),
305
304
  }));
306
- adapter.state.toDevRev = {
305
+ adapterState.sdkState.toDevRev = {
307
306
  attachmentsMetadata: {
308
307
  artifactIds: ['artifact1'],
309
308
  lastProcessed: 0,
@@ -322,9 +321,7 @@ describe(`${worker_adapter_1.WorkerAdapter.name}.streamAttachments`, () => {
322
321
  });
323
322
  // Assert
324
323
  expect(result).toEqual({ error: 'Mock error' });
325
- expect(adapter.state.toDevRev.attachmentsMetadata.artifactIds).toEqual([
326
- 'artifact1',
327
- ]);
324
+ expect(adapterState.sdkState.toDevRev.attachmentsMetadata.artifactIds).toEqual(['artifact1']);
328
325
  });
329
326
  it('should emit progress event and exit process on timeout, preserving state for resumption', async () => {
330
327
  // Arrange
@@ -332,7 +329,7 @@ describe(`${worker_adapter_1.WorkerAdapter.name}.streamAttachments`, () => {
332
329
  const exitSpy = jest
333
330
  .spyOn(process, 'exit')
334
331
  .mockImplementation(() => undefined);
335
- adapter.state.toDevRev = {
332
+ adapterState.sdkState.toDevRev = {
336
333
  attachmentsMetadata: {
337
334
  artifactIds: ['artifact1', 'artifact2', 'artifact3'],
338
335
  lastProcessed: 0,
@@ -366,11 +363,7 @@ describe(`${worker_adapter_1.WorkerAdapter.name}.streamAttachments`, () => {
366
363
  // Assert
367
364
  expect(emitSpy).toHaveBeenCalledWith(types_1.ExtractorEventType.AttachmentExtractionProgress);
368
365
  expect(exitSpy).toHaveBeenCalledWith(0);
369
- expect(adapter.state.toDevRev.attachmentsMetadata.artifactIds).toEqual([
370
- 'artifact1',
371
- 'artifact2',
372
- 'artifact3',
373
- ]);
366
+ expect(adapterState.sdkState.toDevRev.attachmentsMetadata.artifactIds).toEqual(['artifact1', 'artifact2', 'artifact3']);
374
367
  expect(adapter['uploader'].getAttachmentsFromArtifactId).toHaveBeenCalledTimes(1);
375
368
  exitSpy.mockRestore();
376
369
  });
@@ -382,7 +375,7 @@ describe(`${worker_adapter_1.WorkerAdapter.name}.streamAttachments`, () => {
382
375
  const exitSpy = jest
383
376
  .spyOn(process, 'exit')
384
377
  .mockImplementation(() => undefined);
385
- adapter.state.toDevRev = {
378
+ adapterState.sdkState.toDevRev = {
386
379
  attachmentsMetadata: {
387
380
  artifactIds: ['artifact1', 'artifact2', 'artifact3'],
388
381
  lastProcessed: 0,
@@ -424,16 +417,13 @@ describe(`${worker_adapter_1.WorkerAdapter.name}.streamAttachments`, () => {
424
417
  expect(exitSpy).toHaveBeenCalledWith(0);
425
418
  // - Artifact 1 was shifted out cleanly; artifact 2 remains (timeout caught
426
419
  // before its shift) along with the untouched artifact 3
427
- expect(adapter.state.toDevRev.attachmentsMetadata.artifactIds).toEqual([
428
- 'artifact2',
429
- 'artifact3',
430
- ]);
420
+ expect(adapterState.sdkState.toDevRev.attachmentsMetadata.artifactIds).toEqual(['artifact2', 'artifact3']);
431
421
  exitSpy.mockRestore();
432
422
  });
433
423
  it('should reset lastProcessed and attachment IDs list after processing all artifacts', async () => {
434
424
  // Arrange
435
425
  const mockStream = jest.fn();
436
- adapter.state.toDevRev = {
426
+ adapterState.sdkState.toDevRev = {
437
427
  attachmentsMetadata: {
438
428
  artifactIds: ['artifact1'],
439
429
  lastProcessed: 0,
@@ -470,11 +460,11 @@ describe(`${worker_adapter_1.WorkerAdapter.name}.streamAttachments`, () => {
470
460
  stream: mockStream,
471
461
  });
472
462
  // Assert
473
- expect(adapter.state.toDevRev.attachmentsMetadata.artifactIds).toHaveLength(0);
474
- expect(adapter.state.toDevRev.attachmentsMetadata.lastProcessed).toBe(0);
463
+ expect(adapterState.sdkState.toDevRev.attachmentsMetadata.artifactIds).toHaveLength(0);
464
+ expect(adapterState.sdkState.toDevRev.attachmentsMetadata.lastProcessed).toBe(0);
475
465
  });
476
466
  });
477
- describe(`${worker_adapter_1.WorkerAdapter.name}.processAttachment`, () => {
467
+ describe(`${extraction_adapter_1.ExtractionAdapter.name}.processAttachment`, () => {
478
468
  let adapter;
479
469
  beforeEach(() => {
480
470
  jest.clearAllMocks();
@@ -662,7 +652,7 @@ describe(`${worker_adapter_1.WorkerAdapter.name}.processAttachment`, () => {
662
652
  expect((_a = result === null || result === void 0 ? void 0 : result.error) === null || _a === void 0 ? void 0 : _a.message).toContain('Error while opening attachment stream');
663
653
  });
664
654
  });
665
- describe(`${worker_adapter_1.WorkerAdapter.name}.initializeRepos — event size threshold`, () => {
655
+ describe(`${extraction_adapter_1.ExtractionAdapter.name}.initializeRepos — event size threshold`, () => {
666
656
  it('should set isTimeout=true once the cumulative artifact payload exceeds EVENT_SIZE_THRESHOLD_BYTES', () => {
667
657
  // Arrange
668
658
  const { adapter } = makeAdapter();
@@ -684,7 +674,7 @@ describe(`${worker_adapter_1.WorkerAdapter.name}.initializeRepos — event size
684
674
  expect(adapter.isTimeout).toBe(true);
685
675
  });
686
676
  });
687
- describe(`${worker_adapter_1.WorkerAdapter.name}.getRepo`, () => {
677
+ describe(`${extraction_adapter_1.ExtractionAdapter.name}.getRepo`, () => {
688
678
  it('should return undefined when the requested repo was never initialised', () => {
689
679
  // Arrange
690
680
  const { adapter } = makeAdapter();
@@ -694,7 +684,7 @@ describe(`${worker_adapter_1.WorkerAdapter.name}.getRepo`, () => {
694
684
  expect(result).toBeUndefined();
695
685
  });
696
686
  });
697
- describe(`${worker_adapter_1.WorkerAdapter.name}.destroyHttpStream`, () => {
687
+ describe(`${extraction_adapter_1.ExtractionAdapter.name}.destroyHttpStream`, () => {
698
688
  let adapter;
699
689
  beforeEach(() => {
700
690
  ({ adapter } = makeAdapter());
@@ -752,7 +742,7 @@ describe(`${worker_adapter_1.WorkerAdapter.name}.destroyHttpStream`, () => {
752
742
  expect(() => adapter['destroyHttpStream'](httpStream)).not.toThrow();
753
743
  });
754
744
  });
755
- describe(`${worker_adapter_1.WorkerAdapter.name} — extractionScope`, () => {
745
+ describe(`${extraction_adapter_1.ExtractionAdapter.name} — extractionScope`, () => {
756
746
  it('should return empty object by default', () => {
757
747
  const { adapter } = makeAdapter();
758
748
  expect(adapter.extractionScope).toEqual({});
@@ -767,7 +757,7 @@ describe(`${worker_adapter_1.WorkerAdapter.name} — extractionScope`, () => {
767
757
  expect(adapter.extractionScope).toEqual(extractionScope);
768
758
  });
769
759
  });
770
- describe(`${worker_adapter_1.WorkerAdapter.name} — shouldExtract`, () => {
760
+ describe(`${extraction_adapter_1.ExtractionAdapter.name} — shouldExtract`, () => {
771
761
  it('should return true when extraction scope is empty', () => {
772
762
  const { adapter } = makeAdapter();
773
763
  expect(adapter.shouldExtract('tasks')).toBe(true);
@@ -0,0 +1,44 @@
1
+ import { Mappers } from '../../mappers/mappers';
2
+ import { LoadingState } from '../../state/loading-state';
3
+ import { AirSyncEvent, EventData, ExtractorEventType } from '../../types/extraction';
4
+ import { ExternalSystemAttachment, ExternalSystemItem, ExternalSystemLoadingFunction, FileToLoad, ItemTypesToLoadParams, ItemTypeToLoad, LoaderEventType, LoaderReport, LoadItemResponse, LoadItemTypesResponse } from '../../types/loading';
5
+ import { WorkerAdapterOptions } from '../../types/workers';
6
+ import { BaseAdapter } from './base-adapter';
7
+ /**
8
+ * LoadingAdapter is the adapter passed to loading tasks. It exposes the loading
9
+ * surface (item/attachment loading, mappers, loader reports).
10
+ *
11
+ * @public
12
+ */
13
+ export declare class LoadingAdapter<ConnectorState> extends BaseAdapter<ConnectorState, LoadingState<ConnectorState>> {
14
+ private loaderReports;
15
+ private _processedFiles;
16
+ private _mappers;
17
+ constructor(params: {
18
+ event: AirSyncEvent;
19
+ adapterState: LoadingState<ConnectorState>;
20
+ options?: WorkerAdapterOptions;
21
+ });
22
+ get reports(): LoaderReport[];
23
+ get processedFiles(): string[];
24
+ get mappers(): Mappers;
25
+ protected beforeEmit(): Promise<void>;
26
+ protected buildEmitPayload(newEventType: ExtractorEventType | LoaderEventType): EventData;
27
+ protected afterEmit(): void;
28
+ loadItemTypes({ itemTypesToLoad, }: ItemTypesToLoadParams): Promise<LoadItemTypesResponse>;
29
+ getLoaderBatches({ supportedItemTypes, }: {
30
+ supportedItemTypes: string[];
31
+ }): Promise<FileToLoad[]>;
32
+ loadAttachments({ create, }: {
33
+ create: ExternalSystemLoadingFunction<ExternalSystemAttachment>;
34
+ }): Promise<LoadItemTypesResponse>;
35
+ loadItem({ item, itemTypeToLoad, }: {
36
+ item: ExternalSystemItem;
37
+ itemTypeToLoad: ItemTypeToLoad;
38
+ }): Promise<LoadItemResponse>;
39
+ loadAttachment({ item, create, }: {
40
+ item: ExternalSystemAttachment;
41
+ create: ExternalSystemLoadingFunction<ExternalSystemAttachment>;
42
+ }): Promise<LoadItemResponse>;
43
+ }
44
+ //# sourceMappingURL=loading-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loading-adapter.d.ts","sourceRoot":"","sources":["../../../src/multithreading/adapters/loading-adapter.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAEhD,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EACL,YAAY,EACZ,SAAS,EAET,kBAAkB,EACnB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAEL,wBAAwB,EACxB,kBAAkB,EAClB,6BAA6B,EAC7B,UAAU,EACV,qBAAqB,EACrB,cAAc,EACd,eAAe,EACf,YAAY,EACZ,gBAAgB,EAChB,qBAAqB,EAEtB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAE3D,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAM7C;;;;;GAKG;AACH,qBAAa,cAAc,CAAC,cAAc,CAAE,SAAQ,WAAW,CAC7D,cAAc,EACd,YAAY,CAAC,cAAc,CAAC,CAC7B;IACC,OAAO,CAAC,aAAa,CAAiB;IACtC,OAAO,CAAC,eAAe,CAAW;IAClC,OAAO,CAAC,QAAQ,CAAU;gBAEd,MAAM,EAAE;QAClB,KAAK,EAAE,YAAY,CAAC;QACpB,YAAY,EAAE,YAAY,CAAC,cAAc,CAAC,CAAC;QAC3C,OAAO,CAAC,EAAE,oBAAoB,CAAC;KAChC;IAUD,IAAI,OAAO,IAAI,YAAY,EAAE,CAE5B;IAED,IAAI,cAAc,IAAI,MAAM,EAAE,CAE7B;IAED,IAAI,OAAO,IAAI,OAAO,CAErB;cAEe,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3C,SAAS,CAAC,gBAAgB,CACxB,YAAY,EAAE,kBAAkB,GAAG,eAAe,GACjD,SAAS;IAYZ,SAAS,CAAC,SAAS,IAAI,IAAI;IAIrB,aAAa,CAAC,EAClB,eAAe,GAChB,EAAE,qBAAqB,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAwInD,gBAAgB,CAAC,EACrB,kBAAkB,GACnB,EAAE;QACD,kBAAkB,EAAE,MAAM,EAAE,CAAC;KAC9B;IA4BK,eAAe,CAAC,EACpB,MAAM,GACP,EAAE;QACD,MAAM,EAAE,6BAA6B,CAAC,wBAAwB,CAAC,CAAC;KACjE,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAyG5B,QAAQ,CAAC,EACb,IAAI,EACJ,cAAc,GACf,EAAE;QACD,IAAI,EAAE,kBAAkB,CAAC;QACzB,cAAc,EAAE,cAAc,CAAC;KAChC,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAwMvB,cAAc,CAAC,EACnB,IAAI,EACJ,MAAM,GACP,EAAE;QACD,IAAI,EAAE,wBAAwB,CAAC;QAC/B,MAAM,EAAE,6BAA6B,CAAC,wBAAwB,CAAC,CAAC;KACjE,GAAG,OAAO,CAAC,gBAAgB,CAAC;CAsD9B"}
@@ -19,4 +19,4 @@ export declare function addReportToLoaderReport({ loaderReports, report, }: {
19
19
  loaderReports: LoaderReport[];
20
20
  report: LoaderReport;
21
21
  }): LoaderReport[];
22
- //# sourceMappingURL=worker-adapter.helpers.d.ts.map
22
+ //# sourceMappingURL=loading-adapter.helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loading-adapter.helpers.d.ts","sourceRoot":"","sources":["../../../src/multithreading/adapters/loading-adapter.helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,UAAU,EACV,YAAY,EACZ,eAAe,EAChB,MAAM,qBAAqB,CAAC;AAE7B;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,EAC7B,kBAAkB,EAClB,SAAS,GACV,EAAE;IACD,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,SAAS,EAAE,eAAe,EAAE,CAAC;CAC9B,GAAG,UAAU,EAAE,CA8Bf;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,EACtC,aAAa,EACb,MAAM,GACP,EAAE;IACD,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,MAAM,EAAE,YAAY,CAAC;CACtB,GAAG,YAAY,EAAE,CA4BjB"}