@twin.org/blob-storage-service 0.0.1-next.8 → 0.0.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.
Files changed (28) hide show
  1. package/dist/cjs/index.cjs +643 -114
  2. package/dist/esm/index.mjs +645 -117
  3. package/dist/types/blobStorageRoutes.d.ts +17 -3
  4. package/dist/types/blobStorageService.d.ts +55 -31
  5. package/dist/types/entities/blobStorageEntry.d.ts +55 -0
  6. package/dist/types/index.d.ts +2 -1
  7. package/dist/types/models/IBlobStorageServiceConfig.d.ts +9 -2
  8. package/dist/types/models/IBlobStorageServiceConstructorOptions.d.ts +19 -0
  9. package/dist/types/restEntryPoints.d.ts +5 -0
  10. package/docs/changelog.md +161 -1
  11. package/docs/open-api/spec.json +1230 -1853
  12. package/docs/reference/classes/BlobStorageEntry.md +109 -0
  13. package/docs/reference/classes/BlobStorageService.md +188 -49
  14. package/docs/reference/functions/blobStorageCreate.md +9 -3
  15. package/docs/reference/functions/blobStorageGet.md +9 -3
  16. package/docs/reference/functions/blobStorageGetContent.md +9 -3
  17. package/docs/reference/functions/blobStorageList.md +31 -0
  18. package/docs/reference/functions/blobStorageRemove.md +9 -3
  19. package/docs/reference/functions/blobStorageUpdate.md +9 -3
  20. package/docs/reference/functions/generateRestRoutesBlobStorage.md +24 -4
  21. package/docs/reference/index.md +3 -1
  22. package/docs/reference/interfaces/IBlobStorageServiceConfig.md +17 -7
  23. package/docs/reference/interfaces/IBlobStorageServiceConstructorOptions.md +33 -0
  24. package/docs/reference/variables/restEntryPoints.md +4 -0
  25. package/locales/en.json +2 -1
  26. package/package.json +15 -13
  27. package/dist/types/entities/blobMetadata.d.ts +0 -22
  28. package/docs/reference/classes/BlobMetadata.md +0 -45
@@ -1,11 +1,16 @@
1
- import { Guards, ComponentFactory, Is, Converter, GeneralError, Validation, Urn, NotFoundError } from '@twin.org/core';
1
+ import { HttpParameterHelper } from '@twin.org/api-models';
2
+ import { BlobStorageTypes, BlobStorageContexts, BlobStorageCompressionType, BlobStorageConnectorFactory } from '@twin.org/blob-storage-models';
3
+ import { StringHelper, Guards, ComponentFactory, Coerce, Is, Converter, GeneralError, Validation, Compression, ObjectHelper, Urn, NotFoundError } from '@twin.org/core';
4
+ import { SchemaOrgContexts, SchemaOrgTypes, SchemaOrgDataTypes } from '@twin.org/standards-schema-org';
2
5
  import { HttpStatusCode, HeaderTypes, MimeTypes, MimeTypeHelper } from '@twin.org/web';
3
- import { BlobStorageConnectorFactory } from '@twin.org/blob-storage-models';
4
- import { JsonLdHelper } from '@twin.org/data-json-ld';
6
+ import { Sha256 } from '@twin.org/crypto';
7
+ import { JsonLdHelper, JsonLdProcessor } from '@twin.org/data-json-ld';
8
+ import { ComparisonOperator, LogicalOperator, SortDirection, EntitySchemaHelper, property, entity, EntitySchemaFactory } from '@twin.org/entity';
5
9
  import { EntityStorageConnectorFactory } from '@twin.org/entity-storage-models';
6
10
  import { VaultConnectorFactory, VaultEncryptionType } from '@twin.org/vault-models';
7
- import { property, entity, EntitySchemaFactory, EntitySchemaHelper } from '@twin.org/entity';
8
11
 
12
+ // Copyright 2024 IOTA Stiftung.
13
+ // SPDX-License-Identifier: Apache-2.0.
9
14
  /**
10
15
  * The source used when communicating about these routes.
11
16
  */
