@strapi/data-transfer 5.10.4 → 5.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/engine/index.d.ts.map +1 -1
- package/dist/index.js +153 -105
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +157 -109
- package/dist/index.mjs.map +1 -1
- package/dist/strapi/providers/remote-source/index.d.ts +2 -0
- package/dist/strapi/providers/remote-source/index.d.ts.map +1 -1
- package/dist/strapi/queries/link.d.ts.map +1 -1
- package/dist/strapi/remote/handlers/pull.d.ts.map +1 -1
- package/package.json +6 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/engine/index.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,WAAW,EAAiC,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/engine/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var stream$1 = require('stream');
|
|
4
|
+
var promises = require('stream/promises');
|
|
4
5
|
var path = require('path');
|
|
5
6
|
var os = require('os');
|
|
6
7
|
var streamChain = require('stream-chain');
|
|
@@ -654,8 +655,7 @@ const TRANSFER_STAGES = Object.freeze([
|
|
|
654
655
|
};
|
|
655
656
|
const DEFAULT_VERSION_STRATEGY = 'ignore';
|
|
656
657
|
const DEFAULT_SCHEMA_STRATEGY = 'strict';
|
|
657
|
-
var _metadata$1 = /*#__PURE__*/ _class_private_field_loose_key$6("_metadata"), _schema = /*#__PURE__*/ _class_private_field_loose_key$6("_schema"), _handlers = /*#__PURE__*/ _class_private_field_loose_key$6("_handlers"),
|
|
658
|
-
_currentStream = /*#__PURE__*/ _class_private_field_loose_key$6("_currentStream"), /**
|
|
658
|
+
var _metadata$1 = /*#__PURE__*/ _class_private_field_loose_key$6("_metadata"), _schema = /*#__PURE__*/ _class_private_field_loose_key$6("_schema"), _handlers = /*#__PURE__*/ _class_private_field_loose_key$6("_handlers"), _currentStreamController = /*#__PURE__*/ _class_private_field_loose_key$6("_currentStreamController"), _aborted = /*#__PURE__*/ _class_private_field_loose_key$6("_aborted"), /**
|
|
659
659
|
* Create and return a transform stream based on the given stage and options.
|
|
660
660
|
*
|
|
661
661
|
* Allowed transformations includes 'filter' and 'map'.
|
|
@@ -770,11 +770,9 @@ class TransferEngine {
|
|
|
770
770
|
}
|
|
771
771
|
// Cause an ongoing transfer to abort gracefully
|
|
772
772
|
async abortTransfer() {
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
}
|
|
777
|
-
_class_private_field_loose_base$6(this, _currentStream)[_currentStream].destroy(err);
|
|
773
|
+
_class_private_field_loose_base$6(this, _aborted)[_aborted] = true;
|
|
774
|
+
_class_private_field_loose_base$6(this, _currentStreamController)[_currentStreamController]?.abort();
|
|
775
|
+
throw new TransferEngineError('fatal', 'Transfer aborted.');
|
|
778
776
|
}
|
|
779
777
|
async init() {
|
|
780
778
|
// Resolve providers' resource and store
|
|
@@ -1084,7 +1082,11 @@ class TransferEngine {
|
|
|
1084
1082
|
writable: true,
|
|
1085
1083
|
value: void 0
|
|
1086
1084
|
});
|
|
1087
|
-
Object.defineProperty(this,
|
|
1085
|
+
Object.defineProperty(this, _currentStreamController, {
|
|
1086
|
+
writable: true,
|
|
1087
|
+
value: void 0
|
|
1088
|
+
});
|
|
1089
|
+
Object.defineProperty(this, _aborted, {
|
|
1088
1090
|
writable: true,
|
|
1089
1091
|
value: void 0
|
|
1090
1092
|
});
|
|
@@ -1094,6 +1096,7 @@ class TransferEngine {
|
|
|
1094
1096
|
schemaDiff: [],
|
|
1095
1097
|
errors: {}
|
|
1096
1098
|
};
|
|
1099
|
+
_class_private_field_loose_base$6(this, _aborted)[_aborted] = false;
|
|
1097
1100
|
this.diagnostics = createDiagnosticReporter();
|
|
1098
1101
|
validateProvider('source', sourceProvider);
|
|
1099
1102
|
validateProvider('destination', destinationProvider);
|
|
@@ -1292,6 +1295,9 @@ function assertSchemasMatching(sourceSchemas, destinationSchemas) {
|
|
|
1292
1295
|
}
|
|
1293
1296
|
}
|
|
1294
1297
|
async function transferStage(options) {
|
|
1298
|
+
if (_class_private_field_loose_base$6(this, _aborted)[_aborted]) {
|
|
1299
|
+
throw new TransferEngineError('fatal', 'Transfer aborted.');
|
|
1300
|
+
}
|
|
1295
1301
|
const { stage, source, destination, transform, tracker } = options;
|
|
1296
1302
|
const updateEndTime = ()=>{
|
|
1297
1303
|
const stageData = this.progress.data[stage];
|
|
@@ -1323,27 +1329,42 @@ async function transferStage(options) {
|
|
|
1323
1329
|
return;
|
|
1324
1330
|
}
|
|
1325
1331
|
_class_private_field_loose_base$6(this, _emitStageUpdate)[_emitStageUpdate]('start', stage);
|
|
1326
|
-
|
|
1327
|
-
|
|
1332
|
+
try {
|
|
1333
|
+
const streams = [
|
|
1334
|
+
source
|
|
1335
|
+
];
|
|
1328
1336
|
if (transform) {
|
|
1329
|
-
|
|
1337
|
+
streams.push(transform);
|
|
1330
1338
|
}
|
|
1331
1339
|
if (tracker) {
|
|
1332
|
-
|
|
1333
|
-
}
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1340
|
+
streams.push(tracker);
|
|
1341
|
+
}
|
|
1342
|
+
streams.push(destination);
|
|
1343
|
+
// NOTE: to debug/confirm backpressure issues from misbehaving stream, uncomment the following lines
|
|
1344
|
+
// source.on('pause', () => console.log(`[${stage}] Source paused due to backpressure`));
|
|
1345
|
+
// source.on('resume', () => console.log(`[${stage}] Source resumed`));
|
|
1346
|
+
// destination.on('drain', () =>
|
|
1347
|
+
// console.log(`[${stage}] Destination drained, resuming data flow`)
|
|
1348
|
+
// );
|
|
1349
|
+
// destination.on('error', (err) => console.error(`[${stage}] Destination error:`, err));
|
|
1350
|
+
const controller = new AbortController();
|
|
1351
|
+
const { signal } = controller;
|
|
1352
|
+
// Store the controller so you can cancel later
|
|
1353
|
+
_class_private_field_loose_base$6(this, _currentStreamController)[_currentStreamController] = controller;
|
|
1354
|
+
await promises.pipeline(streams, {
|
|
1355
|
+
signal
|
|
1356
|
+
});
|
|
1357
|
+
_class_private_field_loose_base$6(this, _emitStageUpdate)[_emitStageUpdate]('finish', stage);
|
|
1358
|
+
} catch (e) {
|
|
1359
|
+
updateEndTime();
|
|
1360
|
+
_class_private_field_loose_base$6(this, _emitStageUpdate)[_emitStageUpdate]('error', stage);
|
|
1361
|
+
this.reportError(e, 'error');
|
|
1362
|
+
if (!destination.destroyed) {
|
|
1338
1363
|
destination.destroy(e);
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
resolve();
|
|
1344
|
-
});
|
|
1345
|
-
});
|
|
1346
|
-
_class_private_field_loose_base$6(this, _emitStageUpdate)[_emitStageUpdate]('finish', stage);
|
|
1364
|
+
}
|
|
1365
|
+
} finally{
|
|
1366
|
+
updateEndTime();
|
|
1367
|
+
}
|
|
1347
1368
|
}
|
|
1348
1369
|
async function resolveProviderResource() {
|
|
1349
1370
|
const sourceMetadata = await this.sourceProvider.getMetadata();
|
|
@@ -1915,9 +1936,7 @@ const createLinkQuery = (strapi, trx)=>{
|
|
|
1915
1936
|
assignOrderColumns();
|
|
1916
1937
|
const qb = connection.insert(payload).into(addSchema(joinTable.name));
|
|
1917
1938
|
if (trx) {
|
|
1918
|
-
await
|
|
1919
|
-
await qb.transacting(nestedTrx);
|
|
1920
|
-
});
|
|
1939
|
+
await qb.transacting(trx);
|
|
1921
1940
|
}
|
|
1922
1941
|
}
|
|
1923
1942
|
if ('morphColumn' in attribute && attribute.morphColumn) {
|
|
@@ -2370,7 +2389,7 @@ class LocalStrapiDestinationProvider {
|
|
|
2370
2389
|
const provider = strapi.config.get('plugin::upload').provider;
|
|
2371
2390
|
const fileId = fileEntitiesMapper?.[uploadData.id];
|
|
2372
2391
|
if (!fileId) {
|
|
2373
|
-
callback(new Error(`File ID not found for ID: ${uploadData.id}`));
|
|
2392
|
+
return callback(new Error(`File ID not found for ID: ${uploadData.id}`));
|
|
2374
2393
|
}
|
|
2375
2394
|
try {
|
|
2376
2395
|
await strapi.plugin('upload').provider.uploadStream(uploadData);
|
|
@@ -2421,9 +2440,9 @@ class LocalStrapiDestinationProvider {
|
|
|
2421
2440
|
provider
|
|
2422
2441
|
}
|
|
2423
2442
|
});
|
|
2424
|
-
callback();
|
|
2443
|
+
return callback();
|
|
2425
2444
|
} catch (error) {
|
|
2426
|
-
callback(new Error(`Error while uploading asset ${chunk.filename} ${error}`));
|
|
2445
|
+
return callback(new Error(`Error while uploading asset ${chunk.filename} ${error}`));
|
|
2427
2446
|
}
|
|
2428
2447
|
});
|
|
2429
2448
|
}
|
|
@@ -2960,7 +2979,7 @@ function reportError(message, error) {
|
|
|
2960
2979
|
}
|
|
2961
2980
|
function handleStreamError(streamType, err) {
|
|
2962
2981
|
const { message, stack } = err;
|
|
2963
|
-
const errorMessage = `Error in ${streamType} read stream: ${message}`;
|
|
2982
|
+
const errorMessage = `[Data transfer] Error in ${streamType} read stream: ${message}`;
|
|
2964
2983
|
const formattedError = {
|
|
2965
2984
|
message: errorMessage,
|
|
2966
2985
|
stack,
|
|
@@ -3132,17 +3151,6 @@ const connectToWebsocket = (address, options, diagnostics)=>{
|
|
|
3132
3151
|
const trimTrailingSlash = (input)=>{
|
|
3133
3152
|
return input.replace(/\/$/, '');
|
|
3134
3153
|
};
|
|
3135
|
-
const wait = (ms)=>{
|
|
3136
|
-
return new Promise((resolve)=>{
|
|
3137
|
-
setTimeout(resolve, ms);
|
|
3138
|
-
});
|
|
3139
|
-
};
|
|
3140
|
-
const waitUntil = async (test, interval)=>{
|
|
3141
|
-
while(!test()){
|
|
3142
|
-
await wait(interval);
|
|
3143
|
-
}
|
|
3144
|
-
return Promise.resolve();
|
|
3145
|
-
};
|
|
3146
3154
|
|
|
3147
3155
|
const TRANSFER_PATH = '/transfer/runner';
|
|
3148
3156
|
const TRANSFER_METHODS = [
|
|
@@ -3563,6 +3571,17 @@ class RemoteStrapiSourceProvider {
|
|
|
3563
3571
|
});
|
|
3564
3572
|
// Init the asset map
|
|
3565
3573
|
const assets = {};
|
|
3574
|
+
// Watch for stalled assets; if we don't receive a chunk within timeout, abort transfer
|
|
3575
|
+
const resetTimeout = (assetID)=>{
|
|
3576
|
+
if (assets[assetID].timeout) {
|
|
3577
|
+
clearTimeout(assets[assetID].timeout);
|
|
3578
|
+
}
|
|
3579
|
+
assets[assetID].timeout = setTimeout(()=>{
|
|
3580
|
+
_class_private_field_loose_base$2(this, _reportInfo$2)[_reportInfo$2](`Asset ${assetID} transfer stalled, aborting.`);
|
|
3581
|
+
assets[assetID].status = 'errored';
|
|
3582
|
+
assets[assetID].stream.destroy(new Error(`Asset ${assetID} transfer timed out`));
|
|
3583
|
+
}, this.options.streamTimeout);
|
|
3584
|
+
};
|
|
3566
3585
|
stream/**
|
|
3567
3586
|
* Process a payload of many transfer assets and performs the following tasks:
|
|
3568
3587
|
* - Start: creates a stream for new assets.
|
|
@@ -3573,56 +3592,46 @@ class RemoteStrapiSourceProvider {
|
|
|
3573
3592
|
const { action, assetID } = item;
|
|
3574
3593
|
// Creates the stream to send the incoming asset through
|
|
3575
3594
|
if (action === 'start') {
|
|
3576
|
-
//
|
|
3595
|
+
// if a transfer has already been started for the same asset ID, something is wrong
|
|
3577
3596
|
if (assets[assetID]) {
|
|
3578
|
-
|
|
3597
|
+
throw new Error(`Asset ${assetID} already started`);
|
|
3579
3598
|
}
|
|
3599
|
+
_class_private_field_loose_base$2(this, _reportInfo$2)[_reportInfo$2](`Asset ${assetID} starting`);
|
|
3580
3600
|
// Register the asset
|
|
3581
3601
|
assets[assetID] = {
|
|
3582
3602
|
...item.data,
|
|
3583
3603
|
stream: new stream$1.PassThrough(),
|
|
3584
|
-
status: '
|
|
3604
|
+
status: 'ok',
|
|
3585
3605
|
queue: []
|
|
3586
3606
|
};
|
|
3607
|
+
resetTimeout(assetID);
|
|
3587
3608
|
// Connect the individual asset stream to the main asset stage stream
|
|
3588
3609
|
// Note: nothing is transferred until data chunks are fed to the asset stream
|
|
3589
3610
|
await this.writeAsync(pass, assets[assetID]);
|
|
3590
|
-
} else if (action === 'stream') {
|
|
3591
|
-
// If the asset hasn't been registered, or if it's been closed already,
|
|
3611
|
+
} else if (action === 'stream' || action === 'end') {
|
|
3612
|
+
// If the asset hasn't been registered, or if it's been closed already, something is wrong
|
|
3592
3613
|
if (!assets[assetID]) {
|
|
3593
|
-
|
|
3614
|
+
throw new Error(`No id matching ${assetID} for stream action`);
|
|
3594
3615
|
}
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
// The resource is busy, queue the current chunk so that it gets transferred as soon as possible
|
|
3601
|
-
case 'busy':
|
|
3602
|
-
assets[assetID].queue.push(item);
|
|
3603
|
-
break;
|
|
3616
|
+
// On every action, reset the timeout timer
|
|
3617
|
+
if (action === 'stream') {
|
|
3618
|
+
resetTimeout(assetID);
|
|
3619
|
+
} else {
|
|
3620
|
+
clearTimeout(assets[assetID].timeout);
|
|
3604
3621
|
}
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
if (!assets[assetID]) {
|
|
3608
|
-
continue;
|
|
3622
|
+
if (assets[assetID].status === 'closed') {
|
|
3623
|
+
throw new Error(`Asset ${assetID} is closed`);
|
|
3609
3624
|
}
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
waitUntil(()=>assets[assetID].status !== 'busy', 100),
|
|
3621
|
-
// Or: if the last chunks are still not processed after ten seconds
|
|
3622
|
-
wait(10000)
|
|
3623
|
-
]);
|
|
3624
|
-
await closeAssetStream(assetID);
|
|
3625
|
-
break;
|
|
3625
|
+
assets[assetID].queue.push(item);
|
|
3626
|
+
}
|
|
3627
|
+
}
|
|
3628
|
+
// each new payload will start new processQueue calls, which may cause some extra calls
|
|
3629
|
+
// it's essentially saying "start processing this asset again, I added more data to the queue"
|
|
3630
|
+
for(const assetID in assets){
|
|
3631
|
+
if (Object.prototype.hasOwnProperty.call(assets, assetID)) {
|
|
3632
|
+
const asset = assets[assetID];
|
|
3633
|
+
if (asset.queue?.length > 0) {
|
|
3634
|
+
await processQueue(assetID);
|
|
3626
3635
|
}
|
|
3627
3636
|
}
|
|
3628
3637
|
}
|
|
@@ -3630,38 +3639,48 @@ class RemoteStrapiSourceProvider {
|
|
|
3630
3639
|
pass.end();
|
|
3631
3640
|
});
|
|
3632
3641
|
/**
|
|
3633
|
-
*
|
|
3634
|
-
|
|
3642
|
+
* Start processing the queue for a given assetID
|
|
3643
|
+
*
|
|
3644
|
+
* Even though this is a loop that attempts to process the entire queue, it is safe to call this more than once
|
|
3645
|
+
* for the same asset id because the queue is shared globally, the items are shifted off, and immediately written
|
|
3646
|
+
*/ const processQueue = async (id)=>{
|
|
3635
3647
|
if (!assets[id]) {
|
|
3636
3648
|
throw new Error(`Failed to write asset chunk for "${id}". Asset not found.`);
|
|
3637
3649
|
}
|
|
3638
|
-
const
|
|
3639
|
-
|
|
3650
|
+
const asset = assets[id];
|
|
3651
|
+
const { status: currentStatus } = asset;
|
|
3652
|
+
if ([
|
|
3653
|
+
'closed',
|
|
3654
|
+
'errored'
|
|
3655
|
+
].includes(currentStatus)) {
|
|
3640
3656
|
throw new Error(`Failed to write asset chunk for "${id}". The asset is currently "${currentStatus}"`);
|
|
3641
3657
|
}
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3658
|
+
while(asset.queue.length > 0){
|
|
3659
|
+
const data = asset.queue.shift();
|
|
3660
|
+
if (!data) {
|
|
3661
|
+
throw new Error(`Invalid chunk found for ${id}`);
|
|
3662
|
+
}
|
|
3663
|
+
try {
|
|
3664
|
+
// if this is an end chunk, close the asset stream
|
|
3665
|
+
if (data.action === 'end') {
|
|
3666
|
+
_class_private_field_loose_base$2(this, _reportInfo$2)[_reportInfo$2](`Ending asset stream for ${id}`);
|
|
3667
|
+
await closeAssetStream(id);
|
|
3668
|
+
break; // Exit the loop after closing the stream
|
|
3669
|
+
}
|
|
3670
|
+
// Save the current chunk
|
|
3671
|
+
await writeChunkToStream(id, data);
|
|
3672
|
+
} catch {
|
|
3673
|
+
if (!assets[id]) {
|
|
3674
|
+
throw new Error(`No id matching ${id} for writeAssetChunk`);
|
|
3675
|
+
}
|
|
3653
3676
|
}
|
|
3654
|
-
// Unlock the asset
|
|
3655
|
-
assets[id].status = 'idle';
|
|
3656
|
-
} catch {
|
|
3657
|
-
assets[id].status = 'errored';
|
|
3658
3677
|
}
|
|
3659
3678
|
};
|
|
3660
3679
|
/**
|
|
3661
3680
|
* Writes a chunk of data to the asset's stream.
|
|
3662
3681
|
*
|
|
3663
3682
|
* Only check if the targeted asset exists, no other validation is done.
|
|
3664
|
-
*/ const
|
|
3683
|
+
*/ const writeChunkToStream = async (id, data)=>{
|
|
3665
3684
|
const asset = assets[id];
|
|
3666
3685
|
if (!asset) {
|
|
3667
3686
|
throw new Error(`Failed to write asset chunk for "${id}". Asset not found.`);
|
|
@@ -3682,9 +3701,11 @@ class RemoteStrapiSourceProvider {
|
|
|
3682
3701
|
await new Promise((resolve, reject)=>{
|
|
3683
3702
|
const { stream } = assets[id];
|
|
3684
3703
|
stream.on('close', ()=>{
|
|
3685
|
-
delete assets[id];
|
|
3686
3704
|
resolve();
|
|
3687
|
-
}).on('error',
|
|
3705
|
+
}).on('error', (e)=>{
|
|
3706
|
+
assets[id].status = 'errored';
|
|
3707
|
+
reject(new Error(`Failed to close asset "${id}". Asset stream error: ${e.toString()}`));
|
|
3708
|
+
}).end();
|
|
3688
3709
|
});
|
|
3689
3710
|
};
|
|
3690
3711
|
return pass;
|
|
@@ -3799,6 +3820,9 @@ class RemoteStrapiSourceProvider {
|
|
|
3799
3820
|
});
|
|
3800
3821
|
this.name = 'source::remote-strapi';
|
|
3801
3822
|
this.type = 'source';
|
|
3823
|
+
this.defaultOptions = {
|
|
3824
|
+
streamTimeout: 15000
|
|
3825
|
+
};
|
|
3802
3826
|
this.writeAsync = (stream, data)=>{
|
|
3803
3827
|
return new Promise((resolve, reject)=>{
|
|
3804
3828
|
stream.write(data, (error)=>{
|
|
@@ -3809,7 +3833,10 @@ class RemoteStrapiSourceProvider {
|
|
|
3809
3833
|
});
|
|
3810
3834
|
});
|
|
3811
3835
|
};
|
|
3812
|
-
this.options =
|
|
3836
|
+
this.options = {
|
|
3837
|
+
...this.defaultOptions,
|
|
3838
|
+
...options
|
|
3839
|
+
};
|
|
3813
3840
|
this.ws = null;
|
|
3814
3841
|
this.dispatcher = null;
|
|
3815
3842
|
}
|
|
@@ -4742,6 +4769,18 @@ const createPullController = handlerControllerFactory((proto)=>({
|
|
|
4742
4769
|
kind: 'warning'
|
|
4743
4770
|
});
|
|
4744
4771
|
},
|
|
4772
|
+
onError (error) {
|
|
4773
|
+
this.diagnostics?.report({
|
|
4774
|
+
details: {
|
|
4775
|
+
message: error.message,
|
|
4776
|
+
error,
|
|
4777
|
+
createdAt: new Date(),
|
|
4778
|
+
name: error.name,
|
|
4779
|
+
severity: 'fatal'
|
|
4780
|
+
},
|
|
4781
|
+
kind: 'error'
|
|
4782
|
+
});
|
|
4783
|
+
},
|
|
4745
4784
|
assertValidTransferAction (action) {
|
|
4746
4785
|
// Abstract the constant to string[] to allow looser check on the given action
|
|
4747
4786
|
const validActions = VALID_TRANSFER_ACTIONS;
|
|
@@ -4815,6 +4854,15 @@ const createPullController = handlerControllerFactory((proto)=>({
|
|
|
4815
4854
|
let batch = [];
|
|
4816
4855
|
const stream = this.streams?.[stage];
|
|
4817
4856
|
const batchLength = ()=>Buffer.byteLength(JSON.stringify(batch));
|
|
4857
|
+
const maybeConfirm = async (data)=>{
|
|
4858
|
+
try {
|
|
4859
|
+
await this.confirm(data);
|
|
4860
|
+
} catch (error) {
|
|
4861
|
+
// Handle the error, log it, or take other appropriate actions
|
|
4862
|
+
strapi?.log.error(`[Data transfer] Message confirmation failed: ${error?.message}`);
|
|
4863
|
+
this.onError(error);
|
|
4864
|
+
}
|
|
4865
|
+
};
|
|
4818
4866
|
const sendBatch = async ()=>{
|
|
4819
4867
|
await this.confirm({
|
|
4820
4868
|
type: 'transfer',
|
|
@@ -4823,6 +4871,7 @@ const createPullController = handlerControllerFactory((proto)=>({
|
|
|
4823
4871
|
error: null,
|
|
4824
4872
|
id
|
|
4825
4873
|
});
|
|
4874
|
+
batch = [];
|
|
4826
4875
|
};
|
|
4827
4876
|
if (!stream) {
|
|
4828
4877
|
throw new ProviderTransferError(`No available stream found for ${stage}`);
|
|
@@ -4833,7 +4882,6 @@ const createPullController = handlerControllerFactory((proto)=>({
|
|
|
4833
4882
|
batch.push(chunk);
|
|
4834
4883
|
if (batchLength() >= batchSize) {
|
|
4835
4884
|
await sendBatch();
|
|
4836
|
-
batch = [];
|
|
4837
4885
|
}
|
|
4838
4886
|
} else {
|
|
4839
4887
|
await this.confirm({
|
|
@@ -4849,7 +4897,6 @@ const createPullController = handlerControllerFactory((proto)=>({
|
|
|
4849
4897
|
}
|
|
4850
4898
|
if (batch.length > 0 && stage !== 'assets') {
|
|
4851
4899
|
await sendBatch();
|
|
4852
|
-
batch = [];
|
|
4853
4900
|
}
|
|
4854
4901
|
await this.confirm({
|
|
4855
4902
|
type: 'transfer',
|
|
@@ -4859,7 +4906,8 @@ const createPullController = handlerControllerFactory((proto)=>({
|
|
|
4859
4906
|
id
|
|
4860
4907
|
});
|
|
4861
4908
|
} catch (e) {
|
|
4862
|
-
|
|
4909
|
+
// TODO: if this confirm fails, can we abort the whole transfer?
|
|
4910
|
+
await maybeConfirm({
|
|
4863
4911
|
type: 'transfer',
|
|
4864
4912
|
data: null,
|
|
4865
4913
|
ended: true,
|
|
@@ -4908,7 +4956,7 @@ const createPullController = handlerControllerFactory((proto)=>({
|
|
|
4908
4956
|
};
|
|
4909
4957
|
const BATCH_MAX_SIZE = 1024 * 1024; // 1MB
|
|
4910
4958
|
if (!assets) {
|
|
4911
|
-
throw new Error('
|
|
4959
|
+
throw new Error('Assets read stream could not be created');
|
|
4912
4960
|
}
|
|
4913
4961
|
/**
|
|
4914
4962
|
* Generates batches of 1MB of data from the assets stream to avoid
|