@twin.org/blob-storage-connector-gcp 0.0.2-next.4 → 0.0.3-next.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.
@@ -1,15 +1,15 @@
1
- import { Storage } from '@google-cloud/storage';
2
- import { Guards, Is, ObjectHelper, Converter, ComponentFactory, BaseError, Urn, GeneralError } from '@twin.org/core';
3
- import { Sha256 } from '@twin.org/crypto';
4
- import { MimeTypes } from '@twin.org/web';
5
-
6
1
  // Copyright 2024 IOTA Stiftung.
7
2
  // SPDX-License-Identifier: Apache-2.0.
3
+ import { Storage } from "@google-cloud/storage";
4
+ import { ContextIdHelper, ContextIdStore } from "@twin.org/context";
5
+ import { BaseError, ComponentFactory, Converter, GeneralError, Guards, Is, ObjectHelper, Urn } from "@twin.org/core";
6
+ import { Sha256 } from "@twin.org/crypto";
7
+ import { MimeTypes } from "@twin.org/web";
8
8
  /**
9
9
  * Class for performing blob storage operations on GCP Storage.
10
10
  * See https://cloud.google.com/storage/docs/reference/libraries for more information.
11
11
  */
12
- class GcpBlobStorageConnector {
12
+ export class GcpBlobStorageConnector {
13
13
  /**
14
14
  * The namespace for the items.
15
15
  */
@@ -17,12 +17,17 @@ class GcpBlobStorageConnector {
17
17
  /**
18
18
  * Runtime name for the class.
19
19
  */
20
- CLASS_NAME = "GcpBlobStorageConnector";
20
+ static CLASS_NAME = "GcpBlobStorageConnector";
21
21
  /**
22
22
  * The configuration for the connector.
23
23
  * @internal
24
24
  */
25
25
  _config;
26
+ /**
27
+ * The keys to use from the context ids to create partitions.
28
+ * @internal
29
+ */
30
+ _partitionContextIds;
26
31
  /**
27
32
  * The GCP Storage client.
28
33
  * @internal
@@ -33,22 +38,30 @@ class GcpBlobStorageConnector {
33
38
  * @param options The options for the connector.
34
39
  */
35
40
  constructor(options) {
36
- Guards.object(this.CLASS_NAME, "options", options);
37
- Guards.object(this.CLASS_NAME, "options.config", options.config);
38
- Guards.stringValue(this.CLASS_NAME, "options.config.projectId", options.config.projectId);
41
+ Guards.object(GcpBlobStorageConnector.CLASS_NAME, "options", options);
42
+ Guards.object(GcpBlobStorageConnector.CLASS_NAME, "options.config", options.config);
43
+ Guards.stringValue(GcpBlobStorageConnector.CLASS_NAME, "options.config.projectId", options.config.projectId);
39
44
  let credentials;
40
45
  if (!Is.empty(options.config.credentials)) {
41
- Guards.stringBase64(this.CLASS_NAME, "options.config.credentials", options.config.credentials);
46
+ Guards.stringBase64(GcpBlobStorageConnector.CLASS_NAME, "options.config.credentials", options.config.credentials);
42
47
  credentials = ObjectHelper.fromBytes(Converter.base64ToBytes(options.config.credentials));
43
48
  }
44
- Guards.stringValue(this.CLASS_NAME, "options.config.bucketName", options.config.bucketName);
49
+ Guards.stringValue(GcpBlobStorageConnector.CLASS_NAME, "options.config.bucketName", options.config.bucketName);
45
50
  this._config = options.config;
51
+ this._partitionContextIds = options.partitionContextIds;
46
52
  this._storage = new Storage({
47
53
  projectId: this._config.projectId,
48
54
  apiEndpoint: this._config.apiEndpoint,
49
55
  credentials
50
56
  });
51
57
  }
58
+ /**
59
+ * Returns the class name of the component.
60
+ * @returns The class name of the component.
61
+ */
62
+ className() {
63
+ return GcpBlobStorageConnector.CLASS_NAME;
64
+ }
52
65
  /**
53
66
  * Bootstrap the component by creating and initializing any resources it needs.
54
67
  * @param nodeLoggingComponentType The node logging component type.
@@ -57,20 +70,12 @@ class GcpBlobStorageConnector {
57
70
  async bootstrap(nodeLoggingComponentType) {
58
71
  const nodeLogging = ComponentFactory.getIfExists(nodeLoggingComponentType);
59
72
  try {
60
- await nodeLogging?.log({
61
- level: "info",
62
- source: this.CLASS_NAME,
63
- message: "bucketCreating",
64
- data: {
65
- bucket: this._config.bucketName
66
- }
67
- });
68
73
  const [buckets] = await this._storage.getBuckets();
69
74
  const bucketExists = buckets.some(bucket => bucket.name === this._config.bucketName);
70
75
  if (bucketExists) {
71
76
  await nodeLogging?.log({
72
77
  level: "info",
73
- source: this.CLASS_NAME,
78
+ source: GcpBlobStorageConnector.CLASS_NAME,
74
79
  message: "bucketExists",
75
80
  data: {
76
81
  bucket: this._config.bucketName
@@ -78,21 +83,21 @@ class GcpBlobStorageConnector {
78
83
  });
79
84
  }
80
85
  else {
81
- await this._storage.createBucket(this._config.bucketName);
82
86
  await nodeLogging?.log({
83
87
  level: "info",
84
- source: this.CLASS_NAME,
85
- message: "bucketCreated",
88
+ source: GcpBlobStorageConnector.CLASS_NAME,
89
+ message: "bucketCreating",
86
90
  data: {
87
91
  bucket: this._config.bucketName
88
92
  }
89
93
  });
94
+ await this._storage.createBucket(this._config.bucketName);
90
95
  }
91
96
  }
92
97
  catch (err) {
93
98
  await nodeLogging?.log({
94
99
  level: "error",
95
- source: this.CLASS_NAME,
100
+ source: GcpBlobStorageConnector.CLASS_NAME,
96
101
  message: "bucketCreateFailed",
97
102
  data: {
98
103
  bucket: this._config.bucketName
@@ -109,18 +114,20 @@ class GcpBlobStorageConnector {
109
114
  * @returns The id of the stored blob in urn format.
110
115
  */
111
116
  async set(blob) {
112
- Guards.uint8Array(this.CLASS_NAME, "blob", blob);
117
+ Guards.uint8Array(GcpBlobStorageConnector.CLASS_NAME, "blob", blob);
118
+ const contextIds = await ContextIdStore.getContextIds();
119
+ const partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);
113
120
  try {
114
121
  const id = Converter.bytesToHex(Sha256.sum256(blob));
115
122
  const bucket = this._storage.bucket(this._config.bucketName);
116
- const file = bucket.file(id);
123
+ const file = bucket.file(`${partitionKey ?? "root"}/${id}`);
117
124
  await file.save(blob, {
118
125
  contentType: MimeTypes.OctetStream
119
126
  });
120
127
  return `blob:${new Urn(GcpBlobStorageConnector.NAMESPACE, id).toString()}`;
121
128
  }
122
129
  catch (err) {
123
- throw new GeneralError(this.CLASS_NAME, "setBlobFailed", undefined, err);
130
+ throw new GeneralError(GcpBlobStorageConnector.CLASS_NAME, "setBlobFailed", undefined, err);
124
131
  }
125
132
  }
126
133
  /**
@@ -129,10 +136,12 @@ class GcpBlobStorageConnector {
129
136
  * @returns The data for the blob if it can be found or undefined.
130
137
  */
131
138
  async get(id) {
132
- Urn.guard(this.CLASS_NAME, "id", id);
139
+ Urn.guard(GcpBlobStorageConnector.CLASS_NAME, "id", id);
140
+ const contextIds = await ContextIdStore.getContextIds();
141
+ const partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);
133
142
  const urnParsed = Urn.fromValidString(id);
134
143
  if (urnParsed.namespaceMethod() !== GcpBlobStorageConnector.NAMESPACE) {
135
- throw new GeneralError(this.CLASS_NAME, "namespaceMismatch", {
144
+ throw new GeneralError(GcpBlobStorageConnector.CLASS_NAME, "namespaceMismatch", {
136
145
  namespace: GcpBlobStorageConnector.NAMESPACE,
137
146
  id
138
147
  });
@@ -140,7 +149,7 @@ class GcpBlobStorageConnector {
140
149
  try {
141
150
  const key = urnParsed.namespaceSpecific(1);
142
151
  const bucket = this._storage.bucket(this._config.bucketName);
143
- const file = bucket.file(key);
152
+ const file = bucket.file(`${partitionKey ?? "root"}/${key}`);
144
153
  const [exists] = await file.exists();
145
154
  if (!exists) {
146
155
  return undefined;
@@ -149,7 +158,7 @@ class GcpBlobStorageConnector {
149
158
  return new Uint8Array(contents);
150
159
  }
151
160
  catch (err) {
152
- throw new GeneralError(this.CLASS_NAME, "getBlobFailed", { id }, err);
161
+ throw new GeneralError(GcpBlobStorageConnector.CLASS_NAME, "getBlobFailed", { id }, err);
153
162
  }
154
163
  }
155
164
  /**
@@ -158,10 +167,12 @@ class GcpBlobStorageConnector {
158
167
  * @returns True if the blob was found.
159
168
  */
160
169
  async remove(id) {
161
- Urn.guard(this.CLASS_NAME, "id", id);
170
+ Urn.guard(GcpBlobStorageConnector.CLASS_NAME, "id", id);
171
+ const contextIds = await ContextIdStore.getContextIds();
172
+ const partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);
162
173
  const urnParsed = Urn.fromValidString(id);
163
174
  if (urnParsed.namespaceMethod() !== GcpBlobStorageConnector.NAMESPACE) {
164
- throw new GeneralError(this.CLASS_NAME, "namespaceMismatch", {
175
+ throw new GeneralError(GcpBlobStorageConnector.CLASS_NAME, "namespaceMismatch", {
165
176
  namespace: GcpBlobStorageConnector.NAMESPACE,
166
177
  id
167
178
  });
@@ -169,7 +180,7 @@ class GcpBlobStorageConnector {
169
180
  try {
170
181
  const key = urnParsed.namespaceSpecific(1);
171
182
  const bucket = this._storage.bucket(this._config.bucketName);
172
- const file = bucket.file(key);
183
+ const file = bucket.file(`${partitionKey ?? "root"}/${key}`);
173
184
  const [exists] = await file.exists();
174
185
  if (!exists) {
175
186
  return false;
@@ -178,9 +189,8 @@ class GcpBlobStorageConnector {
178
189
  return true;
179
190
  }
180
191
  catch (err) {
181
- throw new GeneralError(this.CLASS_NAME, "removeBlobFailed", { id }, err);
192
+ throw new GeneralError(GcpBlobStorageConnector.CLASS_NAME, "removeBlobFailed", { id }, err);
182
193
  }
183
194
  }
184
195
  }
185
-
186
- export { GcpBlobStorageConnector };
196
+ //# sourceMappingURL=gcpBlobStorageConnector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gcpBlobStorageConnector.js","sourceRoot":"","sources":["../../src/gcpBlobStorageConnector.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAEhD,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,EACN,SAAS,EACT,gBAAgB,EAChB,SAAS,EACT,YAAY,EACZ,MAAM,EACN,EAAE,EACF,YAAY,EACZ,GAAG,EACH,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAG1C,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAK1C;;;GAGG;AACH,MAAM,OAAO,uBAAuB;IACnC;;OAEG;IACI,MAAM,CAAU,SAAS,GAAW,KAAK,CAAC;IAEjD;;OAEG;IACI,MAAM,CAAU,UAAU,6BAA6C;IAE9E;;;OAGG;IACc,OAAO,CAAiC;IAEzD;;;OAGG;IACc,oBAAoB,CAAY;IAEjD;;;OAGG;IACc,QAAQ,CAAU;IAEnC;;;OAGG;IACH,YAAY,OAAmD;QAC9D,MAAM,CAAC,MAAM,CAAC,uBAAuB,CAAC,UAAU,aAAmB,OAAO,CAAC,CAAC;QAC5E,MAAM,CAAC,MAAM,CACZ,uBAAuB,CAAC,UAAU,oBAElC,OAAO,CAAC,MAAM,CACd,CAAC;QACF,MAAM,CAAC,WAAW,CACjB,uBAAuB,CAAC,UAAU,8BAElC,OAAO,CAAC,MAAM,CAAC,SAAS,CACxB,CAAC;QAEF,IAAI,WAAiC,CAAC;QACtC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC;YAC3C,MAAM,CAAC,YAAY,CAClB,uBAAuB,CAAC,UAAU,gCAElC,OAAO,CAAC,MAAM,CAAC,WAAW,CAC1B,CAAC;YACF,WAAW,GAAG,YAAY,CAAC,SAAS,CACnC,SAAS,CAAC,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC,CACnD,CAAC;QACH,CAAC;QAED,MAAM,CAAC,WAAW,CACjB,uBAAuB,CAAC,UAAU,+BAElC,OAAO,CAAC,MAAM,CAAC,UAAU,CACzB,CAAC;QAEF,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC;QAC9B,IAAI,CAAC,oBAAoB,GAAG,OAAO,CAAC,mBAAmB,CAAC;QACxD,IAAI,CAAC,QAAQ,GAAG,IAAI,OAAO,CAAC;YAC3B,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,SAAS;YACjC,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,WAAW;YACrC,WAAW;SACX,CAAC,CAAC;IACJ,CAAC;IAED;;;OAGG;IACI,SAAS;QACf,OAAO,uBAAuB,CAAC,UAAU,CAAC;IAC3C,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,SAAS,CAAC,wBAAiC;QACvD,MAAM,WAAW,GAAG,gBAAgB,CAAC,WAAW,CAAoB,wBAAwB,CAAC,CAAC;QAE9F,IAAI,CAAC;YACJ,MAAM,CAAC,OAAO,CAAC,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC;YACnD,MAAM,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YAErF,IAAI,YAAY,EAAE,CAAC;gBAClB,MAAM,WAAW,EAAE,GAAG,CAAC;oBACtB,KAAK,EAAE,MAAM;oBACb,MAAM,EAAE,uBAAuB,CAAC,UAAU;oBAC1C,OAAO,EAAE,cAAc;oBACvB,IAAI,EAAE;wBACL,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,UAAU;qBAC/B;iBACD,CAAC,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACP,MAAM,WAAW,EAAE,GAAG,CAAC;oBACtB,KAAK,EAAE,MAAM;oBACb,MAAM,EAAE,uBAAuB,CAAC,UAAU;oBAC1C,OAAO,EAAE,gBAAgB;oBACzB,IAAI,EAAE;wBACL,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,UAAU;qBAC/B;iBACD,CAAC,CAAC;gBACH,MAAM,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YAC3D,CAAC;QACF,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,MAAM,WAAW,EAAE,GAAG,CAAC;gBACtB,KAAK,EAAE,OAAO;gBACd,MAAM,EAAE,uBAAuB,CAAC,UAAU;gBAC1C,OAAO,EAAE,oBAAoB;gBAC7B,IAAI,EAAE;oBACL,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,UAAU;iBAC/B;gBACD,KAAK,EAAE,SAAS,CAAC,SAAS,CAAC,GAAG,CAAC;aAC/B,CAAC,CAAC;YAEH,OAAO,KAAK,CAAC;QACd,CAAC;QAED,OAAO,IAAI,CAAC;IACb,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,GAAG,CAAC,IAAgB;QAChC,MAAM,CAAC,UAAU,CAAC,uBAAuB,CAAC,UAAU,UAAgB,IAAI,CAAC,CAAC;QAE1E,MAAM,UAAU,GAAG,MAAM,cAAc,CAAC,aAAa,EAAE,CAAC;QACxD,MAAM,YAAY,GAAG,eAAe,CAAC,kBAAkB,CAAC,UAAU,EAAE,IAAI,CAAC,oBAAoB,CAAC,CAAC;QAE/F,IAAI,CAAC;YACJ,MAAM,EAAE,GAAG,SAAS,CAAC,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;YACrD,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YAC7D,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,YAAY,IAAI,MAAM,IAAI,EAAE,EAAE,CAAC,CAAC;YAE5D,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;gBACrB,WAAW,EAAE,SAAS,CAAC,WAAW;aAClC,CAAC,CAAC;YAEH,OAAO,QAAQ,IAAI,GAAG,CAAC,uBAAuB,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC;QAC5E,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,MAAM,IAAI,YAAY,CAAC,uBAAuB,CAAC,UAAU,EAAE,eAAe,EAAE,SAAS,EAAE,GAAG,CAAC,CAAC;QAC7F,CAAC;IACF,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,GAAG,CAAC,EAAU;QAC1B,GAAG,CAAC,KAAK,CAAC,uBAAuB,CAAC,UAAU,QAAc,EAAE,CAAC,CAAC;QAE9D,MAAM,UAAU,GAAG,MAAM,cAAc,CAAC,aAAa,EAAE,CAAC;QACxD,MAAM,YAAY,GAAG,eAAe,CAAC,kBAAkB,CAAC,UAAU,EAAE,IAAI,CAAC,oBAAoB,CAAC,CAAC;QAE/F,MAAM,SAAS,GAAG,GAAG,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC;QAE1C,IAAI,SAAS,CAAC,eAAe,EAAE,KAAK,uBAAuB,CAAC,SAAS,EAAE,CAAC;YACvE,MAAM,IAAI,YAAY,CAAC,uBAAuB,CAAC,UAAU,EAAE,mBAAmB,EAAE;gBAC/E,SAAS,EAAE,uBAAuB,CAAC,SAAS;gBAC5C,EAAE;aACF,CAAC,CAAC;QACJ,CAAC;QAED,IAAI,CAAC;YACJ,MAAM,GAAG,GAAG,SAAS,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC;YAC3C,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YAC7D,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,YAAY,IAAI,MAAM,IAAI,GAAG,EAAE,CAAC,CAAC;YAE7D,MAAM,CAAC,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;YACrC,IAAI,CAAC,MAAM,EAAE,CAAC;gBACb,OAAO,SAAS,CAAC;YAClB,CAAC;YAED,MAAM,CAAC,QAAQ,CAAC,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;YACzC,OAAO,IAAI,UAAU,CAAC,QAAQ,CAAC,CAAC;QACjC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,MAAM,IAAI,YAAY,CAAC,uBAAuB,CAAC,UAAU,EAAE,eAAe,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC;QAC1F,CAAC;IACF,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,MAAM,CAAC,EAAU;QAC7B,GAAG,CAAC,KAAK,CAAC,uBAAuB,CAAC,UAAU,QAAc,EAAE,CAAC,CAAC;QAE9D,MAAM,UAAU,GAAG,MAAM,cAAc,CAAC,aAAa,EAAE,CAAC;QACxD,MAAM,YAAY,GAAG,eAAe,CAAC,kBAAkB,CAAC,UAAU,EAAE,IAAI,CAAC,oBAAoB,CAAC,CAAC;QAE/F,MAAM,SAAS,GAAG,GAAG,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC;QAE1C,IAAI,SAAS,CAAC,eAAe,EAAE,KAAK,uBAAuB,CAAC,SAAS,EAAE,CAAC;YACvE,MAAM,IAAI,YAAY,CAAC,uBAAuB,CAAC,UAAU,EAAE,mBAAmB,EAAE;gBAC/E,SAAS,EAAE,uBAAuB,CAAC,SAAS;gBAC5C,EAAE;aACF,CAAC,CAAC;QACJ,CAAC;QAED,IAAI,CAAC;YACJ,MAAM,GAAG,GAAG,SAAS,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC;YAC3C,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YAC7D,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,YAAY,IAAI,MAAM,IAAI,GAAG,EAAE,CAAC,CAAC;YAE7D,MAAM,CAAC,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;YACrC,IAAI,CAAC,MAAM,EAAE,CAAC;gBACb,OAAO,KAAK,CAAC;YACd,CAAC;YAED,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;YACpB,OAAO,IAAI,CAAC;QACb,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,MAAM,IAAI,YAAY,CAAC,uBAAuB,CAAC,UAAU,EAAE,kBAAkB,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC;QAC7F,CAAC;IACF,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { Storage } from \"@google-cloud/storage\";\nimport type { IBlobStorageConnector } from \"@twin.org/blob-storage-models\";\nimport { ContextIdHelper, ContextIdStore } from \"@twin.org/context\";\nimport {\n\tBaseError,\n\tComponentFactory,\n\tConverter,\n\tGeneralError,\n\tGuards,\n\tIs,\n\tObjectHelper,\n\tUrn\n} 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 { MimeTypes } from \"@twin.org/web\";\nimport type { JWTInput } from \"google-auth-library\";\nimport type { IGcpBlobStorageConnectorConfig } from \"./models/IGcpBlobStorageConnectorConfig.js\";\nimport type { IGcpBlobStorageConnectorConstructorOptions } from \"./models/IGcpBlobStorageConnectorConstructorOptions.js\";\n\n/**\n * Class for performing blob storage operations on GCP Storage.\n * See https://cloud.google.com/storage/docs/reference/libraries for more information.\n */\nexport class GcpBlobStorageConnector implements IBlobStorageConnector {\n\t/**\n\t * The namespace for the items.\n\t */\n\tpublic static readonly NAMESPACE: string = \"gcp\";\n\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<GcpBlobStorageConnector>();\n\n\t/**\n\t * The configuration for the connector.\n\t * @internal\n\t */\n\tprivate readonly _config: IGcpBlobStorageConnectorConfig;\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 GCP Storage client.\n\t * @internal\n\t */\n\tprivate readonly _storage: Storage;\n\n\t/**\n\t * Create a new instance of GcpBlobStorageConnector.\n\t * @param options The options for the connector.\n\t */\n\tconstructor(options: IGcpBlobStorageConnectorConstructorOptions) {\n\t\tGuards.object(GcpBlobStorageConnector.CLASS_NAME, nameof(options), options);\n\t\tGuards.object<IGcpBlobStorageConnectorConfig>(\n\t\t\tGcpBlobStorageConnector.CLASS_NAME,\n\t\t\tnameof(options.config),\n\t\t\toptions.config\n\t\t);\n\t\tGuards.stringValue(\n\t\t\tGcpBlobStorageConnector.CLASS_NAME,\n\t\t\tnameof(options.config.projectId),\n\t\t\toptions.config.projectId\n\t\t);\n\n\t\tlet credentials: JWTInput | undefined;\n\t\tif (!Is.empty(options.config.credentials)) {\n\t\t\tGuards.stringBase64(\n\t\t\t\tGcpBlobStorageConnector.CLASS_NAME,\n\t\t\t\tnameof(options.config.credentials),\n\t\t\t\toptions.config.credentials\n\t\t\t);\n\t\t\tcredentials = ObjectHelper.fromBytes<JWTInput>(\n\t\t\t\tConverter.base64ToBytes(options.config.credentials)\n\t\t\t);\n\t\t}\n\n\t\tGuards.stringValue(\n\t\t\tGcpBlobStorageConnector.CLASS_NAME,\n\t\t\tnameof(options.config.bucketName),\n\t\t\toptions.config.bucketName\n\t\t);\n\n\t\tthis._config = options.config;\n\t\tthis._partitionContextIds = options.partitionContextIds;\n\t\tthis._storage = new Storage({\n\t\t\tprojectId: this._config.projectId,\n\t\t\tapiEndpoint: this._config.apiEndpoint,\n\t\t\tcredentials\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 GcpBlobStorageConnector.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 [buckets] = await this._storage.getBuckets();\n\t\t\tconst bucketExists = buckets.some(bucket => bucket.name === this._config.bucketName);\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: GcpBlobStorageConnector.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: GcpBlobStorageConnector.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._storage.createBucket(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: GcpBlobStorageConnector.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(GcpBlobStorageConnector.CLASS_NAME, nameof(blob), blob);\n\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);\n\n\t\ttry {\n\t\t\tconst id = Converter.bytesToHex(Sha256.sum256(blob));\n\t\t\tconst bucket = this._storage.bucket(this._config.bucketName);\n\t\t\tconst file = bucket.file(`${partitionKey ?? \"root\"}/${id}`);\n\n\t\t\tawait file.save(blob, {\n\t\t\t\tcontentType: MimeTypes.OctetStream\n\t\t\t});\n\n\t\t\treturn `blob:${new Urn(GcpBlobStorageConnector.NAMESPACE, id).toString()}`;\n\t\t} catch (err) {\n\t\t\tthrow new GeneralError(GcpBlobStorageConnector.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(GcpBlobStorageConnector.CLASS_NAME, nameof(id), id);\n\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);\n\n\t\tconst urnParsed = Urn.fromValidString(id);\n\n\t\tif (urnParsed.namespaceMethod() !== GcpBlobStorageConnector.NAMESPACE) {\n\t\t\tthrow new GeneralError(GcpBlobStorageConnector.CLASS_NAME, \"namespaceMismatch\", {\n\t\t\t\tnamespace: GcpBlobStorageConnector.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 bucket = this._storage.bucket(this._config.bucketName);\n\t\t\tconst file = bucket.file(`${partitionKey ?? \"root\"}/${key}`);\n\n\t\t\tconst [exists] = await file.exists();\n\t\t\tif (!exists) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\tconst [contents] = await file.download();\n\t\t\treturn new Uint8Array(contents);\n\t\t} catch (err) {\n\t\t\tthrow new GeneralError(GcpBlobStorageConnector.CLASS_NAME, \"getBlobFailed\", { id }, err);\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(GcpBlobStorageConnector.CLASS_NAME, nameof(id), id);\n\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);\n\n\t\tconst urnParsed = Urn.fromValidString(id);\n\n\t\tif (urnParsed.namespaceMethod() !== GcpBlobStorageConnector.NAMESPACE) {\n\t\t\tthrow new GeneralError(GcpBlobStorageConnector.CLASS_NAME, \"namespaceMismatch\", {\n\t\t\t\tnamespace: GcpBlobStorageConnector.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 bucket = this._storage.bucket(this._config.bucketName);\n\t\t\tconst file = bucket.file(`${partitionKey ?? \"root\"}/${key}`);\n\n\t\t\tconst [exists] = await file.exists();\n\t\t\tif (!exists) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tawait file.delete();\n\t\t\treturn true;\n\t\t} catch (err) {\n\t\t\tthrow new GeneralError(GcpBlobStorageConnector.CLASS_NAME, \"removeBlobFailed\", { id }, err);\n\t\t}\n\t}\n}\n"]}
@@ -0,0 +1,6 @@
1
+ // Copyright 2024 IOTA Stiftung.
2
+ // SPDX-License-Identifier: Apache-2.0.
3
+ export * from "./gcpBlobStorageConnector.js";
4
+ export * from "./models/IGcpBlobStorageConnectorConfig.js";
5
+ export * from "./models/IGcpBlobStorageConnectorConstructorOptions.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,8BAA8B,CAAC;AAC7C,cAAc,4CAA4C,CAAC;AAC3D,cAAc,wDAAwD,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nexport * from \"./gcpBlobStorageConnector.js\";\nexport * from \"./models/IGcpBlobStorageConnectorConfig.js\";\nexport * from \"./models/IGcpBlobStorageConnectorConstructorOptions.js\";\n"]}
@@ -0,0 +1,4 @@
1
+ // Copyright 2024 IOTA Stiftung.
2
+ // SPDX-License-Identifier: Apache-2.0.
3
+ export {};
4
+ //# sourceMappingURL=IGcpBlobStorageConnectorConfig.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IGcpBlobStorageConnectorConfig.js","sourceRoot":"","sources":["../../../src/models/IGcpBlobStorageConnectorConfig.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 GCP Blob Storage Connector.\n */\nexport interface IGcpBlobStorageConnectorConfig {\n\t/**\n\t * The GCP project ID.\n\t */\n\tprojectId: string;\n\n\t/**\n\t * The GCP credentials, a base64 encoded version of the JWTInput data type.\n\t */\n\tcredentials?: string;\n\n\t/**\n\t * The GCP bucket name.\n\t */\n\tbucketName: string;\n\n\t/**\n\t * Optional endpoint for GCP Storage emulator.\n\t */\n\tapiEndpoint?: string;\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=IGcpBlobStorageConnectorConstructorOptions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IGcpBlobStorageConnectorConstructorOptions.js","sourceRoot":"","sources":["../../../src/models/IGcpBlobStorageConnectorConstructorOptions.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IGcpBlobStorageConnectorConfig } from \"./IGcpBlobStorageConnectorConfig.js\";\n\n/**\n * Options for the GCP Blob Storage Connector constructor.\n */\nexport interface IGcpBlobStorageConnectorConstructorOptions {\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: IGcpBlobStorageConnectorConfig;\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import type { IBlobStorageConnector } from "@twin.org/blob-storage-models";
2
- import type { IGcpBlobStorageConnectorConstructorOptions } from "./models/IGcpBlobStorageConnectorConstructorOptions";
2
+ import type { IGcpBlobStorageConnectorConstructorOptions } from "./models/IGcpBlobStorageConnectorConstructorOptions.js";
3
3
  /**
4
4
  * Class for performing blob storage operations on GCP Storage.
5
5
  * See https://cloud.google.com/storage/docs/reference/libraries for more information.
@@ -12,12 +12,17 @@ export declare class GcpBlobStorageConnector implements IBlobStorageConnector {
12
12
  /**
13
13
  * Runtime name for the class.
14
14
  */
15
- readonly CLASS_NAME: string;
15
+ static readonly CLASS_NAME: string;
16
16
  /**
17
17
  * Create a new instance of GcpBlobStorageConnector.
18
18
  * @param options The options for the connector.
19
19
  */
20
20
  constructor(options: IGcpBlobStorageConnectorConstructorOptions);
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.
@@ -1,3 +1,3 @@
1
- export * from "./gcpBlobStorageConnector";
2
- export * from "./models/IGcpBlobStorageConnectorConfig";
3
- export * from "./models/IGcpBlobStorageConnectorConstructorOptions";
1
+ export * from "./gcpBlobStorageConnector.js";
2
+ export * from "./models/IGcpBlobStorageConnectorConfig.js";
3
+ export * from "./models/IGcpBlobStorageConnectorConstructorOptions.js";
@@ -1,8 +1,12 @@
1
- import type { IGcpBlobStorageConnectorConfig } from "./IGcpBlobStorageConnectorConfig";
1
+ import type { IGcpBlobStorageConnectorConfig } from "./IGcpBlobStorageConnectorConfig.js";
2
2
  /**
3
3
  * Options for the GCP Blob Storage Connector constructor.
4
4
  */
5
5
  export interface IGcpBlobStorageConnectorConstructorOptions {
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
  */
package/docs/changelog.md CHANGED
@@ -1,5 +1,39 @@
1
1
  # @twin.org/blob-storage-connector-gcp - Changelog
2
2
 
3
+ ## [0.0.3-next.1](https://github.com/twinfoundation/blob-storage/compare/blob-storage-connector-gcp-v0.0.3-next.0...blob-storage-connector-gcp-v0.0.3-next.1) (2025-11-11)
4
+
5
+
6
+ ### Features
7
+
8
+ * add context id features ([#30](https://github.com/twinfoundation/blob-storage/issues/30)) ([fbf1c92](https://github.com/twinfoundation/blob-storage/commit/fbf1c9276424c841ef5ef3f4de8469ab3fba7e9c))
9
+ * Add GCP blob storage connector ([#8](https://github.com/twinfoundation/blob-storage/issues/8)) ([4f6d579](https://github.com/twinfoundation/blob-storage/commit/4f6d579c01b3ae13ebcd9029b279da62e4fde859))
10
+ * add validate-locales ([f20fcec](https://github.com/twinfoundation/blob-storage/commit/f20fceced91e39a0c9edb770b2e43ce944c92f3c))
11
+ * eslint migration to flat config ([e4239dd](https://github.com/twinfoundation/blob-storage/commit/e4239dd1c721955cff7f0357255d2bba15319972))
12
+ * update dependencies ([56f0094](https://github.com/twinfoundation/blob-storage/commit/56f0094b68d8bd22864cd899ac1b61d95540f719))
13
+ * update framework core ([ff339fe](https://github.com/twinfoundation/blob-storage/commit/ff339fe7e3f09ddff429907834bdf43617e9c05e))
14
+ * use shared store mechanism ([#12](https://github.com/twinfoundation/blob-storage/issues/12)) ([cae8110](https://github.com/twinfoundation/blob-storage/commit/cae8110681847a1ac4fcac968b8196694e49c320))
15
+
16
+
17
+ ### Dependencies
18
+
19
+ * The following workspace dependencies were updated
20
+ * dependencies
21
+ * @twin.org/blob-storage-models bumped from 0.0.3-next.0 to 0.0.3-next.1
22
+
23
+ ## [0.0.2-next.5](https://github.com/twinfoundation/blob-storage/compare/blob-storage-connector-gcp-v0.0.2-next.4...blob-storage-connector-gcp-v0.0.2-next.5) (2025-10-09)
24
+
25
+
26
+ ### Features
27
+
28
+ * add validate-locales ([f20fcec](https://github.com/twinfoundation/blob-storage/commit/f20fceced91e39a0c9edb770b2e43ce944c92f3c))
29
+
30
+
31
+ ### Dependencies
32
+
33
+ * The following workspace dependencies were updated
34
+ * dependencies
35
+ * @twin.org/blob-storage-models bumped from 0.0.2-next.4 to 0.0.2-next.5
36
+
3
37
  ## [0.0.2-next.4](https://github.com/twinfoundation/blob-storage/compare/blob-storage-connector-gcp-v0.0.2-next.3...blob-storage-connector-gcp-v0.0.2-next.4) (2025-10-02)
4
38
 
5
39
 
@@ -39,15 +39,29 @@ The namespace for the items.
39
39
 
40
40
  ### CLASS\_NAME
41
41
 
42
- > `readonly` **CLASS\_NAME**: `string`
42
+ > `readonly` `static` **CLASS\_NAME**: `string`
43
43
 
44
44
  Runtime name for the class.
45
45
 
46
+ ## Methods
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
+
46
60
  #### Implementation of
47
61
 
48
- `IBlobStorageConnector.CLASS_NAME`
62
+ `IBlobStorageConnector.className`
49
63
 
50
- ## Methods
64
+ ***
51
65
 
52
66
  ### bootstrap()
53
67
 
@@ -103,7 +117,7 @@ The id of the stored blob in urn format.
103
117
 
104
118
  ### get()
105
119
 
106
- > **get**(`id`): `Promise`\<`undefined` \| `Uint8Array`\<`ArrayBufferLike`\>\>
120
+ > **get**(`id`): `Promise`\<`Uint8Array`\<`ArrayBufferLike`\> \| `undefined`\>
107
121
 
108
122
  Get the blob.
109
123
 
@@ -117,7 +131,7 @@ The id of the blob to get in urn format.
117
131
 
118
132
  #### Returns
119
133
 
120
- `Promise`\<`undefined` \| `Uint8Array`\<`ArrayBufferLike`\>\>
134
+ `Promise`\<`Uint8Array`\<`ArrayBufferLike`\> \| `undefined`\>
121
135
 
122
136
  The data for the blob if it can be found or undefined.
123
137
 
@@ -4,6 +4,14 @@ Options for the GCP 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**: [`IGcpBlobStorageConnectorConfig`](IGcpBlobStorageConnectorConfig.md)
package/locales/en.json CHANGED
@@ -2,7 +2,6 @@
2
2
  "info": {
3
3
  "gcpBlobStorageConnector": {
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-gcp",
3
- "version": "0.0.2-next.4",
3
+ "version": "0.0.3-next.1",
4
4
  "description": "Blob Storage connector implementation using Google Cloud Storage",
5
5
  "repository": {
6
6
  "type": "git",
@@ -14,28 +14,27 @@
14
14
  "node": ">=20.0.0"
15
15
  },
16
16
  "dependencies": {
17
- "@google-cloud/storage": "7.17.1",
18
- "@twin.org/blob-storage-models": "0.0.2-next.4",
17
+ "@google-cloud/storage": "7.17.3",
18
+ "@twin.org/blob-storage-models": "0.0.3-next.1",
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
  "@twin.org/web": "next"
24
25
  },
25
- "main": "./dist/cjs/index.cjs",
26
- "module": "./dist/esm/index.mjs",
26
+ "main": "./dist/es/index.js",
27
27
  "types": "./dist/types/index.d.ts",
28
28
  "exports": {
29
29
  ".": {
30
30
  "types": "./dist/types/index.d.ts",
31
- "require": "./dist/cjs/index.cjs",
32
- "import": "./dist/esm/index.mjs"
31
+ "import": "./dist/es/index.js",
32
+ "default": "./dist/es/index.js"
33
33
  },
34
34
  "./locales/*.json": "./locales/*.json"
35
35
  },
36
36
  "files": [
37
- "dist/cjs",
38
- "dist/esm",
37
+ "dist/es",
39
38
  "dist/types",
40
39
  "locales",
41
40
  "docs"
@@ -54,5 +53,9 @@
54
53
  "connector",
55
54
  "adapter",
56
55
  "integration"
57
- ]
56
+ ],
57
+ "bugs": {
58
+ "url": "git+https://github.com/twinfoundation/blob-storage/issues"
59
+ },
60
+ "homepage": "https://twindev.org"
58
61
  }
@@ -1,188 +0,0 @@
1
- 'use strict';
2
-
3
- var storage = require('@google-cloud/storage');
4
- var core = require('@twin.org/core');
5
- var crypto = require('@twin.org/crypto');
6
- var web = require('@twin.org/web');
7
-
8
- // Copyright 2024 IOTA Stiftung.
9
- // SPDX-License-Identifier: Apache-2.0.
10
- /**
11
- * Class for performing blob storage operations on GCP Storage.
12
- * See https://cloud.google.com/storage/docs/reference/libraries for more information.
13
- */
14
- class GcpBlobStorageConnector {
15
- /**
16
- * The namespace for the items.
17
- */
18
- static NAMESPACE = "gcp";
19
- /**
20
- * Runtime name for the class.
21
- */
22
- CLASS_NAME = "GcpBlobStorageConnector";
23
- /**
24
- * The configuration for the connector.
25
- * @internal
26
- */
27
- _config;
28
- /**
29
- * The GCP Storage client.
30
- * @internal
31
- */
32
- _storage;
33
- /**
34
- * Create a new instance of GcpBlobStorageConnector.
35
- * @param options The options for the connector.
36
- */
37
- constructor(options) {
38
- core.Guards.object(this.CLASS_NAME, "options", options);
39
- core.Guards.object(this.CLASS_NAME, "options.config", options.config);
40
- core.Guards.stringValue(this.CLASS_NAME, "options.config.projectId", options.config.projectId);
41
- let credentials;
42
- if (!core.Is.empty(options.config.credentials)) {
43
- core.Guards.stringBase64(this.CLASS_NAME, "options.config.credentials", options.config.credentials);
44
- credentials = core.ObjectHelper.fromBytes(core.Converter.base64ToBytes(options.config.credentials));
45
- }
46
- core.Guards.stringValue(this.CLASS_NAME, "options.config.bucketName", options.config.bucketName);
47
- this._config = options.config;
48
- this._storage = new storage.Storage({
49
- projectId: this._config.projectId,
50
- apiEndpoint: this._config.apiEndpoint,
51
- credentials
52
- });
53
- }
54
- /**
55
- * Bootstrap the component by creating and initializing any resources it needs.
56
- * @param nodeLoggingComponentType The node logging component type.
57
- * @returns True if the bootstrapping process was successful.
58
- */
59
- async bootstrap(nodeLoggingComponentType) {
60
- const nodeLogging = core.ComponentFactory.getIfExists(nodeLoggingComponentType);
61
- try {
62
- await nodeLogging?.log({
63
- level: "info",
64
- source: this.CLASS_NAME,
65
- message: "bucketCreating",
66
- data: {
67
- bucket: this._config.bucketName
68
- }
69
- });
70
- const [buckets] = await this._storage.getBuckets();
71
- const bucketExists = buckets.some(bucket => bucket.name === this._config.bucketName);
72
- if (bucketExists) {
73
- await nodeLogging?.log({
74
- level: "info",
75
- source: this.CLASS_NAME,
76
- message: "bucketExists",
77
- data: {
78
- bucket: this._config.bucketName
79
- }
80
- });
81
- }
82
- else {
83
- await this._storage.createBucket(this._config.bucketName);
84
- await nodeLogging?.log({
85
- level: "info",
86
- source: this.CLASS_NAME,
87
- message: "bucketCreated",
88
- data: {
89
- bucket: this._config.bucketName
90
- }
91
- });
92
- }
93
- }
94
- catch (err) {
95
- await nodeLogging?.log({
96
- level: "error",
97
- source: this.CLASS_NAME,
98
- message: "bucketCreateFailed",
99
- data: {
100
- bucket: this._config.bucketName
101
- },
102
- error: core.BaseError.fromError(err)
103
- });
104
- return false;
105
- }
106
- return true;
107
- }
108
- /**
109
- * Set the blob.
110
- * @param blob The data for the blob.
111
- * @returns The id of the stored blob in urn format.
112
- */
113
- async set(blob) {
114
- core.Guards.uint8Array(this.CLASS_NAME, "blob", blob);
115
- try {
116
- const id = core.Converter.bytesToHex(crypto.Sha256.sum256(blob));
117
- const bucket = this._storage.bucket(this._config.bucketName);
118
- const file = bucket.file(id);
119
- await file.save(blob, {
120
- contentType: web.MimeTypes.OctetStream
121
- });
122
- return `blob:${new core.Urn(GcpBlobStorageConnector.NAMESPACE, id).toString()}`;
123
- }
124
- catch (err) {
125
- throw new core.GeneralError(this.CLASS_NAME, "setBlobFailed", undefined, err);
126
- }
127
- }
128
- /**
129
- * Get the blob.
130
- * @param id The id of the blob to get in urn format.
131
- * @returns The data for the blob if it can be found or undefined.
132
- */
133
- async get(id) {
134
- core.Urn.guard(this.CLASS_NAME, "id", id);
135
- const urnParsed = core.Urn.fromValidString(id);
136
- if (urnParsed.namespaceMethod() !== GcpBlobStorageConnector.NAMESPACE) {
137
- throw new core.GeneralError(this.CLASS_NAME, "namespaceMismatch", {
138
- namespace: GcpBlobStorageConnector.NAMESPACE,
139
- id
140
- });
141
- }
142
- try {
143
- const key = urnParsed.namespaceSpecific(1);
144
- const bucket = this._storage.bucket(this._config.bucketName);
145
- const file = bucket.file(key);
146
- const [exists] = await file.exists();
147
- if (!exists) {
148
- return undefined;
149
- }
150
- const [contents] = await file.download();
151
- return new Uint8Array(contents);
152
- }
153
- catch (err) {
154
- throw new core.GeneralError(this.CLASS_NAME, "getBlobFailed", { id }, err);
155
- }
156
- }
157
- /**
158
- * Remove the blob.
159
- * @param id The id of the blob to remove in urn format.
160
- * @returns True if the blob was found.
161
- */
162
- async remove(id) {
163
- core.Urn.guard(this.CLASS_NAME, "id", id);
164
- const urnParsed = core.Urn.fromValidString(id);
165
- if (urnParsed.namespaceMethod() !== GcpBlobStorageConnector.NAMESPACE) {
166
- throw new core.GeneralError(this.CLASS_NAME, "namespaceMismatch", {
167
- namespace: GcpBlobStorageConnector.NAMESPACE,
168
- id
169
- });
170
- }
171
- try {
172
- const key = urnParsed.namespaceSpecific(1);
173
- const bucket = this._storage.bucket(this._config.bucketName);
174
- const file = bucket.file(key);
175
- const [exists] = await file.exists();
176
- if (!exists) {
177
- return false;
178
- }
179
- await file.delete();
180
- return true;
181
- }
182
- catch (err) {
183
- throw new core.GeneralError(this.CLASS_NAME, "removeBlobFailed", { id }, err);
184
- }
185
- }
186
- }
187
-
188
- exports.GcpBlobStorageConnector = GcpBlobStorageConnector;