@@ -23,13 +28,19 @@ const tagsBlobStorage = [
23
28
  * The REST routes for blob storage.
24
29
  * @param baseRouteName Prefix to prepend to the paths.
25
30
  * @param componentName The name of the component to use in the routes stored in the ComponentFactory.
31
+ * @param options Additional options for the routes.
32
+ * @param options.typeName Optional type name to use in the routes, defaults to Blob Storage.
33
+ * @param options.tagName Optional name to use in OpenAPI spec for tag.
26
34
  * @returns The generated routes.
27
35
  */
28
- function generateRestRoutesBlobStorage(baseRouteName, componentName) {
36
+ function generateRestRoutesBlobStorage(baseRouteName, componentName, options) {
37
+ const typeName = options?.typeName ?? "Blob Storage";
38
+ const lowerName = typeName.toLowerCase();
39
+ const camelTypeName = StringHelper.camelCase(typeName);
29
40
  const blobStorageCreateRoute = {
30
- operationId: "blobStorageCreate",
31
- summary: "Create a blob in to storage",
32
- tag: tagsBlobStorage[0].name,
41
+ operationId: `${camelTypeName}Create`,
42
+ summary: `Create an entry in ${lowerName}`,
43
+ tag: options?.tagName ?? tagsBlobStorage[0].name,
33
44
  method: "POST",
34
45
  path: `${baseRouteName}/`,
35
46
  handler: async (httpRequestContext, request) => blobStorageCreate(httpRequestContext, componentName, request),
@@ -37,12 +48,12 @@ function generateRestRoutesBlobStorage(baseRouteName, componentName) {
37
48
  type: "IBlobStorageCreateRequest",
38
49
  examples: [
39
50
  {
40
- id: "blobStorageCreateExample",
51
+ id: `${camelTypeName}CreateRequestExample`,
41
52
  request: {
42
53
  body: {
43
54
  blob: "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZw==",
44
55
  metadata: {
45
- "@context": "http://schema.org/",
56
+ "@context": "https://schema.org",
46
57
  "@type": "DigitalDocument",
47
58
  name: "myfile.pdf"
48
59
  }
@@ -56,7 +67,7 @@ function generateRestRoutesBlobStorage(baseRouteName, componentName) {
56
67
  type: "ICreatedResponse",
57
68
  examples: [
58
69
  {
59
- id: "blobStorageCreateResponseExample",
70
+ id: `${camelTypeName}CreateResponseExample`,
60
71
  response: {
61
72
  statusCode: HttpStatusCode.created,
62
73
  headers: {
@@ -69,9 +80,9 @@ function generateRestRoutesBlobStorage(baseRouteName, componentName) {
69
80
  ]
70
81
  };
71
82
  const blobStorageGetRoute = {
72
- operationId: "blobStorageGet",
73
- summary: "Get the blob metadata from storage",
74
- tag: tagsBlobStorage[0].name,
83
+ operationId: `${camelTypeName}Get`,
84
+ summary: `Get the metadata for an item from ${lowerName}`,
85
+ tag: options?.tagName ?? tagsBlobStorage[0].name,
75
86
  method: "GET",
76
87
  path: `${baseRouteName}/:id`,
77
88
  handler: async (httpRequestContext, request) => blobStorageGet(httpRequestContext, componentName, request),
@@ -79,13 +90,13 @@ function generateRestRoutesBlobStorage(baseRouteName, componentName) {
79
90
  type: "IBlobStorageGetRequest",
80
91
  examples: [
81
92
  {
82
- id: "blobStorageGetRequestExample",
93
+ id: `${camelTypeName}GetRequestExample`,
83
94
  request: {
84
95
  pathParams: {
85
96
  id: "blob-memory:c57d94b088f4c6d2cb32ded014813d0c786aa00134c8ee22f84b1e2545602a70"
86
97
  },
87
98
  query: {
88
- includeContent: true
99
+ includeContent: "true"
89
100
  }
90
101
  }
91
102
  }
@@ -96,11 +107,54 @@ function generateRestRoutesBlobStorage(baseRouteName, componentName) {
96
107
  type: "IBlobStorageGetResponse",
97
108
  examples: [
98
109
  {
99
- id: "blobStorageGetResponseExample",
110
+ id: `${camelTypeName}GetResponseExample`,
100
111
  response: {
101
112
  body: {
113
+ "@context": [
114
+ BlobStorageContexts.ContextRoot,
115
+ BlobStorageContexts.ContextRootCommon,
116
+ SchemaOrgContexts.ContextRoot
117
+ ],
118
+ type: BlobStorageTypes.Entry,
119
+ id: "blob-memory:c57d94b088f4c6d2cb32ded014813d0c786aa00134c8ee22f84b1e2545602a70",
120
+ dateCreated: "2024-01-01T00:00:00Z",
121
+ encodingFormat: MimeTypes.Pdf,
122
+ blobSize: 42,
123
+ blobHash: "sha256:c57d94b088f4c6d2cb32ded014813d0c786aa00134c8ee22f84b1e2545602a70",
124
+ fileExtension: "pdf",
102
125
  metadata: {
103
- "@context": "http://schema.org/",
126
+ "@context": "https://schema.org",
127
+ "@type": "DigitalDocument",
128
+ name: "myfile.pdf"
129
+ },
130
+ blob: "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZw=="
131
+ }
132
+ }
133
+ }
134
+ ]
135
+ },
136
+ {
137
+ type: "IBlobStorageGetResponse",
138
+ mimeType: MimeTypes.JsonLd,
139
+ examples: [
140
+ {
141
+ id: `${camelTypeName}GetResponseJsonLdExample`,
142
+ response: {
143
+ body: {
144
+ "@context": [
145
+ BlobStorageContexts.ContextRoot,
146
+ BlobStorageContexts.ContextRootCommon,
147
+ SchemaOrgContexts.ContextRoot
148
+ ],
149
+ type: BlobStorageTypes.Entry,
150
+ id: "blob-memory:c57d94b088f4c6d2cb32ded014813d0c786aa00134c8ee22f84b1e2545602a70",
151
+ dateCreated: "2024-01-01T00:00:00Z",
152
+ encodingFormat: MimeTypes.Pdf,
153
+ blobSize: 42,
154
+ blobHash: "sha256:c57d94b088f4c6d2cb32ded014813d0c786aa00134c8ee22f84b1e2545602a70",
155
+ fileExtension: "pdf",
156
+ metadata: {
157
+ "@context": "https://schema.org",
104
158
  "@type": "DigitalDocument",
105
159
  name: "myfile.pdf"
106
160
  },
@@ -116,9 +170,9 @@ function generateRestRoutesBlobStorage(baseRouteName, componentName) {
116
170
  ]
117
171
  };
118
172
  const blobStorageGetContentRoute = {
119
- operationId: "blobStorageGetContent",
120
- summary: "Get the blob from storage",
121
- tag: tagsBlobStorage[0].name,
173
+ operationId: `${camelTypeName}GetContent`,
174
+ summary: `Get the content for an item in ${lowerName}`,
175
+ tag: options?.tagName ?? tagsBlobStorage[0].name,
122
176
  method: "GET",
123
177
  path: `${baseRouteName}/:id/content`,
124
178
  handler: async (httpRequestContext, request) => blobStorageGetContent(httpRequestContext, componentName, request),
@@ -126,13 +180,13 @@ function generateRestRoutesBlobStorage(baseRouteName, componentName) {
126
180
  type: "IBlobStorageGetRequest",
127
181
  examples: [
128
182
  {
129
- id: "blobStorageGetContentRequestExample",
183
+ id: `${camelTypeName}GetContentRequestExample`,
130
184
  request: {
131
185
  pathParams: {
132
186
  id: "blob-memory:c57d94b088f4c6d2cb32ded014813d0c786aa00134c8ee22f84b1e2545602a70"
133
187
  },
134
188
  query: {
135
- download: true,
189
+ download: "true",
136
190
  filename: "my-file.pdf"
137
191
  }
138
192
  }
@@ -145,8 +199,8 @@ function generateRestRoutesBlobStorage(baseRouteName, componentName) {
145
199
  mimeType: MimeTypes.OctetStream,
146
200
  examples: [
147
201
  {
148
- id: "blobStorageGetContentResponseExample",
149
- description: `The content of the blob, which will be a specific mime type if one can be detected from the content (or set as mimeType in the metadata), or defaults to ${MimeTypes.OctetStream}.`,
202
+ id: `${camelTypeName}GetContentResponseExample`,
203
+ description: `The content of the blob, which will be a specific mime type if one can be detected from the content (or set as encodingFormat in the entry), or defaults to ${MimeTypes.OctetStream}.`,
150
204
  response: {
151
205
  body: new Uint8Array()
152
206
  }
@@ -159,9 +213,9 @@ function generateRestRoutesBlobStorage(baseRouteName, componentName) {
159
213
  ]
160
214
  };
161
215
  const blobStorageUpdateRoute = {
162
- operationId: "blobStorageUpdate",
163
- summary: "Update a blob metadata in storage",
164
- tag: tagsBlobStorage[0].name,
216
+ operationId: `${camelTypeName}Update`,
217
+ summary: `Update the metadata for an item in ${lowerName}`,
218
+ tag: options?.tagName ?? tagsBlobStorage[0].name,
165
219
  method: "PUT",
166
220
  path: `${baseRouteName}/:id`,
167
221
  handler: async (httpRequestContext, request) => blobStorageUpdate(httpRequestContext, componentName, request),
@@ -169,14 +223,14 @@ function generateRestRoutesBlobStorage(baseRouteName, componentName) {
169
223
  type: "IBlobStorageUpdateRequest",
170
224
  examples: [
171
225
  {
172
- id: "blobStorageUpdateExample",
226
+ id: `${camelTypeName}UpdateRequestExample`,
173
227
  request: {
174
228
  pathParams: {
175
229
  id: "blob-memory:c57d94b088f4c6d2cb32ded014813d0c786aa00134c8ee22f84b1e2545602a70"
176
230
  },
177
231
  body: {
178
232
  metadata: {
179
- "@context": "http://schema.org/",
233
+ "@context": "https://schema.org",
180
234
  "@type": "DigitalDocument",
181
235
  name: "myfile.pdf"
182
236
  }
@@ -192,9 +246,9 @@ function generateRestRoutesBlobStorage(baseRouteName, componentName) {
192
246
  ]
193
247
  };
194
248
  const blobStorageRemoveRoute = {
195
- operationId: "blobStorageRemove",
196
- summary: "Remove the blob from storage",
197
- tag: tagsBlobStorage[0].name,
249
+ operationId: `${camelTypeName}Remove`,
250
+ summary: `Remove an item from ${lowerName}`,
251
+ tag: options?.tagName ?? tagsBlobStorage[0].name,
198
252
  method: "DELETE",
199
253
  path: `${baseRouteName}/:id`,
200
254
  handler: async (httpRequestContext, request) => blobStorageRemove(httpRequestContext, componentName, request),
@@ -202,7 +256,7 @@ function generateRestRoutesBlobStorage(baseRouteName, componentName) {
202
256
  type: "IBlobStorageRemoveRequest",
203
257
  examples: [
204
258
  {
205
- id: "blobStorageRemoveRequestExample",
259
+ id: `${camelTypeName}RemoveRequestExample`,
206
260
  request: {
207
261
  pathParams: {
208
262
  id: "blob-memory:c57d94b088f4c6d2cb32ded014813d0c786aa00134c8ee22f84b1e2545602a70"
@@ -220,12 +274,116 @@ function generateRestRoutesBlobStorage(baseRouteName, componentName) {
220
274
  }
221
275
  ]
222
276
  };
277
+ const blobStorageListRoute = {
278
+ operationId: `${camelTypeName}Query`,
279
+ summary: `Query the items from ${lowerName}`,
280
+ tag: options?.tagName ?? tagsBlobStorage[0].name,
281
+ method: "GET",
282
+ path: `${baseRouteName}/`,
283
+ handler: async (httpRequestContext, request) => blobStorageList(httpRequestContext, componentName, request),
284
+ requestType: {
285
+ type: "IBlobStorageListRequest",
286
+ examples: [
287
+ {
288
+ id: `${camelTypeName}ListRequestExample`,
289
+ request: {}
290
+ }
291
+ ]
292
+ },
293
+ responseType: [
294
+ {
295
+ type: "IBlobStorageListResponse",
296
+ examples: [
297
+ {
298
+ id: `${camelTypeName}ListResponseExample`,
299
+ response: {
300
+ body: {
301
+ "@context": [
302
+ SchemaOrgContexts.ContextRoot,
303
+ BlobStorageContexts.ContextRoot,
304
+ BlobStorageContexts.ContextRootCommon
305
+ ],
306
+ type: SchemaOrgTypes.ItemList,
307
+ [SchemaOrgTypes.ItemListElement]: [
308
+ {
309
+ "@context": [
310
+ BlobStorageContexts.ContextRoot,
311
+ BlobStorageContexts.ContextRootCommon,
312
+ SchemaOrgContexts.ContextRoot
313
+ ],
314
+ type: BlobStorageTypes.Entry,
315
+ id: "blob-memory:c57d94b088f4c6d2cb32ded014813d0c786aa00134c8ee22f84b1e2545602a70",
316
+ dateCreated: "2024-01-01T00:00:00Z",
317
+ encodingFormat: MimeTypes.Pdf,
318
+ blobSize: 42,
319
+ blobHash: "sha256:c57d94b088f4c6d2cb32ded014813d0c786aa00134c8ee22f84b1e2545602a70",
320
+ fileExtension: "pdf",
321
+ metadata: {
322
+ "@context": "https://schema.org",
323
+ "@type": "DigitalDocument",
324
+ name: "myfile.pdf"
325
+ },
326
+ blob: "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZw=="
327
+ }
328
+ ]
329
+ }
330
+ }
331
+ }
332
+ ]
333
+ },
334
+ {
335
+ type: "IBlobStorageListResponse",
336
+ mimeType: MimeTypes.JsonLd,
337
+ examples: [
338
+ {
339
+ id: `${camelTypeName}ListResponseJsonLdExample`,
340
+ response: {
341
+ body: {
342
+ "@context": [
343
+ SchemaOrgContexts.ContextRoot,
344
+ BlobStorageContexts.ContextRoot,
345
+ BlobStorageContexts.ContextRootCommon
346
+ ],
347
+ type: SchemaOrgTypes.ItemList,
348
+ [SchemaOrgTypes.ItemListElement]: [
349
+ {
350
+ "@context": [
351
+ BlobStorageContexts.ContextRoot,
352
+ BlobStorageContexts.ContextRootCommon,
353
+ SchemaOrgContexts.ContextRoot
354
+ ],
355
+ type: BlobStorageTypes.Entry,
356
+ id: "blob-memory:c57d94b088f4c6d2cb32ded014813d0c786aa00134c8ee22f84b1e2545602a70",
357
+ dateCreated: "2024-01-01T00:00:00Z",
358
+ encodingFormat: MimeTypes.Pdf,
359
+ blobSize: 42,
360
+ blobHash: "sha256:c57d94b088f4c6d2cb32ded014813d0c786aa00134c8ee22f84b1e2545602a70",
361
+ fileExtension: "pdf",
362
+ metadata: {
363
+ "@context": "https://schema.org",
364
+ "@type": "DigitalDocument",
365
+ name: "myfile.pdf"
366
+ },
367
+ blob: "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZw=="
368
+ }
369
+ ]
370
+ }
371
+ }
372
+ }
373
+ ]
374
+ },
375
+ {
376
+ type: "INotFoundResponse"
377
+ }
378
+ ]
379
+ };
223
380
  return [
224
381
  blobStorageCreateRoute,
225
382
  blobStorageGetRoute,
226
383
  blobStorageGetContentRoute,
227
384
  blobStorageUpdateRoute,
228
- blobStorageRemoveRoute
385
+ blobStorageRemoveRoute,
386
+ blobStorageListRoute
229
387
  ];
230
388
  }
231
389
  /**
@@ -240,7 +398,11 @@ async function blobStorageCreate(httpRequestContext, componentName, request) {
240
398
  Guards.object(ROUTES_SOURCE, "request.body", request.body);
241
399
  Guards.stringBase64(ROUTES_SOURCE, "request.body.blob", request.body.blob);
242
400
  const component = ComponentFactory.get(componentName);
243
- const id = await component.create(request.body.blob, request.body.mimeType, request.body.extension, request.body.metadata, request.body.namespace, httpRequestContext.nodeIdentity);
401
+ const id = await component.create(request.body.blob, request.body.encodingFormat, request.body.fileExtension, request.body.metadata, {
402
+ disableEncryption: request.body.disableEncryption,
403
+ overrideVaultKeyId: request.body.overrideVaultKeyId,
404
+ namespace: request.body.namespace
405
+ }, httpRequestContext.userIdentity, httpRequestContext.nodeIdentity);
244
406
  return {
245
407
  statusCode: HttpStatusCode.created,
246
408
  headers: {
@@ -259,9 +421,17 @@ async function blobStorageGet(httpRequestContext, componentName, request) {
259
421
  Guards.object(ROUTES_SOURCE, "request", request);
260
422
  Guards.object(ROUTES_SOURCE, "request.pathParams", request.pathParams);
261
423
  Guards.stringValue(ROUTES_SOURCE, "request.pathParams.id", request.pathParams.id);
424
+ const mimeType = request.headers?.[HeaderTypes.Accept] === MimeTypes.JsonLd ? "jsonld" : "json";
262
425
  const component = ComponentFactory.get(componentName);
263
- const result = await component.get(request.pathParams.id, request.query?.includeContent ?? false, httpRequestContext.nodeIdentity);
426
+ const result = await component.get(request.pathParams.id, {
427
+ includeContent: Coerce.boolean(request.query?.includeContent),
428
+ decompress: Coerce.boolean(request.query?.decompress),
429
+ overrideVaultKeyId: request.query?.overrideVaultKeyId
430
+ }, httpRequestContext.userIdentity, httpRequestContext.nodeIdentity);
264
431
  return {
432
+ headers: {
433
+ [HeaderTypes.ContentType]: mimeType === "json" ? MimeTypes.Json : MimeTypes.JsonLd
434
+ },
265
435
  body: result
266
436
  };
267
437
  }
@@ -277,18 +447,34 @@ async function blobStorageGetContent(httpRequestContext, componentName, request)
277
447
  Guards.object(ROUTES_SOURCE, "request.pathParams", request.pathParams);
278
448
  Guards.stringValue(ROUTES_SOURCE, "request.pathParams.id", request.pathParams.id);
279
449
  const component = ComponentFactory.get(componentName);
280
- const result = await component.get(request.pathParams.id, true, httpRequestContext.nodeIdentity);
281
- const mimeType = result?.mimeType ?? MimeTypes.OctetStream;
450
+ const decompress = Coerce.boolean(request.query?.decompress);
451
+ const download = Coerce.boolean(request.query?.download) ?? false;
452
+ const result = await component.get(request.pathParams.id, {
453
+ includeContent: true,
454
+ decompress,
455
+ overrideVaultKeyId: request.query?.overrideVaultKeyId
456
+ }, httpRequestContext.userIdentity, httpRequestContext.nodeIdentity);
457
+ const encodingFormat = result?.encodingFormat ?? MimeTypes.OctetStream;
458
+ let compressedEncodingFormat;
459
+ let compressedExtension = "";
460
+ // If the entry is compressed and we are not decompressing
461
+ // we need to override the encoding format to the compressed type
462
+ // and append an additional extension to the filename.
463
+ if (result.compression && !decompress) {
464
+ compressedEncodingFormat =
465
+ result.compression === BlobStorageCompressionType.Gzip ? MimeTypes.Gzip : MimeTypes.Zlib;
466
+ compressedExtension = `.${MimeTypeHelper.defaultExtension(compressedEncodingFormat)}`;
467
+ }
282
468
  let filename = request.query?.filename;
283
469
  if (!Is.stringValue(filename)) {
284
- filename = `file.${result.extension ?? MimeTypeHelper.defaultExtension(mimeType)}`;
470
+ filename = `file.${result.fileExtension ?? MimeTypeHelper.defaultExtension(encodingFormat)}${compressedExtension}`;
285
471
  }
286
472
  return {
287
473
  body: Is.stringBase64(result.blob) ? Converter.base64ToBytes(result.blob) : new Uint8Array(),
288
474
  attachment: {
289
- mimeType,
475
+ mimeType: compressedEncodingFormat ?? encodingFormat,
290
476
  filename,
291
- inline: !(request.query?.download ?? false)
477
+ inline: !download
292
478
  }
293
479
  };
294
480
  }
@@ -304,7 +490,7 @@ async function blobStorageUpdate(httpRequestContext, componentName, request) {
304
490
  Guards.object(ROUTES_SOURCE, "request.pathParams", request.pathParams);
305
491
  Guards.stringValue(ROUTES_SOURCE, "request.pathParams.id", request.pathParams.id);
306
492
  const component = ComponentFactory.get(componentName);
307
- await component.update(request.pathParams.id, request.body.mimeType, request.body.extension, request.body.metadata);
493
+ await component.update(request.pathParams.id, request.body.encodingFormat, request.body.fileExtension, request.body.metadata, httpRequestContext.userIdentity, httpRequestContext.nodeIdentity);
308
494
  return {
309
495
  statusCode: HttpStatusCode.noContent
310
496
  };
@@ -321,11 +507,30 @@ async function blobStorageRemove(httpRequestContext, componentName, request) {
321
507
  Guards.object(ROUTES_SOURCE, "request.pathParams", request.pathParams);
322
508
  Guards.stringValue(ROUTES_SOURCE, "request.pathParams.id", request.pathParams.id);
323
509
  const component = ComponentFactory.get(componentName);
324
- await component.remove(request.pathParams.id);
510
+ await component.remove(request.pathParams.id, httpRequestContext.userIdentity, httpRequestContext.nodeIdentity);
325
511
  return {
326
512
  statusCode: HttpStatusCode.noContent
327
513
  };
328
514
  }
515
+ /**
516
+ * List the entries from blob storage.
517
+ * @param httpRequestContext The request context for the API.
518
+ * @param componentName The name of the component to use in the routes.
519
+ * @param request The request.
520
+ * @returns The response object with additional http response properties.
521
+ */
522
+ async function blobStorageList(httpRequestContext, componentName, request) {
523
+ Guards.object(ROUTES_SOURCE, "request", request);
524
+ const mimeType = request.headers?.[HeaderTypes.Accept] === MimeTypes.JsonLd ? "jsonld" : "json";
525
+ const component = ComponentFactory.get(componentName);
526
+ const result = await component.query(HttpParameterHelper.objectFromString(request.query?.conditions), request.query?.orderBy, request.query?.orderByDirection, request.query?.cursor, Coerce.number(request.query?.pageSize), httpRequestContext.userIdentity, httpRequestContext.nodeIdentity);
527
+ return {
528
+ headers: {
529
+ [HeaderTypes.ContentType]: mimeType === "json" ? MimeTypes.Json : MimeTypes.JsonLd
530
+ },
531
+ body: result
532
+ };
533
+ }
329
534
 
330
535
  // Copyright 2024 IOTA Stiftung.
331
536
  // SPDX-License-Identifier: Apache-2.0.
@@ -351,7 +556,7 @@ class BlobStorageService {
351
556
  * The storage connector for the metadata.
352
557
  * @internal
353
558
  */
354
- _metadataEntityStorage;
559
+ _entryEntityStorage;
355
560
  /**
356
561
  * The vault connector for the encryption, can be undefined if no encryption required.
357
562
  * @internal
@@ -362,71 +567,116 @@ class BlobStorageService {
362
567
  * @internal
363
568
  */
364
569
  _vaultKeyId;
570
+ /**
571
+ * Include the node identity when performing storage operations, defaults to true.
572
+ * @internal
573
+ */
574
+ _includeNodeIdentity;
575
+ /**
576
+ * Include the user identity when performing storage operations, defaults to true.
577
+ * @internal
578
+ */
579
+ _includeUserIdentity;
365
580
  /**
366
581
  * Create a new instance of BlobStorageService.
367
- * @param options The dependencies for the service.
368
- * @param options.metadataEntityStorageType The type of the storage connector for the metadata, defaults to "blob-metadata".
369
- * @param options.vaultConnectorType The type of the vault connector for encryption, if undefined no encryption will be performed.
370
- * @param options.config The configuration for the service.
582
+ * @param options The options for the service.
371
583
  */
372
584
  constructor(options) {
373
585
  const names = BlobStorageConnectorFactory.names();
374
586
  if (names.length === 0) {
375
587
  throw new GeneralError(this.CLASS_NAME, "noConnectors");
376
588
  }
377
- this._metadataEntityStorage = EntityStorageConnectorFactory.get(options?.metadataEntityStorageType ?? "blob-metadata");
589
+ this._entryEntityStorage = EntityStorageConnectorFactory.get(options?.entryEntityStorageType ?? "blob-storage-entry");
378
590
  if (Is.stringValue(options?.vaultConnectorType)) {
379
- this._vaultConnector = VaultConnectorFactory.getIfExists(options.vaultConnectorType);
591
+ this._vaultConnector = VaultConnectorFactory.get(options.vaultConnectorType);
380
592
  }
381
593
  this._defaultNamespace = options?.config?.defaultNamespace ?? names[0];
382
- this._vaultKeyId = options?.config?.vaultKeyId ?? "blob-storage";
594
+ this._vaultKeyId = options?.config?.vaultKeyId;
595
+ this._includeNodeIdentity = options?.config?.includeNodeIdentity ?? true;
596
+ this._includeUserIdentity = options?.config?.includeUserIdentity ?? true;
597
+ SchemaOrgDataTypes.registerRedirects();
383
598
  }
384
599
  /**
385
600
  * Create the blob with some metadata.
386
601
  * @param blob The data for the blob in base64 format.
387
- * @param mimeType Mime type for the blob, will be detected if left undefined.
388
- * @param extension Extension for the blob, will be detected if left undefined.
602
+ * @param encodingFormat Mime type for the blob, will be detected if left undefined.
603
+ * @param fileExtension Extension for the blob, will be detected if left undefined.
389
604
  * @param metadata Data for the custom metadata as JSON-LD.
390
- * @param namespace The namespace to use for storing, defaults to component configured namespace.
391
- * @param nodeIdentity The node identity which controls the vault key.
605
+ * @param options Optional options for the creation of the blob.
606
+ * @param options.disableEncryption Disables encryption if enabled by default.
607
+ * @param options.overrideVaultKeyId Use a different vault key id for encryption, if not provided the default vault key id will be used.
608
+ * @param options.compress Optional compression type to use for the blob, defaults to no compression.*
609
+ * @param options.namespace The namespace to use for storing, defaults to component configured namespace.
610
+ * @param userIdentity The user identity to use with storage operations.
611
+ * @param nodeIdentity The node identity to use with storage operations.
392
612
  * @returns The id of the stored blob in urn format.
393
613
  */
394
- async create(blob, mimeType, extension, metadata, namespace, nodeIdentity) {
614
+ async create(blob, encodingFormat, fileExtension, metadata, options, userIdentity, nodeIdentity) {
395
615
  Guards.stringBase64(this.CLASS_NAME, "blob", blob);
396
- if (this._vaultConnector) {
616
+ if (this._includeUserIdentity) {
617
+ Guards.stringValue(this.CLASS_NAME, "userIdentity", userIdentity);
618
+ }
619
+ const disableEncryption = options?.disableEncryption ?? false;
620
+ const vaultKeyId = options?.overrideVaultKeyId ?? this._vaultKeyId;
621
+ const encryptionEnabled = !disableEncryption && Is.stringValue(vaultKeyId);
622
+ if (this._includeNodeIdentity || encryptionEnabled) {
397
623
  Guards.stringValue(this.CLASS_NAME, "nodeIdentity", nodeIdentity);
398
624
  }
399
625
  try {
400
- const connectorNamespace = namespace ?? this._defaultNamespace;
626
+ const connectorNamespace = options?.namespace ?? this._defaultNamespace;
401
627
  const blobStorageConnector = BlobStorageConnectorFactory.get(connectorNamespace);
402
628
  // Convert the base64 data into bytes
403
629
  let storeBlob = Converter.base64ToBytes(blob);
630
+ const blobSize = storeBlob.length;
404
631
  // See if we can detect the mime type and default extension for the data.
405
632
  // If not already supplied by the caller. We have to perform this operation
406
633
  // on the unencrypted data.
407
- if (!Is.stringValue(mimeType)) {
408
- mimeType = await MimeTypeHelper.detect(storeBlob);
634
+ if (!Is.stringValue(encodingFormat)) {
635
+ encodingFormat = await MimeTypeHelper.detect(storeBlob);
409
636
  }
410
- if (!Is.stringValue(extension) && Is.stringValue(mimeType)) {
411
- extension = await MimeTypeHelper.defaultExtension(mimeType);
637
+ if (!Is.stringValue(fileExtension) && Is.stringValue(encodingFormat)) {
638
+ fileExtension = await MimeTypeHelper.defaultExtension(encodingFormat);
412
639
  }
413
640
  if (Is.object(metadata)) {
414
641
  const validationFailures = [];
415
642
  JsonLdHelper.validate(metadata, validationFailures);
416
643
  Validation.asValidationError(this.CLASS_NAME, "metadata", validationFailures);
417
644
  }
645
+ const blobHash = `sha256:${Converter.bytesToBase64(Sha256.sum256(storeBlob))}`;
646
+ if (!Is.empty(options?.compress)) {
647
+ storeBlob = await Compression.compress(storeBlob, options.compress);
648
+ }
418
649
  // If we have a vault connector then encrypt the data.
419
- if (this._vaultConnector) {
420
- storeBlob = await this._vaultConnector.encrypt(`${nodeIdentity}/${this._vaultKeyId}`, VaultEncryptionType.ChaCha20Poly1305, storeBlob);
650
+ if (encryptionEnabled) {
651
+ if (Is.empty(this._vaultConnector)) {
652
+ throw new GeneralError(this.CLASS_NAME, "vaultConnectorNotConfigured");
653
+ }
654
+ storeBlob = await this._vaultConnector.encrypt(`${nodeIdentity}/${vaultKeyId}`, VaultEncryptionType.ChaCha20Poly1305, storeBlob);
421
655
  }
422
656
  // Set the blob in the storage connector, which may now be encrypted
423
657
  const blobId = await blobStorageConnector.set(storeBlob);
424
- await this._metadataEntityStorage.set({
658
+ // Now store the entry in entity storage
659
+ const blobEntry = {
425
660
  id: blobId,
426
- mimeType,
427
- extension,
428
- metadata
429
- });
661
+ dateCreated: new Date(Date.now()).toISOString(),
662
+ blobSize,
663
+ blobHash,
664
+ encodingFormat,
665
+ fileExtension,
666
+ metadata,
667
+ isEncrypted: encryptionEnabled,
668
+ compression: options?.compress
669
+ };
670
+ const conditions = [];
671
+ if (this._includeUserIdentity) {
672
+ ObjectHelper.propertySet(blobEntry, "userIdentity", userIdentity);
673
+ conditions.push({ property: "userIdentity", value: userIdentity });
674
+ }
675
+ if (this._includeNodeIdentity) {
676
+ ObjectHelper.propertySet(blobEntry, "nodeIdentity", nodeIdentity);
677
+ conditions.push({ property: "nodeIdentity", value: nodeIdentity });
678
+ }
679
+ await this._entryEntityStorage.set(blobEntry, conditions);
430
680
  return blobId;
431
681
  }
432
682
  catch (error) {
@@ -434,21 +684,40 @@ class BlobStorageService {
434
684
  }
435
685
  }
436
686
  /**
437
- * Get the blob and metadata.
687
+ * Get the blob entry.
438
688
  * @param id The id of the blob to get in urn format.
439
- * @param includeContent Include the content, or just get the metadata.
440
- * @param nodeIdentity The node identity which controls the vault key.
441
- * @returns The metadata and data for the blob if it can be found.
689
+ * @param options Optional options for the retrieval of the blob.
690
+ * @param options.includeContent Include the content, or just get the metadata.
691
+ * @param options.overrideVaultKeyId Use a different vault key id for decryption, if not provided the default vault key id will be used.
692
+ * @param options.decompress If the content should be decompressed, if it was compressed when stored, defaults to true.
693
+ * @param userIdentity The user identity to use with storage operations.
694
+ * @param nodeIdentity The node identity to use with storage operations.
695
+ * @returns The entry and data for the blob if it can be found.
442
696
  * @throws Not found error if the blob cannot be found.
443
697
  */
444
- async get(id, includeContent, nodeIdentity) {
698
+ async get(id, options, userIdentity, nodeIdentity) {
445
699
  Urn.guard(this.CLASS_NAME, "id", id);
446
- if (this._vaultConnector && includeContent) {
700
+ const includeContent = options?.includeContent ?? false;
701
+ const vaultKeyId = options?.overrideVaultKeyId ?? this._vaultKeyId;
702
+ const conditions = [];
703
+ if (this._includeUserIdentity) {
704
+ Guards.stringValue(this.CLASS_NAME, "userIdentity", userIdentity);
705
+ conditions.push({
706
+ property: "userIdentity",
707
+ comparison: ComparisonOperator.Equals,
708
+ value: userIdentity
709
+ });
710
+ }
711
+ if (this._includeNodeIdentity || (Is.notEmpty(this._vaultConnector) && includeContent)) {
447
712
  Guards.stringValue(this.CLASS_NAME, "nodeIdentity", nodeIdentity);
713
+ conditions.push({
714
+ property: "nodeIdentity",
715
+ comparison: ComparisonOperator.Equals,
716
+ value: nodeIdentity
717
+ });
448
718
  }
449
719
  try {
450
- // Get the metadata
451
- const blobMetadata = await this._metadataEntityStorage.get(id);
720
+ const blobEntry = await this.internalGet(id, userIdentity, nodeIdentity);
452
721
  let returnBlob;
453
722
  if (includeContent) {
454
723
  const blobStorageConnector = this.getConnector(id);
@@ -456,17 +725,20 @@ class BlobStorageService {
456
725
  if (Is.undefined(returnBlob)) {
457
726
  throw new NotFoundError(this.CLASS_NAME, "blobNotFound", id);
458
727
  }
459
- // If we have a vault connector then decrypt the data.
460
- if (this._vaultConnector) {
461
- returnBlob = await this._vaultConnector.decrypt(`${nodeIdentity}/${this._vaultKeyId}`, VaultEncryptionType.ChaCha20Poly1305, returnBlob);
728
+ // If the data is encrypted then decrypt it.
729
+ const decryptionEnabled = blobEntry.isEncrypted && Is.stringValue(vaultKeyId);
730
+ if (decryptionEnabled) {
731
+ if (Is.empty(this._vaultConnector)) {
732
+ throw new GeneralError(this.CLASS_NAME, "vaultConnectorNotConfigured");
733
+ }
734
+ returnBlob = await this._vaultConnector.decrypt(`${nodeIdentity}/${vaultKeyId}`, VaultEncryptionType.ChaCha20Poly1305, returnBlob);
735
+ }
736
+ if (!Is.empty(blobEntry.compression) && (options?.decompress ?? true)) {
737
+ returnBlob = await Compression.decompress(returnBlob, blobEntry.compression);
462
738
  }
463
739
  }
464
- return {
465
- blob: Is.uint8Array(returnBlob) ? Converter.bytesToBase64(returnBlob) : undefined,
466
- mimeType: blobMetadata?.mimeType,
467
- extension: blobMetadata?.extension,
468
- metadata: blobMetadata?.metadata
469
- };
740
+ const jsonLd = this.entryToJsonLd(blobEntry, returnBlob);
741
+ return JsonLdProcessor.compact(jsonLd, jsonLd["@context"]);
470
742
  }
471
743
  catch (error) {
472
744
  throw new GeneralError(this.CLASS_NAME, "getFailed", undefined, error);
@@ -474,18 +746,26 @@ class BlobStorageService {
474
746
  }
475
747
  /**
476
748
  * Update the blob with metadata.
477
- * @param id The id of the blob metadata to update.
478
- * @param mimeType Mime type for the blob, will be detected if left undefined.
479
- * @param extension Extension for the blob, will be detected if left undefined.
749
+ * @param id The id of the blob entry to update.
750
+ * @param encodingFormat Mime type for the blob, will be detected if left undefined.
751
+ * @param fileExtension Extension for the blob, will be detected if left undefined.
480
752
  * @param metadata Data for the custom metadata as JSON-LD.
753
+ * @param userIdentity The user identity to use with storage operations.
754
+ * @param nodeIdentity The node identity to use with storage operations.
481
755
  * @returns Nothing.
482
756
  * @throws Not found error if the blob cannot be found.
483
757
  */
484
- async update(id, mimeType, extension, metadata) {
758
+ async update(id, encodingFormat, fileExtension, metadata, userIdentity, nodeIdentity) {
485
759
  Urn.guard(this.CLASS_NAME, "id", id);
760
+ if (this._includeUserIdentity) {
761
+ Guards.stringValue(this.CLASS_NAME, "userIdentity", userIdentity);
762
+ }
763
+ if (this._includeNodeIdentity || Is.notEmpty(this._vaultConnector)) {
764
+ Guards.stringValue(this.CLASS_NAME, "nodeIdentity", nodeIdentity);
765
+ }
486
766
  try {
487
- const blobMetadata = await this._metadataEntityStorage.get(id);
488
- if (Is.undefined(blobMetadata)) {
767
+ const blobEntry = await this._entryEntityStorage.get(id);
768
+ if (Is.undefined(blobEntry)) {
489
769
  throw new NotFoundError(this.CLASS_NAME, "blobNotFound", id);
490
770
  }
491
771
  if (Is.object(metadata)) {
@@ -493,12 +773,29 @@ class BlobStorageService {
493
773
  await JsonLdHelper.validate(metadata, validationFailures);
494
774
  Validation.asValidationError(this.CLASS_NAME, "metadata", validationFailures);
495
775
  }
496
- await this._metadataEntityStorage.set({
497
- id: blobMetadata.id,
498
- mimeType: mimeType ?? blobMetadata.mimeType,
499
- extension: extension ?? blobMetadata.extension,
500
- metadata: metadata ?? blobMetadata.metadata
501
- });
776
+ // Now store the entry in entity storage
777
+ const updatedBlobEntry = {
778
+ id: blobEntry.id,
779
+ dateCreated: blobEntry.dateCreated,
780
+ dateModified: new Date(Date.now()).toISOString(),
781
+ blobSize: blobEntry.blobSize,
782
+ blobHash: blobEntry.blobHash,
783
+ encodingFormat: encodingFormat ?? blobEntry.encodingFormat,
784
+ fileExtension: fileExtension ?? blobEntry.fileExtension,
785
+ metadata: metadata ?? blobEntry.metadata,
786
+ isEncrypted: blobEntry.isEncrypted,
787
+ compression: blobEntry.compression
788
+ };
789
+ const conditions = [];
790
+ if (this._includeUserIdentity) {
791
+ ObjectHelper.propertySet(updatedBlobEntry, "userIdentity", userIdentity);
792
+ conditions.push({ property: "userIdentity", value: userIdentity });
793
+ }
794
+ if (this._includeNodeIdentity) {
795
+ ObjectHelper.propertySet(updatedBlobEntry, "nodeIdentity", nodeIdentity);
796
+ conditions.push({ property: "nodeIdentity", value: nodeIdentity });
797
+ }
798
+ await this._entryEntityStorage.set(updatedBlobEntry, conditions);
502
799
  }
503
800
  catch (error) {
504
801
  throw new GeneralError(this.CLASS_NAME, "updateFailed", undefined, error);
@@ -507,22 +804,100 @@ class BlobStorageService {
507
804
  /**
508
805
  * Remove the blob.
509
806
  * @param id The id of the blob to remove in urn format.
807
+ * @param userIdentity The user identity to use with storage operations.
808
+ * @param nodeIdentity The node identity to use with storage operations.
510
809
  * @returns Nothing.
511
810
  */
512
- async remove(id) {
811
+ async remove(id, userIdentity, nodeIdentity) {
513
812
  Urn.guard(this.CLASS_NAME, "id", id);
813
+ if (this._includeUserIdentity) {
814
+ Guards.stringValue(this.CLASS_NAME, "userIdentity", userIdentity);
815
+ }
816
+ if (this._includeNodeIdentity || Is.notEmpty(this._vaultConnector)) {
817
+ Guards.stringValue(this.CLASS_NAME, "nodeIdentity", nodeIdentity);
818
+ }
514
819
  try {
515
820
  const blobStorageConnector = this.getConnector(id);
821
+ const conditions = [];
822
+ if (this._includeUserIdentity) {
823
+ conditions.push({ property: "userIdentity", value: userIdentity });
824
+ }
825
+ if (this._includeNodeIdentity) {
826
+ conditions.push({ property: "nodeIdentity", value: nodeIdentity });
827
+ }
828
+ await this._entryEntityStorage.remove(id, conditions);
516
829
  const removed = await blobStorageConnector.remove(id);
517
830
  if (!removed) {
518
831
  throw new NotFoundError(this.CLASS_NAME, "blobNotFound", id);
519
832
  }
520
- await this._metadataEntityStorage.remove(id);
521
833
  }
522
834
  catch (error) {
523
835
  throw new GeneralError(this.CLASS_NAME, "removeFailed", undefined, error);
524
836
  }
525
837
  }
838
+ /**
839
+ * Query all the blob storage entries which match the conditions.
840
+ * @param conditions The conditions to match for the entries.
841
+ * @param orderBy The order for the results, defaults to created.
842
+ * @param orderByDirection The direction for the order, defaults to descending.
843
+ * @param cursor The cursor to request the next page of entries.
844
+ * @param pageSize The suggested number of entries to return in each chunk, in some scenarios can return a different amount.
845
+ * @param userIdentity The user identity to use with storage operations.
846
+ * @param nodeIdentity The node identity to use with storage operations.
847
+ * @returns All the entries for the storage matching the conditions,
848
+ * and a cursor which can be used to request more entities.
849
+ */
850
+ async query(conditions, orderBy, orderByDirection, cursor, pageSize, userIdentity, nodeIdentity) {
851
+ const finalConditions = {
852
+ conditions: [],
853
+ logicalOperator: LogicalOperator.And
854
+ };
855
+ if (this._includeNodeIdentity) {
856
+ Guards.stringValue(this.CLASS_NAME, "nodeIdentity", nodeIdentity);
857
+ finalConditions.conditions.push({
858
+ property: "nodeIdentity",
859
+ comparison: ComparisonOperator.Equals,
860
+ value: nodeIdentity
861
+ });
862
+ }
863
+ if (this._includeUserIdentity) {
864
+ Guards.stringValue(this.CLASS_NAME, "userIdentity", userIdentity);
865
+ finalConditions.conditions.push({
866
+ property: "userIdentity",
867
+ comparison: ComparisonOperator.Equals,
868
+ value: userIdentity
869
+ });
870
+ }
871
+ if (!Is.empty(conditions)) {
872
+ finalConditions.conditions.push(conditions);
873
+ }
874
+ const orderProperty = orderBy ?? "dateCreated";
875
+ const orderDirection = orderByDirection ?? SortDirection.Descending;
876
+ const result = await this._entryEntityStorage.query(finalConditions.conditions.length > 0 ? finalConditions : undefined, [
877
+ {
878
+ property: orderProperty,
879
+ sortDirection: orderDirection
880
+ }
881
+ ], undefined, cursor, pageSize);
882
+ let context = [
883
+ SchemaOrgContexts.ContextRoot,
884
+ BlobStorageContexts.ContextRoot,
885
+ BlobStorageContexts.ContextRootCommon
886
+ ];
887
+ const entriesJsonLd = [];
888
+ for (const entry of result.entities) {
889
+ // The entries are never Partial as we don't allow custom property requests.
890
+ entriesJsonLd.push(this.entryToJsonLd(entry));
891
+ context = JsonLdProcessor.combineContexts(context, entry.metadata?.["@context"]);
892
+ }
893
+ const jsonLd = {
894
+ "@context": context,
895
+ type: SchemaOrgTypes.ItemList,
896
+ [SchemaOrgTypes.ItemListElement]: entriesJsonLd,
897
+ [SchemaOrgTypes.NextItem]: result.cursor
898
+ };
899
+ return JsonLdProcessor.compact(jsonLd, jsonLd["@context"]);
900
+ }
526
901
  /**
527
902
  * Get the connector from the uri.
528
903
  * @param id The id of the blob storage item in urn format.
@@ -539,53 +914,206 @@ class BlobStorageService {
539
914
  }
540
915
  return BlobStorageConnectorFactory.get(idUri.namespaceMethod());
541
916
  }
917
+ /**
918
+ * Get an entity.
919
+ * @param id The id of the entity to get, or the index value if secondaryIndex is set.
920
+ * @param secondaryIndex Get the item using a secondary index.
921
+ * @param userIdentity The user identity to use with storage operations.
922
+ * @param nodeIdentity The node identity to use with storage operations.
923
+ * @returns The object if it can be found or throws.
924
+ * @internal
925
+ */
926
+ async internalGet(id, userIdentity, nodeIdentity) {
927
+ const conditions = [];
928
+ if (this._includeUserIdentity) {
929
+ Guards.stringValue(this.CLASS_NAME, "userIdentity", userIdentity);
930
+ conditions.push({
931
+ property: "userIdentity",
932
+ comparison: ComparisonOperator.Equals,
933
+ value: userIdentity
934
+ });
935
+ }
936
+ if (this._includeNodeIdentity) {
937
+ Guards.stringValue(this.CLASS_NAME, "nodeIdentity", nodeIdentity);
938
+ conditions.push({
939
+ property: "nodeIdentity",
940
+ comparison: ComparisonOperator.Equals,
941
+ value: nodeIdentity
942
+ });
943
+ }
944
+ let entity;
945
+ if (conditions.length === 0) {
946
+ entity = await this._entryEntityStorage.get(id);
947
+ }
948
+ else {
949
+ const schema = this._entryEntityStorage.getSchema();
950
+ const primaryKey = EntitySchemaHelper.getPrimaryKey(schema);
951
+ conditions.unshift({
952
+ property: primaryKey.property,
953
+ comparison: ComparisonOperator.Equals,
954
+ value: id
955
+ });
956
+ const results = await this._entryEntityStorage.query({
957
+ conditions,
958
+ logicalOperator: LogicalOperator.And
959
+ }, undefined, undefined, undefined, 1);
960
+ entity = results.entities[0];
961
+ }
962
+ if (Is.empty(entity)) {
963
+ throw new NotFoundError(this.CLASS_NAME, "entityNotFound", id);
964
+ }
965
+ return ObjectHelper.omit(entity, ["nodeIdentity", "userIdentity"]);
966
+ }
967
+ /**
968
+ * Convert the entry to JSON-LD.
969
+ * @param entry The entry to convert.
970
+ * @param blob The optional blob to return.
971
+ * @returns The JSON-LD representation of the entry.
972
+ * @internal
973
+ */
974
+ entryToJsonLd(entry, blob) {
975
+ const jsonLd = {
976
+ "@context": JsonLdProcessor.combineContexts([
977
+ BlobStorageContexts.ContextRoot,
978
+ BlobStorageContexts.ContextRootCommon,
979
+ SchemaOrgContexts.ContextRoot
980
+ ], entry?.metadata?.["@context"]),
981
+ id: entry.id,
982
+ type: BlobStorageTypes.Entry,
983
+ dateCreated: entry.dateCreated,
984
+ dateModified: entry.dateModified,
985
+ blobSize: entry.blobSize,
986
+ blobHash: entry.blobHash,
987
+ encodingFormat: entry?.encodingFormat,
988
+ fileExtension: entry?.fileExtension,
989
+ metadata: entry?.metadata,
990
+ blob: Is.uint8Array(blob) ? Converter.bytesToBase64(blob) : undefined,
991
+ isEncrypted: entry.isEncrypted,
992
+ compression: entry.compression
993
+ };
994
+ return jsonLd;
995
+ }
542
996
  }
543
997
 
544
998
  /**
545
- * Class representing metadata for the blob storage.
999
+ * Class representing entry for the blob storage.
546
1000
  */
547
- let BlobMetadata = class BlobMetadata {
1001
+ let BlobStorageEntry = class BlobStorageEntry {
548
1002
  /**
549
1003
  * The id for the blob.
550
1004
  */
551
1005
  id;
1006
+ /**
1007
+ * The date/time when the entry was created.
1008
+ */
1009
+ dateCreated;
1010
+ /**
1011
+ * The date/time when the entry was modified.
1012
+ */
1013
+ dateModified;
1014
+ /**
1015
+ * The length of the data in the blob.
1016
+ */
1017
+ blobSize;
1018
+ /**
1019
+ * The hash of the data in the blob.
1020
+ */
1021
+ blobHash;
552
1022
  /**
553
1023
  * The mime type for the blob.
554
1024
  */
555
- mimeType;
1025
+ encodingFormat;
556
1026
  /**
557
1027
  * The extension.
558
1028
  */
559
- extension;
1029
+ fileExtension;
560
1030
  /**
561
1031
  * The metadata for the blob as JSON-LD.
562
1032
  */
563
1033
  metadata;
1034
+ /**
1035
+ * Is the entry encrypted.
1036
+ */
1037
+ isEncrypted;
1038
+ /**
1039
+ * Is the entry compressed.
1040
+ */
1041
+ compression;
1042
+ /**
1043
+ * The user identity that created the blob.
1044
+ */
1045
+ userIdentity;
1046
+ /**
1047
+ * The node identity that created the blob.
1048
+ */
1049
+ nodeIdentity;
564
1050
  };
565
1051
  __decorate([
566
1052
  property({ type: "string", isPrimary: true }),
567
1053
  __metadata("design:type", String)
568
- ], BlobMetadata.prototype, "id", void 0);
1054
+ ], BlobStorageEntry.prototype, "id", void 0);
569
1055
  __decorate([
570
- property({ type: "string" }),
1056
+ property({ type: "string", format: "date-time", sortDirection: SortDirection.Descending }),
1057
+ __metadata("design:type", String)
1058
+ ], BlobStorageEntry.prototype, "dateCreated", void 0);
1059
+ __decorate([
1060
+ property({
1061
+ type: "string",
1062
+ format: "date-time",
1063
+ sortDirection: SortDirection.Descending,
1064
+ optional: true
1065
+ }),
571
1066
  __metadata("design:type", String)
572
- ], BlobMetadata.prototype, "mimeType", void 0);
1067
+ ], BlobStorageEntry.prototype, "dateModified", void 0);
1068
+ __decorate([
1069
+ property({ type: "number" }),
1070
+ __metadata("design:type", Number)
1071
+ ], BlobStorageEntry.prototype, "blobSize", void 0);
573
1072
  __decorate([
574
1073
  property({ type: "string" }),
575
1074
  __metadata("design:type", String)
576
- ], BlobMetadata.prototype, "extension", void 0);
1075
+ ], BlobStorageEntry.prototype, "blobHash", void 0);
1076
+ __decorate([
1077
+ property({ type: "string", optional: true }),
1078
+ __metadata("design:type", String)
1079
+ ], BlobStorageEntry.prototype, "encodingFormat", void 0);
1080
+ __decorate([
1081
+ property({ type: "string", optional: true }),
1082
+ __metadata("design:type", String)
1083
+ ], BlobStorageEntry.prototype, "fileExtension", void 0);
577
1084
  __decorate([
578
- property({ type: "object", itemTypeRef: "IJsonLdNodeObject" }),
1085
+ property({ type: "object", itemTypeRef: "IJsonLdNodeObject", optional: true }),
579
1086
  __metadata("design:type", Object)
580
- ], BlobMetadata.prototype, "metadata", void 0);
581
- BlobMetadata = __decorate([
1087
+ ], BlobStorageEntry.prototype, "metadata", void 0);
1088
+ __decorate([
1089
+ property({ type: "boolean" }),
1090
+ __metadata("design:type", Boolean)
1091
+ ], BlobStorageEntry.prototype, "isEncrypted", void 0);
1092
+ __decorate([
1093
+ property({ type: "string", optional: true }),
1094
+ __metadata("design:type", String)
1095
+ ], BlobStorageEntry.prototype, "compression", void 0);
1096
+ __decorate([
1097
+ property({ type: "string", optional: true }),
1098
+ __metadata("design:type", String)
1099
+ ], BlobStorageEntry.prototype, "userIdentity", void 0);
1100
+ __decorate([
1101
+ property({ type: "string", optional: true }),
1102
+ __metadata("design:type", String)
1103
+ ], BlobStorageEntry.prototype, "nodeIdentity", void 0);
1104
+ BlobStorageEntry = __decorate([
582
1105
  entity()
583
- ], BlobMetadata);
1106
+ ], BlobStorageEntry);
584
1107
 
1108
+ /**
1109
+ * These are dummy entry points for the blob storage service.
1110
+ * In reality your application would create its own entry points based on the
1111
+ * blob types it wants to store, using a custom defaultBaseRoute.
1112
+ */
585
1113
  const restEntryPoints = [
586
1114
  {
587
- name: "blobStorage",
588
- defaultBaseRoute: "blob",
1115
+ name: "blob-storage",
1116
+ defaultBaseRoute: "blob-storage",
589
1117
  tags: tagsBlobStorage,
590
1118
  generateRoutes: generateRestRoutesBlobStorage
591
1119
  }
@@ -597,7 +1125,7 @@ const restEntryPoints = [
597
1125
  * Initialize the schema for the blob storage entities.
598
1126
  */
599
1127
  function initSchema() {
600
- EntitySchemaFactory.register("BlobMetadata", () => EntitySchemaHelper.getSchema(BlobMetadata));
1128
+ EntitySchemaFactory.register("BlobStorageEntry", () => EntitySchemaHelper.getSchema(BlobStorageEntry));
601
1129
  }
602
1130
 
603
- export { BlobMetadata, BlobStorageService, blobStorageCreate, blobStorageGet, blobStorageGetContent, blobStorageRemove, blobStorageUpdate, generateRestRoutesBlobStorage, initSchema, restEntryPoints, tagsBlobStorage };
1131
+ export { BlobStorageEntry, BlobStorageService, blobStorageCreate, blobStorageGet, blobStorageGetContent, blobStorageList, blobStorageRemove, blobStorageUpdate, generateRestRoutesBlobStorage, initSchema, restEntryPoints, tagsBlobStorage };