@balena/pinejs 21.3.1-build-webresource-delete-reliable-7d16043f72b2a015f309d0e0a504d9ce22844008-1 → 21.3.2-build-renovate-minio-mc-2025-x-da1cec8047050b0fc2defc6939dbe2b5e5302ab4-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/.pinejs-cache.json +1 -1
- package/.versionbot/CHANGELOG.yml +26 -34
- package/CHANGELOG.md +6 -4
- package/VERSION +1 -1
- package/docker-compose.npm-test.yml +1 -1
- package/out/config-loader/config-loader.js +1 -1
- package/out/config-loader/config-loader.js.map +1 -1
- package/out/tasks/index.d.ts +0 -1
- package/out/tasks/index.js +0 -5
- package/out/tasks/index.js.map +1 -1
- package/out/webresource-handler/index.d.ts +1 -1
- package/out/webresource-handler/index.js +44 -52
- package/out/webresource-handler/index.js.map +1 -1
- package/package.json +2 -2
- package/src/config-loader/config-loader.ts +1 -1
- package/src/tasks/index.ts +0 -8
- package/src/webresource-handler/index.ts +83 -67
- package/out/tasks/pine-tasks.d.ts +0 -1
- package/out/tasks/pine-tasks.js +0 -7
- package/out/tasks/pine-tasks.js.map +0 -1
- package/out/webresource-handler/delete-file-task.d.ts +0 -1
- package/out/webresource-handler/delete-file-task.js +0 -37
- package/out/webresource-handler/delete-file-task.js.map +0 -1
- package/src/tasks/pine-tasks.ts +0 -7
- package/src/webresource-handler/delete-file-task.ts +0 -42
@@ -15,8 +15,6 @@ import { errors, permissions } from '../server-glue/module.js';
|
|
15
15
|
import type { WebResourceType as WebResource } from '@balena/sbvr-types';
|
16
16
|
import { TypedError } from 'typed-error';
|
17
17
|
import type { Resolvable } from '../sbvr-api/common-types.js';
|
18
|
-
import type { Tx } from '../database-layer/db.js';
|
19
|
-
import { isPineTasksAvailable } from '../tasks/index.js';
|
20
18
|
|
21
19
|
export * from './handlers/index.js';
|
22
20
|
|
@@ -318,34 +316,12 @@ export const getWebResourceFields = (
|
|
318
316
|
.map((f) => sqlNameToODataName(f.fieldName));
|
319
317
|
};
|
320
318
|
|
321
|
-
const
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
passthrough: {
|
328
|
-
req: permissions.root,
|
329
|
-
tx,
|
330
|
-
},
|
331
|
-
body: {
|
332
|
-
key: crypto.randomUUID(),
|
333
|
-
is_executed_by__handler: 'delete_webresource_file',
|
334
|
-
is_executed_with__parameter_set: {
|
335
|
-
fileKey: fileKey,
|
336
|
-
},
|
337
|
-
attempt_limit: 2 ** 31 - 1,
|
338
|
-
},
|
339
|
-
});
|
340
|
-
}),
|
341
|
-
);
|
342
|
-
} else {
|
343
|
-
await Promise.all(
|
344
|
-
keysToDelete.map(async (fileKey) => {
|
345
|
-
return await configuredWebResourceHandler?.removeFile(fileKey);
|
346
|
-
}),
|
347
|
-
);
|
348
|
-
}
|
319
|
+
const deleteFiles = async (
|
320
|
+
keysToDelete: string[],
|
321
|
+
webResourceHandler: WebResourceHandler,
|
322
|
+
) => {
|
323
|
+
const promises = keysToDelete.map((r) => webResourceHandler.removeFile(r));
|
324
|
+
await Promise.all(promises);
|
349
325
|
};
|
350
326
|
|
351
327
|
const throwIfWebresourceNotInMultipart = (
|
@@ -362,22 +338,18 @@ const throwIfWebresourceNotInMultipart = (
|
|
362
338
|
}
|
363
339
|
};
|
364
340
|
|
365
|
-
const getCreateWebResourceHooks = (
|
341
|
+
const getCreateWebResourceHooks = (
|
342
|
+
webResourceHandler: WebResourceHandler,
|
343
|
+
): sbvrUtils.Hooks => {
|
366
344
|
return {
|
367
345
|
PRERUN: (hookArgs) => {
|
368
346
|
const webResourceFields = getWebResourceFields(hookArgs.request);
|
369
347
|
throwIfWebresourceNotInMultipart(webResourceFields, hookArgs);
|
370
348
|
},
|
371
|
-
'POSTRUN-ERROR':
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
return;
|
376
|
-
}
|
377
|
-
|
378
|
-
const keysToDelete = getWebResourcesKeysFromRequest(fields, request);
|
379
|
-
// Explicitely not passing tx as it will be rolledback as it is an error hook
|
380
|
-
await deleteOnStorage(keysToDelete);
|
349
|
+
'POSTRUN-ERROR': ({ tx, request }) => {
|
350
|
+
tx.on('rollback', () => {
|
351
|
+
void deleteRollbackPendingFields(request, webResourceHandler);
|
352
|
+
});
|
381
353
|
},
|
382
354
|
};
|
383
355
|
};
|
@@ -406,12 +378,21 @@ const getWebResourcesKeysFromRequest = (
|
|
406
378
|
.filter((href) => href != null);
|
407
379
|
};
|
408
380
|
|
409
|
-
const getRemoveWebResourceHooks = (
|
381
|
+
const getRemoveWebResourceHooks = (
|
382
|
+
webResourceHandler: WebResourceHandler,
|
383
|
+
): sbvrUtils.Hooks => {
|
410
384
|
return {
|
411
385
|
PRERUN: async (args) => {
|
412
386
|
const { api, request, tx } = args;
|
413
387
|
let webResourceFields = getWebResourceFields(request);
|
414
388
|
|
389
|
+
throwIfWebresourceNotInMultipart(webResourceFields, args);
|
390
|
+
|
391
|
+
// Request failed on DB roundtrip (e.g. DB constraint) and pending files need to be deleted
|
392
|
+
tx.on('rollback', () => {
|
393
|
+
void deleteRollbackPendingFields(request, webResourceHandler);
|
394
|
+
});
|
395
|
+
|
415
396
|
if (request.method === 'PATCH') {
|
416
397
|
webResourceFields = Object.entries(request.values)
|
417
398
|
.filter(
|
@@ -420,8 +401,6 @@ const getRemoveWebResourceHooks = (): sbvrUtils.Hooks => {
|
|
420
401
|
)
|
421
402
|
.map(([key]) => key);
|
422
403
|
}
|
423
|
-
request.custom.webResourceFields = webResourceFields;
|
424
|
-
throwIfWebresourceNotInMultipart(webResourceFields, args);
|
425
404
|
|
426
405
|
if (webResourceFields.length === 0) {
|
427
406
|
// No need to delete anything as no file is in the wire
|
@@ -432,32 +411,43 @@ const getRemoveWebResourceHooks = (): sbvrUtils.Hooks => {
|
|
432
411
|
// This can only be validated here because we need to first ensure the
|
433
412
|
// request is actually modifying a webresource before erroring out
|
434
413
|
if (request.method === 'PATCH' && request.odataQuery?.key == null) {
|
414
|
+
// When we get here, files have already been uploaded. We need to mark them for deletion.
|
415
|
+
const keysToDelete = getWebResourcesKeysFromRequest(
|
416
|
+
webResourceFields,
|
417
|
+
request,
|
418
|
+
);
|
419
|
+
|
420
|
+
// Set deletion of files on the wire as request will throw
|
421
|
+
tx.on('end', () => {
|
422
|
+
deletePendingFiles(keysToDelete, request, webResourceHandler);
|
423
|
+
});
|
424
|
+
|
435
425
|
throw new errors.BadRequestError(
|
436
426
|
'WebResources can only be updated when providing a resource key.',
|
437
427
|
);
|
438
428
|
}
|
439
429
|
|
430
|
+
// This can be > 1 in both DELETE requests or PATCH requests to not accessible IDs.
|
440
431
|
const ids = await sbvrUtils.getAffectedIds(args);
|
441
432
|
if (ids.length === 0) {
|
442
433
|
// Set deletion of files on the wire as no resource was affected
|
443
|
-
request
|
434
|
+
// Note that for DELETE requests it should not find any request on the wire
|
435
|
+
const keysToDelete = getWebResourcesKeysFromRequest(
|
444
436
|
webResourceFields,
|
445
437
|
request,
|
446
438
|
);
|
439
|
+
deletePendingFiles(keysToDelete, request, webResourceHandler);
|
447
440
|
return;
|
448
441
|
}
|
449
442
|
|
450
|
-
// If it reaches here, it means that it will try to patch/delete the webresource
|
451
|
-
// So we need (before postrun) get what are the current keys to be deleted
|
452
|
-
// if post run succeeds
|
453
443
|
const webResources = (await api.get({
|
454
444
|
resource: request.resourceName,
|
455
445
|
passthrough: {
|
456
|
-
tx,
|
446
|
+
tx: args.tx,
|
457
447
|
req: permissions.root,
|
458
448
|
},
|
459
449
|
options: {
|
460
|
-
$select:
|
450
|
+
$select: webResourceFields,
|
461
451
|
$filter: {
|
462
452
|
id: {
|
463
453
|
$in: ids,
|
@@ -465,34 +455,60 @@ const getRemoveWebResourceHooks = (): sbvrUtils.Hooks => {
|
|
465
455
|
},
|
466
456
|
},
|
467
457
|
})) as WebResourcesDbResponse[] | undefined | null;
|
468
|
-
request.custom.onPostRunDelete = getWebResourcesHrefs(webResources);
|
469
|
-
},
|
470
458
|
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
);
|
480
|
-
// Explicitely not passing tx as it will be rolledback as it is an error hook
|
481
|
-
await deleteOnStorage(keysToDelete);
|
459
|
+
// Deletes previous stored resources in case they were patched or the whole entity was deleted
|
460
|
+
tx.on('end', () => {
|
461
|
+
deletePendingFiles(
|
462
|
+
getWebResourcesHrefs(webResources),
|
463
|
+
request,
|
464
|
+
webResourceHandler,
|
465
|
+
);
|
466
|
+
});
|
482
467
|
},
|
483
468
|
};
|
484
469
|
};
|
485
470
|
|
471
|
+
const deleteRollbackPendingFields = async (
|
472
|
+
request: uriParser.ODataRequest,
|
473
|
+
webResourceHandler: WebResourceHandler,
|
474
|
+
) => {
|
475
|
+
const fields = getWebResourceFields(request);
|
476
|
+
|
477
|
+
if (fields.length === 0) {
|
478
|
+
return;
|
479
|
+
}
|
480
|
+
|
481
|
+
const keysToDelete = getWebResourcesKeysFromRequest(fields, request);
|
482
|
+
await deleteFiles(keysToDelete, webResourceHandler);
|
483
|
+
};
|
484
|
+
|
485
|
+
const deletePendingFiles = (
|
486
|
+
keysToDelete: string[],
|
487
|
+
request: uriParser.ODataRequest,
|
488
|
+
webResourceHandler: WebResourceHandler,
|
489
|
+
): void => {
|
490
|
+
// on purpose does not await for this promise to resolve
|
491
|
+
try {
|
492
|
+
void deleteFiles(keysToDelete, webResourceHandler);
|
493
|
+
} catch (err) {
|
494
|
+
getLogger(request.vocabulary).error(`Failed to delete pending files`, err);
|
495
|
+
}
|
496
|
+
};
|
497
|
+
|
486
498
|
export const getDefaultHandler = (): WebResourceHandler => {
|
487
499
|
return new NoopHandler();
|
488
500
|
};
|
489
501
|
|
490
|
-
export const setupUploadHooks = (
|
502
|
+
export const setupUploadHooks = (
|
503
|
+
handler: WebResourceHandler,
|
504
|
+
apiRoot: string,
|
505
|
+
resourceName: string,
|
506
|
+
) => {
|
491
507
|
sbvrUtils.addPureHook(
|
492
508
|
'DELETE',
|
493
509
|
apiRoot,
|
494
510
|
resourceName,
|
495
|
-
getRemoveWebResourceHooks(),
|
511
|
+
getRemoveWebResourceHooks(handler),
|
496
512
|
);
|
497
513
|
|
498
514
|
sbvrUtils.addPureHook(
|
@@ -500,13 +516,13 @@ export const setupUploadHooks = (apiRoot: string, resourceName: string) => {
|
|
500
516
|
apiRoot,
|
501
517
|
resourceName,
|
502
518
|
// PATCH also needs to remove the old resource in case a webresource was modified
|
503
|
-
getRemoveWebResourceHooks(),
|
519
|
+
getRemoveWebResourceHooks(handler),
|
504
520
|
);
|
505
521
|
|
506
522
|
sbvrUtils.addPureHook(
|
507
523
|
'POST',
|
508
524
|
apiRoot,
|
509
525
|
resourceName,
|
510
|
-
getCreateWebResourceHooks(),
|
526
|
+
getCreateWebResourceHooks(handler),
|
511
527
|
);
|
512
528
|
};
|
@@ -1 +0,0 @@
|
|
1
|
-
export declare const addPineTaskHandlers: () => void;
|
package/out/tasks/pine-tasks.js
DELETED
@@ -1,7 +0,0 @@
|
|
1
|
-
import { tasks } from '../server-glue/module.js';
|
2
|
-
import { addDeleteFileTaskHandler } from '../webresource-handler/delete-file-task.js';
|
3
|
-
export const addPineTaskHandlers = () => {
|
4
|
-
addDeleteFileTaskHandler();
|
5
|
-
void tasks.worker?.start();
|
6
|
-
};
|
7
|
-
//# sourceMappingURL=pine-tasks.js.map
|
@@ -1 +0,0 @@
|
|
1
|
-
{"version":3,"file":"pine-tasks.js","sourceRoot":"","sources":["../../src/tasks/pine-tasks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;AACjD,OAAO,EAAE,wBAAwB,EAAE,MAAM,4CAA4C,CAAC;AAEtF,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAG,EAAE;IACvC,wBAAwB,EAAE,CAAC;IAC3B,KAAK,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC;AAC5B,CAAC,CAAC"}
|
@@ -1 +0,0 @@
|
|
1
|
-
export declare const addDeleteFileTaskHandler: () => void;
|
@@ -1,37 +0,0 @@
|
|
1
|
-
import { addTaskHandler } from '../tasks/index.js';
|
2
|
-
import { getWebresourceHandler } from './index.js';
|
3
|
-
const deleteFileSchema = {
|
4
|
-
type: 'object',
|
5
|
-
properties: {
|
6
|
-
fileKey: {
|
7
|
-
type: 'string',
|
8
|
-
},
|
9
|
-
},
|
10
|
-
required: ['fileKey'],
|
11
|
-
additionalProperties: false,
|
12
|
-
};
|
13
|
-
export const addDeleteFileTaskHandler = () => {
|
14
|
-
addTaskHandler('delete_webresource_file', async (task) => {
|
15
|
-
const handler = getWebresourceHandler();
|
16
|
-
if (!handler) {
|
17
|
-
return {
|
18
|
-
error: 'Webresource handler not available',
|
19
|
-
status: 'failed',
|
20
|
-
};
|
21
|
-
}
|
22
|
-
try {
|
23
|
-
await handler.removeFile(task.params.fileKey);
|
24
|
-
return {
|
25
|
-
status: 'succeeded',
|
26
|
-
};
|
27
|
-
}
|
28
|
-
catch (error) {
|
29
|
-
console.error('Error deleting file:', error);
|
30
|
-
return {
|
31
|
-
error: `${error}`,
|
32
|
-
status: 'failed',
|
33
|
-
};
|
34
|
-
}
|
35
|
-
}, deleteFileSchema);
|
36
|
-
};
|
37
|
-
//# sourceMappingURL=delete-file-task.js.map
|
@@ -1 +0,0 @@
|
|
1
|
-
{"version":3,"file":"delete-file-task.js","sourceRoot":"","sources":["../../src/webresource-handler/delete-file-task.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAEnD,MAAM,gBAAgB,GAAG;IACxB,IAAI,EAAE,QAAQ;IACd,UAAU,EAAE;QACX,OAAO,EAAE;YACR,IAAI,EAAE,QAAQ;SACd;KACD;IACD,QAAQ,EAAE,CAAC,SAAS,CAAC;IACrB,oBAAoB,EAAE,KAAK;CAClB,CAAC;AAEX,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAAG,EAAE;IAC5C,cAAc,CACb,yBAAyB,EACzB,KAAK,EAAE,IAAI,EAAE,EAAE;QACd,MAAM,OAAO,GAAG,qBAAqB,EAAE,CAAC;QACxC,IAAI,CAAC,OAAO,EAAE,CAAC;YACd,OAAO;gBACN,KAAK,EAAE,mCAAmC;gBAC1C,MAAM,EAAE,QAAQ;aAChB,CAAC;QACH,CAAC;QAED,IAAI,CAAC;YACJ,MAAM,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAC9C,OAAO;gBACN,MAAM,EAAE,WAAW;aACnB,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,OAAO,CAAC,KAAK,CAAC,sBAAsB,EAAE,KAAK,CAAC,CAAC;YAC7C,OAAO;gBACN,KAAK,EAAE,GAAG,KAAK,EAAE;gBACjB,MAAM,EAAE,QAAQ;aAChB,CAAC;QACH,CAAC;IACF,CAAC,EACD,gBAAgB,CAChB,CAAC;AACH,CAAC,CAAC"}
|
package/src/tasks/pine-tasks.ts
DELETED
@@ -1,42 +0,0 @@
|
|
1
|
-
import { addTaskHandler } from '../tasks/index.js';
|
2
|
-
import { getWebresourceHandler } from './index.js';
|
3
|
-
|
4
|
-
const deleteFileSchema = {
|
5
|
-
type: 'object',
|
6
|
-
properties: {
|
7
|
-
fileKey: {
|
8
|
-
type: 'string',
|
9
|
-
},
|
10
|
-
},
|
11
|
-
required: ['fileKey'],
|
12
|
-
additionalProperties: false,
|
13
|
-
} as const;
|
14
|
-
|
15
|
-
export const addDeleteFileTaskHandler = () => {
|
16
|
-
addTaskHandler(
|
17
|
-
'delete_webresource_file',
|
18
|
-
async (task) => {
|
19
|
-
const handler = getWebresourceHandler();
|
20
|
-
if (!handler) {
|
21
|
-
return {
|
22
|
-
error: 'Webresource handler not available',
|
23
|
-
status: 'failed',
|
24
|
-
};
|
25
|
-
}
|
26
|
-
|
27
|
-
try {
|
28
|
-
await handler.removeFile(task.params.fileKey);
|
29
|
-
return {
|
30
|
-
status: 'succeeded',
|
31
|
-
};
|
32
|
-
} catch (error) {
|
33
|
-
console.error('Error deleting file:', error);
|
34
|
-
return {
|
35
|
-
error: `${error}`,
|
36
|
-
status: 'failed',
|
37
|
-
};
|
38
|
-
}
|
39
|
-
},
|
40
|
-
deleteFileSchema,
|
41
|
-
);
|
42
|
-
};
|