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