@devrev/ts-adaas 1.2.6 → 1.4.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/README.md +1 -272
- package/dist/common/install-initial-domain-mapping.d.ts +2 -2
- package/dist/http/axios-client.d.ts +23 -0
- package/dist/http/axios-client.js +27 -3
- package/dist/repo/repo.d.ts +1 -1
- package/dist/state/state.d.ts +1 -1
- package/dist/state/state.interfaces.d.ts +6 -1
- package/dist/state/state.js +25 -2
- package/dist/tests/test-helpers.js +1 -1
- package/dist/types/extraction.d.ts +3 -1
- package/dist/types/workers.d.ts +4 -0
- package/dist/workers/process-task.js +2 -0
- package/dist/workers/spawn.d.ts +1 -1
- package/dist/workers/spawn.js +2 -1
- package/dist/workers/worker-adapter.d.ts +24 -2
- package/dist/workers/worker-adapter.js +156 -39
- package/dist/workers/worker-adapter.test.d.ts +1 -0
- package/dist/workers/worker-adapter.test.js +664 -0
- package/package.json +8 -8
|
@@ -39,6 +39,104 @@ function createWorkerAdapter({ event, adapterState, options, }) {
|
|
|
39
39
|
class WorkerAdapter {
|
|
40
40
|
constructor({ event, adapterState, options, }) {
|
|
41
41
|
this.repos = [];
|
|
42
|
+
/**
|
|
43
|
+
* Transforms an array of attachments into array of batches of the specified size.
|
|
44
|
+
*
|
|
45
|
+
* @param {Object} parameters - The parameters object
|
|
46
|
+
* @param {NormalizedAttachment[]} parameters.attachments - Array of attachments to be processed
|
|
47
|
+
* @param {number} [parameters.batchSize=1] - The size of each batch (defaults to 1)
|
|
48
|
+
* @param {ConnectorState} parameters.adapter - The adapter instance
|
|
49
|
+
* @returns {NormalizedAttachment[][]} An array of attachment batches
|
|
50
|
+
*/
|
|
51
|
+
this.defaultAttachmentsReducer = ({ attachments, batchSize = 1 }) => {
|
|
52
|
+
// Transform the attachments array into smaller batches
|
|
53
|
+
const batches = attachments.reduce((result, item, index) => {
|
|
54
|
+
// Determine the index of the current batch
|
|
55
|
+
const batchIndex = Math.floor(index / batchSize);
|
|
56
|
+
// Initialize a new batch if it doesn't already exist
|
|
57
|
+
if (!result[batchIndex]) {
|
|
58
|
+
result[batchIndex] = [];
|
|
59
|
+
}
|
|
60
|
+
// Append the current item to the current batch
|
|
61
|
+
result[batchIndex].push(item);
|
|
62
|
+
return result;
|
|
63
|
+
}, []);
|
|
64
|
+
// Return the array of batches
|
|
65
|
+
return batches;
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* This iterator function processes attachments batch by batch, saves progress to state, and handles rate limiting.
|
|
69
|
+
*
|
|
70
|
+
* @param {Object} parameters - The parameters object
|
|
71
|
+
* @param {NormalizedAttachment[][]} parameters.reducedAttachments - Array of attachment batches to process
|
|
72
|
+
* @param {Object} parameters.adapter - The connector adapter that contains state and processing methods
|
|
73
|
+
* @param {Object} parameters.stream - Stream object for logging or progress reporting
|
|
74
|
+
* @returns {Promise<{delay?: number} | void>} Returns an object with delay information if rate-limited, otherwise void
|
|
75
|
+
* @throws Will not throw exceptions but will log warnings for processing failures
|
|
76
|
+
*/
|
|
77
|
+
this.defaultAttachmentsIterator = async ({ reducedAttachments, adapter, stream }) => {
|
|
78
|
+
if (!adapter.state.toDevRev) {
|
|
79
|
+
const error = new Error(`toDevRev state is not defined.`);
|
|
80
|
+
console.error(error.message);
|
|
81
|
+
return { error };
|
|
82
|
+
}
|
|
83
|
+
// Get index of the last processed batch of this artifact
|
|
84
|
+
const lastProcessedBatchIndex = adapter.state.toDevRev.attachmentsMetadata.lastProcessed || 0;
|
|
85
|
+
// Get the list of successfully processed attachments in previous (possibly incomplete) batch extraction.
|
|
86
|
+
// If no such list exists, create an empty one.
|
|
87
|
+
if (!adapter.state.toDevRev.attachmentsMetadata
|
|
88
|
+
.lastProcessedAttachmentsIdsList) {
|
|
89
|
+
adapter.state.toDevRev.attachmentsMetadata.lastProcessedAttachmentsIdsList =
|
|
90
|
+
[];
|
|
91
|
+
}
|
|
92
|
+
// Loop through the batches of attachments
|
|
93
|
+
for (let i = lastProcessedBatchIndex; i < reducedAttachments.length; i++) {
|
|
94
|
+
const attachmentsBatch = reducedAttachments[i];
|
|
95
|
+
// Create a list of promises for parallel processing
|
|
96
|
+
const promises = [];
|
|
97
|
+
for (const attachment of attachmentsBatch) {
|
|
98
|
+
if (adapter.state.toDevRev.attachmentsMetadata.lastProcessedAttachmentsIdsList.includes(attachment.id)) {
|
|
99
|
+
console.log(`Attachment with ID ${attachment.id} has already been processed. Skipping.`);
|
|
100
|
+
continue; // Skip if the attachment ID is already processed
|
|
101
|
+
}
|
|
102
|
+
const promise = adapter
|
|
103
|
+
.processAttachment(attachment, stream)
|
|
104
|
+
.then((response) => {
|
|
105
|
+
var _a, _b, _c;
|
|
106
|
+
// Check if rate limit was hit
|
|
107
|
+
if (response === null || response === void 0 ? void 0 : response.delay) {
|
|
108
|
+
// Store this promise result to be checked later
|
|
109
|
+
return { delay: response.delay };
|
|
110
|
+
}
|
|
111
|
+
// No rate limiting, process normally
|
|
112
|
+
if ((_b = (_a = adapter.state.toDevRev) === null || _a === void 0 ? void 0 : _a.attachmentsMetadata) === null || _b === void 0 ? void 0 : _b.lastProcessedAttachmentsIdsList) {
|
|
113
|
+
(_c = adapter.state.toDevRev) === null || _c === void 0 ? void 0 : _c.attachmentsMetadata.lastProcessedAttachmentsIdsList.push(attachment.id);
|
|
114
|
+
}
|
|
115
|
+
return null; // Return null for successful processing
|
|
116
|
+
})
|
|
117
|
+
.catch((error) => {
|
|
118
|
+
console.warn(`Skipping attachment with ID ${attachment.id} due to error: ${error}`);
|
|
119
|
+
return null; // Return null for errors too
|
|
120
|
+
});
|
|
121
|
+
promises.push(promise);
|
|
122
|
+
}
|
|
123
|
+
// Wait for all promises to settle and check for rate limiting
|
|
124
|
+
const results = await Promise.all(promises);
|
|
125
|
+
// Check if any of the results indicate rate limiting
|
|
126
|
+
const rateLimit = results.find(result => result === null || result === void 0 ? void 0 : result.delay);
|
|
127
|
+
if (rateLimit) {
|
|
128
|
+
// Return the delay information to the caller
|
|
129
|
+
return { delay: rateLimit.delay };
|
|
130
|
+
}
|
|
131
|
+
if (adapter.state.toDevRev) {
|
|
132
|
+
// Update the last processed batch index
|
|
133
|
+
adapter.state.toDevRev.attachmentsMetadata.lastProcessed = i + 1;
|
|
134
|
+
// Reset successfullyProcessedAttachments list
|
|
135
|
+
adapter.state.toDevRev.attachmentsMetadata.lastProcessedAttachmentsIdsList.length = 0;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return {};
|
|
139
|
+
};
|
|
42
140
|
this.event = event;
|
|
43
141
|
this.options = options;
|
|
44
142
|
this.adapterState = adapterState;
|
|
@@ -88,7 +186,7 @@ class WorkerAdapter {
|
|
|
88
186
|
getRepo(itemType) {
|
|
89
187
|
const repo = this.repos.find((repo) => repo.itemType === itemType);
|
|
90
188
|
if (!repo) {
|
|
91
|
-
console.error(`Repo
|
|
189
|
+
console.error(`Repo for item type ${itemType} not found.`);
|
|
92
190
|
return;
|
|
93
191
|
}
|
|
94
192
|
return repo;
|
|
@@ -117,7 +215,14 @@ class WorkerAdapter {
|
|
|
117
215
|
}
|
|
118
216
|
// We want to upload all the repos before emitting the event, except for the external sync units done event
|
|
119
217
|
if (newEventType !== extraction_1.ExtractorEventType.ExtractionExternalSyncUnitsDone) {
|
|
120
|
-
|
|
218
|
+
try {
|
|
219
|
+
await this.uploadAllRepos();
|
|
220
|
+
}
|
|
221
|
+
catch (error) {
|
|
222
|
+
console.error('Error while uploading repos', error);
|
|
223
|
+
node_worker_threads_1.parentPort === null || node_worker_threads_1.parentPort === void 0 ? void 0 : node_worker_threads_1.parentPort.postMessage(workers_1.WorkerMessageSubject.WorkerMessageExit);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
121
226
|
}
|
|
122
227
|
// If the extraction is done, we want to save the timestamp of the last successful sync
|
|
123
228
|
if (newEventType === extraction_1.ExtractorEventType.ExtractionAttachmentsDone) {
|
|
@@ -164,7 +269,10 @@ class WorkerAdapter {
|
|
|
164
269
|
}
|
|
165
270
|
async uploadAllRepos() {
|
|
166
271
|
for (const repo of this.repos) {
|
|
167
|
-
await repo.upload();
|
|
272
|
+
const error = await repo.upload();
|
|
273
|
+
if (error) {
|
|
274
|
+
throw error;
|
|
275
|
+
}
|
|
168
276
|
}
|
|
169
277
|
}
|
|
170
278
|
handleTimeout() {
|
|
@@ -501,7 +609,7 @@ class WorkerAdapter {
|
|
|
501
609
|
event: this.event,
|
|
502
610
|
});
|
|
503
611
|
if (error) {
|
|
504
|
-
console.warn('Error while streaming attachment', error
|
|
612
|
+
console.warn('Error while streaming attachment', error);
|
|
505
613
|
return { error };
|
|
506
614
|
}
|
|
507
615
|
else if (delay) {
|
|
@@ -573,27 +681,35 @@ class WorkerAdapter {
|
|
|
573
681
|
* Streams the attachments to the DevRev platform.
|
|
574
682
|
* The attachments are streamed to the platform and the artifact information is returned.
|
|
575
683
|
* @param {{ stream, processors }: { stream: ExternalSystemAttachmentStreamingFunction, processors?: ExternalSystemAttachmentProcessors }} Params - The parameters to stream the attachments
|
|
576
|
-
* @returns {Promise<StreamAttachmentsReturnType>} - The response object containing the
|
|
684
|
+
* @returns {Promise<StreamAttachmentsReturnType>} - The response object containing the ssorAttachment artifact information
|
|
577
685
|
* or error information if there was an error
|
|
578
686
|
*/
|
|
579
|
-
async streamAttachments({ stream, processors,
|
|
580
|
-
|
|
687
|
+
async streamAttachments({ stream, processors, batchSize = 1, // By default, we want to stream one attachment at a time
|
|
688
|
+
}) {
|
|
689
|
+
var _a, _b;
|
|
690
|
+
if (batchSize <= 0) {
|
|
691
|
+
const error = new Error(`Invalid attachments batch size: ${batchSize}. Batch size must be greater than 0.`);
|
|
692
|
+
console.error(error.message);
|
|
693
|
+
return { error };
|
|
694
|
+
}
|
|
581
695
|
const repos = [
|
|
582
696
|
{
|
|
583
697
|
itemType: 'ssor_attachment',
|
|
584
698
|
},
|
|
585
699
|
];
|
|
586
700
|
this.initializeRepos(repos);
|
|
587
|
-
|
|
588
|
-
if (!
|
|
589
|
-
|
|
701
|
+
// If there are no attachments metadata artifact IDs in state, finish here
|
|
702
|
+
if (!((_b = (_a = this.state.toDevRev) === null || _a === void 0 ? void 0 : _a.attachmentsMetadata) === null || _b === void 0 ? void 0 : _b.artifactIds) ||
|
|
703
|
+
this.state.toDevRev.attachmentsMetadata.artifactIds.length === 0) {
|
|
590
704
|
console.log(`No attachments metadata artifact IDs found in state.`);
|
|
591
705
|
return;
|
|
592
706
|
}
|
|
593
707
|
else {
|
|
594
|
-
console.log(`Found ${
|
|
708
|
+
console.log(`Found ${this.state.toDevRev.attachmentsMetadata.artifactIds.length} attachments metadata artifact IDs in state.`);
|
|
595
709
|
}
|
|
596
|
-
|
|
710
|
+
// Loop through the attachments metadata artifact IDs
|
|
711
|
+
while (this.state.toDevRev.attachmentsMetadata.artifactIds.length > 0) {
|
|
712
|
+
const attachmentsMetadataArtifactId = this.state.toDevRev.attachmentsMetadata.artifactIds[0];
|
|
597
713
|
console.log(`Started processing attachments for attachments metadata artifact ID: ${attachmentsMetadataArtifactId}.`);
|
|
598
714
|
const { attachments, error } = await this.uploader.getAttachmentsFromArtifactId({
|
|
599
715
|
artifact: attachmentsMetadataArtifactId,
|
|
@@ -604,43 +720,44 @@ class WorkerAdapter {
|
|
|
604
720
|
}
|
|
605
721
|
if (!attachments || attachments.length === 0) {
|
|
606
722
|
console.warn(`No attachments found for artifact ID: ${attachmentsMetadataArtifactId}.`);
|
|
723
|
+
// Remove empty artifact and reset lastProcessed
|
|
724
|
+
this.state.toDevRev.attachmentsMetadata.artifactIds.shift();
|
|
725
|
+
this.state.toDevRev.attachmentsMetadata.lastProcessed = 0;
|
|
607
726
|
continue;
|
|
608
727
|
}
|
|
609
728
|
console.log(`Found ${attachments.length} attachments for artifact ID: ${attachmentsMetadataArtifactId}.`);
|
|
729
|
+
// Use the reducer to split into batches.
|
|
730
|
+
let reducer;
|
|
731
|
+
// Use the iterator to process each batch, streaming all attachments inside one batch in parallel.
|
|
732
|
+
let iterator;
|
|
610
733
|
if (processors) {
|
|
611
734
|
console.log(`Using custom processors for attachments.`);
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
const response = await iterator({
|
|
615
|
-
reducedAttachments,
|
|
616
|
-
adapter: this,
|
|
617
|
-
stream,
|
|
618
|
-
});
|
|
619
|
-
if ((response === null || response === void 0 ? void 0 : response.delay) || (response === null || response === void 0 ? void 0 : response.error)) {
|
|
620
|
-
return response;
|
|
621
|
-
}
|
|
735
|
+
reducer = processors.reducer;
|
|
736
|
+
iterator = processors.iterator;
|
|
622
737
|
}
|
|
623
738
|
else {
|
|
624
739
|
console.log(`Using default processors for attachments.`);
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
return response;
|
|
630
|
-
}
|
|
631
|
-
else if (response === null || response === void 0 ? void 0 : response.error) {
|
|
632
|
-
console.warn(`Skipping attachment with ID ${attachment.id} due to error.`);
|
|
633
|
-
}
|
|
634
|
-
if (this.state.toDevRev) {
|
|
635
|
-
this.state.toDevRev.attachmentsMetadata.lastProcessed += 1;
|
|
636
|
-
}
|
|
637
|
-
}
|
|
740
|
+
reducer = this
|
|
741
|
+
.defaultAttachmentsReducer;
|
|
742
|
+
iterator = this
|
|
743
|
+
.defaultAttachmentsIterator;
|
|
638
744
|
}
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
this
|
|
642
|
-
|
|
745
|
+
const reducedAttachments = reducer({
|
|
746
|
+
attachments,
|
|
747
|
+
adapter: this,
|
|
748
|
+
batchSize,
|
|
749
|
+
});
|
|
750
|
+
const response = await iterator({
|
|
751
|
+
reducedAttachments,
|
|
752
|
+
adapter: this,
|
|
753
|
+
stream,
|
|
754
|
+
});
|
|
755
|
+
if ((response === null || response === void 0 ? void 0 : response.delay) || (response === null || response === void 0 ? void 0 : response.error)) {
|
|
756
|
+
return response;
|
|
643
757
|
}
|
|
758
|
+
console.log(`Finished processing all attachments for artifact ID: ${attachmentsMetadataArtifactId}.`);
|
|
759
|
+
this.state.toDevRev.attachmentsMetadata.artifactIds.shift();
|
|
760
|
+
this.state.toDevRev.attachmentsMetadata.lastProcessed = 0;
|
|
644
761
|
}
|
|
645
762
|
return;
|
|
646
763
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|