@twin.org/blob-storage-service 0.0.1-next.9 → 0.0.2-next.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/index.cjs +476 -160
- package/dist/esm/index.mjs +479 -164
- package/dist/types/blobStorageRoutes.d.ts +10 -2
- package/dist/types/blobStorageService.d.ts +46 -35
- package/dist/types/entities/blobStorageEntry.d.ts +51 -0
- package/dist/types/index.d.ts +2 -1
- package/dist/types/models/IBlobStorageServiceConfig.d.ts +3 -8
- package/dist/types/models/IBlobStorageServiceConstructorOptions.d.ts +19 -0
- package/docs/changelog.md +190 -1
- package/docs/open-api/spec.json +1231 -1853
- package/docs/reference/classes/BlobStorageEntry.md +101 -0
- package/docs/reference/classes/BlobStorageService.md +158 -69
- package/docs/reference/functions/blobStorageCreate.md +9 -3
- package/docs/reference/functions/blobStorageGet.md +9 -3
- package/docs/reference/functions/blobStorageGetContent.md +9 -3
- package/docs/reference/functions/blobStorageList.md +31 -0
- package/docs/reference/functions/blobStorageRemove.md +9 -3
- package/docs/reference/functions/blobStorageUpdate.md +9 -3
- package/docs/reference/functions/generateRestRoutesBlobStorage.md +15 -7
- package/docs/reference/index.md +3 -1
- package/docs/reference/interfaces/IBlobStorageServiceConfig.md +4 -18
- package/docs/reference/interfaces/IBlobStorageServiceConstructorOptions.md +33 -0
- package/locales/en.json +2 -1
- package/package.json +7 -5
- package/dist/types/entities/blobMetadata.d.ts +0 -30
- package/docs/reference/classes/BlobMetadata.md +0 -61
package/dist/cjs/index.cjs
CHANGED
|
@@ -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
|
|
8
|
+
var crypto = require('@twin.org/crypto');
|
|
6
9
|
var dataJsonLd = require('@twin.org/data-json-ld');
|
|
7
10
|
var entity = require('@twin.org/entity');
|
|
8
11
|
var entityStorageModels = require('@twin.org/entity-storage-models');
|
|
9
12
|
var vaultModels = require('@twin.org/vault-models');
|
|
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
|
*/
|
|
@@ -50,7 +55,7 @@ function generateRestRoutesBlobStorage(baseRouteName, componentName, options) {
|
|
|
50
55
|
body: {
|
|
51
56
|
blob: "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZw==",
|
|
52
57
|
metadata: {
|
|
53
|
-
"@context": "
|
|
58
|
+
"@context": "https://schema.org",
|
|
54
59
|
"@type": "DigitalDocument",
|
|
55
60
|
name: "myfile.pdf"
|
|
56
61
|
}
|
|
@@ -93,7 +98,7 @@ function generateRestRoutesBlobStorage(baseRouteName, componentName, options) {
|
|
|
93
98
|
id: "blob-memory:c57d94b088f4c6d2cb32ded014813d0c786aa00134c8ee22f84b1e2545602a70"
|
|
94
99
|
},
|
|
95
100
|
query: {
|
|
96
|
-
includeContent: true
|
|
101
|
+
includeContent: "true"
|
|
97
102
|
}
|
|
98
103
|
}
|
|
99
104
|
}
|
|
@@ -107,8 +112,51 @@ function generateRestRoutesBlobStorage(baseRouteName, componentName, options) {
|
|
|
107
112
|
id: `${camelTypeName}GetResponseExample`,
|
|
108
113
|
response: {
|
|
109
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`,
|
|
144
|
+
response: {
|
|
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",
|
|
110
158
|
metadata: {
|
|
111
|
-
"@context": "
|
|
159
|
+
"@context": "https://schema.org",
|
|
112
160
|
"@type": "DigitalDocument",
|
|
113
161
|
name: "myfile.pdf"
|
|
114
162
|
},
|
|
@@ -140,7 +188,7 @@ function generateRestRoutesBlobStorage(baseRouteName, componentName, options) {
|
|
|
140
188
|
id: "blob-memory:c57d94b088f4c6d2cb32ded014813d0c786aa00134c8ee22f84b1e2545602a70"
|
|
141
189
|
},
|
|
142
190
|
query: {
|
|
143
|
-
download: true,
|
|
191
|
+
download: "true",
|
|
144
192
|
filename: "my-file.pdf"
|
|
145
193
|
}
|
|
146
194
|
}
|
|
@@ -154,7 +202,7 @@ function generateRestRoutesBlobStorage(baseRouteName, componentName, options) {
|
|
|
154
202
|
examples: [
|
|
155
203
|
{
|
|
156
204
|
id: `${camelTypeName}GetContentResponseExample`,
|
|
157
|
-
description: `The content of the blob, which will be a specific mime type if one can be detected from the content (or set as
|
|
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}.`,
|
|
158
206
|
response: {
|
|
159
207
|
body: new Uint8Array()
|
|
160
208
|
}
|
|
@@ -184,7 +232,7 @@ function generateRestRoutesBlobStorage(baseRouteName, componentName, options) {
|
|
|
184
232
|
},
|
|
185
233
|
body: {
|
|
186
234
|
metadata: {
|
|
187
|
-
"@context": "
|
|
235
|
+
"@context": "https://schema.org",
|
|
188
236
|
"@type": "DigitalDocument",
|
|
189
237
|
name: "myfile.pdf"
|
|
190
238
|
}
|
|
@@ -228,12 +276,116 @@ function generateRestRoutesBlobStorage(baseRouteName, componentName, options) {
|
|
|
228
276
|
}
|
|
229
277
|
]
|
|
230
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
|
+
};
|
|
231
382
|
return [
|
|
232
383
|
blobStorageCreateRoute,
|
|
233
384
|
blobStorageGetRoute,
|
|
234
385
|
blobStorageGetContentRoute,
|
|
235
386
|
blobStorageUpdateRoute,
|
|
236
|
-
blobStorageRemoveRoute
|
|
387
|
+
blobStorageRemoveRoute,
|
|
388
|
+
blobStorageListRoute
|
|
237
389
|
];
|
|
238
390
|
}
|
|
239
391
|
/**
|
|
@@ -248,7 +400,11 @@ async function blobStorageCreate(httpRequestContext, componentName, request) {
|
|
|
248
400
|
core.Guards.object(ROUTES_SOURCE, "request.body", request.body);
|
|
249
401
|
core.Guards.stringBase64(ROUTES_SOURCE, "request.body.blob", request.body.blob);
|
|
250
402
|
const component = core.ComponentFactory.get(componentName);
|
|
251
|
-
const id = await component.create(request.body.blob, request.body.
|
|
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);
|
|
252
408
|
return {
|
|
253
409
|
statusCode: web.HttpStatusCode.created,
|
|
254
410
|
headers: {
|
|
@@ -267,9 +423,17 @@ async function blobStorageGet(httpRequestContext, componentName, request) {
|
|
|
267
423
|
core.Guards.object(ROUTES_SOURCE, "request", request);
|
|
268
424
|
core.Guards.object(ROUTES_SOURCE, "request.pathParams", request.pathParams);
|
|
269
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";
|
|
270
427
|
const component = core.ComponentFactory.get(componentName);
|
|
271
|
-
const result = await component.get(request.pathParams.id,
|
|
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);
|
|
272
433
|
return {
|
|
434
|
+
headers: {
|
|
435
|
+
[web.HeaderTypes.ContentType]: mimeType === "json" ? web.MimeTypes.Json : web.MimeTypes.JsonLd
|
|
436
|
+
},
|
|
273
437
|
body: result
|
|
274
438
|
};
|
|
275
439
|
}
|
|
@@ -285,18 +449,34 @@ async function blobStorageGetContent(httpRequestContext, componentName, request)
|
|
|
285
449
|
core.Guards.object(ROUTES_SOURCE, "request.pathParams", request.pathParams);
|
|
286
450
|
core.Guards.stringValue(ROUTES_SOURCE, "request.pathParams.id", request.pathParams.id);
|
|
287
451
|
const component = core.ComponentFactory.get(componentName);
|
|
288
|
-
const
|
|
289
|
-
const
|
|
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
|
+
}
|
|
290
470
|
let filename = request.query?.filename;
|
|
291
471
|
if (!core.Is.stringValue(filename)) {
|
|
292
|
-
filename = `file.${result.
|
|
472
|
+
filename = `file.${result.fileExtension ?? web.MimeTypeHelper.defaultExtension(encodingFormat)}${compressedExtension}`;
|
|
293
473
|
}
|
|
294
474
|
return {
|
|
295
475
|
body: core.Is.stringBase64(result.blob) ? core.Converter.base64ToBytes(result.blob) : new Uint8Array(),
|
|
296
476
|
attachment: {
|
|
297
|
-
mimeType,
|
|
477
|
+
mimeType: compressedEncodingFormat ?? encodingFormat,
|
|
298
478
|
filename,
|
|
299
|
-
inline: !
|
|
479
|
+
inline: !download
|
|
300
480
|
}
|
|
301
481
|
};
|
|
302
482
|
}
|
|
@@ -312,7 +492,7 @@ async function blobStorageUpdate(httpRequestContext, componentName, request) {
|
|
|
312
492
|
core.Guards.object(ROUTES_SOURCE, "request.pathParams", request.pathParams);
|
|
313
493
|
core.Guards.stringValue(ROUTES_SOURCE, "request.pathParams.id", request.pathParams.id);
|
|
314
494
|
const component = core.ComponentFactory.get(componentName);
|
|
315
|
-
await component.update(request.pathParams.id, request.body.
|
|
495
|
+
await component.update(request.pathParams.id, request.body.encodingFormat, request.body.fileExtension, request.body.metadata, httpRequestContext.userIdentity);
|
|
316
496
|
return {
|
|
317
497
|
statusCode: web.HttpStatusCode.noContent
|
|
318
498
|
};
|
|
@@ -329,11 +509,30 @@ async function blobStorageRemove(httpRequestContext, componentName, request) {
|
|
|
329
509
|
core.Guards.object(ROUTES_SOURCE, "request.pathParams", request.pathParams);
|
|
330
510
|
core.Guards.stringValue(ROUTES_SOURCE, "request.pathParams.id", request.pathParams.id);
|
|
331
511
|
const component = core.ComponentFactory.get(componentName);
|
|
332
|
-
await component.remove(request.pathParams.id, httpRequestContext.userIdentity
|
|
512
|
+
await component.remove(request.pathParams.id, httpRequestContext.userIdentity);
|
|
333
513
|
return {
|
|
334
514
|
statusCode: web.HttpStatusCode.noContent
|
|
335
515
|
};
|
|
336
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);
|
|
529
|
+
return {
|
|
530
|
+
headers: {
|
|
531
|
+
[web.HeaderTypes.ContentType]: mimeType === "json" ? web.MimeTypes.Json : web.MimeTypes.JsonLd
|
|
532
|
+
},
|
|
533
|
+
body: result
|
|
534
|
+
};
|
|
535
|
+
}
|
|
337
536
|
|
|
338
537
|
// Copyright 2024 IOTA Stiftung.
|
|
339
538
|
// SPDX-License-Identifier: Apache-2.0.
|
|
@@ -343,8 +542,9 @@ async function blobStorageRemove(httpRequestContext, componentName, request) {
|
|
|
343
542
|
class BlobStorageService {
|
|
344
543
|
/**
|
|
345
544
|
* The namespace supported by the blob storage service.
|
|
545
|
+
* @internal
|
|
346
546
|
*/
|
|
347
|
-
static
|
|
547
|
+
static _NAMESPACE = "blob";
|
|
348
548
|
/**
|
|
349
549
|
* Runtime name for the class.
|
|
350
550
|
*/
|
|
@@ -359,7 +559,7 @@ class BlobStorageService {
|
|
|
359
559
|
* The storage connector for the metadata.
|
|
360
560
|
* @internal
|
|
361
561
|
*/
|
|
362
|
-
|
|
562
|
+
_entryEntityStorage;
|
|
363
563
|
/**
|
|
364
564
|
* The vault connector for the encryption, can be undefined if no encryption required.
|
|
365
565
|
* @internal
|
|
@@ -371,97 +571,103 @@ class BlobStorageService {
|
|
|
371
571
|
*/
|
|
372
572
|
_vaultKeyId;
|
|
373
573
|
/**
|
|
374
|
-
* Include the
|
|
375
|
-
* @internal
|
|
376
|
-
*/
|
|
377
|
-
_includeNodeIdentity;
|
|
378
|
-
/**
|
|
379
|
-
* Include the user identity when performing storage operations, defaults to true.
|
|
574
|
+
* Include the user identity when performing storage operations, can be used to partition for users, defaults to false.
|
|
380
575
|
* @internal
|
|
381
576
|
*/
|
|
382
|
-
|
|
577
|
+
_partitionPerUser;
|
|
383
578
|
/**
|
|
384
579
|
* Create a new instance of BlobStorageService.
|
|
385
|
-
* @param options The
|
|
386
|
-
* @param options.metadataEntityStorageType The type of the storage connector for the metadata, defaults to "blob-metadata".
|
|
387
|
-
* @param options.vaultConnectorType The type of the vault connector for encryption, if undefined no encryption will be performed.
|
|
388
|
-
* @param options.config The configuration for the service.
|
|
580
|
+
* @param options The options for the service.
|
|
389
581
|
*/
|
|
390
582
|
constructor(options) {
|
|
391
583
|
const names = blobStorageModels.BlobStorageConnectorFactory.names();
|
|
392
584
|
if (names.length === 0) {
|
|
393
585
|
throw new core.GeneralError(this.CLASS_NAME, "noConnectors");
|
|
394
586
|
}
|
|
395
|
-
this.
|
|
587
|
+
this._entryEntityStorage = entityStorageModels.EntityStorageConnectorFactory.get(options?.entryEntityStorageType ?? "blob-storage-entry");
|
|
396
588
|
if (core.Is.stringValue(options?.vaultConnectorType)) {
|
|
397
|
-
this._vaultConnector = vaultModels.VaultConnectorFactory.
|
|
589
|
+
this._vaultConnector = vaultModels.VaultConnectorFactory.get(options.vaultConnectorType);
|
|
398
590
|
}
|
|
399
591
|
this._defaultNamespace = options?.config?.defaultNamespace ?? names[0];
|
|
400
|
-
this._vaultKeyId = options?.config?.vaultKeyId
|
|
401
|
-
this.
|
|
402
|
-
|
|
592
|
+
this._vaultKeyId = options?.config?.vaultKeyId;
|
|
593
|
+
this._partitionPerUser = options?.config?.partitionPerUser ?? false;
|
|
594
|
+
standardsSchemaOrg.SchemaOrgDataTypes.registerRedirects();
|
|
403
595
|
}
|
|
404
596
|
/**
|
|
405
597
|
* Create the blob with some metadata.
|
|
406
598
|
* @param blob The data for the blob in base64 format.
|
|
407
|
-
* @param
|
|
408
|
-
* @param
|
|
599
|
+
* @param encodingFormat Mime type for the blob, will be detected if left undefined.
|
|
600
|
+
* @param fileExtension Extension for the blob, will be detected if left undefined.
|
|
409
601
|
* @param metadata Data for the custom metadata as JSON-LD.
|
|
410
|
-
* @param
|
|
602
|
+
* @param options Optional options for the creation of the blob.
|
|
603
|
+
* @param options.disableEncryption Disables encryption if enabled by default.
|
|
604
|
+
* @param options.overrideVaultKeyId Use a different vault key id for encryption, if not provided the default vault key id will be used.
|
|
605
|
+
* @param options.compress Optional compression type to use for the blob, defaults to no compression.*
|
|
606
|
+
* @param options.namespace The namespace to use for storing, defaults to component configured namespace.
|
|
411
607
|
* @param userIdentity The user identity to use with storage operations.
|
|
412
608
|
* @param nodeIdentity The node identity to use with storage operations.
|
|
413
609
|
* @returns The id of the stored blob in urn format.
|
|
414
610
|
*/
|
|
415
|
-
async create(blob,
|
|
611
|
+
async create(blob, encodingFormat, fileExtension, metadata, options, userIdentity, nodeIdentity) {
|
|
416
612
|
core.Guards.stringBase64(this.CLASS_NAME, "blob", blob);
|
|
417
|
-
if (this.
|
|
613
|
+
if (this._partitionPerUser) {
|
|
418
614
|
core.Guards.stringValue(this.CLASS_NAME, "userIdentity", userIdentity);
|
|
419
615
|
}
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
616
|
+
const disableEncryption = options?.disableEncryption ?? false;
|
|
617
|
+
const vaultKeyId = options?.overrideVaultKeyId ?? this._vaultKeyId;
|
|
618
|
+
const encryptionEnabled = !disableEncryption && core.Is.stringValue(vaultKeyId);
|
|
423
619
|
try {
|
|
424
|
-
const connectorNamespace = namespace ?? this._defaultNamespace;
|
|
620
|
+
const connectorNamespace = options?.namespace ?? this._defaultNamespace;
|
|
425
621
|
const blobStorageConnector = blobStorageModels.BlobStorageConnectorFactory.get(connectorNamespace);
|
|
426
622
|
// Convert the base64 data into bytes
|
|
427
623
|
let storeBlob = core.Converter.base64ToBytes(blob);
|
|
624
|
+
const blobSize = storeBlob.length;
|
|
428
625
|
// See if we can detect the mime type and default extension for the data.
|
|
429
626
|
// If not already supplied by the caller. We have to perform this operation
|
|
430
627
|
// on the unencrypted data.
|
|
431
|
-
if (!core.Is.stringValue(
|
|
432
|
-
|
|
628
|
+
if (!core.Is.stringValue(encodingFormat)) {
|
|
629
|
+
encodingFormat = await web.MimeTypeHelper.detect(storeBlob);
|
|
433
630
|
}
|
|
434
|
-
if (!core.Is.stringValue(
|
|
435
|
-
|
|
631
|
+
if (!core.Is.stringValue(fileExtension) && core.Is.stringValue(encodingFormat)) {
|
|
632
|
+
fileExtension = await web.MimeTypeHelper.defaultExtension(encodingFormat);
|
|
436
633
|
}
|
|
437
634
|
if (core.Is.object(metadata)) {
|
|
438
635
|
const validationFailures = [];
|
|
439
636
|
dataJsonLd.JsonLdHelper.validate(metadata, validationFailures);
|
|
440
637
|
core.Validation.asValidationError(this.CLASS_NAME, "metadata", validationFailures);
|
|
441
638
|
}
|
|
639
|
+
const blobHash = `sha256:${core.Converter.bytesToBase64(crypto.Sha256.sum256(storeBlob))}`;
|
|
640
|
+
if (!core.Is.empty(options?.compress)) {
|
|
641
|
+
storeBlob = await core.Compression.compress(storeBlob, options.compress);
|
|
642
|
+
}
|
|
442
643
|
// If we have a vault connector then encrypt the data.
|
|
443
|
-
if (
|
|
444
|
-
|
|
644
|
+
if (encryptionEnabled) {
|
|
645
|
+
core.Guards.stringValue(this.CLASS_NAME, "nodeIdentity", nodeIdentity);
|
|
646
|
+
if (core.Is.empty(this._vaultConnector)) {
|
|
647
|
+
throw new core.GeneralError(this.CLASS_NAME, "vaultConnectorNotConfigured");
|
|
648
|
+
}
|
|
649
|
+
storeBlob = await this._vaultConnector.encrypt(`${nodeIdentity}/${vaultKeyId}`, vaultModels.VaultEncryptionType.ChaCha20Poly1305, storeBlob);
|
|
445
650
|
}
|
|
446
651
|
// Set the blob in the storage connector, which may now be encrypted
|
|
447
652
|
const blobId = await blobStorageConnector.set(storeBlob);
|
|
448
|
-
// Now store the
|
|
449
|
-
const
|
|
653
|
+
// Now store the entry in entity storage
|
|
654
|
+
const blobEntry = {
|
|
450
655
|
id: blobId,
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
656
|
+
dateCreated: new Date(Date.now()).toISOString(),
|
|
657
|
+
blobSize,
|
|
658
|
+
blobHash,
|
|
659
|
+
encodingFormat,
|
|
660
|
+
fileExtension,
|
|
661
|
+
metadata,
|
|
662
|
+
isEncrypted: encryptionEnabled,
|
|
663
|
+
compression: options?.compress
|
|
454
664
|
};
|
|
455
665
|
const conditions = [];
|
|
456
|
-
if (this.
|
|
457
|
-
core.ObjectHelper.propertySet(
|
|
666
|
+
if (this._partitionPerUser) {
|
|
667
|
+
core.ObjectHelper.propertySet(blobEntry, "userIdentity", userIdentity);
|
|
458
668
|
conditions.push({ property: "userIdentity", value: userIdentity });
|
|
459
669
|
}
|
|
460
|
-
|
|
461
|
-
core.ObjectHelper.propertySet(blobMetadata, "nodeIdentity", nodeIdentity);
|
|
462
|
-
conditions.push({ property: "nodeIdentity", value: nodeIdentity });
|
|
463
|
-
}
|
|
464
|
-
await this._metadataEntityStorage.set(blobMetadata, conditions);
|
|
670
|
+
await this._entryEntityStorage.set(blobEntry, conditions);
|
|
465
671
|
return blobId;
|
|
466
672
|
}
|
|
467
673
|
catch (error) {
|
|
@@ -469,18 +675,23 @@ class BlobStorageService {
|
|
|
469
675
|
}
|
|
470
676
|
}
|
|
471
677
|
/**
|
|
472
|
-
* Get the blob
|
|
678
|
+
* Get the blob entry.
|
|
473
679
|
* @param id The id of the blob to get in urn format.
|
|
474
|
-
* @param
|
|
680
|
+
* @param options Optional options for the retrieval of the blob.
|
|
681
|
+
* @param options.includeContent Include the content, or just get the metadata.
|
|
682
|
+
* @param options.overrideVaultKeyId Use a different vault key id for decryption, if not provided the default vault key id will be used.
|
|
683
|
+
* @param options.decompress If the content should be decompressed, if it was compressed when stored, defaults to true.
|
|
475
684
|
* @param userIdentity The user identity to use with storage operations.
|
|
476
685
|
* @param nodeIdentity The node identity to use with storage operations.
|
|
477
|
-
* @returns The
|
|
686
|
+
* @returns The entry and data for the blob if it can be found.
|
|
478
687
|
* @throws Not found error if the blob cannot be found.
|
|
479
688
|
*/
|
|
480
|
-
async get(id,
|
|
689
|
+
async get(id, options, userIdentity, nodeIdentity) {
|
|
481
690
|
core.Urn.guard(this.CLASS_NAME, "id", id);
|
|
691
|
+
const includeContent = options?.includeContent ?? false;
|
|
692
|
+
const vaultKeyId = options?.overrideVaultKeyId ?? this._vaultKeyId;
|
|
482
693
|
const conditions = [];
|
|
483
|
-
if (this.
|
|
694
|
+
if (this._partitionPerUser) {
|
|
484
695
|
core.Guards.stringValue(this.CLASS_NAME, "userIdentity", userIdentity);
|
|
485
696
|
conditions.push({
|
|
486
697
|
property: "userIdentity",
|
|
@@ -488,16 +699,8 @@ class BlobStorageService {
|
|
|
488
699
|
value: userIdentity
|
|
489
700
|
});
|
|
490
701
|
}
|
|
491
|
-
if (this._includeNodeIdentity || (core.Is.notEmpty(this._vaultConnector) && includeContent)) {
|
|
492
|
-
core.Guards.stringValue(this.CLASS_NAME, "nodeIdentity", nodeIdentity);
|
|
493
|
-
conditions.push({
|
|
494
|
-
property: "nodeIdentity",
|
|
495
|
-
comparison: entity.ComparisonOperator.Equals,
|
|
496
|
-
value: nodeIdentity
|
|
497
|
-
});
|
|
498
|
-
}
|
|
499
702
|
try {
|
|
500
|
-
const
|
|
703
|
+
const blobEntry = await this.internalGet(id, userIdentity);
|
|
501
704
|
let returnBlob;
|
|
502
705
|
if (includeContent) {
|
|
503
706
|
const blobStorageConnector = this.getConnector(id);
|
|
@@ -505,17 +708,21 @@ class BlobStorageService {
|
|
|
505
708
|
if (core.Is.undefined(returnBlob)) {
|
|
506
709
|
throw new core.NotFoundError(this.CLASS_NAME, "blobNotFound", id);
|
|
507
710
|
}
|
|
508
|
-
// If
|
|
509
|
-
|
|
510
|
-
|
|
711
|
+
// If the data is encrypted then decrypt it.
|
|
712
|
+
const decryptionEnabled = blobEntry.isEncrypted && core.Is.stringValue(vaultKeyId);
|
|
713
|
+
if (decryptionEnabled) {
|
|
714
|
+
core.Guards.stringValue(this.CLASS_NAME, "nodeIdentity", nodeIdentity);
|
|
715
|
+
if (core.Is.empty(this._vaultConnector)) {
|
|
716
|
+
throw new core.GeneralError(this.CLASS_NAME, "vaultConnectorNotConfigured");
|
|
717
|
+
}
|
|
718
|
+
returnBlob = await this._vaultConnector.decrypt(`${nodeIdentity}/${vaultKeyId}`, vaultModels.VaultEncryptionType.ChaCha20Poly1305, returnBlob);
|
|
719
|
+
}
|
|
720
|
+
if (!core.Is.empty(blobEntry.compression) && (options?.decompress ?? true)) {
|
|
721
|
+
returnBlob = await core.Compression.decompress(returnBlob, blobEntry.compression);
|
|
511
722
|
}
|
|
512
723
|
}
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
mimeType: blobMetadata?.mimeType,
|
|
516
|
-
extension: blobMetadata?.extension,
|
|
517
|
-
metadata: blobMetadata?.metadata
|
|
518
|
-
};
|
|
724
|
+
const jsonLd = this.entryToJsonLd(blobEntry, returnBlob);
|
|
725
|
+
return dataJsonLd.JsonLdProcessor.compact(jsonLd, jsonLd["@context"]);
|
|
519
726
|
}
|
|
520
727
|
catch (error) {
|
|
521
728
|
throw new core.GeneralError(this.CLASS_NAME, "getFailed", undefined, error);
|
|
@@ -523,26 +730,22 @@ class BlobStorageService {
|
|
|
523
730
|
}
|
|
524
731
|
/**
|
|
525
732
|
* Update the blob with metadata.
|
|
526
|
-
* @param id The id of the blob
|
|
527
|
-
* @param
|
|
528
|
-
* @param
|
|
733
|
+
* @param id The id of the blob entry to update.
|
|
734
|
+
* @param encodingFormat Mime type for the blob, will be detected if left undefined.
|
|
735
|
+
* @param fileExtension Extension for the blob, will be detected if left undefined.
|
|
529
736
|
* @param metadata Data for the custom metadata as JSON-LD.
|
|
530
737
|
* @param userIdentity The user identity to use with storage operations.
|
|
531
|
-
* @param nodeIdentity The node identity to use with storage operations.
|
|
532
738
|
* @returns Nothing.
|
|
533
739
|
* @throws Not found error if the blob cannot be found.
|
|
534
740
|
*/
|
|
535
|
-
async update(id,
|
|
741
|
+
async update(id, encodingFormat, fileExtension, metadata, userIdentity) {
|
|
536
742
|
core.Urn.guard(this.CLASS_NAME, "id", id);
|
|
537
|
-
if (this.
|
|
743
|
+
if (this._partitionPerUser) {
|
|
538
744
|
core.Guards.stringValue(this.CLASS_NAME, "userIdentity", userIdentity);
|
|
539
745
|
}
|
|
540
|
-
if (this._includeNodeIdentity || core.Is.notEmpty(this._vaultConnector)) {
|
|
541
|
-
core.Guards.stringValue(this.CLASS_NAME, "nodeIdentity", nodeIdentity);
|
|
542
|
-
}
|
|
543
746
|
try {
|
|
544
|
-
const
|
|
545
|
-
if (core.Is.undefined(
|
|
747
|
+
const blobEntry = await this._entryEntityStorage.get(id);
|
|
748
|
+
if (core.Is.undefined(blobEntry)) {
|
|
546
749
|
throw new core.NotFoundError(this.CLASS_NAME, "blobNotFound", id);
|
|
547
750
|
}
|
|
548
751
|
if (core.Is.object(metadata)) {
|
|
@@ -550,23 +753,25 @@ class BlobStorageService {
|
|
|
550
753
|
await dataJsonLd.JsonLdHelper.validate(metadata, validationFailures);
|
|
551
754
|
core.Validation.asValidationError(this.CLASS_NAME, "metadata", validationFailures);
|
|
552
755
|
}
|
|
553
|
-
// Now store the
|
|
554
|
-
const
|
|
555
|
-
id:
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
756
|
+
// Now store the entry in entity storage
|
|
757
|
+
const updatedBlobEntry = {
|
|
758
|
+
id: blobEntry.id,
|
|
759
|
+
dateCreated: blobEntry.dateCreated,
|
|
760
|
+
dateModified: new Date(Date.now()).toISOString(),
|
|
761
|
+
blobSize: blobEntry.blobSize,
|
|
762
|
+
blobHash: blobEntry.blobHash,
|
|
763
|
+
encodingFormat: encodingFormat ?? blobEntry.encodingFormat,
|
|
764
|
+
fileExtension: fileExtension ?? blobEntry.fileExtension,
|
|
765
|
+
metadata: metadata ?? blobEntry.metadata,
|
|
766
|
+
isEncrypted: blobEntry.isEncrypted,
|
|
767
|
+
compression: blobEntry.compression
|
|
559
768
|
};
|
|
560
769
|
const conditions = [];
|
|
561
|
-
if (this.
|
|
562
|
-
core.ObjectHelper.propertySet(
|
|
770
|
+
if (this._partitionPerUser) {
|
|
771
|
+
core.ObjectHelper.propertySet(updatedBlobEntry, "userIdentity", userIdentity);
|
|
563
772
|
conditions.push({ property: "userIdentity", value: userIdentity });
|
|
564
773
|
}
|
|
565
|
-
|
|
566
|
-
core.ObjectHelper.propertySet(updatedBlobMetadata, "nodeIdentity", nodeIdentity);
|
|
567
|
-
conditions.push({ property: "nodeIdentity", value: nodeIdentity });
|
|
568
|
-
}
|
|
569
|
-
await this._metadataEntityStorage.set(updatedBlobMetadata, conditions);
|
|
774
|
+
await this._entryEntityStorage.set(updatedBlobEntry, conditions);
|
|
570
775
|
}
|
|
571
776
|
catch (error) {
|
|
572
777
|
throw new core.GeneralError(this.CLASS_NAME, "updateFailed", undefined, error);
|
|
@@ -576,27 +781,20 @@ class BlobStorageService {
|
|
|
576
781
|
* Remove the blob.
|
|
577
782
|
* @param id The id of the blob to remove in urn format.
|
|
578
783
|
* @param userIdentity The user identity to use with storage operations.
|
|
579
|
-
* @param nodeIdentity The node identity to use with storage operations.
|
|
580
784
|
* @returns Nothing.
|
|
581
785
|
*/
|
|
582
|
-
async remove(id, userIdentity
|
|
786
|
+
async remove(id, userIdentity) {
|
|
583
787
|
core.Urn.guard(this.CLASS_NAME, "id", id);
|
|
584
|
-
if (this.
|
|
788
|
+
if (this._partitionPerUser) {
|
|
585
789
|
core.Guards.stringValue(this.CLASS_NAME, "userIdentity", userIdentity);
|
|
586
790
|
}
|
|
587
|
-
if (this._includeNodeIdentity || core.Is.notEmpty(this._vaultConnector)) {
|
|
588
|
-
core.Guards.stringValue(this.CLASS_NAME, "nodeIdentity", nodeIdentity);
|
|
589
|
-
}
|
|
590
791
|
try {
|
|
591
792
|
const blobStorageConnector = this.getConnector(id);
|
|
592
793
|
const conditions = [];
|
|
593
|
-
if (this.
|
|
794
|
+
if (this._partitionPerUser) {
|
|
594
795
|
conditions.push({ property: "userIdentity", value: userIdentity });
|
|
595
796
|
}
|
|
596
|
-
|
|
597
|
-
conditions.push({ property: "nodeIdentity", value: nodeIdentity });
|
|
598
|
-
}
|
|
599
|
-
await this._metadataEntityStorage.remove(id, conditions);
|
|
797
|
+
await this._entryEntityStorage.remove(id, conditions);
|
|
600
798
|
const removed = await blobStorageConnector.remove(id);
|
|
601
799
|
if (!removed) {
|
|
602
800
|
throw new core.NotFoundError(this.CLASS_NAME, "blobNotFound", id);
|
|
@@ -606,6 +804,60 @@ class BlobStorageService {
|
|
|
606
804
|
throw new core.GeneralError(this.CLASS_NAME, "removeFailed", undefined, error);
|
|
607
805
|
}
|
|
608
806
|
}
|
|
807
|
+
/**
|
|
808
|
+
* Query all the blob storage entries which match the conditions.
|
|
809
|
+
* @param conditions The conditions to match for the entries.
|
|
810
|
+
* @param orderBy The order for the results, defaults to created.
|
|
811
|
+
* @param orderByDirection The direction for the order, defaults to descending.
|
|
812
|
+
* @param cursor The cursor to request the next page of entries.
|
|
813
|
+
* @param pageSize The suggested number of entries to return in each chunk, in some scenarios can return a different amount.
|
|
814
|
+
* @param userIdentity The user identity to use with storage operations.
|
|
815
|
+
* @returns All the entries for the storage matching the conditions,
|
|
816
|
+
* and a cursor which can be used to request more entities.
|
|
817
|
+
*/
|
|
818
|
+
async query(conditions, orderBy, orderByDirection, cursor, pageSize, userIdentity) {
|
|
819
|
+
const finalConditions = {
|
|
820
|
+
conditions: [],
|
|
821
|
+
logicalOperator: entity.LogicalOperator.And
|
|
822
|
+
};
|
|
823
|
+
if (this._partitionPerUser) {
|
|
824
|
+
core.Guards.stringValue(this.CLASS_NAME, "userIdentity", userIdentity);
|
|
825
|
+
finalConditions.conditions.push({
|
|
826
|
+
property: "userIdentity",
|
|
827
|
+
comparison: entity.ComparisonOperator.Equals,
|
|
828
|
+
value: userIdentity
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
if (!core.Is.empty(conditions)) {
|
|
832
|
+
finalConditions.conditions.push(conditions);
|
|
833
|
+
}
|
|
834
|
+
const orderProperty = orderBy ?? "dateCreated";
|
|
835
|
+
const orderDirection = orderByDirection ?? entity.SortDirection.Descending;
|
|
836
|
+
const result = await this._entryEntityStorage.query(finalConditions.conditions.length > 0 ? finalConditions : undefined, [
|
|
837
|
+
{
|
|
838
|
+
property: orderProperty,
|
|
839
|
+
sortDirection: orderDirection
|
|
840
|
+
}
|
|
841
|
+
], undefined, cursor, pageSize);
|
|
842
|
+
let context = [
|
|
843
|
+
standardsSchemaOrg.SchemaOrgContexts.ContextRoot,
|
|
844
|
+
blobStorageModels.BlobStorageContexts.ContextRoot,
|
|
845
|
+
blobStorageModels.BlobStorageContexts.ContextRootCommon
|
|
846
|
+
];
|
|
847
|
+
const entriesJsonLd = [];
|
|
848
|
+
for (const entry of result.entities) {
|
|
849
|
+
// The entries are never Partial as we don't allow custom property requests.
|
|
850
|
+
entriesJsonLd.push(this.entryToJsonLd(entry));
|
|
851
|
+
context = dataJsonLd.JsonLdProcessor.combineContexts(context, entry.metadata?.["@context"]);
|
|
852
|
+
}
|
|
853
|
+
const jsonLd = {
|
|
854
|
+
"@context": context,
|
|
855
|
+
type: standardsSchemaOrg.SchemaOrgTypes.ItemList,
|
|
856
|
+
[standardsSchemaOrg.SchemaOrgTypes.ItemListElement]: entriesJsonLd,
|
|
857
|
+
[standardsSchemaOrg.SchemaOrgTypes.NextItem]: result.cursor
|
|
858
|
+
};
|
|
859
|
+
return dataJsonLd.JsonLdProcessor.compact(jsonLd, jsonLd["@context"]);
|
|
860
|
+
}
|
|
609
861
|
/**
|
|
610
862
|
* Get the connector from the uri.
|
|
611
863
|
* @param id The id of the blob storage item in urn format.
|
|
@@ -614,9 +866,9 @@ class BlobStorageService {
|
|
|
614
866
|
*/
|
|
615
867
|
getConnector(id) {
|
|
616
868
|
const idUri = core.Urn.fromValidString(id);
|
|
617
|
-
if (idUri.namespaceIdentifier() !== BlobStorageService.
|
|
869
|
+
if (idUri.namespaceIdentifier() !== BlobStorageService._NAMESPACE) {
|
|
618
870
|
throw new core.GeneralError(this.CLASS_NAME, "namespaceMismatch", {
|
|
619
|
-
namespace: BlobStorageService.
|
|
871
|
+
namespace: BlobStorageService._NAMESPACE,
|
|
620
872
|
id
|
|
621
873
|
});
|
|
622
874
|
}
|
|
@@ -627,13 +879,12 @@ class BlobStorageService {
|
|
|
627
879
|
* @param id The id of the entity to get, or the index value if secondaryIndex is set.
|
|
628
880
|
* @param secondaryIndex Get the item using a secondary index.
|
|
629
881
|
* @param userIdentity The user identity to use with storage operations.
|
|
630
|
-
* @param nodeIdentity The node identity to use with storage operations.
|
|
631
882
|
* @returns The object if it can be found or throws.
|
|
632
883
|
* @internal
|
|
633
884
|
*/
|
|
634
|
-
async internalGet(id, userIdentity
|
|
885
|
+
async internalGet(id, userIdentity) {
|
|
635
886
|
const conditions = [];
|
|
636
|
-
if (this.
|
|
887
|
+
if (this._partitionPerUser) {
|
|
637
888
|
core.Guards.stringValue(this.CLASS_NAME, "userIdentity", userIdentity);
|
|
638
889
|
conditions.push({
|
|
639
890
|
property: "userIdentity",
|
|
@@ -641,27 +892,19 @@ class BlobStorageService {
|
|
|
641
892
|
value: userIdentity
|
|
642
893
|
});
|
|
643
894
|
}
|
|
644
|
-
if (this._includeNodeIdentity) {
|
|
645
|
-
core.Guards.stringValue(this.CLASS_NAME, "nodeIdentity", nodeIdentity);
|
|
646
|
-
conditions.push({
|
|
647
|
-
property: "nodeIdentity",
|
|
648
|
-
comparison: entity.ComparisonOperator.Equals,
|
|
649
|
-
value: nodeIdentity
|
|
650
|
-
});
|
|
651
|
-
}
|
|
652
895
|
let entity$1;
|
|
653
896
|
if (conditions.length === 0) {
|
|
654
|
-
entity$1 = await this.
|
|
897
|
+
entity$1 = await this._entryEntityStorage.get(id);
|
|
655
898
|
}
|
|
656
899
|
else {
|
|
657
|
-
const schema = this.
|
|
900
|
+
const schema = this._entryEntityStorage.getSchema();
|
|
658
901
|
const primaryKey = entity.EntitySchemaHelper.getPrimaryKey(schema);
|
|
659
902
|
conditions.unshift({
|
|
660
903
|
property: primaryKey.property,
|
|
661
904
|
comparison: entity.ComparisonOperator.Equals,
|
|
662
905
|
value: id
|
|
663
906
|
});
|
|
664
|
-
const results = await this.
|
|
907
|
+
const results = await this._entryEntityStorage.query({
|
|
665
908
|
conditions,
|
|
666
909
|
logicalOperator: entity.LogicalOperator.And
|
|
667
910
|
}, undefined, undefined, undefined, 1);
|
|
@@ -670,68 +913,140 @@ class BlobStorageService {
|
|
|
670
913
|
if (core.Is.empty(entity$1)) {
|
|
671
914
|
throw new core.NotFoundError(this.CLASS_NAME, "entityNotFound", id);
|
|
672
915
|
}
|
|
673
|
-
core.ObjectHelper.
|
|
674
|
-
|
|
675
|
-
|
|
916
|
+
return core.ObjectHelper.omit(entity$1, ["userIdentity"]);
|
|
917
|
+
}
|
|
918
|
+
/**
|
|
919
|
+
* Convert the entry to JSON-LD.
|
|
920
|
+
* @param entry The entry to convert.
|
|
921
|
+
* @param blob The optional blob to return.
|
|
922
|
+
* @returns The JSON-LD representation of the entry.
|
|
923
|
+
* @internal
|
|
924
|
+
*/
|
|
925
|
+
entryToJsonLd(entry, blob) {
|
|
926
|
+
const jsonLd = {
|
|
927
|
+
"@context": dataJsonLd.JsonLdProcessor.combineContexts([
|
|
928
|
+
blobStorageModels.BlobStorageContexts.ContextRoot,
|
|
929
|
+
blobStorageModels.BlobStorageContexts.ContextRootCommon,
|
|
930
|
+
standardsSchemaOrg.SchemaOrgContexts.ContextRoot
|
|
931
|
+
], entry?.metadata?.["@context"]),
|
|
932
|
+
id: entry.id,
|
|
933
|
+
type: blobStorageModels.BlobStorageTypes.Entry,
|
|
934
|
+
dateCreated: entry.dateCreated,
|
|
935
|
+
dateModified: entry.dateModified,
|
|
936
|
+
blobSize: entry.blobSize,
|
|
937
|
+
blobHash: entry.blobHash,
|
|
938
|
+
encodingFormat: entry?.encodingFormat,
|
|
939
|
+
fileExtension: entry?.fileExtension,
|
|
940
|
+
metadata: entry?.metadata,
|
|
941
|
+
blob: core.Is.uint8Array(blob) ? core.Converter.bytesToBase64(blob) : undefined,
|
|
942
|
+
isEncrypted: entry.isEncrypted,
|
|
943
|
+
compression: entry.compression
|
|
944
|
+
};
|
|
945
|
+
return jsonLd;
|
|
676
946
|
}
|
|
677
947
|
}
|
|
678
948
|
|
|
679
949
|
/**
|
|
680
|
-
* Class representing
|
|
950
|
+
* Class representing entry for the blob storage.
|
|
681
951
|
*/
|
|
682
|
-
exports.
|
|
952
|
+
exports.BlobStorageEntry = class BlobStorageEntry {
|
|
683
953
|
/**
|
|
684
954
|
* The id for the blob.
|
|
685
955
|
*/
|
|
686
956
|
id;
|
|
957
|
+
/**
|
|
958
|
+
* The date/time when the entry was created.
|
|
959
|
+
*/
|
|
960
|
+
dateCreated;
|
|
961
|
+
/**
|
|
962
|
+
* The date/time when the entry was modified.
|
|
963
|
+
*/
|
|
964
|
+
dateModified;
|
|
965
|
+
/**
|
|
966
|
+
* The length of the data in the blob.
|
|
967
|
+
*/
|
|
968
|
+
blobSize;
|
|
969
|
+
/**
|
|
970
|
+
* The hash of the data in the blob.
|
|
971
|
+
*/
|
|
972
|
+
blobHash;
|
|
687
973
|
/**
|
|
688
974
|
* The mime type for the blob.
|
|
689
975
|
*/
|
|
690
|
-
|
|
976
|
+
encodingFormat;
|
|
691
977
|
/**
|
|
692
978
|
* The extension.
|
|
693
979
|
*/
|
|
694
|
-
|
|
980
|
+
fileExtension;
|
|
695
981
|
/**
|
|
696
982
|
* The metadata for the blob as JSON-LD.
|
|
697
983
|
*/
|
|
698
984
|
metadata;
|
|
699
985
|
/**
|
|
700
|
-
*
|
|
986
|
+
* Is the entry encrypted.
|
|
701
987
|
*/
|
|
702
|
-
|
|
988
|
+
isEncrypted;
|
|
703
989
|
/**
|
|
704
|
-
*
|
|
990
|
+
* Is the entry compressed.
|
|
991
|
+
*/
|
|
992
|
+
compression;
|
|
993
|
+
/**
|
|
994
|
+
* The user identity that created the blob.
|
|
705
995
|
*/
|
|
706
|
-
|
|
996
|
+
userIdentity;
|
|
707
997
|
};
|
|
708
998
|
__decorate([
|
|
709
999
|
entity.property({ type: "string", isPrimary: true }),
|
|
710
1000
|
__metadata("design:type", String)
|
|
711
|
-
], exports.
|
|
1001
|
+
], exports.BlobStorageEntry.prototype, "id", void 0);
|
|
712
1002
|
__decorate([
|
|
713
|
-
entity.property({ type: "string" }),
|
|
1003
|
+
entity.property({ type: "string", format: "date-time", sortDirection: entity.SortDirection.Descending }),
|
|
1004
|
+
__metadata("design:type", String)
|
|
1005
|
+
], exports.BlobStorageEntry.prototype, "dateCreated", void 0);
|
|
1006
|
+
__decorate([
|
|
1007
|
+
entity.property({
|
|
1008
|
+
type: "string",
|
|
1009
|
+
format: "date-time",
|
|
1010
|
+
sortDirection: entity.SortDirection.Descending,
|
|
1011
|
+
optional: true
|
|
1012
|
+
}),
|
|
714
1013
|
__metadata("design:type", String)
|
|
715
|
-
], exports.
|
|
1014
|
+
], exports.BlobStorageEntry.prototype, "dateModified", void 0);
|
|
1015
|
+
__decorate([
|
|
1016
|
+
entity.property({ type: "number" }),
|
|
1017
|
+
__metadata("design:type", Number)
|
|
1018
|
+
], exports.BlobStorageEntry.prototype, "blobSize", void 0);
|
|
716
1019
|
__decorate([
|
|
717
1020
|
entity.property({ type: "string" }),
|
|
718
1021
|
__metadata("design:type", String)
|
|
719
|
-
], exports.
|
|
1022
|
+
], exports.BlobStorageEntry.prototype, "blobHash", void 0);
|
|
1023
|
+
__decorate([
|
|
1024
|
+
entity.property({ type: "string", optional: true }),
|
|
1025
|
+
__metadata("design:type", String)
|
|
1026
|
+
], exports.BlobStorageEntry.prototype, "encodingFormat", void 0);
|
|
1027
|
+
__decorate([
|
|
1028
|
+
entity.property({ type: "string", optional: true }),
|
|
1029
|
+
__metadata("design:type", String)
|
|
1030
|
+
], exports.BlobStorageEntry.prototype, "fileExtension", void 0);
|
|
720
1031
|
__decorate([
|
|
721
|
-
entity.property({ type: "object", itemTypeRef: "IJsonLdNodeObject" }),
|
|
1032
|
+
entity.property({ type: "object", itemTypeRef: "IJsonLdNodeObject", optional: true }),
|
|
722
1033
|
__metadata("design:type", Object)
|
|
723
|
-
], exports.
|
|
1034
|
+
], exports.BlobStorageEntry.prototype, "metadata", void 0);
|
|
724
1035
|
__decorate([
|
|
725
|
-
entity.property({ type: "
|
|
1036
|
+
entity.property({ type: "boolean" }),
|
|
1037
|
+
__metadata("design:type", Boolean)
|
|
1038
|
+
], exports.BlobStorageEntry.prototype, "isEncrypted", void 0);
|
|
1039
|
+
__decorate([
|
|
1040
|
+
entity.property({ type: "string", optional: true }),
|
|
726
1041
|
__metadata("design:type", String)
|
|
727
|
-
], exports.
|
|
1042
|
+
], exports.BlobStorageEntry.prototype, "compression", void 0);
|
|
728
1043
|
__decorate([
|
|
729
|
-
entity.property({ type: "string" }),
|
|
1044
|
+
entity.property({ type: "string", optional: true }),
|
|
730
1045
|
__metadata("design:type", String)
|
|
731
|
-
], exports.
|
|
732
|
-
exports.
|
|
1046
|
+
], exports.BlobStorageEntry.prototype, "userIdentity", void 0);
|
|
1047
|
+
exports.BlobStorageEntry = __decorate([
|
|
733
1048
|
entity.entity()
|
|
734
|
-
], exports.
|
|
1049
|
+
], exports.BlobStorageEntry);
|
|
735
1050
|
|
|
736
1051
|
/**
|
|
737
1052
|
* These are dummy entry points for the blob storage service.
|
|
@@ -753,13 +1068,14 @@ const restEntryPoints = [
|
|
|
753
1068
|
* Initialize the schema for the blob storage entities.
|
|
754
1069
|
*/
|
|
755
1070
|
function initSchema() {
|
|
756
|
-
entity.EntitySchemaFactory.register("
|
|
1071
|
+
entity.EntitySchemaFactory.register("BlobStorageEntry", () => entity.EntitySchemaHelper.getSchema(exports.BlobStorageEntry));
|
|
757
1072
|
}
|
|
758
1073
|
|
|
759
1074
|
exports.BlobStorageService = BlobStorageService;
|
|
760
1075
|
exports.blobStorageCreate = blobStorageCreate;
|
|
761
1076
|
exports.blobStorageGet = blobStorageGet;
|
|
762
1077
|
exports.blobStorageGetContent = blobStorageGetContent;
|
|
1078
|
+
exports.blobStorageList = blobStorageList;
|
|
763
1079
|
exports.blobStorageRemove = blobStorageRemove;
|
|
764
1080
|
exports.blobStorageUpdate = blobStorageUpdate;
|
|
765
1081
|
exports.generateRestRoutesBlobStorage = generateRestRoutesBlobStorage;
|