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