@twin.org/blob-storage-connector-aws-s3 0.0.2-next.5 → 0.0.3-next.2

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.
@@ -0,0 +1,6 @@
1
+ // Copyright 2024 IOTA Stiftung.
2
+ // SPDX-License-Identifier: Apache-2.0.
3
+ export * from "./s3BlobStorageConnector.js";
4
+ export * from "./models/IS3BlobStorageConnectorConfig.js";
5
+ export * from "./models/IS3BlobStorageConnectorConstructorOptions.js";
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,2CAA2C,CAAC;AAC1D,cAAc,uDAAuD,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nexport * from \"./s3BlobStorageConnector.js\";\nexport * from \"./models/IS3BlobStorageConnectorConfig.js\";\nexport * from \"./models/IS3BlobStorageConnectorConstructorOptions.js\";\n"]}
@@ -0,0 +1,4 @@
1
+ // Copyright 2024 IOTA Stiftung.
2
+ // SPDX-License-Identifier: Apache-2.0.
3
+ export {};
4
+ //# sourceMappingURL=IS3BlobStorageConnectorConfig.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IS3BlobStorageConnectorConfig.js","sourceRoot":"","sources":["../../../src/models/IS3BlobStorageConnectorConfig.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * Configuration for the S3 Blob Storage Connector.\n */\nexport interface IS3BlobStorageConnectorConfig {\n\t/**\n\t * The AWS region.\n\t */\n\tregion: string;\n\n\t/**\n\t * The S3 bucket name.\n\t */\n\tbucketName: string;\n\n\t/**\n\t * The authentication mode.\n\t * - \"credentials\": Use access key ID and secret access key.\n\t * - \"pod\": Use IAM role attached to the pod (e.g., in EKS).\n\t * @default credentials\n\t */\n\tauthMode?: \"credentials\" | \"pod\";\n\n\t/**\n\t * The AWS access key ID.\n\t */\n\taccessKeyId?: string;\n\n\t/**\n\t * The AWS secret access key.\n\t */\n\tsecretAccessKey?: string;\n\n\t/**\n\t * Optional endpoint for S3-compatible storage (e.g., MinIO).\n\t */\n\tendpoint?: string;\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=IS3BlobStorageConnectorConstructorOptions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IS3BlobStorageConnectorConstructorOptions.js","sourceRoot":"","sources":["../../../src/models/IS3BlobStorageConnectorConstructorOptions.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IS3BlobStorageConnectorConfig } from \"./IS3BlobStorageConnectorConfig.js\";\n\n/**\n * Options for the S3 Blob Storage Connector constructor.\n */\nexport interface IS3BlobStorageConnectorConstructorOptions {\n\t/**\n\t * The keys to use from the context ids to create partitions.\n\t */\n\tpartitionContextIds?: string[];\n\n\t/**\n\t * The configuration for the connector.\n\t */\n\tconfig: IS3BlobStorageConnectorConfig;\n}\n"]}
@@ -1,14 +1,14 @@
1
- import { S3Client, ListBucketsCommand, CreateBucketCommand, PutObjectCommand, GetObjectCommand, HeadObjectCommand, DeleteObjectCommand } from '@aws-sdk/client-s3';
2
- import { Guards, ComponentFactory, BaseError, Converter, Urn, GeneralError } from '@twin.org/core';
3
- import { Sha256 } from '@twin.org/crypto';
4
-
5
1
  // Copyright 2024 IOTA Stiftung.
6
2
  // SPDX-License-Identifier: Apache-2.0.
