@twin.org/blob-storage-service 0.0.1-next.3 → 0.0.1-next.31
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 +565 -101
- package/dist/esm/index.mjs +567 -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 +49 -1
- package/docs/open-api/spec.json +2247 -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, 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';
|
|
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,116 @@ 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
|
+
SchemaOrgContexts.ContextRoot,
|
|
303
|
+
BlobStorageContexts.ContextRoot,
|
|
304
|
+
BlobStorageContexts.ContextRootCommon
|
|
305
|
+
],
|
|
306
|
+
type: SchemaOrgTypes.ItemList,
|
|
307
|
+
[SchemaOrgTypes.ItemListElement]: [
|
|
308
|
+
{
|
|
309
|
+
"@context": [
|
|
310
|
+
BlobStorageContexts.ContextRoot,
|
|
311
|
+
BlobStorageContexts.ContextRootCommon,
|
|
312
|
+
SchemaOrgContexts.ContextRoot
|
|
313
|
+
],
|
|
314
|
+
type: BlobStorageTypes.Entry,
|
|
315
|
+
id: "blob-memory:c57d94b088f4c6d2cb32ded014813d0c786aa00134c8ee22f84b1e2545602a70",
|
|
316
|
+
dateCreated: "2024-01-01T00:00:00Z",
|
|
317
|
+
encodingFormat: MimeTypes.Pdf,
|
|
318
|
+
blobSize: 42,
|
|
319
|
+
blobHash: "sha256:c57d94b088f4c6d2cb32ded014813d0c786aa00134c8ee22f84b1e2545602a70",
|
|
320
|
+
fileExtension: "pdf",
|
|
321
|
+
metadata: {
|
|
322
|
+
"@context": "https://schema.org",
|
|
323
|
+
"@type": "DigitalDocument",
|
|
324
|
+
name: "myfile.pdf"
|
|
325
|
+
},
|
|
326
|
+
blob: "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZw=="
|
|
327
|
+
}
|
|
328
|
+
]
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
]
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
type: "IBlobStorageListResponse",
|
|
336
|
+
mimeType: MimeTypes.JsonLd,
|
|
337
|
+
examples: [
|
|
338
|
+
{
|
|
339
|
+
id: `${camelTypeName}ListResponseJsonLdExample`,
|
|
340
|
+
response: {
|
|
341
|
+
body: {
|
|
342
|
+
"@context": [
|
|
343
|
+
SchemaOrgContexts.ContextRoot,
|
|
344
|
+
BlobStorageContexts.ContextRoot,
|
|
345
|
+
BlobStorageContexts.ContextRootCommon
|
|
346
|
+
],
|
|
347
|
+
type: SchemaOrgTypes.ItemList,
|
|
348
|
+
[SchemaOrgTypes.ItemListElement]: [
|
|
349
|
+
{
|
|
350
|
+
"@context": [
|
|
351
|
+
BlobStorageContexts.ContextRoot,
|
|
352
|
+
BlobStorageContexts.ContextRootCommon,
|
|
353
|
+
SchemaOrgContexts.ContextRoot
|
|
354
|
+
],
|
|
355
|
+
type: BlobStorageTypes.Entry,
|
|
356
|
+
id: "blob-memory:c57d94b088f4c6d2cb32ded014813d0c786aa00134c8ee22f84b1e2545602a70",
|
|
357
|
+
dateCreated: "2024-01-01T00:00:00Z",
|
|
358
|
+
encodingFormat: MimeTypes.Pdf,
|
|
359
|
+
blobSize: 42,
|
|
360
|
+
blobHash: "sha256:c57d94b088f4c6d2cb32ded014813d0c786aa00134c8ee22f84b1e2545602a70",
|
|
361
|
+
fileExtension: "pdf",
|
|
362
|
+
metadata: {
|
|
363
|
+
"@context": "https://schema.org",
|
|
364
|
+
"@type": "DigitalDocument",
|
|
365
|
+
name: "myfile.pdf"
|
|
366
|
+
},
|
|
367
|
+
blob: "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZw=="
|
|
368
|
+
}
|
|
369
|
+
]
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
]
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
type: "INotFoundResponse"
|
|
377
|
+
}
|
|
378
|
+
]
|
|
379
|
+
};
|
|
223
380
|
return [
|
|
224
381
|
blobStorageCreateRoute,
|
|
225
382
|
blobStorageGetRoute,
|
|
226
383
|
blobStorageGetContentRoute,
|
|
227
384
|
blobStorageUpdateRoute,
|
|
228
|
-
blobStorageRemoveRoute
|
|
385
|
+
blobStorageRemoveRoute,
|
|
386
|
+
blobStorageListRoute
|
|
229
387
|
];
|
|
230
388
|
}
|
|
231
389
|
/**
|
|
@@ -240,11 +398,11 @@ async function blobStorageCreate(httpRequestContext, componentName, request) {
|
|
|
240
398
|
Guards.object(ROUTES_SOURCE, "request.body", request.body);
|
|
241
399
|
Guards.stringBase64(ROUTES_SOURCE, "request.body.blob", request.body.blob);
|
|
242
400
|
const component = ComponentFactory.get(componentName);
|
|
243
|
-
const id = await component.create(request.body.blob, request.body.
|
|
401
|
+
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
402
|
return {
|
|
245
403
|
statusCode: HttpStatusCode.created,
|
|
246
404
|
headers: {
|
|
247
|
-
|
|
405
|
+
location: id
|
|
248
406
|
}
|
|
249
407
|
};
|
|
250
408
|
}
|
|
@@ -259,9 +417,13 @@ async function blobStorageGet(httpRequestContext, componentName, request) {
|
|
|
259
417
|
Guards.object(ROUTES_SOURCE, "request", request);
|
|
260
418
|
Guards.object(ROUTES_SOURCE, "request.pathParams", request.pathParams);
|
|
261
419
|
Guards.stringValue(ROUTES_SOURCE, "request.pathParams.id", request.pathParams.id);
|
|
420
|
+
const mimeType = request.headers?.[HeaderTypes.Accept] === MimeTypes.JsonLd ? "jsonld" : "json";
|
|
262
421
|
const component = ComponentFactory.get(componentName);
|
|
263
|
-
const result = await component.get(request.pathParams.id, request.query?.includeContent ?? false, httpRequestContext.nodeIdentity);
|
|
422
|
+
const result = await component.get(request.pathParams.id, request.query?.includeContent ?? false, httpRequestContext.userIdentity, httpRequestContext.nodeIdentity);
|
|
264
423
|
return {
|
|
424
|
+
headers: {
|
|
425
|
+
[HeaderTypes.ContentType]: mimeType === "json" ? MimeTypes.Json : MimeTypes.JsonLd
|
|
426
|
+
},
|
|
265
427
|
body: result
|
|
266
428
|
};
|
|
267
429
|
}
|
|
@@ -277,16 +439,16 @@ async function blobStorageGetContent(httpRequestContext, componentName, request)
|
|
|
277
439
|
Guards.object(ROUTES_SOURCE, "request.pathParams", request.pathParams);
|
|
278
440
|
Guards.stringValue(ROUTES_SOURCE, "request.pathParams.id", request.pathParams.id);
|
|
279
441
|
const component = ComponentFactory.get(componentName);
|
|
280
|
-
const result = await component.get(request.pathParams.id, true, httpRequestContext.nodeIdentity);
|
|
281
|
-
const
|
|
442
|
+
const result = await component.get(request.pathParams.id, true, httpRequestContext.userIdentity, httpRequestContext.nodeIdentity);
|
|
443
|
+
const encodingFormat = result?.encodingFormat ?? MimeTypes.OctetStream;
|
|
282
444
|
let filename = request.query?.filename;
|
|
283
445
|
if (!Is.stringValue(filename)) {
|
|
284
|
-
filename = `file.${result.
|
|
446
|
+
filename = `file.${result.fileExtension ?? MimeTypeHelper.defaultExtension(encodingFormat)}`;
|
|
285
447
|
}
|
|
286
448
|
return {
|
|
287
449
|
body: Is.stringBase64(result.blob) ? Converter.base64ToBytes(result.blob) : new Uint8Array(),
|
|
288
450
|
attachment: {
|
|
289
|
-
mimeType,
|
|
451
|
+
mimeType: encodingFormat,
|
|
290
452
|
filename,
|
|
291
453
|
inline: !(request.query?.download ?? false)
|
|
292
454
|
}
|
|
@@ -304,7 +466,7 @@ async function blobStorageUpdate(httpRequestContext, componentName, request) {
|
|
|
304
466
|
Guards.object(ROUTES_SOURCE, "request.pathParams", request.pathParams);
|
|
305
467
|
Guards.stringValue(ROUTES_SOURCE, "request.pathParams.id", request.pathParams.id);
|
|
306
468
|
const component = ComponentFactory.get(componentName);
|
|
307
|
-
await component.update(request.pathParams.id, request.body.
|
|
469
|
+
await component.update(request.pathParams.id, request.body.encodingFormat, request.body.fileExtension, request.body.metadata, httpRequestContext.userIdentity, httpRequestContext.nodeIdentity);
|
|
308
470
|
return {
|
|
309
471
|
statusCode: HttpStatusCode.noContent
|
|
310
472
|
};
|
|
@@ -321,11 +483,30 @@ async function blobStorageRemove(httpRequestContext, componentName, request) {
|
|
|
321
483
|
Guards.object(ROUTES_SOURCE, "request.pathParams", request.pathParams);
|
|
322
484
|
Guards.stringValue(ROUTES_SOURCE, "request.pathParams.id", request.pathParams.id);
|
|
323
485
|
const component = ComponentFactory.get(componentName);
|
|
324
|
-
await component.remove(request.pathParams.id);
|
|
486
|
+
await component.remove(request.pathParams.id, httpRequestContext.userIdentity, httpRequestContext.nodeIdentity);
|
|
325
487
|
return {
|
|
326
488
|
statusCode: HttpStatusCode.noContent
|
|
327
489
|
};
|
|
328
490
|
}
|
|
491
|
+
/**
|
|
492
|
+
* List the entries from blob storage.
|
|
493
|
+
* @param httpRequestContext The request context for the API.
|
|
494
|
+
* @param componentName The name of the component to use in the routes.
|
|
495
|
+
* @param request The request.
|
|
496
|
+
* @returns The response object with additional http response properties.
|
|
497
|
+
*/
|
|
498
|
+
async function blobStorageList(httpRequestContext, componentName, request) {
|
|
499
|
+
Guards.object(ROUTES_SOURCE, "request", request);
|
|
500
|
+
const mimeType = request.headers?.[HeaderTypes.Accept] === MimeTypes.JsonLd ? "jsonld" : "json";
|
|
501
|
+
const component = ComponentFactory.get(componentName);
|
|
502
|
+
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);
|
|
503
|
+
return {
|
|
504
|
+
headers: {
|
|
505
|
+
[HeaderTypes.ContentType]: mimeType === "json" ? MimeTypes.Json : MimeTypes.JsonLd
|
|
506
|
+
},
|
|
507
|
+
body: result
|
|
508
|
+
};
|
|
509
|
+
}
|
|
329
510
|
|
|
330
511
|
// Copyright 2024 IOTA Stiftung.
|
|
331
512
|
// SPDX-License-Identifier: Apache-2.0.
|
|
@@ -351,7 +532,7 @@ class BlobStorageService {
|
|
|
351
532
|
* The storage connector for the metadata.
|
|
352
533
|
* @internal
|
|
353
534
|
*/
|
|
354
|
-
|
|
535
|
+
_entryEntityStorage;
|
|
355
536
|
/**
|
|
356
537
|
* The vault connector for the encryption, can be undefined if no encryption required.
|
|
357
538
|
* @internal
|
|
@@ -362,38 +543,52 @@ class BlobStorageService {
|
|
|
362
543
|
* @internal
|
|
363
544
|
*/
|
|
364
545
|
_vaultKeyId;
|
|
546
|
+
/**
|
|
547
|
+
* Include the node identity when performing storage operations, defaults to true.
|
|
548
|
+
* @internal
|
|
549
|
+
*/
|
|
550
|
+
_includeNodeIdentity;
|
|
551
|
+
/**
|
|
552
|
+
* Include the user identity when performing storage operations, defaults to true.
|
|
553
|
+
* @internal
|
|
554
|
+
*/
|
|
555
|
+
_includeUserIdentity;
|
|
365
556
|
/**
|
|
366
557
|
* 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.
|
|
558
|
+
* @param options The options for the service.
|
|
371
559
|
*/
|
|
372
560
|
constructor(options) {
|
|
373
561
|
const names = BlobStorageConnectorFactory.names();
|
|
374
562
|
if (names.length === 0) {
|
|
375
563
|
throw new GeneralError(this.CLASS_NAME, "noConnectors");
|
|
376
564
|
}
|
|
377
|
-
this.
|
|
565
|
+
this._entryEntityStorage = EntityStorageConnectorFactory.get(options?.entryEntityStorageType ?? "blob-storage-entry");
|
|
378
566
|
if (Is.stringValue(options?.vaultConnectorType)) {
|
|
379
567
|
this._vaultConnector = VaultConnectorFactory.getIfExists(options.vaultConnectorType);
|
|
380
568
|
}
|
|
381
569
|
this._defaultNamespace = options?.config?.defaultNamespace ?? names[0];
|
|
382
570
|
this._vaultKeyId = options?.config?.vaultKeyId ?? "blob-storage";
|
|
571
|
+
this._includeNodeIdentity = options?.config?.includeNodeIdentity ?? true;
|
|
572
|
+
this._includeUserIdentity = options?.config?.includeUserIdentity ?? true;
|
|
573
|
+
SchemaOrgDataTypes.registerRedirects();
|
|
383
574
|
}
|
|
384
575
|
/**
|
|
385
576
|
* Create the blob with some metadata.
|
|
386
577
|
* @param blob The data for the blob in base64 format.
|
|
387
|
-
* @param
|
|
388
|
-
* @param
|
|
578
|
+
* @param encodingFormat Mime type for the blob, will be detected if left undefined.
|
|
579
|
+
* @param fileExtension Extension for the blob, will be detected if left undefined.
|
|
389
580
|
* @param metadata Data for the custom metadata as JSON-LD.
|
|
390
581
|
* @param namespace The namespace to use for storing, defaults to component configured namespace.
|
|
391
|
-
* @param
|
|
582
|
+
* @param userIdentity The user identity to use with storage operations.
|
|
583
|
+
* @param nodeIdentity The node identity to use with storage operations.
|
|
392
584
|
* @returns The id of the stored blob in urn format.
|
|
393
585
|
*/
|
|
394
|
-
async create(blob,
|
|
586
|
+
async create(blob, encodingFormat, fileExtension, metadata, namespace, userIdentity, nodeIdentity) {
|
|
395
587
|
Guards.stringBase64(this.CLASS_NAME, "blob", blob);
|
|
396
|
-
if (this.
|
|
588
|
+
if (this._includeUserIdentity) {
|
|
589
|
+
Guards.stringValue(this.CLASS_NAME, "userIdentity", userIdentity);
|
|
590
|
+
}
|
|
591
|
+
if (this._includeNodeIdentity || Is.notEmpty(this._vaultConnector)) {
|
|
397
592
|
Guards.stringValue(this.CLASS_NAME, "nodeIdentity", nodeIdentity);
|
|
398
593
|
}
|
|
399
594
|
try {
|
|
@@ -401,32 +596,48 @@ class BlobStorageService {
|
|
|
401
596
|
const blobStorageConnector = BlobStorageConnectorFactory.get(connectorNamespace);
|
|
402
597
|
// Convert the base64 data into bytes
|
|
403
598
|
let storeBlob = Converter.base64ToBytes(blob);
|
|
599
|
+
const blobSize = storeBlob.length;
|
|
404
600
|
// See if we can detect the mime type and default extension for the data.
|
|
405
601
|
// If not already supplied by the caller. We have to perform this operation
|
|
406
602
|
// on the unencrypted data.
|
|
407
|
-
if (!Is.stringValue(
|
|
408
|
-
|
|
603
|
+
if (!Is.stringValue(encodingFormat)) {
|
|
604
|
+
encodingFormat = await MimeTypeHelper.detect(storeBlob);
|
|
409
605
|
}
|
|
410
|
-
if (!Is.stringValue(
|
|
411
|
-
|
|
606
|
+
if (!Is.stringValue(fileExtension) && Is.stringValue(encodingFormat)) {
|
|
607
|
+
fileExtension = await MimeTypeHelper.defaultExtension(encodingFormat);
|
|
412
608
|
}
|
|
413
609
|
if (Is.object(metadata)) {
|
|
414
610
|
const validationFailures = [];
|
|
415
611
|
JsonLdHelper.validate(metadata, validationFailures);
|
|
416
612
|
Validation.asValidationError(this.CLASS_NAME, "metadata", validationFailures);
|
|
417
613
|
}
|
|
614
|
+
const blobHash = `sha256:${Converter.bytesToBase64(Sha256.sum256(storeBlob))}`;
|
|
418
615
|
// If we have a vault connector then encrypt the data.
|
|
419
616
|
if (this._vaultConnector) {
|
|
420
617
|
storeBlob = await this._vaultConnector.encrypt(`${nodeIdentity}/${this._vaultKeyId}`, VaultEncryptionType.ChaCha20Poly1305, storeBlob);
|
|
421
618
|
}
|
|
422
619
|
// Set the blob in the storage connector, which may now be encrypted
|
|
423
620
|
const blobId = await blobStorageConnector.set(storeBlob);
|
|
424
|
-
|
|
621
|
+
// Now store the entry in entity storage
|
|
622
|
+
const blobEntry = {
|
|
425
623
|
id: blobId,
|
|
426
|
-
|
|
427
|
-
|
|
624
|
+
dateCreated: new Date(Date.now()).toISOString(),
|
|
625
|
+
blobSize,
|
|
626
|
+
blobHash,
|
|
627
|
+
encodingFormat,
|
|
628
|
+
fileExtension,
|
|
428
629
|
metadata
|
|
429
|
-
}
|
|
630
|
+
};
|
|
631
|
+
const conditions = [];
|
|
632
|
+
if (this._includeUserIdentity) {
|
|
633
|
+
ObjectHelper.propertySet(blobEntry, "userIdentity", userIdentity);
|
|
634
|
+
conditions.push({ property: "userIdentity", value: userIdentity });
|
|
635
|
+
}
|
|
636
|
+
if (this._includeNodeIdentity) {
|
|
637
|
+
ObjectHelper.propertySet(blobEntry, "nodeIdentity", nodeIdentity);
|
|
638
|
+
conditions.push({ property: "nodeIdentity", value: nodeIdentity });
|
|
639
|
+
}
|
|
640
|
+
await this._entryEntityStorage.set(blobEntry, conditions);
|
|
430
641
|
return blobId;
|
|
431
642
|
}
|
|
432
643
|
catch (error) {
|
|
@@ -434,21 +645,35 @@ class BlobStorageService {
|
|
|
434
645
|
}
|
|
435
646
|
}
|
|
436
647
|
/**
|
|
437
|
-
* Get the blob
|
|
648
|
+
* Get the blob entry.
|
|
438
649
|
* @param id The id of the blob to get in urn format.
|
|
439
650
|
* @param includeContent Include the content, or just get the metadata.
|
|
440
|
-
* @param
|
|
441
|
-
* @
|
|
651
|
+
* @param userIdentity The user identity to use with storage operations.
|
|
652
|
+
* @param nodeIdentity The node identity to use with storage operations.
|
|
653
|
+
* @returns The entry and data for the blob if it can be found.
|
|
442
654
|
* @throws Not found error if the blob cannot be found.
|
|
443
655
|
*/
|
|
444
|
-
async get(id, includeContent, nodeIdentity) {
|
|
656
|
+
async get(id, includeContent, userIdentity, nodeIdentity) {
|
|
445
657
|
Urn.guard(this.CLASS_NAME, "id", id);
|
|
446
|
-
|
|
658
|
+
const conditions = [];
|
|
659
|
+
if (this._includeUserIdentity) {
|
|
660
|
+
Guards.stringValue(this.CLASS_NAME, "userIdentity", userIdentity);
|
|
661
|
+
conditions.push({
|
|
662
|
+
property: "userIdentity",
|
|
663
|
+
comparison: ComparisonOperator.Equals,
|
|
664
|
+
value: userIdentity
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
if (this._includeNodeIdentity || (Is.notEmpty(this._vaultConnector) && includeContent)) {
|
|
447
668
|
Guards.stringValue(this.CLASS_NAME, "nodeIdentity", nodeIdentity);
|
|
669
|
+
conditions.push({
|
|
670
|
+
property: "nodeIdentity",
|
|
671
|
+
comparison: ComparisonOperator.Equals,
|
|
672
|
+
value: nodeIdentity
|
|
673
|
+
});
|
|
448
674
|
}
|
|
449
675
|
try {
|
|
450
|
-
|
|
451
|
-
const blobMetadata = await this._metadataEntityStorage.get(id);
|
|
676
|
+
const blobEntry = await this.internalGet(id, userIdentity, nodeIdentity);
|
|
452
677
|
let returnBlob;
|
|
453
678
|
if (includeContent) {
|
|
454
679
|
const blobStorageConnector = this.getConnector(id);
|
|
@@ -461,12 +686,8 @@ class BlobStorageService {
|
|
|
461
686
|
returnBlob = await this._vaultConnector.decrypt(`${nodeIdentity}/${this._vaultKeyId}`, VaultEncryptionType.ChaCha20Poly1305, returnBlob);
|
|
462
687
|
}
|
|
463
688
|
}
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
mimeType: blobMetadata?.mimeType,
|
|
467
|
-
extension: blobMetadata?.extension,
|
|
468
|
-
metadata: blobMetadata?.metadata
|
|
469
|
-
};
|
|
689
|
+
const jsonLd = this.entryToJsonLd(blobEntry, returnBlob);
|
|
690
|
+
return JsonLdProcessor.compact(jsonLd, jsonLd["@context"]);
|
|
470
691
|
}
|
|
471
692
|
catch (error) {
|
|
472
693
|
throw new GeneralError(this.CLASS_NAME, "getFailed", undefined, error);
|
|
@@ -474,18 +695,26 @@ class BlobStorageService {
|
|
|
474
695
|
}
|
|
475
696
|
/**
|
|
476
697
|
* Update the blob with metadata.
|
|
477
|
-
* @param id The id of the blob
|
|
478
|
-
* @param
|
|
479
|
-
* @param
|
|
698
|
+
* @param id The id of the blob entry to update.
|
|
699
|
+
* @param encodingFormat Mime type for the blob, will be detected if left undefined.
|
|
700
|
+
* @param fileExtension Extension for the blob, will be detected if left undefined.
|
|
480
701
|
* @param metadata Data for the custom metadata as JSON-LD.
|
|
702
|
+
* @param userIdentity The user identity to use with storage operations.
|
|
703
|
+
* @param nodeIdentity The node identity to use with storage operations.
|
|
481
704
|
* @returns Nothing.
|
|
482
705
|
* @throws Not found error if the blob cannot be found.
|
|
483
706
|
*/
|
|
484
|
-
async update(id,
|
|
707
|
+
async update(id, encodingFormat, fileExtension, metadata, userIdentity, nodeIdentity) {
|
|
485
708
|
Urn.guard(this.CLASS_NAME, "id", id);
|
|
709
|
+
if (this._includeUserIdentity) {
|
|
710
|
+
Guards.stringValue(this.CLASS_NAME, "userIdentity", userIdentity);
|
|
711
|
+
}
|
|
712
|
+
if (this._includeNodeIdentity || Is.notEmpty(this._vaultConnector)) {
|
|
713
|
+
Guards.stringValue(this.CLASS_NAME, "nodeIdentity", nodeIdentity);
|
|
714
|
+
}
|
|
486
715
|
try {
|
|
487
|
-
const
|
|
488
|
-
if (Is.undefined(
|
|
716
|
+
const blobEntry = await this._entryEntityStorage.get(id);
|
|
717
|
+
if (Is.undefined(blobEntry)) {
|
|
489
718
|
throw new NotFoundError(this.CLASS_NAME, "blobNotFound", id);
|
|
490
719
|
}
|
|
491
720
|
if (Is.object(metadata)) {
|
|
@@ -493,12 +722,27 @@ class BlobStorageService {
|
|
|
493
722
|
await JsonLdHelper.validate(metadata, validationFailures);
|
|
494
723
|
Validation.asValidationError(this.CLASS_NAME, "metadata", validationFailures);
|
|
495
724
|
}
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
725
|
+
// Now store the entry in entity storage
|
|
726
|
+
const updatedBlobEntry = {
|
|
727
|
+
id: blobEntry.id,
|
|
728
|
+
dateCreated: blobEntry.dateCreated,
|
|
729
|
+
dateModified: new Date(Date.now()).toISOString(),
|
|
730
|
+
blobSize: blobEntry.blobSize,
|
|
731
|
+
blobHash: blobEntry.blobHash,
|
|
732
|
+
encodingFormat: encodingFormat ?? blobEntry.encodingFormat,
|
|
733
|
+
fileExtension: fileExtension ?? blobEntry.fileExtension,
|
|
734
|
+
metadata: metadata ?? blobEntry.metadata
|
|
735
|
+
};
|
|
736
|
+
const conditions = [];
|
|
737
|
+
if (this._includeUserIdentity) {
|
|
738
|
+
ObjectHelper.propertySet(updatedBlobEntry, "userIdentity", userIdentity);
|
|
739
|
+
conditions.push({ property: "userIdentity", value: userIdentity });
|
|
740
|
+
}
|
|
741
|
+
if (this._includeNodeIdentity) {
|
|
742
|
+
ObjectHelper.propertySet(updatedBlobEntry, "nodeIdentity", nodeIdentity);
|
|
743
|
+
conditions.push({ property: "nodeIdentity", value: nodeIdentity });
|
|
744
|
+
}
|
|
745
|
+
await this._entryEntityStorage.set(updatedBlobEntry, conditions);
|
|
502
746
|
}
|
|
503
747
|
catch (error) {
|
|
504
748
|
throw new GeneralError(this.CLASS_NAME, "updateFailed", undefined, error);
|
|
@@ -507,22 +751,104 @@ class BlobStorageService {
|
|
|
507
751
|
/**
|
|
508
752
|
* Remove the blob.
|
|
509
753
|
* @param id The id of the blob to remove in urn format.
|
|
754
|
+
* @param userIdentity The user identity to use with storage operations.
|
|
755
|
+
* @param nodeIdentity The node identity to use with storage operations.
|
|
510
756
|
* @returns Nothing.
|
|
511
757
|
*/
|
|
512
|
-
async remove(id) {
|
|
758
|
+
async remove(id, userIdentity, nodeIdentity) {
|
|
513
759
|
Urn.guard(this.CLASS_NAME, "id", id);
|
|
760
|
+
if (this._includeUserIdentity) {
|
|
761
|
+
Guards.stringValue(this.CLASS_NAME, "userIdentity", userIdentity);
|
|
762
|
+
}
|
|
763
|
+
if (this._includeNodeIdentity || Is.notEmpty(this._vaultConnector)) {
|
|
764
|
+
Guards.stringValue(this.CLASS_NAME, "nodeIdentity", nodeIdentity);
|
|
765
|
+
}
|
|
514
766
|
try {
|
|
515
767
|
const blobStorageConnector = this.getConnector(id);
|
|
768
|
+
const conditions = [];
|
|
769
|
+
if (this._includeUserIdentity) {
|
|
770
|
+
conditions.push({ property: "userIdentity", value: userIdentity });
|
|
771
|
+
}
|
|
772
|
+
if (this._includeNodeIdentity) {
|
|
773
|
+
conditions.push({ property: "nodeIdentity", value: nodeIdentity });
|
|
774
|
+
}
|
|
775
|
+
await this._entryEntityStorage.remove(id, conditions);
|
|
516
776
|
const removed = await blobStorageConnector.remove(id);
|
|
517
777
|
if (!removed) {
|
|
518
778
|
throw new NotFoundError(this.CLASS_NAME, "blobNotFound", id);
|
|
519
779
|
}
|
|
520
|
-
await this._metadataEntityStorage.remove(id);
|
|
521
780
|
}
|
|
522
781
|
catch (error) {
|
|
523
782
|
throw new GeneralError(this.CLASS_NAME, "removeFailed", undefined, error);
|
|
524
783
|
}
|
|
525
784
|
}
|
|
785
|
+
/**
|
|
786
|
+
* Query all the blob storage entries which match the conditions.
|
|
787
|
+
* @param conditions The conditions to match for the entries.
|
|
788
|
+
* @param orderBy The order for the results, defaults to created.
|
|
789
|
+
* @param orderByDirection The direction for the order, defaults to descending.
|
|
790
|
+
* @param cursor The cursor to request the next page of entries.
|
|
791
|
+
* @param pageSize The suggested number of entries to return in each chunk, in some scenarios can return a different amount.
|
|
792
|
+
* @param userIdentity The user identity to use with storage operations.
|
|
793
|
+
* @param nodeIdentity The node identity to use with storage operations.
|
|
794
|
+
* @returns All the entries for the storage matching the conditions,
|
|
795
|
+
* and a cursor which can be used to request more entities.
|
|
796
|
+
*/
|
|
797
|
+
async query(conditions, orderBy, orderByDirection, cursor, pageSize, userIdentity, nodeIdentity) {
|
|
798
|
+
const finalConditions = {
|
|
799
|
+
conditions: [],
|
|
800
|
+
logicalOperator: LogicalOperator.And
|
|
801
|
+
};
|
|
802
|
+
if (this._includeNodeIdentity) {
|
|
803
|
+
Guards.stringValue(this.CLASS_NAME, "nodeIdentity", nodeIdentity);
|
|
804
|
+
finalConditions.conditions.push({
|
|
805
|
+
property: "nodeIdentity",
|
|
806
|
+
comparison: ComparisonOperator.Equals,
|
|
807
|
+
value: nodeIdentity
|
|
808
|
+
});
|
|
809
|
+
}
|
|
810
|
+
if (this._includeUserIdentity) {
|
|
811
|
+
Guards.stringValue(this.CLASS_NAME, "userIdentity", userIdentity);
|
|
812
|
+
finalConditions.conditions.push({
|
|
813
|
+
property: "userIdentity",
|
|
814
|
+
comparison: ComparisonOperator.Equals,
|
|
815
|
+
value: userIdentity
|
|
816
|
+
});
|
|
817
|
+
}
|
|
818
|
+
if (!Is.empty(conditions)) {
|
|
819
|
+
finalConditions.conditions.push(conditions);
|
|
820
|
+
}
|
|
821
|
+
const orderProperty = orderBy ?? "dateCreated";
|
|
822
|
+
const orderDirection = orderByDirection ?? SortDirection.Descending;
|
|
823
|
+
const result = await this._entryEntityStorage.query(finalConditions.conditions.length > 0 ? finalConditions : undefined, [
|
|
824
|
+
{
|
|
825
|
+
property: orderProperty,
|
|
826
|
+
sortDirection: orderDirection
|
|
827
|
+
}
|
|
828
|
+
], undefined, cursor, pageSize);
|
|
829
|
+
for (const entity of result.entities) {
|
|
830
|
+
ObjectHelper.propertyDelete(entity, "nodeIdentity");
|
|
831
|
+
ObjectHelper.propertyDelete(entity, "userIdentity");
|
|
832
|
+
}
|
|
833
|
+
let context = [
|
|
834
|
+
SchemaOrgContexts.ContextRoot,
|
|
835
|
+
BlobStorageContexts.ContextRoot,
|
|
836
|
+
BlobStorageContexts.ContextRootCommon
|
|
837
|
+
];
|
|
838
|
+
const entriesJsonLd = [];
|
|
839
|
+
for (const entry of result.entities) {
|
|
840
|
+
// The entries are never Partial as we don't allow custom property requests.
|
|
841
|
+
entriesJsonLd.push(this.entryToJsonLd(entry));
|
|
842
|
+
context = JsonLdProcessor.combineContexts(context, entry.metadata?.["@context"]);
|
|
843
|
+
}
|
|
844
|
+
const jsonLd = {
|
|
845
|
+
"@context": context,
|
|
846
|
+
type: SchemaOrgTypes.ItemList,
|
|
847
|
+
[SchemaOrgTypes.ItemListElement]: entriesJsonLd,
|
|
848
|
+
[SchemaOrgTypes.NextItem]: result.cursor
|
|
849
|
+
};
|
|
850
|
+
return JsonLdProcessor.compact(jsonLd, jsonLd["@context"]);
|
|
851
|
+
}
|
|
526
852
|
/**
|
|
527
853
|
* Get the connector from the uri.
|
|
528
854
|
* @param id The id of the blob storage item in urn format.
|
|
@@ -539,53 +865,190 @@ class BlobStorageService {
|
|
|
539
865
|
}
|
|
540
866
|
return BlobStorageConnectorFactory.get(idUri.namespaceMethod());
|
|
541
867
|
}
|
|
868
|
+
/**
|
|
869
|
+
* Get an entity.
|
|
870
|
+
* @param id The id of the entity to get, or the index value if secondaryIndex is set.
|
|
871
|
+
* @param secondaryIndex Get the item using a secondary index.
|
|
872
|
+
* @param userIdentity The user identity to use with storage operations.
|
|
873
|
+
* @param nodeIdentity The node identity to use with storage operations.
|
|
874
|
+
* @returns The object if it can be found or throws.
|
|
875
|
+
* @internal
|
|
876
|
+
*/
|
|
877
|
+
async internalGet(id, userIdentity, nodeIdentity) {
|
|
878
|
+
const conditions = [];
|
|
879
|
+
if (this._includeUserIdentity) {
|
|
880
|
+
Guards.stringValue(this.CLASS_NAME, "userIdentity", userIdentity);
|
|
881
|
+
conditions.push({
|
|
882
|
+
property: "userIdentity",
|
|
883
|
+
comparison: ComparisonOperator.Equals,
|
|
884
|
+
value: userIdentity
|
|
885
|
+
});
|
|
886
|
+
}
|
|
887
|
+
if (this._includeNodeIdentity) {
|
|
888
|
+
Guards.stringValue(this.CLASS_NAME, "nodeIdentity", nodeIdentity);
|
|
889
|
+
conditions.push({
|
|
890
|
+
property: "nodeIdentity",
|
|
891
|
+
comparison: ComparisonOperator.Equals,
|
|
892
|
+
value: nodeIdentity
|
|
893
|
+
});
|
|
894
|
+
}
|
|
895
|
+
let entity;
|
|
896
|
+
if (conditions.length === 0) {
|
|
897
|
+
entity = await this._entryEntityStorage.get(id);
|
|
898
|
+
}
|
|
899
|
+
else {
|
|
900
|
+
const schema = this._entryEntityStorage.getSchema();
|
|
901
|
+
const primaryKey = EntitySchemaHelper.getPrimaryKey(schema);
|
|
902
|
+
conditions.unshift({
|
|
903
|
+
property: primaryKey.property,
|
|
904
|
+
comparison: ComparisonOperator.Equals,
|
|
905
|
+
value: id
|
|
906
|
+
});
|
|
907
|
+
const results = await this._entryEntityStorage.query({
|
|
908
|
+
conditions,
|
|
909
|
+
logicalOperator: LogicalOperator.And
|
|
910
|
+
}, undefined, undefined, undefined, 1);
|
|
911
|
+
entity = results.entities[0];
|
|
912
|
+
}
|
|
913
|
+
if (Is.empty(entity)) {
|
|
914
|
+
throw new NotFoundError(this.CLASS_NAME, "entityNotFound", id);
|
|
915
|
+
}
|
|
916
|
+
ObjectHelper.propertyDelete(entity, "nodeIdentity");
|
|
917
|
+
ObjectHelper.propertyDelete(entity, "userIdentity");
|
|
918
|
+
return entity;
|
|
919
|
+
}
|
|
920
|
+
/**
|
|
921
|
+
* Convert the entry to JSON-LD.
|
|
922
|
+
* @param entry The entry to convert.
|
|
923
|
+
* @param blob The optional blob to return.
|
|
924
|
+
* @returns The JSON-LD representation of the entry.
|
|
925
|
+
* @internal
|
|
926
|
+
*/
|
|
927
|
+
entryToJsonLd(entry, blob) {
|
|
928
|
+
const jsonLd = {
|
|
929
|
+
"@context": JsonLdProcessor.combineContexts([
|
|
930
|
+
BlobStorageContexts.ContextRoot,
|
|
931
|
+
BlobStorageContexts.ContextRootCommon,
|
|
932
|
+
SchemaOrgContexts.ContextRoot
|
|
933
|
+
], entry?.metadata?.["@context"]),
|
|
934
|
+
id: entry.id,
|
|
935
|
+
type: BlobStorageTypes.Entry,
|
|
936
|
+
dateCreated: entry.dateCreated,
|
|
937
|
+
dateModified: entry.dateModified,
|
|
938
|
+
blobSize: entry.blobSize,
|
|
939
|
+
blobHash: entry.blobHash,
|
|
940
|
+
encodingFormat: entry?.encodingFormat,
|
|
941
|
+
fileExtension: entry?.fileExtension,
|
|
942
|
+
metadata: entry?.metadata,
|
|
943
|
+
blob: Is.uint8Array(blob) ? Converter.bytesToBase64(blob) : undefined
|
|
944
|
+
};
|
|
945
|
+
return jsonLd;
|
|
946
|
+
}
|
|
542
947
|
}
|
|
543
948
|
|
|
544
949
|
/**
|
|
545
|
-
* Class representing
|
|
950
|
+
* Class representing entry for the blob storage.
|
|
546
951
|
*/
|
|
547
|
-
let
|
|
952
|
+
let BlobStorageEntry = class BlobStorageEntry {
|
|
548
953
|
/**
|
|
549
954
|
* The id for the blob.
|
|
550
955
|
*/
|
|
551
956
|
id;
|
|
957
|
+
/**
|
|
958
|
+
* The date/time when the entry was created.
|
|
959
|
+
*/
|
|
960
|
+
dateCreated;
|
|
961
|
+
/**
|
|
962
|
+
* The date/time when the entry was modified.
|
|
963
|
+
*/
|
|
964
|
+
dateModified;
|
|
965
|
+
/**
|
|
966
|
+
* The length of the data in the blob.
|
|
967
|
+
*/
|
|
968
|
+
blobSize;
|
|
969
|
+
/**
|
|
970
|
+
* The hash of the data in the blob.
|
|
971
|
+
*/
|
|
972
|
+
blobHash;
|
|
552
973
|
/**
|
|
553
974
|
* The mime type for the blob.
|
|
554
975
|
*/
|
|
555
|
-
|
|
976
|
+
encodingFormat;
|
|
556
977
|
/**
|
|
557
978
|
* The extension.
|
|
558
979
|
*/
|
|
559
|
-
|
|
980
|
+
fileExtension;
|
|
560
981
|
/**
|
|
561
982
|
* The metadata for the blob as JSON-LD.
|
|
562
983
|
*/
|
|
563
984
|
metadata;
|
|
985
|
+
/**
|
|
986
|
+
* The user identity that created the blob.
|
|
987
|
+
*/
|
|
988
|
+
userIdentity;
|
|
989
|
+
/**
|
|
990
|
+
* The node identity that created the blob.
|
|
991
|
+
*/
|
|
992
|
+
nodeIdentity;
|
|
564
993
|
};
|
|
565
994
|
__decorate([
|
|
566
995
|
property({ type: "string", isPrimary: true }),
|
|
567
996
|
__metadata("design:type", String)
|
|
568
|
-
],
|
|
997
|
+
], BlobStorageEntry.prototype, "id", void 0);
|
|
569
998
|
__decorate([
|
|
570
|
-
property({ type: "string" }),
|
|
999
|
+
property({ type: "string", format: "date-time", sortDirection: SortDirection.Descending }),
|
|
571
1000
|
__metadata("design:type", String)
|
|
572
|
-
],
|
|
1001
|
+
], BlobStorageEntry.prototype, "dateCreated", void 0);
|
|
1002
|
+
__decorate([
|
|
1003
|
+
property({
|
|
1004
|
+
type: "string",
|
|
1005
|
+
format: "date-time",
|
|
1006
|
+
sortDirection: SortDirection.Descending,
|
|
1007
|
+
optional: true
|
|
1008
|
+
}),
|
|
1009
|
+
__metadata("design:type", String)
|
|
1010
|
+
], BlobStorageEntry.prototype, "dateModified", void 0);
|
|
1011
|
+
__decorate([
|
|
1012
|
+
property({ type: "number" }),
|
|
1013
|
+
__metadata("design:type", Number)
|
|
1014
|
+
], BlobStorageEntry.prototype, "blobSize", void 0);
|
|
573
1015
|
__decorate([
|
|
574
1016
|
property({ type: "string" }),
|
|
575
1017
|
__metadata("design:type", String)
|
|
576
|
-
],
|
|
1018
|
+
], BlobStorageEntry.prototype, "blobHash", void 0);
|
|
1019
|
+
__decorate([
|
|
1020
|
+
property({ type: "string", optional: true }),
|
|
1021
|
+
__metadata("design:type", String)
|
|
1022
|
+
], BlobStorageEntry.prototype, "encodingFormat", void 0);
|
|
1023
|
+
__decorate([
|
|
1024
|
+
property({ type: "string", optional: true }),
|
|
1025
|
+
__metadata("design:type", String)
|
|
1026
|
+
], BlobStorageEntry.prototype, "fileExtension", void 0);
|
|
577
1027
|
__decorate([
|
|
578
|
-
property({ type: "object", itemTypeRef: "IJsonLdNodeObject" }),
|
|
1028
|
+
property({ type: "object", itemTypeRef: "IJsonLdNodeObject", optional: true }),
|
|
579
1029
|
__metadata("design:type", Object)
|
|
580
|
-
],
|
|
581
|
-
|
|
1030
|
+
], BlobStorageEntry.prototype, "metadata", void 0);
|
|
1031
|
+
__decorate([
|
|
1032
|
+
property({ type: "string", optional: true }),
|
|
1033
|
+
__metadata("design:type", String)
|
|
1034
|
+
], BlobStorageEntry.prototype, "userIdentity", void 0);
|
|
1035
|
+
__decorate([
|
|
1036
|
+
property({ type: "string", optional: true }),
|
|
1037
|
+
__metadata("design:type", String)
|
|
1038
|
+
], BlobStorageEntry.prototype, "nodeIdentity", void 0);
|
|
1039
|
+
BlobStorageEntry = __decorate([
|
|
582
1040
|
entity()
|
|
583
|
-
],
|
|
1041
|
+
], BlobStorageEntry);
|
|
584
1042
|
|
|
1043
|
+
/**
|
|
1044
|
+
* These are dummy entry points for the blob storage service.
|
|
1045
|
+
* In reality your application would create its own entry points based on the
|
|
1046
|
+
* blob types it wants to store, using a custom defaultBaseRoute.
|
|
1047
|
+
*/
|
|
585
1048
|
const restEntryPoints = [
|
|
586
1049
|
{
|
|
587
|
-
name: "
|
|
588
|
-
defaultBaseRoute: "blob",
|
|
1050
|
+
name: "blob-storage",
|
|
1051
|
+
defaultBaseRoute: "blob-storage",
|
|
589
1052
|
tags: tagsBlobStorage,
|
|
590
1053
|
generateRoutes: generateRestRoutesBlobStorage
|
|
591
1054
|
}
|
|
@@ -597,7 +1060,7 @@ const restEntryPoints = [
|
|
|
597
1060
|
* Initialize the schema for the blob storage entities.
|
|
598
1061
|
*/
|
|
599
1062
|
function initSchema() {
|
|
600
|
-
EntitySchemaFactory.register("
|
|
1063
|
+
EntitySchemaFactory.register("BlobStorageEntry", () => EntitySchemaHelper.getSchema(BlobStorageEntry));
|
|
601
1064
|
}
|
|
602
1065
|
|
|
603
|
-
export {
|
|
1066
|
+
export { BlobStorageEntry, BlobStorageService, blobStorageCreate, blobStorageGet, blobStorageGetContent, blobStorageList, blobStorageRemove, blobStorageUpdate, generateRestRoutesBlobStorage, initSchema, restEntryPoints, tagsBlobStorage };
|