@qrvey/object-storage 0.0.10-beta.2 → 0.0.10-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.
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # @qrvey/object-storage
2
2
 
3
3
  ![install size](https://packagephobia.com/badge?p=%40qrvey%2Fobject-storage)
4
- ![coverage](https://img.shields.io/badge/unit_test_coverage-9%25-brightgreen)
4
+ ![coverage](https://img.shields.io/badge/unit_test_coverage-8%25-brightgreen)
5
5
 
6
6
  The `@qrvey/object-storage` package provides a unified interface for ...
7
7
 
package/dist/cjs/index.js CHANGED
@@ -16,6 +16,11 @@ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
16
16
  var __getOwnPropSymbols = Object.getOwnPropertySymbols;
17
17
  var __hasOwnProp = Object.prototype.hasOwnProperty;
18
18
  var __propIsEnum = Object.prototype.propertyIsEnumerable;
19
+ var __knownSymbol = (name, symbol) => {
20
+ if (symbol = Symbol[name])
21
+ return symbol;
22
+ throw Error("Symbol." + name + " is not defined");
23
+ };
19
24
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
20
25
  var __spreadValues = (a, b) => {
21
26
  for (var prop in b || (b = {}))
@@ -41,6 +46,7 @@ var __objRest = (source, exclude) => {
41
46
  }
42
47
  return target;
43
48
  };
49
+ var __forAwait = (obj, it, method) => (it = obj[__knownSymbol("asyncIterator")]) ? it.call(obj) : (obj = obj[__knownSymbol("iterator")](), it = {}, method = (key, fn) => (fn = obj[key]) && (it[key] = (arg) => new Promise((yes, no, done) => (arg = fn.call(obj, arg), done = arg.done, Promise.resolve(arg.value).then((value) => yes({ value, done }), no)))), method("next"), method("return"), it);
44
50
 
45
51
  // src/shared/utils/errorHandler.ts
46
52
  var errorMessages = {
@@ -453,6 +459,71 @@ var BlobStorageService = class {
453
459
  );
454
460
  }
455
461
  }
462
+ async listMultipartUploadsForBucket() {
463
+ const containerClient = this.blobServiceClient.getContainerClient(
464
+ this.containerName
465
+ );
466
+ const blobList = containerClient.listBlobsFlat();
467
+ const uploads = [];
468
+ try {
469
+ for (var iter = __forAwait(blobList), more, temp, error; more = !(temp = await iter.next()).done; more = false) {
470
+ const blob = temp.value;
471
+ uploads.push({
472
+ uploadId: blob.name,
473
+ key: blob.name,
474
+ initiated: blob.properties.createdOn || /* @__PURE__ */ new Date()
475
+ });
476
+ }
477
+ } catch (temp) {
478
+ error = [temp];
479
+ } finally {
480
+ try {
481
+ more && (temp = iter.return) && await temp.call(iter);
482
+ } finally {
483
+ if (error)
484
+ throw error[0];
485
+ }
486
+ }
487
+ return {
488
+ bucketName: this.containerName,
489
+ uploads
490
+ };
491
+ }
492
+ /**
493
+ * Retrieves the list of parts for a multipart upload.
494
+ *
495
+ * @param {string} blobName - The name of the blob (file) in Azure Blob Storage.
496
+ * @return {Promise<ListPartsMultipartUploadResponse>} A promise that resolves to the list of parts for the multipart upload.
497
+ */
498
+ async listMultipartUploadsForKey(blobName) {
499
+ var _a;
500
+ const containerClient = this.blobServiceClient.getContainerClient(
501
+ this.containerName
502
+ );
503
+ const blobClient = containerClient.getBlockBlobClient(blobName);
504
+ const listResponse = await blobClient.getBlockList("all");
505
+ const parts = ((_a = listResponse == null ? void 0 : listResponse.committedBlocks) == null ? void 0 : _a.map((block, index) => ({
506
+ PartNumber: index + 1,
507
+ LastModified: /* @__PURE__ */ new Date(),
508
+ // Azure Blob Storage doesn't provide the last modified date for individual parts
509
+ ETag: block.name,
510
+ Size: block.size
511
+ }))) || [];
512
+ return {
513
+ Parts: parts
514
+ };
515
+ }
516
+ /**
517
+ * Generates a unique upload ID for multipart uploads.
518
+ *
519
+ * @return {Promise<string>} A promise that resolves to a unique upload ID.
520
+ */
521
+ async generateUploadIdMultipart() {
522
+ const timestamp = Date.now();
523
+ const randomNum = Math.floor(Math.random() * 100);
524
+ const blockIdBase = (timestamp - randomNum).toString();
525
+ return blockIdBase;
526
+ }
456
527
  /**
457
528
  * Uploads a part of a file as a block to Azure Blob Storage and updates the metadata with the block ID.
458
529
  *
@@ -467,17 +538,10 @@ var BlobStorageService = class {
467
538
  this.containerName
468
539
  );
469
540
  const blobClient = containerClient.getBlockBlobClient(blobName);
470
- let blockIdBase = uploadId;
471
- if (!blockIdBase) {
472
- const timestamp = Date.now();
473
- const randomNum = Math.floor(Math.random() * 100);
474
- blockIdBase = (timestamp - randomNum).toString();
475
- }
541
+ const blockIdBase = uploadId || await this.generateUploadIdMultipart();
476
542
  const partId = partNumber.toString().padStart(6, "0");
477
543
  const partIdBase64 = Buffer.from(partId).toString("base64");
478
544
  await blobClient.stageBlock(partIdBase64, file, file.length);
479
- const blockIdStorage = BlockIdStorage.getInstance();
480
- blockIdStorage.addBlockId(blockIdBase, partIdBase64);
481
545
  return blockIdBase;
482
546
  }
483
547
  /**
@@ -489,17 +553,17 @@ var BlobStorageService = class {
489
553
  * @throws {Error} If no block IDs are found in the metadata.
490
554
  */
491
555
  async completeMultipartUpload(blobName, uploadId) {
556
+ var _a;
492
557
  const containerClient = this.blobServiceClient.getContainerClient(
493
558
  this.containerName
494
559
  );
495
560
  const blobClient = containerClient.getBlockBlobClient(blobName);
496
- const blockIdStorage = BlockIdStorage.getInstance();
497
- const blockIds = blockIdStorage.getBlockIds(uploadId);
561
+ const listMultipartUploads = await this.listMultipartUploadsForKey(blobName);
562
+ const blockIds = ((_a = listMultipartUploads == null ? void 0 : listMultipartUploads.Parts) == null ? void 0 : _a.map((part) => part.ETag)) || [];
498
563
  if (blockIds.length === 0) {
499
564
  throw new Error("No block IDs found in metadata.");
500
565
  }
501
566
  await blobClient.commitBlockList(blockIds);
502
- blockIdStorage.clearBlockIds(uploadId);
503
567
  }
504
568
  /**
505
569
  * Aborts a multipart upload by deleting the specified blob and clearing its block IDs metadata.
@@ -517,6 +581,17 @@ var BlobStorageService = class {
517
581
  blockIdStorage.clearBlockIds(uploadId);
518
582
  await blobClient.delete();
519
583
  }
584
+ async getMultipartUploadPresignedUrl(blobName, uploadId, partNumber, expiresInMinutes = 2) {
585
+ const partId = partNumber.toString().padStart(6, "0");
586
+ const partIdBase64 = Buffer.from(partId).toString("base64");
587
+ const sasUrl = await this.getSignatureUrl(
588
+ blobName,
589
+ expiresInMinutes,
590
+ "w"
591
+ );
592
+ const url = `${sasUrl}&comp=block&blockid=${partIdBase64}`;
593
+ return url;
594
+ }
520
595
  };
521
596
 
522
597
  // src/services/storage/s3/s3Helpers.ts
@@ -773,18 +848,69 @@ var S3StorageService = class {
773
848
  );
774
849
  }
775
850
  }
851
+ /**
852
+ * Generates an upload ID for multipart upload in the S3 bucket.
853
+ *
854
+ * @param {string} key - The key of the object to upload.
855
+ * @return {Promise<string>} A promise that resolves to the generated upload ID.
856
+ * @throws {Error} If no upload ID was generated.
857
+ */
858
+ async generateUploadIdMultipart(key) {
859
+ const params = {
860
+ Bucket: this.bucketName,
861
+ Key: key
862
+ };
863
+ const response = await this.s3.createMultipartUpload(params);
864
+ if (!(response == null ? void 0 : response.UploadId))
865
+ throw new Error("No upload ID was generated");
866
+ return response.UploadId;
867
+ }
868
+ async listMultipartUploadsForBucket() {
869
+ const params = {
870
+ Bucket: this.bucketName
871
+ };
872
+ const response = await this.s3.listMultipartUploads(params);
873
+ return {
874
+ bucketName: this.bucketName,
875
+ uploads: response.Uploads ? response.Uploads.map((upload) => {
876
+ var _a, _b;
877
+ return {
878
+ uploadId: upload.UploadId || "",
879
+ key: upload.Key || "",
880
+ initiator: ((_a = upload == null ? void 0 : upload.Initiator) == null ? void 0 : _a.ID) || "",
881
+ owner: ((_b = upload == null ? void 0 : upload.Owner) == null ? void 0 : _b.ID) || "",
882
+ storageClass: upload.StorageClass || "",
883
+ initiated: upload.Initiated || ""
884
+ };
885
+ }) : []
886
+ };
887
+ }
888
+ /**
889
+ * Retrieves the list of parts for a multipart upload.
890
+ *
891
+ * @param {string} key - The key of the object being uploaded.
892
+ * @param {string} uploadId - The ID of the multipart upload.
893
+ * @return {Promise<ListPartsMultipartUploadResponse>} A promise that resolves to the list of parts for the multipart upload.
894
+ */
895
+ async listMultipartUploadsForKey(key, uploadId) {
896
+ const params = {
897
+ Bucket: this.bucketName,
898
+ Key: key,
899
+ UploadId: uploadId
900
+ };
901
+ const response = await this.s3.listParts(params);
902
+ const parts = response.Parts ? response.Parts.map((part) => ({
903
+ PartNumber: part.PartNumber,
904
+ LastModified: part.LastModified,
905
+ ETag: part.ETag,
906
+ Size: part.Size
907
+ })) : [];
908
+ return {
909
+ Parts: parts
910
+ };
911
+ }
776
912
  async uploadMultipart(key, file, partNumber, uploadId) {
777
- let upId = uploadId;
778
- if (!upId) {
779
- const params2 = {
780
- Bucket: this.bucketName,
781
- Key: key
782
- };
783
- const response = await this.s3.createMultipartUpload(params2);
784
- if (!(response == null ? void 0 : response.UploadId))
785
- throw new Error("No upload ID was generated");
786
- upId = response.UploadId;
787
- }
913
+ const upId = uploadId || await this.generateUploadIdMultipart(key);
788
914
  const params = {
789
915
  Bucket: this.bucketName,
790
916
  Key: key,
@@ -796,12 +922,10 @@ var S3StorageService = class {
796
922
  return upId;
797
923
  }
798
924
  async completeMultipartUpload(key, uploadId) {
799
- const listPartsParams = {
800
- Bucket: this.bucketName,
801
- Key: key,
802
- UploadId: uploadId
803
- };
804
- const partsResponse = await this.s3.listParts(listPartsParams);
925
+ const partsResponse = await this.listMultipartUploadsForKey(
926
+ key,
927
+ uploadId
928
+ );
805
929
  const partsList = (partsResponse == null ? void 0 : partsResponse.Parts) && partsResponse.Parts.map(
806
930
  // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
807
931
  (_a) => {
@@ -809,11 +933,14 @@ var S3StorageService = class {
809
933
  return partList;
810
934
  }
811
935
  );
812
- const completeMultipartParams = __spreadProps(__spreadValues({}, listPartsParams), {
936
+ const completeMultipartParams = {
937
+ Bucket: this.bucketName,
938
+ Key: key,
939
+ UploadId: uploadId,
813
940
  MultipartUpload: {
814
941
  Parts: partsList
815
942
  }
816
- });
943
+ };
817
944
  await this.s3.completeMultipartUpload(completeMultipartParams);
818
945
  }
819
946
  async abortMultipartUpload(key, uploadId) {
@@ -824,6 +951,21 @@ var S3StorageService = class {
824
951
  };
825
952
  await this.s3.abortMultipartUpload(params);
826
953
  }
954
+ async getMultipartUploadPresignedUrl(key, uploadId, partNumber, expiresInMinutes = 60) {
955
+ const uploadPartParams = {
956
+ Bucket: this.bucketName,
957
+ Key: key,
958
+ UploadId: uploadId,
959
+ PartNumber: parseInt(partNumber, 10)
960
+ };
961
+ const expiresIn = expiresInMinutes * 60;
962
+ const presignedUrl = await s3RequestPresigner.getSignedUrl(
963
+ this.s3Client,
964
+ new clientS3.UploadPartCommand(uploadPartParams),
965
+ { expiresIn }
966
+ );
967
+ return presignedUrl;
968
+ }
827
969
  };
828
970
 
829
971
  // src/shared/utils/constants.ts
@@ -1002,6 +1144,44 @@ var ObjectStorageService = class _ObjectStorageService {
1002
1144
  bucketName
1003
1145
  ).then((instance) => instance.getHeadBucket());
1004
1146
  }
1147
+ /**
1148
+ * Generates a unique upload ID for a multipart upload.
1149
+ *
1150
+ * @param {string} key - The key of the object to upload.
1151
+ * @param {string} [bucketName] - The name of the bucket. If not provided, the default bucket name will be used.
1152
+ * @return {Promise<string>} A promise that resolves to the generated upload ID.
1153
+ */
1154
+ static async generateUploadIdMultipart(key, bucketName) {
1155
+ return _ObjectStorageService.getObjectStorageServiceInstance(
1156
+ bucketName
1157
+ ).then((instance) => instance.generateUploadIdMultipart(key));
1158
+ }
1159
+ /**
1160
+ * Retrieves a list of multipart uploads for a specified key in the object storage service.
1161
+ *
1162
+ * @param {string} key - The key of the object to retrieve uploads for.
1163
+ * @param {string} [bucketName] - The name of the bucket. If not provided, the default bucket name will be used.
1164
+ * @param {string} [uploadId] - The upload ID to filter the results by.
1165
+ * @return {Promise<ListPartsMultipartUploadResponse>} A promise that resolves to the list of multipart uploads for the specified key.
1166
+ */
1167
+ static async listMultipartUploadsForKey(key, bucketName, uploadId) {
1168
+ return _ObjectStorageService.getObjectStorageServiceInstance(
1169
+ bucketName
1170
+ ).then(
1171
+ (instance) => instance.listMultipartUploadsForKey(key, uploadId)
1172
+ );
1173
+ }
1174
+ /**
1175
+ * Retrieves a list of all multipart uploads for a specified bucket in the object storage service.
1176
+ *
1177
+ * @param {string} [bucketName] - The name of the bucket. If not provided, the default bucket name will be used.
1178
+ * @return {Promise<ListMultipartUploadsResponse>} A promise that resolves to the list of multipart uploads for the specified bucket.
1179
+ */
1180
+ static async listMultipartUploadsForBucket(bucketName) {
1181
+ return _ObjectStorageService.getObjectStorageServiceInstance(
1182
+ bucketName
1183
+ ).then((instance) => instance.listMultipartUploadsForBucket());
1184
+ }
1005
1185
  /**
1006
1186
  * Uploads a multipart file to the specified bucket in the object storage service.
1007
1187
  *
@@ -1045,6 +1225,25 @@ var ObjectStorageService = class _ObjectStorageService {
1045
1225
  bucketName
1046
1226
  ).then((instance) => instance.abortMultipartUpload(key, uploadId));
1047
1227
  }
1228
+ /**
1229
+ * Retrieves a presigned URL for a specific part of a multipart upload.
1230
+ *
1231
+ * @param {string} key - The key of the object for which to generate the presigned URL.
1232
+ * @param {string} uploadId - The ID of the multipart upload.
1233
+ * @param {string} partNumber - The number of the part for which to generate the presigned URL.
1234
+ * @param {number} [expiresInMinutes] - The number of minutes until the presigned URL expires. Default is 60 minutes.
1235
+ * @return {Promise<string>} A Promise that resolves to the presigned URL for the specified part of the multipart upload.
1236
+ */
1237
+ static async getMultipartUploadPresignedUrl(key, uploadId, partNumber, expiresInMinutes) {
1238
+ return _ObjectStorageService.getObjectStorageServiceInstance().then(
1239
+ (instance) => instance.getMultipartUploadPresignedUrl(
1240
+ key,
1241
+ uploadId,
1242
+ partNumber,
1243
+ expiresInMinutes
1244
+ )
1245
+ );
1246
+ }
1048
1247
  };
1049
1248
 
1050
1249
  exports.ObjectStorageService = ObjectStorageService;