3
+ import { CreateBucketCommand, DeleteObjectCommand, GetObjectCommand, HeadObjectCommand, ListBucketsCommand, PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
4
+ import { ContextIdHelper, ContextIdStore } from "@twin.org/context";
5
+ import { BaseError, ComponentFactory, Converter, GeneralError, Guards, Urn } from "@twin.org/core";
6
+ import { Sha256 } from "@twin.org/crypto";
7
7
  /**
8
8
  * Class for performing blob storage operations on S3.
9
9
  * See https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/s3/ for more information.
10
10
  */
11
- class S3BlobStorageConnector {
11
+ export class S3BlobStorageConnector {
12
12
  /**
13
13
  * The namespace for the items.
14
14
  */
@@ -22,6 +22,11 @@ class S3BlobStorageConnector {
22
22
  * @internal
23
23
  */
24
24
  _config;
25
+ /**
26
+ * The keys to use from the context ids to create partitions.
27
+ * @internal
28
+ */
29
+ _partitionContextIds;
25
30
  /**
26
31
  * The S3 client.
27
32
  * @internal
@@ -46,6 +51,7 @@ class S3BlobStorageConnector {
46
51
  secretAccessKey: options.config.secretAccessKey
47
52
  };
48
53
  }
54
+ this._partitionContextIds = options.partitionContextIds;
49
55
  this._config = options.config;
50
56
  this._s3Client = new S3Client({
51
57
  region: this._config.region,
@@ -54,6 +60,13 @@ class S3BlobStorageConnector {
54
60
  forcePathStyle: true
55
61
  });
56
62
  }
63
+ /**
64
+ * Returns the class name of the component.
65
+ * @returns The class name of the component.
66
+ */
67
+ className() {
68
+ return S3BlobStorageConnector.CLASS_NAME;
69
+ }
57
70
  /**
58
71
  * Bootstrap the component by creating and initializing any resources it needs.
59
72
  * @param nodeLoggingComponentType The node logging component type.
@@ -62,14 +75,6 @@ class S3BlobStorageConnector {
62
75
  async bootstrap(nodeLoggingComponentType) {
63
76
  const nodeLogging = ComponentFactory.getIfExists(nodeLoggingComponentType);
64
77
  try {
65
- await nodeLogging?.log({
66
- level: "info",
67
- source: S3BlobStorageConnector.CLASS_NAME,
68
- message: "bucketCreating",
69
- data: {
70
- bucket: this._config.bucketName
71
- }
72
- });
73
78
  const listBucketsCommand = new ListBucketsCommand({});
74
79
  const bucketsList = await this._s3Client.send(listBucketsCommand);
75
80
  const bucketExists = bucketsList.Buckets?.some(bucket => bucket.Name === this._config.bucketName);
@@ -84,15 +89,15 @@ class S3BlobStorageConnector {
84
89
  });
85
90
  }
86
91
  else {
87
- await this._s3Client.send(new CreateBucketCommand({ Bucket: this._config.bucketName }));
88
92
  await nodeLogging?.log({
89
93
  level: "info",
90
94
  source: S3BlobStorageConnector.CLASS_NAME,
91
- message: "bucketCreated",
95
+ message: "bucketCreating",
92
96
  data: {
93
97
  bucket: this._config.bucketName
94
98
  }
95
99
  });
100
+ await this._s3Client.send(new CreateBucketCommand({ Bucket: this._config.bucketName }));
96
101
  }
97
102
  }
98
103
  catch (err) {
@@ -118,9 +123,11 @@ class S3BlobStorageConnector {
118
123
  Guards.uint8Array(S3BlobStorageConnector.CLASS_NAME, "blob", blob);
119
124
  try {
120
125
  const id = Converter.bytesToHex(Sha256.sum256(blob));
126
+ const contextIds = await ContextIdStore.getContextIds();
127
+ const partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);
121
128
  const command = new PutObjectCommand({
122
129
  Bucket: this._config.bucketName,
123
- Key: id,
130
+ Key: `${partitionKey ?? "root"}/${id}`,
124
131
  Body: blob
125
132
  });
126
133
  await this._s3Client.send(command);
@@ -138,6 +145,8 @@ class S3BlobStorageConnector {
138
145
  async get(id) {
139
146
  Urn.guard(S3BlobStorageConnector.CLASS_NAME, "id", id);
140
147
  const urnParsed = Urn.fromValidString(id);
148
+ const contextIds = await ContextIdStore.getContextIds();
149
+ const partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);
141
150
  if (urnParsed.namespaceMethod() !== S3BlobStorageConnector.NAMESPACE) {
142
151
  throw new GeneralError(S3BlobStorageConnector.CLASS_NAME, "namespaceMismatch", {
143
152
  namespace: S3BlobStorageConnector.NAMESPACE,
@@ -148,7 +157,7 @@ class S3BlobStorageConnector {
148
157
  const key = urnParsed.namespaceSpecific(1);
149
158
  const command = new GetObjectCommand({
150
159
  Bucket: this._config.bucketName,
151
- Key: key
160
+ Key: `${partitionKey ?? "root"}/${key}`
152
161
  });
153
162
  const response = await this._s3Client.send(command);
154
163
  if (response.Body) {
@@ -156,6 +165,9 @@ class S3BlobStorageConnector {
156
165
  }
157
166
  }
158
167
  catch (err) {
168
+ if (BaseError.isErrorName(err, "NoSuchKey")) {
169
+ return undefined;
170
+ }
159
171
  throw new GeneralError(S3BlobStorageConnector.CLASS_NAME, "getBlobFailed", {
160
172
  id,
161
173
  namespace: S3BlobStorageConnector.NAMESPACE
@@ -170,6 +182,8 @@ class S3BlobStorageConnector {
170
182
  async remove(id) {
171
183
  Urn.guard(S3BlobStorageConnector.CLASS_NAME, "id", id);
172
184
  const urnParsed = Urn.fromValidString(id);
185
+ const contextIds = await ContextIdStore.getContextIds();
186
+ const partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);
173
187
  if (urnParsed.namespaceMethod() !== S3BlobStorageConnector.NAMESPACE) {
174
188
  throw new GeneralError(S3BlobStorageConnector.CLASS_NAME, "namespaceMismatch", {
175
189
  namespace: S3BlobStorageConnector.NAMESPACE,
@@ -180,7 +194,7 @@ class S3BlobStorageConnector {
180
194
  const key = urnParsed.namespaceSpecific(1);
181
195
  const headCommand = new HeadObjectCommand({
182
196
  Bucket: this._config.bucketName,
183
- Key: key
197
+ Key: `${partitionKey ?? "root"}/${key}`
184
198
  });
185
199
  try {
186
200
  await this._s3Client.send(headCommand);
@@ -193,7 +207,7 @@ class S3BlobStorageConnector {
193
207
  }
194
208
  const deleteCommand = new DeleteObjectCommand({
195
209
  Bucket: this._config.bucketName,
196
- Key: key
210
+ Key: `${partitionKey ?? "root"}/${key}`
197
211
  });
198
212
  await this._s3Client.send(deleteCommand);
199
213
  return true;
@@ -203,5 +217,4 @@ class S3BlobStorageConnector {
203
217
  }
204
218
  }
205
219
  }
206
-
207
- export { S3BlobStorageConnector };
220
+ //# sourceMappingURL=s3BlobStorageConnector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"s3BlobStorageConnector.js","sourceRoot":"","sources":["../../src/s3BlobStorageConnector.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport {\n\tCreateBucketCommand,\n\tDeleteObjectCommand,\n\tGetObjectCommand,\n\tHeadObjectCommand,\n\tListBucketsCommand,\n\tPutObjectCommand,\n\tS3Client\n} from \"@aws-sdk/client-s3\";\nimport type { IBlobStorageConnector } from \"@twin.org/blob-storage-models\";\nimport { ContextIdHelper, ContextIdStore } from \"@twin.org/context\";\nimport { BaseError, ComponentFactory, Converter, GeneralError, Guards, Urn } from \"@twin.org/core\";\nimport { Sha256 } from \"@twin.org/crypto\";\nimport type { ILoggingComponent } from \"@twin.org/logging-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport type { IS3BlobStorageConnectorConfig } from \"./models/IS3BlobStorageConnectorConfig.js\";\nimport type { IS3BlobStorageConnectorConstructorOptions } from \"./models/IS3BlobStorageConnectorConstructorOptions.js\";\n\n/**\n * Class for performing blob storage operations on S3.\n * See https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/s3/ for more information.\n */\nexport class S3BlobStorageConnector implements IBlobStorageConnector {\n\t/**\n\t * The namespace for the items.\n\t */\n\tpublic static readonly NAMESPACE: string = \"s3\";\n\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<S3BlobStorageConnector>();\n\n\t/**\n\t * The configuration for the connector.\n\t * @internal\n\t */\n\tprivate readonly _config: IS3BlobStorageConnectorConfig;\n\n\t/**\n\t * The keys to use from the context ids to create partitions.\n\t * @internal\n\t */\n\tprivate readonly _partitionContextIds?: string[];\n\n\t/**\n\t * The S3 client.\n\t * @internal\n\t */\n\tprivate readonly _s3Client: S3Client;\n\n\t/**\n\t * Create a new instance of S3BlobStorageConnector.\n\t * @param options The options for the connector.\n\t */\n\tconstructor(options: IS3BlobStorageConnectorConstructorOptions) {\n\t\tGuards.object(S3BlobStorageConnector.CLASS_NAME, nameof(options), options);\n\t\tGuards.object<IS3BlobStorageConnectorConfig>(\n\t\t\tS3BlobStorageConnector.CLASS_NAME,\n\t\t\tnameof(options.config),\n\t\t\toptions.config\n\t\t);\n\t\tGuards.stringValue(\n\t\t\tS3BlobStorageConnector.CLASS_NAME,\n\t\t\tnameof(options.config.region),\n\t\t\toptions.config.region\n\t\t);\n\t\tGuards.stringValue(\n\t\t\tS3BlobStorageConnector.CLASS_NAME,\n\t\t\tnameof(options.config.bucketName),\n\t\t\toptions.config.bucketName\n\t\t);\n\n\t\toptions.config.authMode ??= \"credentials\";\n\n\t\tlet credentials;\n\t\tif (options.config.authMode === \"credentials\") {\n\t\t\tGuards.stringValue(\n\t\t\t\tS3BlobStorageConnector.CLASS_NAME,\n\t\t\t\tnameof(options.config.accessKeyId),\n\t\t\t\toptions.config.accessKeyId\n\t\t\t);\n\t\t\tGuards.stringValue(\n\t\t\t\tS3BlobStorageConnector.CLASS_NAME,\n\t\t\t\tnameof(options.config.secretAccessKey),\n\t\t\t\toptions.config.secretAccessKey\n\t\t\t);\n\t\t\tcredentials = {\n\t\t\t\taccessKeyId: options.config.accessKeyId,\n\t\t\t\tsecretAccessKey: options.config.secretAccessKey\n\t\t\t};\n\t\t}\n\n\t\tthis._partitionContextIds = options.partitionContextIds;\n\n\t\tthis._config = options.config;\n\t\tthis._s3Client = new S3Client({\n\t\t\tregion: this._config.region,\n\t\t\tendpoint: this._config.endpoint,\n\t\t\tcredentials,\n\t\t\tforcePathStyle: true\n\t\t});\n\t}\n\n\t/**\n\t * Returns the class name of the component.\n\t * @returns The class name of the component.\n\t */\n\tpublic className(): string {\n\t\treturn S3BlobStorageConnector.CLASS_NAME;\n\t}\n\n\t/**\n\t * Bootstrap the component by creating and initializing any resources it needs.\n\t * @param nodeLoggingComponentType The node logging component type.\n\t * @returns True if the bootstrapping process was successful.\n\t */\n\tpublic async bootstrap(nodeLoggingComponentType?: string): Promise<boolean> {\n\t\tconst nodeLogging = ComponentFactory.getIfExists<ILoggingComponent>(nodeLoggingComponentType);\n\n\t\ttry {\n\t\t\tconst listBucketsCommand = new ListBucketsCommand({});\n\t\t\tconst bucketsList = await this._s3Client.send(listBucketsCommand);\n\t\t\tconst bucketExists = bucketsList.Buckets?.some(\n\t\t\t\tbucket => bucket.Name === this._config.bucketName\n\t\t\t);\n\n\t\t\tif (bucketExists) {\n\t\t\t\tawait nodeLogging?.log({\n\t\t\t\t\tlevel: \"info\",\n\t\t\t\t\tsource: S3BlobStorageConnector.CLASS_NAME,\n\t\t\t\t\tmessage: \"bucketExists\",\n\t\t\t\t\tdata: {\n\t\t\t\t\t\tbucket: this._config.bucketName\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tawait nodeLogging?.log({\n\t\t\t\t\tlevel: \"info\",\n\t\t\t\t\tsource: S3BlobStorageConnector.CLASS_NAME,\n\t\t\t\t\tmessage: \"bucketCreating\",\n\t\t\t\t\tdata: {\n\t\t\t\t\t\tbucket: this._config.bucketName\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tawait this._s3Client.send(new CreateBucketCommand({ Bucket: this._config.bucketName }));\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tawait nodeLogging?.log({\n\t\t\t\tlevel: \"error\",\n\t\t\t\tsource: S3BlobStorageConnector.CLASS_NAME,\n\t\t\t\tmessage: \"bucketCreateFailed\",\n\t\t\t\tdata: {\n\t\t\t\t\tbucket: this._config.bucketName\n\t\t\t\t},\n\t\t\t\terror: BaseError.fromError(err)\n\t\t\t});\n\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Set the blob.\n\t * @param blob The data for the blob.\n\t * @returns The id of the stored blob in urn format.\n\t */\n\tpublic async set(blob: Uint8Array): Promise<string> {\n\t\tGuards.uint8Array(S3BlobStorageConnector.CLASS_NAME, nameof(blob), blob);\n\n\t\ttry {\n\t\t\tconst id = Converter.bytesToHex(Sha256.sum256(blob));\n\n\t\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\t\tconst partitionKey = ContextIdHelper.combinedContextKey(\n\t\t\t\tcontextIds,\n\t\t\t\tthis._partitionContextIds\n\t\t\t);\n\n\t\t\tconst command = new PutObjectCommand({\n\t\t\t\tBucket: this._config.bucketName,\n\t\t\t\tKey: `${partitionKey ?? \"root\"}/${id}`,\n\t\t\t\tBody: blob\n\t\t\t});\n\n\t\t\tawait this._s3Client.send(command);\n\n\t\t\treturn `blob:${new Urn(S3BlobStorageConnector.NAMESPACE, id).toString()}`;\n\t\t} catch (err) {\n\t\t\tthrow new GeneralError(S3BlobStorageConnector.CLASS_NAME, \"setBlobFailed\", undefined, err);\n\t\t}\n\t}\n\n\t/**\n\t * Get the blob.\n\t * @param id The id of the blob to get in urn format.\n\t * @returns The data for the blob if it can be found or undefined.\n\t */\n\tpublic async get(id: string): Promise<Uint8Array | undefined> {\n\t\tUrn.guard(S3BlobStorageConnector.CLASS_NAME, nameof(id), id);\n\t\tconst urnParsed = Urn.fromValidString(id);\n\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);\n\n\t\tif (urnParsed.namespaceMethod() !== S3BlobStorageConnector.NAMESPACE) {\n\t\t\tthrow new GeneralError(S3BlobStorageConnector.CLASS_NAME, \"namespaceMismatch\", {\n\t\t\t\tnamespace: S3BlobStorageConnector.NAMESPACE,\n\t\t\t\tid\n\t\t\t});\n\t\t}\n\n\t\ttry {\n\t\t\tconst key = urnParsed.namespaceSpecific(1);\n\t\t\tconst command = new GetObjectCommand({\n\t\t\t\tBucket: this._config.bucketName,\n\t\t\t\tKey: `${partitionKey ?? \"root\"}/${key}`\n\t\t\t});\n\n\t\t\tconst response = await this._s3Client.send(command);\n\n\t\t\tif (response.Body) {\n\t\t\t\treturn new Uint8Array(await response.Body.transformToByteArray());\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tif (BaseError.isErrorName(err, \"NoSuchKey\")) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\tthrow new GeneralError(\n\t\t\t\tS3BlobStorageConnector.CLASS_NAME,\n\t\t\t\t\"getBlobFailed\",\n\t\t\t\t{\n\t\t\t\t\tid,\n\t\t\t\t\tnamespace: S3BlobStorageConnector.NAMESPACE\n\t\t\t\t},\n\t\t\t\terr\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Remove the blob.\n\t * @param id The id of the blob to remove in urn format.\n\t * @returns True if the blob was found.\n\t */\n\tpublic async remove(id: string): Promise<boolean> {\n\t\tUrn.guard(S3BlobStorageConnector.CLASS_NAME, nameof(id), id);\n\t\tconst urnParsed = Urn.fromValidString(id);\n\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);\n\n\t\tif (urnParsed.namespaceMethod() !== S3BlobStorageConnector.NAMESPACE) {\n\t\t\tthrow new GeneralError(S3BlobStorageConnector.CLASS_NAME, \"namespaceMismatch\", {\n\t\t\t\tnamespace: S3BlobStorageConnector.NAMESPACE,\n\t\t\t\tid\n\t\t\t});\n\t\t}\n\n\t\ttry {\n\t\t\tconst key = urnParsed.namespaceSpecific(1);\n\n\t\t\tconst headCommand = new HeadObjectCommand({\n\t\t\t\tBucket: this._config.bucketName,\n\t\t\t\tKey: `${partitionKey ?? \"root\"}/${key}`\n\t\t\t});\n\n\t\t\ttry {\n\t\t\t\tawait this._s3Client.send(headCommand);\n\t\t\t} catch (error) {\n\t\t\t\tif (BaseError.isErrorName(error, \"NotFound\")) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tthrow error;\n\t\t\t}\n\n\t\t\tconst deleteCommand = new DeleteObjectCommand({\n\t\t\t\tBucket: this._config.bucketName,\n\t\t\t\tKey: `${partitionKey ?? \"root\"}/${key}`\n\t\t\t});\n\n\t\t\tawait this._s3Client.send(deleteCommand);\n\n\t\t\treturn true;\n\t\t} catch (err) {\n\t\t\tthrow new GeneralError(S3BlobStorageConnector.CLASS_NAME, \"removeBlobFailed\", { id }, err);\n\t\t}\n\t}\n}\n"]}
@@ -1,3 +1,3 @@
1
- export * from "./s3BlobStorageConnector";
2
- export * from "./models/IS3BlobStorageConnectorConfig";
3
- export * from "./models/IS3BlobStorageConnectorConstructorOptions";
1
+ export * from "./s3BlobStorageConnector.js";
2
+ export * from "./models/IS3BlobStorageConnectorConfig.js";
3
+ export * from "./models/IS3BlobStorageConnectorConstructorOptions.js";
@@ -1,8 +1,12 @@
1
- import type { IS3BlobStorageConnectorConfig } from "./IS3BlobStorageConnectorConfig";
1
+ import type { IS3BlobStorageConnectorConfig } from "./IS3BlobStorageConnectorConfig.js";
2
2
  /**
3
3
  * Options for the S3 Blob Storage Connector constructor.
4
4
  */
5
5
  export interface IS3BlobStorageConnectorConstructorOptions {
6
+ /**
7
+ * The keys to use from the context ids to create partitions.
8
+ */
9
+ partitionContextIds?: string[];
6
10
  /**
7
11
  * The configuration for the connector.
8
12
  */
@@ -1,5 +1,5 @@
1
1
  import type { IBlobStorageConnector } from "@twin.org/blob-storage-models";
2
- import type { IS3BlobStorageConnectorConstructorOptions } from "./models/IS3BlobStorageConnectorConstructorOptions";
2
+ import type { IS3BlobStorageConnectorConstructorOptions } from "./models/IS3BlobStorageConnectorConstructorOptions.js";
3
3
  /**
4
4
  * Class for performing blob storage operations on S3.
5
5
  * See https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/s3/ for more information.
@@ -18,6 +18,11 @@ export declare class S3BlobStorageConnector implements IBlobStorageConnector {
18
18
  * @param options The options for the connector.
19
19
  */
20
20
  constructor(options: IS3BlobStorageConnectorConstructorOptions);
21
+ /**
22
+ * Returns the class name of the component.
23
+ * @returns The class name of the component.
24
+ */
25
+ className(): string;
21
26
  /**
22
27
  * Bootstrap the component by creating and initializing any resources it needs.
23
28
  * @param nodeLoggingComponentType The node logging component type.
package/docs/changelog.md CHANGED
@@ -1,5 +1,42 @@
1
1
  # @twin.org/blob-storage-connector-aws-s3 - Changelog
2
2
 
3
+ ## [0.0.3-next.2](https://github.com/twinfoundation/blob-storage/compare/blob-storage-connector-aws-s3-v0.0.3-next.1...blob-storage-connector-aws-s3-v0.0.3-next.2) (2026-01-14)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * missing dependency ([77e92ef](https://github.com/twinfoundation/blob-storage/commit/77e92ef1db734e1859b0846e1eac6d0bdf65f952))
9
+ * missing dependency ([fa35282](https://github.com/twinfoundation/blob-storage/commit/fa35282ed0b6c9a564c0a0cc7956a759585e3f52))
10
+
11
+
12
+ ### Dependencies
13
+
14
+ * The following workspace dependencies were updated
15
+ * dependencies
16
+ * @twin.org/blob-storage-models bumped from 0.0.3-next.1 to 0.0.3-next.2
17
+
18
+ ## [0.0.3-next.1](https://github.com/twinfoundation/blob-storage/compare/blob-storage-connector-aws-s3-v0.0.3-next.0...blob-storage-connector-aws-s3-v0.0.3-next.1) (2025-11-11)
19
+
20
+
21
+ ### Features
22
+
23
+ * add AWS pod authentication mode ([c34b11c](https://github.com/twinfoundation/blob-storage/commit/c34b11cb32d3310a9e59840341a2b0b4221bc780))
24
+ * add context id features ([#30](https://github.com/twinfoundation/blob-storage/issues/30)) ([fbf1c92](https://github.com/twinfoundation/blob-storage/commit/fbf1c9276424c841ef5ef3f4de8469ab3fba7e9c))
25
+ * add validate-locales ([f20fcec](https://github.com/twinfoundation/blob-storage/commit/f20fceced91e39a0c9edb770b2e43ce944c92f3c))
26
+ * Blob storage connector for AWS S3 ([#7](https://github.com/twinfoundation/blob-storage/issues/7)) ([1d7f1e7](https://github.com/twinfoundation/blob-storage/commit/1d7f1e7d323926f7f31229d38eb5de429f6e1554))
27
+ * eslint migration to flat config ([e4239dd](https://github.com/twinfoundation/blob-storage/commit/e4239dd1c721955cff7f0357255d2bba15319972))
28
+ * update dependencies ([56f0094](https://github.com/twinfoundation/blob-storage/commit/56f0094b68d8bd22864cd899ac1b61d95540f719))
29
+ * update framework core ([ff339fe](https://github.com/twinfoundation/blob-storage/commit/ff339fe7e3f09ddff429907834bdf43617e9c05e))
30
+ * update to support fully qualified data type names ([3297d69](https://github.com/twinfoundation/blob-storage/commit/3297d69d332058b0f0141002087f56ba230620e1))
31
+ * use shared store mechanism ([#12](https://github.com/twinfoundation/blob-storage/issues/12)) ([cae8110](https://github.com/twinfoundation/blob-storage/commit/cae8110681847a1ac4fcac968b8196694e49c320))
32
+
33
+
34
+ ### Dependencies
35
+
36
+ * The following workspace dependencies were updated
37
+ * dependencies
38
+ * @twin.org/blob-storage-models bumped from 0.0.3-next.0 to 0.0.3-next.1
39
+
3
40
  ## [0.0.2-next.5](https://github.com/twinfoundation/blob-storage/compare/blob-storage-connector-aws-s3-v0.0.2-next.4...blob-storage-connector-aws-s3-v0.0.2-next.5) (2025-10-09)
4
41
 
5
42
 
@@ -45,6 +45,24 @@ Runtime name for the class.
45
45
 
46
46
  ## Methods
47
47
 
48
+ ### className()
49
+
50
+ > **className**(): `string`
51
+
52
+ Returns the class name of the component.
53
+
54
+ #### Returns
55
+
56
+ `string`
57
+
58
+ The class name of the component.
59
+
60
+ #### Implementation of
61
+
62
+ `IBlobStorageConnector.className`
63
+
64
+ ***
65
+
48
66
  ### bootstrap()
49
67
 
50
68
  > **bootstrap**(`nodeLoggingComponentType?`): `Promise`\<`boolean`\>
@@ -99,7 +117,7 @@ The id of the stored blob in urn format.
99
117
 
100
118
  ### get()
101
119
 
102
- > **get**(`id`): `Promise`\<`undefined` \| `Uint8Array`\<`ArrayBufferLike`\>\>
120
+ > **get**(`id`): `Promise`\<`Uint8Array`\<`ArrayBufferLike`\> \| `undefined`\>
103
121
 
104
122
  Get the blob.
105
123
 
@@ -113,7 +131,7 @@ The id of the blob to get in urn format.
113
131
 
114
132
  #### Returns
115
133
 
116
- `Promise`\<`undefined` \| `Uint8Array`\<`ArrayBufferLike`\>\>
134
+ `Promise`\<`Uint8Array`\<`ArrayBufferLike`\> \| `undefined`\>
117
135
 
118
136
  The data for the blob if it can be found or undefined.
119
137
 
@@ -4,6 +4,14 @@ Options for the S3 Blob Storage Connector constructor.
4
4
 
5
5
  ## Properties
6
6
 
7
+ ### partitionContextIds?
8
+
9
+ > `optional` **partitionContextIds**: `string`[]
10
+
11
+ The keys to use from the context ids to create partitions.
12
+
13
+ ***
14
+
7
15
  ### config
8
16
 
9
17
  > **config**: [`IS3BlobStorageConnectorConfig`](IS3BlobStorageConnectorConfig.md)
package/locales/en.json CHANGED
@@ -2,7 +2,6 @@
2
2
  "info": {
3
3
  "s3BlobStorageConnector": {
4
4
  "bucketCreating": "Creating bucket \"{bucket}\"",
5
- "bucketCreated": "Created bucket \"{bucket}\"",
6
5
  "bucketExists": "Skipping create bucket \"{bucket}\" as it already exists"
7
6
  }
8
7
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@twin.org/blob-storage-connector-aws-s3",
3
- "version": "0.0.2-next.5",
3
+ "version": "0.0.3-next.2",
4
4
  "description": "Blob Storage connector implementation using AWS S3",
5
5
  "repository": {
6
6
  "type": "git",
@@ -14,27 +14,26 @@
14
14
  "node": ">=20.0.0"
15
15
  },
16
16
  "dependencies": {
17
- "@aws-sdk/client-s3": "3.906.0",
18
- "@twin.org/blob-storage-models": "0.0.2-next.5",
17
+ "@aws-sdk/client-s3": "3.928.0",
18
+ "@twin.org/blob-storage-models": "0.0.3-next.2",
19
+ "@twin.org/context": "next",
19
20
  "@twin.org/core": "next",
20
21
  "@twin.org/crypto": "next",
21
22
  "@twin.org/logging-models": "next",
22
23
  "@twin.org/nameof": "next"
23
24
  },
24
- "main": "./dist/cjs/index.cjs",
25
- "module": "./dist/esm/index.mjs",
25
+ "main": "./dist/es/index.js",
26
26
  "types": "./dist/types/index.d.ts",
27
27
  "exports": {
28
28
  ".": {
29
29
  "types": "./dist/types/index.d.ts",
30
- "require": "./dist/cjs/index.cjs",
31
- "import": "./dist/esm/index.mjs"
30
+ "import": "./dist/es/index.js",
31
+ "default": "./dist/es/index.js"
32
32
  },
33
33
  "./locales/*.json": "./locales/*.json"
34
34
  },
35
35
  "files": [
36
- "dist/cjs",
37
- "dist/esm",
36
+ "dist/es",
38
37
  "dist/types",
39
38
  "locales",
40
39
  "docs"
@@ -1,209 +0,0 @@
1
- 'use strict';
2
-
3
- var clientS3 = require('@aws-sdk/client-s3');
4
- var core = require('@twin.org/core');
5
- var crypto = require('@twin.org/crypto');
6
-
7
- // Copyright 2024 IOTA Stiftung.
8
- // SPDX-License-Identifier: Apache-2.0.
9
- /**
10
- * Class for performing blob storage operations on S3.
11
- * See https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/s3/ for more information.
12
- */
13
- class S3BlobStorageConnector {
14
- /**
15
- * The namespace for the items.
16
- */
17
- static NAMESPACE = "s3";
18
- /**
19
- * Runtime name for the class.
20
- */
21
- static CLASS_NAME = "S3BlobStorageConnector";
22
- /**
23
- * The configuration for the connector.
24
- * @internal
25
- */
26
- _config;
27
- /**
28
- * The S3 client.
29
- * @internal
30
- */
31
- _s3Client;
32
- /**
33
- * Create a new instance of S3BlobStorageConnector.
34
- * @param options The options for the connector.
35
- */
36
- constructor(options) {
37
- core.Guards.object(S3BlobStorageConnector.CLASS_NAME, "options", options);
38
- core.Guards.object(S3BlobStorageConnector.CLASS_NAME, "options.config", options.config);
39
- core.Guards.stringValue(S3BlobStorageConnector.CLASS_NAME, "options.config.region", options.config.region);
40
- core.Guards.stringValue(S3BlobStorageConnector.CLASS_NAME, "options.config.bucketName", options.config.bucketName);
41
- options.config.authMode ??= "credentials";
42
- let credentials;
43
- if (options.config.authMode === "credentials") {
44
- core.Guards.stringValue(S3BlobStorageConnector.CLASS_NAME, "options.config.accessKeyId", options.config.accessKeyId);
45
- core.Guards.stringValue(S3BlobStorageConnector.CLASS_NAME, "options.config.secretAccessKey", options.config.secretAccessKey);
46
- credentials = {
47
- accessKeyId: options.config.accessKeyId,
48
- secretAccessKey: options.config.secretAccessKey
49
- };
50
- }
51
- this._config = options.config;
52
- this._s3Client = new clientS3.S3Client({
53
- region: this._config.region,
54
- endpoint: this._config.endpoint,
55
- credentials,
56
- forcePathStyle: true
57
- });
58
- }
59
- /**
60
- * Bootstrap the component by creating and initializing any resources it needs.
61
- * @param nodeLoggingComponentType The node logging component type.
62
- * @returns True if the bootstrapping process was successful.
63
- */
64
- async bootstrap(nodeLoggingComponentType) {
65
- const nodeLogging = core.ComponentFactory.getIfExists(nodeLoggingComponentType);
66
- try {
67
- await nodeLogging?.log({
68
- level: "info",
69
- source: S3BlobStorageConnector.CLASS_NAME,
70
- message: "bucketCreating",
71
- data: {
72
- bucket: this._config.bucketName
73
- }
74
- });
75
- const listBucketsCommand = new clientS3.ListBucketsCommand({});
76
- const bucketsList = await this._s3Client.send(listBucketsCommand);
77
- const bucketExists = bucketsList.Buckets?.some(bucket => bucket.Name === this._config.bucketName);
78
- if (bucketExists) {
79
- await nodeLogging?.log({
80
- level: "info",
81
- source: S3BlobStorageConnector.CLASS_NAME,
82
- message: "bucketExists",
83
- data: {
84
- bucket: this._config.bucketName
85
- }
86
- });
87
- }
88
- else {
89
- await this._s3Client.send(new clientS3.CreateBucketCommand({ Bucket: this._config.bucketName }));
90
- await nodeLogging?.log({
91
- level: "info",
92
- source: S3BlobStorageConnector.CLASS_NAME,
93
- message: "bucketCreated",
94
- data: {
95
- bucket: this._config.bucketName
96
- }
97
- });
98
- }
99
- }
100
- catch (err) {
101
- await nodeLogging?.log({
102
- level: "error",
103
- source: S3BlobStorageConnector.CLASS_NAME,
104
- message: "bucketCreateFailed",
105
- data: {
106
- bucket: this._config.bucketName
107
- },
108
- error: core.BaseError.fromError(err)
109
- });
110
- return false;
111
- }
112
- return true;
113
- }
114
- /**
115
- * Set the blob.
116
- * @param blob The data for the blob.
117
- * @returns The id of the stored blob in urn format.
118
- */
119
- async set(blob) {
120
- core.Guards.uint8Array(S3BlobStorageConnector.CLASS_NAME, "blob", blob);
121
- try {
122
- const id = core.Converter.bytesToHex(crypto.Sha256.sum256(blob));
123
- const command = new clientS3.PutObjectCommand({
124
- Bucket: this._config.bucketName,
125
- Key: id,
126
- Body: blob
127
- });
128
- await this._s3Client.send(command);
129
- return `blob:${new core.Urn(S3BlobStorageConnector.NAMESPACE, id).toString()}`;
130
- }
131
- catch (err) {
132
- throw new core.GeneralError(S3BlobStorageConnector.CLASS_NAME, "setBlobFailed", undefined, err);
133
- }
134
- }
135
- /**
136
- * Get the blob.
137
- * @param id The id of the blob to get in urn format.
138
- * @returns The data for the blob if it can be found or undefined.
139
- */
140
- async get(id) {
141
- core.Urn.guard(S3BlobStorageConnector.CLASS_NAME, "id", id);
142
- const urnParsed = core.Urn.fromValidString(id);
143
- if (urnParsed.namespaceMethod() !== S3BlobStorageConnector.NAMESPACE) {
144
- throw new core.GeneralError(S3BlobStorageConnector.CLASS_NAME, "namespaceMismatch", {
145
- namespace: S3BlobStorageConnector.NAMESPACE,
146
- id
147
- });
148
- }
149
- try {
150
- const key = urnParsed.namespaceSpecific(1);
151
- const command = new clientS3.GetObjectCommand({
152
- Bucket: this._config.bucketName,
153
- Key: key
154
- });
155
- const response = await this._s3Client.send(command);
156
- if (response.Body) {
157
- return new Uint8Array(await response.Body.transformToByteArray());
158
- }
159
- }
160
- catch (err) {
161
- throw new core.GeneralError(S3BlobStorageConnector.CLASS_NAME, "getBlobFailed", {
162
- id,
163
- namespace: S3BlobStorageConnector.NAMESPACE
164
- }, err);
165
- }
166
- }
167
- /**
168
- * Remove the blob.
169
- * @param id The id of the blob to remove in urn format.
170
- * @returns True if the blob was found.
171
- */
172
- async remove(id) {
173
- core.Urn.guard(S3BlobStorageConnector.CLASS_NAME, "id", id);
174
- const urnParsed = core.Urn.fromValidString(id);
175
- if (urnParsed.namespaceMethod() !== S3BlobStorageConnector.NAMESPACE) {
176
- throw new core.GeneralError(S3BlobStorageConnector.CLASS_NAME, "namespaceMismatch", {
177
- namespace: S3BlobStorageConnector.NAMESPACE,
178
- id
179
- });
180
- }
181
- try {
182
- const key = urnParsed.namespaceSpecific(1);
183
- const headCommand = new clientS3.HeadObjectCommand({
184
- Bucket: this._config.bucketName,
185
- Key: key
186
- });
187
- try {
188
- await this._s3Client.send(headCommand);
189
- }
190
- catch (error) {
191
- if (core.BaseError.isErrorName(error, "NotFound")) {
192
- return false;
193
- }
194
- throw error;
195
- }
196
- const deleteCommand = new clientS3.DeleteObjectCommand({
197
- Bucket: this._config.bucketName,
198
- Key: key
199
- });
200
- await this._s3Client.send(deleteCommand);
201
- return true;
202
- }
203
- catch (err) {
204
- throw new core.GeneralError(S3BlobStorageConnector.CLASS_NAME, "removeBlobFailed", { id }, err);
205
- }
206
- }
207
- }
208
-
209
- exports.S3BlobStorageConnector = S3BlobStorageConnector;