@milaboratories/pl-drivers 1.2.35 → 1.3.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/clients/download.d.ts +6 -2
- package/dist/clients/download.d.ts.map +1 -1
- package/dist/clients/helpers.d.ts +2 -2
- package/dist/clients/helpers.d.ts.map +1 -1
- package/dist/drivers/helpers/ls_list_entry.d.ts +8 -13
- package/dist/drivers/helpers/ls_list_entry.d.ts.map +1 -1
- package/dist/drivers/helpers/ls_storage_entry.d.ts +6 -8
- package/dist/drivers/helpers/ls_storage_entry.d.ts.map +1 -1
- package/dist/drivers/ls.d.ts +40 -12
- package/dist/drivers/ls.d.ts.map +1 -1
- package/dist/drivers/types.d.ts +80 -0
- package/dist/drivers/types.d.ts.map +1 -0
- package/dist/drivers/upload.d.ts +18 -26
- package/dist/drivers/upload.d.ts.map +1 -1
- package/dist/helpers/validate.d.ts +2 -0
- package/dist/helpers/validate.d.ts.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +820 -792
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -5
- package/src/clients/download.test.ts +17 -15
- package/src/clients/download.ts +34 -21
- package/src/clients/helpers.ts +13 -43
- package/src/drivers/download_blob.test.ts +4 -4
- package/src/drivers/helpers/ls_list_entry.test.ts +5 -7
- package/src/drivers/helpers/ls_list_entry.ts +31 -36
- package/src/drivers/helpers/ls_storage_entry.ts +18 -62
- package/src/drivers/logs.test.ts +3 -3
- package/src/drivers/ls.test.ts +112 -30
- package/src/drivers/ls.ts +233 -83
- package/src/drivers/types.ts +41 -0
- package/src/drivers/upload.test.ts +43 -82
- package/src/drivers/upload.ts +59 -111
- package/src/helpers/validate.ts +6 -0
- package/src/index.ts +3 -0
|
@@ -1,35 +1,27 @@
|
|
|
1
1
|
import {
|
|
2
|
-
FieldId,
|
|
3
2
|
isNotNullResourceId,
|
|
4
3
|
PlTransaction,
|
|
5
4
|
PollTxAccessor,
|
|
6
5
|
ResourceId,
|
|
7
6
|
TestHelpers
|
|
8
7
|
} from '@milaboratories/pl-client';
|
|
9
|
-
import {
|
|
10
|
-
ConsoleLoggerAdapter,
|
|
11
|
-
HmacSha256Signer,
|
|
12
|
-
Signer
|
|
13
|
-
} from '@milaboratories/ts-helpers';
|
|
14
|
-
import * as fs from 'node:fs';
|
|
8
|
+
import { ConsoleLoggerAdapter, HmacSha256Signer, Signer } from '@milaboratories/ts-helpers';
|
|
15
9
|
import * as fsp from 'node:fs/promises';
|
|
16
10
|
import * as os from 'node:os';
|
|
17
11
|
import * as path from 'node:path';
|
|
18
12
|
import { PlClient } from '@milaboratories/pl-client';
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
|
|
22
|
-
createUploadBlobClient,
|
|
23
|
-
createUploadProgressClient
|
|
24
|
-
} from '../clients/helpers';
|
|
25
|
-
import { Writable, Readable } from 'node:stream';
|
|
13
|
+
import { ImportResourceSnapshot, makeBlobImportSnapshot, UploadDriver } from './upload';
|
|
14
|
+
import { createUploadBlobClient, createUploadProgressClient } from '../clients/helpers';
|
|
15
|
+
|
|
26
16
|
import { test, expect } from '@jest/globals';
|
|
17
|
+
import { SynchronizedTreeState } from '@milaboratories/pl-tree';
|
|
18
|
+
import { Computable } from '@milaboratories/computable';
|
|
27
19
|
|
|
28
20
|
test('upload a blob', async () => {
|
|
29
21
|
await withTest(async ({ client, uploader, signer }: TestArg) => {
|
|
30
22
|
const stats = await writeFile('42', signer);
|
|
31
23
|
const uploadId = await createBlobUpload(client, stats);
|
|
32
|
-
const handleRes = await
|
|
24
|
+
const handleRes = await getSnapshot(client, uploadId);
|
|
33
25
|
|
|
34
26
|
const c = uploader.getProgressId(handleRes);
|
|
35
27
|
|
|
@@ -61,7 +53,7 @@ test('upload a big blob', async () => {
|
|
|
61
53
|
await withTest(async ({ client, uploader, signer }: TestArg) => {
|
|
62
54
|
const stats = await writeFile(hugeString, signer);
|
|
63
55
|
const uploadId = await createBlobUpload(client, stats);
|
|
64
|
-
const handleRes = await
|
|
56
|
+
const handleRes = await getSnapshot(client, uploadId);
|
|
65
57
|
|
|
66
58
|
const c = uploader.getProgressId(handleRes);
|
|
67
59
|
|
|
@@ -113,7 +105,7 @@ test.skip('upload a very big blob', async () => {
|
|
|
113
105
|
'/home/snyssfx/Downloads/Kung Fu Hustle (2004) Open Matte 1080p.mkv'
|
|
114
106
|
);
|
|
115
107
|
const uploadId = await createBlobUpload(client, stats);
|
|
116
|
-
const handleRes = await
|
|
108
|
+
const handleRes = await getSnapshot(client, uploadId);
|
|
117
109
|
|
|
118
110
|
const c = uploader.getProgressId(handleRes);
|
|
119
111
|
|
|
@@ -139,7 +131,7 @@ test('upload a blob with wrong modification time', async () => {
|
|
|
139
131
|
const stats = await writeFile('42', signer);
|
|
140
132
|
stats.mtime -= 1000n;
|
|
141
133
|
const uploadId = await createBlobUpload(client, stats);
|
|
142
|
-
const handleRes = await
|
|
134
|
+
const handleRes = await getSnapshot(client, uploadId);
|
|
143
135
|
|
|
144
136
|
const c = uploader.getProgressId(handleRes);
|
|
145
137
|
|
|
@@ -162,7 +154,7 @@ test('upload a duplicate blob', async () => {
|
|
|
162
154
|
await withTest(async ({ client, uploader, signer }: TestArg) => {
|
|
163
155
|
const stats = await writeFile('42', signer);
|
|
164
156
|
const uploadId = await createBlobUpload(client, stats);
|
|
165
|
-
const handleRes = await
|
|
157
|
+
const handleRes = await getSnapshot(client, uploadId);
|
|
166
158
|
|
|
167
159
|
const cOrig = uploader.getProgressId(handleRes);
|
|
168
160
|
const cDupl = uploader.getProgressId(handleRes);
|
|
@@ -215,9 +207,7 @@ test('upload lots of duplicate blobs concurrently', async () => {
|
|
|
215
207
|
|
|
216
208
|
const { uploadIds } = await createMapOfUploads(client, n, settings);
|
|
217
209
|
|
|
218
|
-
const handles = await Promise.all(
|
|
219
|
-
uploadIds.map((id) => getHandleField(client, id))
|
|
220
|
-
);
|
|
210
|
+
const handles = await Promise.all(uploadIds.map((id) => getSnapshot(client, id)));
|
|
221
211
|
const computables = handles.map((handle) => uploader.getProgressId(handle));
|
|
222
212
|
|
|
223
213
|
for (const c of computables) {
|
|
@@ -247,7 +237,7 @@ test('index a blob', async () => {
|
|
|
247
237
|
'./another_answer_to_the_ultimate_question.txt',
|
|
248
238
|
'library'
|
|
249
239
|
);
|
|
250
|
-
const handleRes = await
|
|
240
|
+
const handleRes = await getSnapshot(client, uploadId);
|
|
251
241
|
|
|
252
242
|
const c = uploader.getProgressId(handleRes);
|
|
253
243
|
|
|
@@ -302,8 +292,7 @@ async function writeFile(
|
|
|
302
292
|
tmpDir?: string,
|
|
303
293
|
fileName: string = 'testUploadABlob.txt'
|
|
304
294
|
): Promise<FileStat> {
|
|
305
|
-
if (tmpDir == undefined)
|
|
306
|
-
tmpDir = await fsp.mkdtemp(path.join(os.tmpdir(), 'test'));
|
|
295
|
+
if (tmpDir == undefined) tmpDir = await fsp.mkdtemp(path.join(os.tmpdir(), 'test'));
|
|
307
296
|
|
|
308
297
|
const fPath = path.join(tmpDir, fileName);
|
|
309
298
|
|
|
@@ -321,11 +310,7 @@ async function getFileStats(signer: Signer, fPath: string): Promise<FileStat> {
|
|
|
321
310
|
return { fPath, mtime, fileSignature, size: stats.size };
|
|
322
311
|
}
|
|
323
312
|
|
|
324
|
-
async function createMapOfUploads(
|
|
325
|
-
c: PlClient,
|
|
326
|
-
n: number,
|
|
327
|
-
settings: FileStat[]
|
|
328
|
-
) {
|
|
313
|
+
async function createMapOfUploads(c: PlClient, n: number, settings: FileStat[]) {
|
|
329
314
|
return await c.withWriteTx(
|
|
330
315
|
'UploaderCreateMapOfUploads',
|
|
331
316
|
async (tx: PlTransaction) => {
|
|
@@ -340,11 +325,7 @@ async function createMapOfUploads(
|
|
|
340
325
|
tx.setField(fId, uploadId);
|
|
341
326
|
}
|
|
342
327
|
|
|
343
|
-
tx.createField(
|
|
344
|
-
{ resourceId: c.clientRoot, fieldName: 'project1' },
|
|
345
|
-
'Dynamic',
|
|
346
|
-
mapId
|
|
347
|
-
);
|
|
328
|
+
tx.createField({ resourceId: c.clientRoot, fieldName: 'project1' }, 'Dynamic', mapId);
|
|
348
329
|
await tx.commit();
|
|
349
330
|
|
|
350
331
|
return {
|
|
@@ -356,20 +337,13 @@ async function createMapOfUploads(
|
|
|
356
337
|
);
|
|
357
338
|
}
|
|
358
339
|
|
|
359
|
-
async function createBlobUpload(
|
|
360
|
-
c: PlClient,
|
|
361
|
-
stat: FileStat
|
|
362
|
-
): Promise<ResourceId> {
|
|
340
|
+
async function createBlobUpload(c: PlClient, stat: FileStat): Promise<ResourceId> {
|
|
363
341
|
return await c.withWriteTx(
|
|
364
342
|
'UploadDriverCreateTest',
|
|
365
343
|
async (tx: PlTransaction) => {
|
|
366
344
|
const uploadId = await createBlobUploadTx(tx, stat);
|
|
367
345
|
|
|
368
|
-
tx.createField(
|
|
369
|
-
{ resourceId: c.clientRoot, fieldName: 'project1' },
|
|
370
|
-
'Dynamic',
|
|
371
|
-
uploadId
|
|
372
|
-
);
|
|
346
|
+
tx.createField({ resourceId: c.clientRoot, fieldName: 'project1' }, 'Dynamic', uploadId);
|
|
373
347
|
await tx.commit();
|
|
374
348
|
|
|
375
349
|
return uploadId;
|
|
@@ -378,10 +352,7 @@ async function createBlobUpload(
|
|
|
378
352
|
);
|
|
379
353
|
}
|
|
380
354
|
|
|
381
|
-
async function createBlobUploadTx(
|
|
382
|
-
tx: PlTransaction,
|
|
383
|
-
stat: FileStat
|
|
384
|
-
): Promise<ResourceId> {
|
|
355
|
+
async function createBlobUploadTx(tx: PlTransaction, stat: FileStat): Promise<ResourceId> {
|
|
385
356
|
const settings = {
|
|
386
357
|
modificationTime: stat.mtime.toString(),
|
|
387
358
|
localPath: stat.fPath,
|
|
@@ -394,11 +365,7 @@ async function createBlobUploadTx(
|
|
|
394
365
|
return await upload.globalId;
|
|
395
366
|
}
|
|
396
367
|
|
|
397
|
-
async function createBlobIndex(
|
|
398
|
-
c: PlClient,
|
|
399
|
-
path: string,
|
|
400
|
-
storageId: string
|
|
401
|
-
): Promise<ResourceId> {
|
|
368
|
+
async function createBlobIndex(c: PlClient, path: string, storageId: string): Promise<ResourceId> {
|
|
402
369
|
return await c.withWriteTx(
|
|
403
370
|
'UploadDriverCreateTest',
|
|
404
371
|
async (tx: PlTransaction) => {
|
|
@@ -407,10 +374,7 @@ async function createBlobIndex(
|
|
|
407
374
|
path: path
|
|
408
375
|
};
|
|
409
376
|
const data = new TextEncoder().encode(JSON.stringify(settings));
|
|
410
|
-
const importInternal = tx.createStruct(
|
|
411
|
-
{ name: 'BlobImportInternal', version: '1' },
|
|
412
|
-
data
|
|
413
|
-
);
|
|
377
|
+
const importInternal = tx.createStruct({ name: 'BlobImportInternal', version: '1' }, data);
|
|
414
378
|
tx.createField(
|
|
415
379
|
{ resourceId: c.clientRoot, fieldName: 'project1' },
|
|
416
380
|
'Dynamic',
|
|
@@ -424,32 +388,29 @@ async function createBlobIndex(
|
|
|
424
388
|
);
|
|
425
389
|
}
|
|
426
390
|
|
|
427
|
-
async function
|
|
391
|
+
async function getSnapshot(
|
|
428
392
|
client: PlClient,
|
|
429
393
|
uploadId: ResourceId
|
|
430
|
-
): Promise<
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
const blob = handle.data.fields.find((f) => f.name == 'blob')?.value;
|
|
436
|
-
const incarnation = handle.data.fields.find(
|
|
437
|
-
(f) => f.name == 'incarnation'
|
|
438
|
-
)?.value;
|
|
439
|
-
|
|
440
|
-
const fields = {
|
|
441
|
-
blob: undefined as ResourceId | undefined,
|
|
442
|
-
incarnation: undefined as ResourceId | undefined
|
|
443
|
-
};
|
|
444
|
-
if (blob != undefined && isNotNullResourceId(blob)) fields.blob = blob;
|
|
445
|
-
if (incarnation != undefined && isNotNullResourceId(incarnation))
|
|
446
|
-
fields.incarnation = incarnation;
|
|
447
|
-
|
|
448
|
-
return {
|
|
449
|
-
...handle.data,
|
|
450
|
-
data: JSON.parse(handle.data.data!.toString()) as UploadOpts,
|
|
451
|
-
fields,
|
|
452
|
-
kv: undefined
|
|
453
|
-
};
|
|
394
|
+
): Promise<ImportResourceSnapshot> {
|
|
395
|
+
const tree = await SynchronizedTreeState.init(client, uploadId, {
|
|
396
|
+
stopPollingDelay: 600,
|
|
397
|
+
pollingInterval: 300
|
|
454
398
|
});
|
|
399
|
+
try {
|
|
400
|
+
const computable = Computable.make((ctx) => {
|
|
401
|
+
const handle = ctx
|
|
402
|
+
.accessor(tree.entry())
|
|
403
|
+
.node()
|
|
404
|
+
.traverse({ field: 'handle', assertFieldType: 'Output' });
|
|
405
|
+
if (!handle) return undefined;
|
|
406
|
+
else return makeBlobImportSnapshot(handle, ctx);
|
|
407
|
+
}).withStableType();
|
|
408
|
+
while (true) {
|
|
409
|
+
const value = await computable.getValue();
|
|
410
|
+
if (value !== undefined) return value;
|
|
411
|
+
await computable.awaitChange();
|
|
412
|
+
}
|
|
413
|
+
} finally {
|
|
414
|
+
await tree.terminate();
|
|
415
|
+
}
|
|
455
416
|
}
|
package/src/drivers/upload.ts
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
|
-
import {
|
|
3
|
-
ResourceId,
|
|
4
|
-
stringifyWithResourceId
|
|
5
|
-
} from '@milaboratories/pl-client';
|
|
2
|
+
import { ResourceId, stringifyWithResourceId } from '@milaboratories/pl-client';
|
|
6
3
|
import {
|
|
7
4
|
Watcher,
|
|
8
5
|
ChangeSource,
|
|
@@ -19,47 +16,55 @@ import {
|
|
|
19
16
|
} from '@milaboratories/ts-helpers';
|
|
20
17
|
import * as sdk from '@milaboratories/pl-model-common';
|
|
21
18
|
import { ProgressStatus, ClientProgress } from '../clients/progress';
|
|
22
|
-
import {
|
|
23
|
-
ClientUpload,
|
|
24
|
-
MTimeError,
|
|
25
|
-
NoFileForUploading,
|
|
26
|
-
UnexpectedEOF
|
|
27
|
-
} from '../clients/upload';
|
|
19
|
+
import { ClientUpload, MTimeError, NoFileForUploading, UnexpectedEOF } from '../clients/upload';
|
|
28
20
|
import {
|
|
29
21
|
InferSnapshot,
|
|
30
22
|
isPlTreeEntry,
|
|
23
|
+
isPlTreeEntryAccessor,
|
|
31
24
|
makeResourceSnapshot,
|
|
32
25
|
PlTreeEntry,
|
|
26
|
+
PlTreeEntryAccessor,
|
|
27
|
+
PlTreeNodeAccessor,
|
|
33
28
|
rsSchema
|
|
34
29
|
} from '@milaboratories/pl-tree';
|
|
35
30
|
import { scheduler } from 'node:timers/promises';
|
|
36
31
|
import { PollingOps } from './helpers/polling_ops';
|
|
37
|
-
import {
|
|
32
|
+
import { ImportFileHandleUploadData } from './types';
|
|
38
33
|
|
|
39
34
|
/** Options from BlobUpload resource that have to be passed to getProgress. */
|
|
40
35
|
|
|
41
|
-
const UploadOptsSchema = z.object({
|
|
42
|
-
localPath: z.string(),
|
|
43
|
-
pathSignature: z.string(),
|
|
44
|
-
modificationTime: z.string()
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
const ImportOptsSchema = z.union([UploadOptsSchema, z.object({})]);
|
|
48
|
-
|
|
49
|
-
export type UploadOpts = z.infer<typeof UploadOptsSchema>;
|
|
50
|
-
|
|
51
36
|
/** ResourceSnapshot that can be passed to GetProgressID */
|
|
52
37
|
export const UploadResourceSnapshot = rsSchema({
|
|
53
|
-
data:
|
|
38
|
+
data: ImportFileHandleUploadData,
|
|
39
|
+
fields: {
|
|
40
|
+
blob: false
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
export const IndexResourceSnapshot = rsSchema({
|
|
54
45
|
fields: {
|
|
55
|
-
|
|
56
|
-
incarnation: false // for BlobIndex
|
|
46
|
+
incarnation: false
|
|
57
47
|
}
|
|
58
48
|
});
|
|
59
49
|
|
|
60
|
-
export type UploadResourceSnapshot = InferSnapshot<
|
|
61
|
-
|
|
62
|
-
|
|
50
|
+
export type UploadResourceSnapshot = InferSnapshot<typeof UploadResourceSnapshot>;
|
|
51
|
+
export type IndexResourceSnapshot = InferSnapshot<typeof IndexResourceSnapshot>;
|
|
52
|
+
|
|
53
|
+
export type ImportResourceSnapshot = UploadResourceSnapshot | IndexResourceSnapshot;
|
|
54
|
+
|
|
55
|
+
export function makeBlobImportSnapshot(
|
|
56
|
+
entryOrAccessor: PlTreeEntry | PlTreeNodeAccessor | PlTreeEntryAccessor,
|
|
57
|
+
ctx: ComputableCtx
|
|
58
|
+
): ImportResourceSnapshot {
|
|
59
|
+
const node = isPlTreeEntry(entryOrAccessor)
|
|
60
|
+
? ctx.accessor(entryOrAccessor).node()
|
|
61
|
+
: isPlTreeEntryAccessor(entryOrAccessor)
|
|
62
|
+
? entryOrAccessor.node()
|
|
63
|
+
: entryOrAccessor;
|
|
64
|
+
if (node.resourceType.name.startsWith('BlobUpload'))
|
|
65
|
+
return makeResourceSnapshot(node, UploadResourceSnapshot);
|
|
66
|
+
else return makeResourceSnapshot(node, IndexResourceSnapshot);
|
|
67
|
+
}
|
|
63
68
|
|
|
64
69
|
export type UploadDriverOps = PollingOps & {
|
|
65
70
|
/** How much parts of a file can be multipart-uploaded to S3 at once. */
|
|
@@ -112,22 +117,21 @@ export class UploadDriver {
|
|
|
112
117
|
|
|
113
118
|
/** Returns a progress id and schedules an upload task if it's necessary. */
|
|
114
119
|
getProgressId(
|
|
115
|
-
|
|
120
|
+
handleResource: ImportResourceSnapshot | PlTreeEntry
|
|
116
121
|
): Computable<sdk.ImportProgress>;
|
|
117
122
|
getProgressId(
|
|
118
|
-
|
|
123
|
+
handleResource: ImportResourceSnapshot | PlTreeEntry,
|
|
119
124
|
ctx: ComputableCtx
|
|
120
125
|
): sdk.ImportProgress;
|
|
121
126
|
getProgressId(
|
|
122
|
-
|
|
127
|
+
handleResource: ImportResourceSnapshot | PlTreeEntry,
|
|
123
128
|
ctx?: ComputableCtx
|
|
124
129
|
): Computable<sdk.ImportProgress> | sdk.ImportProgress {
|
|
125
|
-
if (ctx == undefined)
|
|
126
|
-
return Computable.make((ctx) => this.getProgressId(res, ctx));
|
|
130
|
+
if (ctx == undefined) return Computable.make((ctx) => this.getProgressId(handleResource, ctx));
|
|
127
131
|
|
|
128
|
-
const rInfo:
|
|
129
|
-
?
|
|
130
|
-
:
|
|
132
|
+
const rInfo: ImportResourceSnapshot = isPlTreeEntry(handleResource)
|
|
133
|
+
? makeBlobImportSnapshot(handleResource, ctx)
|
|
134
|
+
: handleResource;
|
|
131
135
|
|
|
132
136
|
const callerId = randomUUID();
|
|
133
137
|
ctx.attacheHooks(this.hooks);
|
|
@@ -135,9 +139,7 @@ export class UploadDriver {
|
|
|
135
139
|
|
|
136
140
|
const result = this.getProgressIdNoCtx(ctx.watcher, rInfo, callerId);
|
|
137
141
|
if (!isProgressStable(result)) {
|
|
138
|
-
ctx.markUnstable(
|
|
139
|
-
`upload/index progress was got, but it's not stable: ${result}`
|
|
140
|
-
);
|
|
142
|
+
ctx.markUnstable(`upload/index progress was got, but it's not stable: ${result}`);
|
|
141
143
|
}
|
|
142
144
|
|
|
143
145
|
return result;
|
|
@@ -145,11 +147,11 @@ export class UploadDriver {
|
|
|
145
147
|
|
|
146
148
|
private getProgressIdNoCtx(
|
|
147
149
|
w: Watcher,
|
|
148
|
-
res:
|
|
150
|
+
res: ImportResourceSnapshot,
|
|
149
151
|
callerId: string
|
|
150
152
|
): sdk.ImportProgress {
|
|
151
153
|
const blobExists =
|
|
152
|
-
res.fields.blob
|
|
154
|
+
'blob' in res.fields ? res.fields.blob !== undefined : res.fields.incarnation !== undefined;
|
|
153
155
|
|
|
154
156
|
const value = this.idToProgress.get(res.id);
|
|
155
157
|
|
|
@@ -195,10 +197,7 @@ export class UploadDriver {
|
|
|
195
197
|
|
|
196
198
|
private scheduledOnNextState: ScheduledRefresh[] = [];
|
|
197
199
|
|
|
198
|
-
private scheduleOnNextState(
|
|
199
|
-
resolve: () => void,
|
|
200
|
-
reject: (err: any) => void
|
|
201
|
-
): void {
|
|
200
|
+
private scheduleOnNextState(resolve: () => void, reject: (err: any) => void): void {
|
|
202
201
|
this.scheduledOnNextState.push({ resolve, reject });
|
|
203
202
|
}
|
|
204
203
|
|
|
@@ -226,9 +225,7 @@ export class UploadDriver {
|
|
|
226
225
|
try {
|
|
227
226
|
await asyncPool(
|
|
228
227
|
this.opts.nConcurrentGetProgresses,
|
|
229
|
-
this.getAllNotDoneProgresses().map(
|
|
230
|
-
(p) => async () => await p.updateStatus()
|
|
231
|
-
)
|
|
228
|
+
this.getAllNotDoneProgresses().map((p) => async () => await p.updateStatus())
|
|
232
229
|
);
|
|
233
230
|
|
|
234
231
|
toNotify.forEach((n) => n.resolve());
|
|
@@ -259,7 +256,8 @@ class ProgressUpdater {
|
|
|
259
256
|
private readonly counter: CallersCounter = new CallersCounter();
|
|
260
257
|
|
|
261
258
|
public progress: sdk.ImportProgress;
|
|
262
|
-
|
|
259
|
+
/** If this is upload progress this field will be defined */
|
|
260
|
+
private uploadData?: ImportFileHandleUploadData;
|
|
263
261
|
public uploadingTerminallyFailed?: boolean;
|
|
264
262
|
|
|
265
263
|
constructor(
|
|
@@ -268,17 +266,16 @@ class ProgressUpdater {
|
|
|
268
266
|
private readonly clientProgress: ClientProgress,
|
|
269
267
|
private readonly nConcurrentPartsUpload: number,
|
|
270
268
|
signer: Signer,
|
|
271
|
-
|
|
272
|
-
public readonly res: UploadResourceSnapshot
|
|
269
|
+
public readonly res: ImportResourceSnapshot
|
|
273
270
|
) {
|
|
274
271
|
const isUpload = res.type.name.startsWith('BlobUpload');
|
|
275
272
|
let isUploadSignMatch: boolean | undefined;
|
|
276
273
|
if (isUpload) {
|
|
277
|
-
this.
|
|
274
|
+
this.uploadData = ImportFileHandleUploadData.parse(res.data);
|
|
278
275
|
isUploadSignMatch = isSignMatch(
|
|
279
276
|
signer,
|
|
280
|
-
this.
|
|
281
|
-
this.
|
|
277
|
+
this.uploadData.localPath,
|
|
278
|
+
this.uploadData.pathSignature
|
|
282
279
|
);
|
|
283
280
|
}
|
|
284
281
|
|
|
@@ -299,9 +296,7 @@ class ProgressUpdater {
|
|
|
299
296
|
}
|
|
300
297
|
|
|
301
298
|
if (this.uploadingTerminallyFailed) {
|
|
302
|
-
this.logger.error(
|
|
303
|
-
`Uploading terminally failed: ${this.progress.lastError}`
|
|
304
|
-
);
|
|
299
|
+
this.logger.error(`Uploading terminally failed: ${this.progress.lastError}`);
|
|
305
300
|
throw new Error(this.progress.lastError);
|
|
306
301
|
}
|
|
307
302
|
|
|
@@ -346,18 +341,16 @@ class ProgressUpdater {
|
|
|
346
341
|
if (this.counter.isZero()) return;
|
|
347
342
|
const parts = await this.clientBlob.initUpload(this.res);
|
|
348
343
|
|
|
349
|
-
this.logger.info(
|
|
350
|
-
`start to upload blob ${this.res.id}, parts count: ${parts.length}`
|
|
351
|
-
);
|
|
344
|
+
this.logger.info(`start to upload blob ${this.res.id}, parts count: ${parts.length}`);
|
|
352
345
|
|
|
353
346
|
const partUploadFn = (part: bigint) => async () => {
|
|
354
347
|
if (this.counter.isZero()) return;
|
|
355
348
|
await this.clientBlob.partUpload(
|
|
356
349
|
this.res,
|
|
357
|
-
this.
|
|
350
|
+
this.uploadData!.localPath,
|
|
358
351
|
part,
|
|
359
352
|
parts.length,
|
|
360
|
-
BigInt(this.
|
|
353
|
+
BigInt(this.uploadData!.modificationTime)
|
|
361
354
|
);
|
|
362
355
|
};
|
|
363
356
|
|
|
@@ -382,8 +375,7 @@ class ProgressUpdater {
|
|
|
382
375
|
|
|
383
376
|
private setDone(done: boolean) {
|
|
384
377
|
this.progress.done = done;
|
|
385
|
-
if (done)
|
|
386
|
-
this.progress.lastError = undefined;
|
|
378
|
+
if (done) this.progress.lastError = undefined;
|
|
387
379
|
}
|
|
388
380
|
|
|
389
381
|
async updateStatus() {
|
|
@@ -394,15 +386,12 @@ class ProgressUpdater {
|
|
|
394
386
|
this.progress.status = protoToStatus(status);
|
|
395
387
|
this.setDone(status.done);
|
|
396
388
|
|
|
397
|
-
if (status.done || status.progress != oldStatus?.progress)
|
|
398
|
-
this.change.markChanged();
|
|
389
|
+
if (status.done || status.progress != oldStatus?.progress) this.change.markChanged();
|
|
399
390
|
} catch (e: any) {
|
|
400
391
|
this.setLastError(e);
|
|
401
392
|
|
|
402
393
|
if (e.name == 'RpcError' && e.code == 'DEADLINE_EXCEEDED') {
|
|
403
|
-
this.logger.warn(
|
|
404
|
-
`deadline exceeded while getting a status of BlobImport`
|
|
405
|
-
);
|
|
394
|
+
this.logger.warn(`deadline exceeded while getting a status of BlobImport`);
|
|
406
395
|
return;
|
|
407
396
|
}
|
|
408
397
|
|
|
@@ -423,44 +412,7 @@ class ProgressUpdater {
|
|
|
423
412
|
}
|
|
424
413
|
|
|
425
414
|
function isProgressStable(p: sdk.ImportProgress) {
|
|
426
|
-
return
|
|
427
|
-
p.done &&
|
|
428
|
-
p.status !== undefined &&
|
|
429
|
-
p.status !== null &&
|
|
430
|
-
p.status.progress >= 1.0
|
|
431
|
-
);
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
export function importToUploadOpts(res: UploadResourceSnapshot): UploadOpts {
|
|
435
|
-
if (res.data == undefined || !('modificationTime' in res.data)) {
|
|
436
|
-
throw new Error(
|
|
437
|
-
'no upload options in BlobUpload resource data: ' +
|
|
438
|
-
stringifyWithResourceId(res.data)
|
|
439
|
-
);
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
const opts = res.data;
|
|
443
|
-
if (opts.modificationTime === undefined) {
|
|
444
|
-
throw new Error(
|
|
445
|
-
'no modification time in data: ' + stringifyWithResourceId(res.data)
|
|
446
|
-
);
|
|
447
|
-
}
|
|
448
|
-
if (opts.localPath === undefined) {
|
|
449
|
-
throw new Error(
|
|
450
|
-
'no local path in data: ' + stringifyWithResourceId(res.data)
|
|
451
|
-
);
|
|
452
|
-
}
|
|
453
|
-
if (opts.pathSignature === undefined) {
|
|
454
|
-
throw new Error(
|
|
455
|
-
'no path signature in data: ' + stringifyWithResourceId(res.data)
|
|
456
|
-
);
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
return {
|
|
460
|
-
modificationTime: opts.modificationTime,
|
|
461
|
-
localPath: opts.localPath,
|
|
462
|
-
pathSignature: opts.pathSignature
|
|
463
|
-
};
|
|
415
|
+
return p.done && p.status !== undefined && p.status !== null && p.status.progress >= 1.0;
|
|
464
416
|
}
|
|
465
417
|
|
|
466
418
|
function protoToStatus(proto: ProgressStatus): sdk.ImportStatus {
|
|
@@ -481,11 +433,7 @@ function isSignMatch(signer: Signer, path: string, signature: string): boolean {
|
|
|
481
433
|
}
|
|
482
434
|
|
|
483
435
|
function nonRecoverableError(e: any) {
|
|
484
|
-
return
|
|
485
|
-
e instanceof MTimeError ||
|
|
486
|
-
e instanceof UnexpectedEOF ||
|
|
487
|
-
e instanceof NoFileForUploading
|
|
488
|
-
);
|
|
436
|
+
return e instanceof MTimeError || e instanceof UnexpectedEOF || e instanceof NoFileForUploading;
|
|
489
437
|
}
|
|
490
438
|
|
|
491
439
|
function isResourceWasDeletedError(e: any) {
|
package/src/index.ts
CHANGED