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