@twin.org/document-management-service 0.0.1-next.2 → 0.0.1-next.20
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 +779 -408
- package/dist/esm/index.mjs +782 -413
- package/dist/types/documentManagementRoutes.d.ts +20 -4
- package/dist/types/documentManagementService.d.ts +84 -34
- package/dist/types/models/IDocumentManagementStorageServiceConstructorOptions.d.ts +5 -0
- package/docs/changelog.md +196 -1
- package/docs/open-api/spec.json +623 -1459
- package/docs/reference/classes/DocumentManagementService.md +193 -61
- package/docs/reference/functions/{documentManagementSet.md → documentManagementCreate.md} +4 -4
- package/docs/reference/functions/documentManagementGetRevision.md +31 -0
- package/docs/reference/functions/documentManagementUpdate.md +31 -0
- package/docs/reference/index.md +3 -1
- package/docs/reference/interfaces/IDocumentManagementServiceConstructorOptions.md +14 -0
- package/locales/en.json +6 -5
- package/package.json +3 -2
package/dist/esm/index.mjs
CHANGED
|
@@ -1,17 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Guards, ComponentFactory, Converter, Coerce, Is,
|
|
3
|
-
import { DocumentTypes } from '@twin.org/document-management-models';
|
|
4
|
-
import { SchemaOrgTypes, SchemaOrgDataTypes } from '@twin.org/standards-schema-org';
|
|
1
|
+
import { AuditableItemGraphTypes, AuditableItemGraphContexts } from '@twin.org/auditable-item-graph-models';
|
|
2
|
+
import { Guards, ComponentFactory, Converter, Coerce, Is, BaseError, GeneralError, Urn, NotFoundError, ObjectHelper } from '@twin.org/core';
|
|
3
|
+
import { DocumentTypes, DocumentContexts } from '@twin.org/document-management-models';
|
|
4
|
+
import { SchemaOrgContexts, SchemaOrgTypes, SchemaOrgDataTypes } from '@twin.org/standards-schema-org';
|
|
5
5
|
import { UneceDocumentCodes } from '@twin.org/standards-unece';
|
|
6
6
|
import { HttpStatusCode, HeaderTypes, MimeTypes } from '@twin.org/web';
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { BlobStorageTypes } from '@twin.org/blob-storage-models';
|
|
7
|
+
import { AttestationContexts } from '@twin.org/attestation-models';
|
|
8
|
+
import { BlobStorageContexts } from '@twin.org/blob-storage-models';
|
|
10
9
|
import { Sha256 } from '@twin.org/crypto';
|
|
11
10
|
import { JsonLdProcessor } from '@twin.org/data-json-ld';
|
|
12
11
|
|
|
13
|
-
// Copyright 2024 IOTA Stiftung.
|
|
14
|
-
// SPDX-License-Identifier: Apache-2.0.
|
|
15
12
|
/**
|
|
16
13
|
* The source used when communicating about these routes.
|
|
17
14
|
*/
|
|
@@ -32,22 +29,19 @@ const tagsDocumentManagement = [
|
|
|
32
29
|
* @returns The generated routes.
|
|
33
30
|
*/
|
|
34
31
|
function generateRestRoutesDocumentManagement(baseRouteName, componentName) {
|
|
35
|
-
const
|
|
32
|
+
const documentManagementCreateRoute = {
|
|
36
33
|
operationId: "DocumentManagementSet",
|
|
37
34
|
summary: "Store a document in an auditable item graph vertex and add its content to blob storage.",
|
|
38
35
|
tag: tagsDocumentManagement[0].name,
|
|
39
36
|
method: "POST",
|
|
40
|
-
path: `${baseRouteName}
|
|
41
|
-
handler: async (httpRequestContext, request) =>
|
|
37
|
+
path: `${baseRouteName}/`,
|
|
38
|
+
handler: async (httpRequestContext, request) => documentManagementCreate(httpRequestContext, componentName, request),
|
|
42
39
|
requestType: {
|
|
43
|
-
type: "
|
|
40
|
+
type: "IDocumentManagementCreateRequest",
|
|
44
41
|
examples: [
|
|
45
42
|
{
|
|
46
|
-
id: "
|
|
43
|
+
id: "DocumentManagementCreateRequestExample",
|
|
47
44
|
request: {
|
|
48
|
-
pathParams: {
|
|
49
|
-
auditableItemGraphId: "aig:123456"
|
|
50
|
-
},
|
|
51
45
|
body: {
|
|
52
46
|
documentId: "2721000",
|
|
53
47
|
documentIdFormat: "bol",
|
|
@@ -69,11 +63,11 @@ function generateRestRoutesDocumentManagement(baseRouteName, componentName) {
|
|
|
69
63
|
type: "ICreatedResponse",
|
|
70
64
|
examples: [
|
|
71
65
|
{
|
|
72
|
-
id: "
|
|
66
|
+
id: "DocumentManagementCreateResponseExample",
|
|
73
67
|
response: {
|
|
74
68
|
statusCode: HttpStatusCode.created,
|
|
75
69
|
headers: {
|
|
76
|
-
[HeaderTypes.Location]: "
|
|
70
|
+
[HeaderTypes.Location]: "aig:123456"
|
|
77
71
|
}
|
|
78
72
|
}
|
|
79
73
|
}
|
|
@@ -81,12 +75,54 @@ function generateRestRoutesDocumentManagement(baseRouteName, componentName) {
|
|
|
81
75
|
}
|
|
82
76
|
]
|
|
83
77
|
};
|
|
78
|
+
const documentManagementUpdateRoute = {
|
|
79
|
+
operationId: "DocumentManagementUpdate",
|
|
80
|
+
summary: "Update a document in an auditable item graph vertex and add its content to blob storage.",
|
|
81
|
+
tag: tagsDocumentManagement[0].name,
|
|
82
|
+
method: "PUT",
|
|
83
|
+
path: `${baseRouteName}/:auditableItemGraphDocumentId`,
|
|
84
|
+
handler: async (httpRequestContext, request) => documentManagementUpdate(httpRequestContext, componentName, request),
|
|
85
|
+
requestType: {
|
|
86
|
+
type: "IDocumentManagementUpdateRequest",
|
|
87
|
+
examples: [
|
|
88
|
+
{
|
|
89
|
+
id: "DocumentManagementUpdateRequestExample",
|
|
90
|
+
request: {
|
|
91
|
+
pathParams: {
|
|
92
|
+
auditableItemGraphDocumentId: "aig:123456"
|
|
93
|
+
},
|
|
94
|
+
body: {
|
|
95
|
+
blob: "SGVsbG8gV29ybGQ=",
|
|
96
|
+
annotationObject: {
|
|
97
|
+
"@context": "https://schema.org",
|
|
98
|
+
"@type": "DigitalDocument",
|
|
99
|
+
name: "myfile.pdf"
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
]
|
|
105
|
+
},
|
|
106
|
+
responseType: [
|
|
107
|
+
{
|
|
108
|
+
type: "INoContentResponse",
|
|
109
|
+
examples: [
|
|
110
|
+
{
|
|
111
|
+
id: "DocumentManagementCreateResponseExample",
|
|
112
|
+
response: {
|
|
113
|
+
statusCode: HttpStatusCode.noContent
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
]
|
|
117
|
+
}
|
|
118
|
+
]
|
|
119
|
+
};
|
|
84
120
|
const documentManagementGetRoute = {
|
|
85
121
|
operationId: "DocumentManagementGet",
|
|
86
122
|
summary: "Get the data for a document from document management",
|
|
87
123
|
tag: tagsDocumentManagement[0].name,
|
|
88
124
|
method: "GET",
|
|
89
|
-
path: `${baseRouteName}/:
|
|
125
|
+
path: `${baseRouteName}/:auditableItemGraphDocumentId`,
|
|
90
126
|
handler: async (httpRequestContext, request) => documentManagementGet(httpRequestContext, componentName, request),
|
|
91
127
|
requestType: {
|
|
92
128
|
type: "IDocumentManagementGetRequest",
|
|
@@ -95,8 +131,7 @@ function generateRestRoutesDocumentManagement(baseRouteName, componentName) {
|
|
|
95
131
|
id: "DocumentManagementGetRequestExample",
|
|
96
132
|
request: {
|
|
97
133
|
pathParams: {
|
|
98
|
-
|
|
99
|
-
documentId: "documents:123456:705:2721000"
|
|
134
|
+
auditableItemGraphDocumentId: "aig:123456"
|
|
100
135
|
}
|
|
101
136
|
}
|
|
102
137
|
}
|
|
@@ -111,16 +146,130 @@ function generateRestRoutesDocumentManagement(baseRouteName, componentName) {
|
|
|
111
146
|
response: {
|
|
112
147
|
body: {
|
|
113
148
|
"@context": [
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
149
|
+
SchemaOrgContexts.ContextRoot,
|
|
150
|
+
DocumentContexts.ContextRoot,
|
|
151
|
+
DocumentContexts.ContextRootCommon
|
|
152
|
+
],
|
|
153
|
+
type: SchemaOrgTypes.ItemList,
|
|
154
|
+
[SchemaOrgTypes.ItemListElement]: [
|
|
155
|
+
{
|
|
156
|
+
"@context": [
|
|
157
|
+
DocumentContexts.ContextRoot,
|
|
158
|
+
DocumentContexts.ContextRootCommon,
|
|
159
|
+
SchemaOrgContexts.ContextRoot
|
|
160
|
+
],
|
|
161
|
+
type: DocumentTypes.Document,
|
|
162
|
+
id: "2721000:0",
|
|
163
|
+
documentId: "2721000",
|
|
164
|
+
documentIdFormat: "bol",
|
|
165
|
+
documentCode: UneceDocumentCodes.BillOfLading,
|
|
166
|
+
documentRevision: 0,
|
|
167
|
+
blobStorageId: "blob-memory:c57d94b088f4c6d2cb32ded014813d0c786aa00134c8ee22f84b1e2545602a70",
|
|
168
|
+
blobHash: "sha256:123456",
|
|
169
|
+
dateCreated: "2024-01-01T00:00:00Z",
|
|
170
|
+
annotationObject: {
|
|
171
|
+
"@context": "https://schema.org",
|
|
172
|
+
"@type": "DigitalDocument",
|
|
173
|
+
name: "myfile.pdf"
|
|
174
|
+
},
|
|
175
|
+
nodeIdentity: "did:entity-storage:0x6363636363636363636363636363636363636363636363636363636363636363",
|
|
176
|
+
userIdentity: "did:entity-storage:0x6363636363636363636363636363636363636363636363636363636363636363"
|
|
177
|
+
}
|
|
178
|
+
]
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
]
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
type: "IDocumentManagementGetResponse",
|
|
186
|
+
mimeType: MimeTypes.JsonLd,
|
|
187
|
+
examples: [
|
|
188
|
+
{
|
|
189
|
+
id: "DocumentManagementGetResponseExample",
|
|
190
|
+
response: {
|
|
191
|
+
body: {
|
|
192
|
+
"@context": [
|
|
193
|
+
SchemaOrgContexts.ContextRoot,
|
|
194
|
+
DocumentContexts.ContextRoot,
|
|
195
|
+
DocumentContexts.ContextRootCommon
|
|
196
|
+
],
|
|
197
|
+
type: SchemaOrgTypes.ItemList,
|
|
198
|
+
[SchemaOrgTypes.ItemListElement]: [
|
|
199
|
+
{
|
|
200
|
+
"@context": [
|
|
201
|
+
DocumentContexts.ContextRoot,
|
|
202
|
+
DocumentContexts.ContextRootCommon,
|
|
203
|
+
SchemaOrgContexts.ContextRoot
|
|
204
|
+
],
|
|
205
|
+
type: DocumentTypes.Document,
|
|
206
|
+
id: "2721000:0",
|
|
207
|
+
documentId: "2721000",
|
|
208
|
+
documentIdFormat: "bol",
|
|
209
|
+
documentCode: UneceDocumentCodes.BillOfLading,
|
|
210
|
+
documentRevision: 0,
|
|
211
|
+
blobStorageId: "blob-memory:c57d94b088f4c6d2cb32ded014813d0c786aa00134c8ee22f84b1e2545602a70",
|
|
212
|
+
blobHash: "sha256:123456",
|
|
213
|
+
dateCreated: "2024-01-01T00:00:00Z",
|
|
214
|
+
annotationObject: {
|
|
215
|
+
"@context": "https://schema.org",
|
|
216
|
+
"@type": "DigitalDocument",
|
|
217
|
+
name: "myfile.pdf"
|
|
218
|
+
},
|
|
219
|
+
nodeIdentity: "did:entity-storage:0x6363636363636363636363636363636363636363636363636363636363636363",
|
|
220
|
+
userIdentity: "did:entity-storage:0x6363636363636363636363636363636363636363636363636363636363636363"
|
|
221
|
+
}
|
|
222
|
+
]
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
]
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
type: "INotFoundResponse"
|
|
230
|
+
}
|
|
231
|
+
]
|
|
232
|
+
};
|
|
233
|
+
const documentManagementGetRevisionRoute = {
|
|
234
|
+
operationId: "DocumentManagementGetRevision",
|
|
235
|
+
summary: "Get the data for a document revision from document management",
|
|
236
|
+
tag: tagsDocumentManagement[0].name,
|
|
237
|
+
method: "GET",
|
|
238
|
+
path: `${baseRouteName}/:auditableItemGraphDocumentId/:revision`,
|
|
239
|
+
handler: async (httpRequestContext, request) => documentManagementGetRevision(httpRequestContext, componentName, request),
|
|
240
|
+
requestType: {
|
|
241
|
+
type: "IDocumentManagementGetRequest",
|
|
242
|
+
examples: [
|
|
243
|
+
{
|
|
244
|
+
id: "DocumentManagementGetRevisionRequestExample",
|
|
245
|
+
request: {
|
|
246
|
+
pathParams: {
|
|
247
|
+
auditableItemGraphDocumentId: "aig:123456",
|
|
248
|
+
revision: "1"
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
]
|
|
253
|
+
},
|
|
254
|
+
responseType: [
|
|
255
|
+
{
|
|
256
|
+
type: "IDocumentManagementGetRevisionResponse",
|
|
257
|
+
examples: [
|
|
258
|
+
{
|
|
259
|
+
id: "DocumentManagementGetRevisionResponseExample",
|
|
260
|
+
response: {
|
|
261
|
+
body: {
|
|
262
|
+
"@context": [
|
|
263
|
+
DocumentContexts.ContextRoot,
|
|
264
|
+
DocumentContexts.ContextRootCommon,
|
|
265
|
+
SchemaOrgContexts.ContextRoot
|
|
117
266
|
],
|
|
118
267
|
type: DocumentTypes.Document,
|
|
119
|
-
id: "
|
|
268
|
+
id: "2721000:0",
|
|
120
269
|
documentId: "2721000",
|
|
121
270
|
documentIdFormat: "bol",
|
|
122
271
|
documentCode: UneceDocumentCodes.BillOfLading,
|
|
123
|
-
documentRevision:
|
|
272
|
+
documentRevision: 1,
|
|
124
273
|
blobStorageId: "blob-memory:c57d94b088f4c6d2cb32ded014813d0c786aa00134c8ee22f84b1e2545602a70",
|
|
125
274
|
blobHash: "sha256:123456",
|
|
126
275
|
dateCreated: "2024-01-01T00:00:00Z",
|
|
@@ -128,31 +277,33 @@ function generateRestRoutesDocumentManagement(baseRouteName, componentName) {
|
|
|
128
277
|
"@context": "https://schema.org",
|
|
129
278
|
"@type": "DigitalDocument",
|
|
130
279
|
name: "myfile.pdf"
|
|
131
|
-
}
|
|
280
|
+
},
|
|
281
|
+
nodeIdentity: "did:entity-storage:0x6363636363636363636363636363636363636363636363636363636363636363",
|
|
282
|
+
userIdentity: "did:entity-storage:0x6363636363636363636363636363636363636363636363636363636363636363"
|
|
132
283
|
}
|
|
133
284
|
}
|
|
134
285
|
}
|
|
135
286
|
]
|
|
136
287
|
},
|
|
137
288
|
{
|
|
138
|
-
type: "
|
|
289
|
+
type: "IDocumentManagementGetRevisionResponse",
|
|
139
290
|
mimeType: MimeTypes.JsonLd,
|
|
140
291
|
examples: [
|
|
141
292
|
{
|
|
142
|
-
id: "
|
|
293
|
+
id: "DocumentManagementGetRevisionResponseExample",
|
|
143
294
|
response: {
|
|
144
295
|
body: {
|
|
145
296
|
"@context": [
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
297
|
+
DocumentContexts.ContextRoot,
|
|
298
|
+
DocumentContexts.ContextRootCommon,
|
|
299
|
+
SchemaOrgContexts.ContextRoot
|
|
149
300
|
],
|
|
150
301
|
type: DocumentTypes.Document,
|
|
151
|
-
id: "
|
|
302
|
+
id: "2721000:0",
|
|
152
303
|
documentId: "2721000",
|
|
153
304
|
documentIdFormat: "bol",
|
|
154
305
|
documentCode: UneceDocumentCodes.BillOfLading,
|
|
155
|
-
documentRevision:
|
|
306
|
+
documentRevision: 1,
|
|
156
307
|
blobStorageId: "blob-memory:c57d94b088f4c6d2cb32ded014813d0c786aa00134c8ee22f84b1e2545602a70",
|
|
157
308
|
blobHash: "sha256:123456",
|
|
158
309
|
dateCreated: "2024-01-01T00:00:00Z",
|
|
@@ -160,7 +311,9 @@ function generateRestRoutesDocumentManagement(baseRouteName, componentName) {
|
|
|
160
311
|
"@context": "https://schema.org",
|
|
161
312
|
"@type": "DigitalDocument",
|
|
162
313
|
name: "myfile.pdf"
|
|
163
|
-
}
|
|
314
|
+
},
|
|
315
|
+
nodeIdentity: "did:entity-storage:0x6363636363636363636363636363636363636363636363636363636363636363",
|
|
316
|
+
userIdentity: "did:entity-storage:0x6363636363636363636363636363636363636363636363636363636363636363"
|
|
164
317
|
}
|
|
165
318
|
}
|
|
166
319
|
}
|
|
@@ -171,12 +324,12 @@ function generateRestRoutesDocumentManagement(baseRouteName, componentName) {
|
|
|
171
324
|
}
|
|
172
325
|
]
|
|
173
326
|
};
|
|
174
|
-
const
|
|
327
|
+
const documentManagementRemoveRevisionRoute = {
|
|
175
328
|
operationId: "DocumentManagementRemove",
|
|
176
329
|
summary: "Remove an document from an auditable item graph vertex",
|
|
177
330
|
tag: tagsDocumentManagement[0].name,
|
|
178
331
|
method: "DELETE",
|
|
179
|
-
path: `${baseRouteName}/:
|
|
332
|
+
path: `${baseRouteName}/:auditableItemGraphDocumentId/:revision`,
|
|
180
333
|
handler: async (httpRequestContext, request) => documentManagementRemove(httpRequestContext, componentName, request),
|
|
181
334
|
requestType: {
|
|
182
335
|
type: "IDocumentManagementRemoveRequest",
|
|
@@ -185,8 +338,8 @@ function generateRestRoutesDocumentManagement(baseRouteName, componentName) {
|
|
|
185
338
|
id: "DocumentManagementRemoveRequestExample",
|
|
186
339
|
request: {
|
|
187
340
|
pathParams: {
|
|
188
|
-
|
|
189
|
-
|
|
341
|
+
auditableItemGraphDocumentId: "aig:1234",
|
|
342
|
+
revision: "1"
|
|
190
343
|
}
|
|
191
344
|
}
|
|
192
345
|
}
|
|
@@ -206,7 +359,7 @@ function generateRestRoutesDocumentManagement(baseRouteName, componentName) {
|
|
|
206
359
|
summary: "Query the items from an auditable item graph vertex",
|
|
207
360
|
tag: tagsDocumentManagement[0].name,
|
|
208
361
|
method: "GET",
|
|
209
|
-
path: `${baseRouteName}
|
|
362
|
+
path: `${baseRouteName}/`,
|
|
210
363
|
handler: async (httpRequestContext, request) => documentManagementQuery(httpRequestContext, componentName, request),
|
|
211
364
|
requestType: {
|
|
212
365
|
type: "IDocumentManagementQueryRequest",
|
|
@@ -214,8 +367,8 @@ function generateRestRoutesDocumentManagement(baseRouteName, componentName) {
|
|
|
214
367
|
{
|
|
215
368
|
id: "DocumentManagementQueryRequestExample",
|
|
216
369
|
request: {
|
|
217
|
-
|
|
218
|
-
|
|
370
|
+
query: {
|
|
371
|
+
documentId: "2721000"
|
|
219
372
|
}
|
|
220
373
|
}
|
|
221
374
|
}
|
|
@@ -229,67 +382,54 @@ function generateRestRoutesDocumentManagement(baseRouteName, componentName) {
|
|
|
229
382
|
id: "DocumentManagementQueryResponseExample",
|
|
230
383
|
response: {
|
|
231
384
|
body: {
|
|
232
|
-
"@context": [
|
|
233
|
-
type:
|
|
234
|
-
|
|
385
|
+
"@context": [SchemaOrgContexts.ContextRoot, AuditableItemGraphContexts.ContextRoot],
|
|
386
|
+
type: [SchemaOrgTypes.ItemList, AuditableItemGraphTypes.VertexList],
|
|
387
|
+
[SchemaOrgTypes.ItemListElement]: [
|
|
235
388
|
{
|
|
236
389
|
"@context": [
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
SchemaOrgTypes.ContextRoot
|
|
390
|
+
AuditableItemGraphContexts.ContextRoot,
|
|
391
|
+
AuditableItemGraphContexts.ContextRootCommon
|
|
240
392
|
],
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
"@context": "https://schema.org",
|
|
252
|
-
"@type": "DigitalDocument",
|
|
253
|
-
name: "myfile.pdf"
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
]
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
]
|
|
261
|
-
},
|
|
262
|
-
{
|
|
263
|
-
type: "IDocumentManagementQueryResponse",
|
|
264
|
-
mimeType: MimeTypes.JsonLd,
|
|
265
|
-
examples: [
|
|
266
|
-
{
|
|
267
|
-
id: "DocumentManagementListResponseJsonLdExample",
|
|
268
|
-
response: {
|
|
269
|
-
body: {
|
|
270
|
-
"@context": [DocumentTypes.ContextRoot, DocumentTypes.ContextRootCommon],
|
|
271
|
-
type: DocumentTypes.DocumentList,
|
|
272
|
-
documents: [
|
|
273
|
-
{
|
|
274
|
-
"@context": [
|
|
275
|
-
DocumentTypes.ContextRoot,
|
|
276
|
-
DocumentTypes.ContextRootCommon,
|
|
277
|
-
SchemaOrgTypes.ContextRoot
|
|
393
|
+
id: "aig:c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7",
|
|
394
|
+
type: AuditableItemGraphTypes.Vertex,
|
|
395
|
+
dateCreated: "2024-08-22T04:13:20.000Z",
|
|
396
|
+
aliases: [
|
|
397
|
+
{
|
|
398
|
+
"@context": [AuditableItemGraphContexts.ContextRoot],
|
|
399
|
+
id: "test-id-0",
|
|
400
|
+
type: AuditableItemGraphTypes.Alias,
|
|
401
|
+
dateCreated: "2024-08-22T04:13:20.000Z"
|
|
402
|
+
}
|
|
278
403
|
],
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
404
|
+
resources: [
|
|
405
|
+
{
|
|
406
|
+
"@context": AuditableItemGraphContexts.ContextRoot,
|
|
407
|
+
type: AuditableItemGraphTypes.Resource,
|
|
408
|
+
dateCreated: "2024-08-22T04:13:20.000Z",
|
|
409
|
+
resourceObject: {
|
|
410
|
+
"@context": [
|
|
411
|
+
"https://schema.twindev.org/documents/",
|
|
412
|
+
"https://schema.twindev.org/common/",
|
|
413
|
+
"https://schema.org"
|
|
414
|
+
],
|
|
415
|
+
type: "Document",
|
|
416
|
+
id: "test-id-0:0",
|
|
417
|
+
documentId: "test-id-0",
|
|
418
|
+
documentCode: "unece:DocumentCodeList#705",
|
|
419
|
+
documentRevision: 0,
|
|
420
|
+
annotationObject: {
|
|
421
|
+
"@context": "https://schema.org",
|
|
422
|
+
type: "DigitalDocument",
|
|
423
|
+
name: "bill-of-lading"
|
|
424
|
+
},
|
|
425
|
+
blobHash: "sha256:E3Duqrp6bHojSx+CzDttAToAiP1eFkCDAPBbKLABVGM=",
|
|
426
|
+
blobStorageId: "blob:memory:1370eeaaba7a6c7a234b1f82cc3b6d013a0088fd5e16408300f05b28b0015463",
|
|
427
|
+
dateCreated: "2024-08-22T04:13:20.000Z",
|
|
428
|
+
nodeIdentity: "did:entity-storage:0x0101010101010101010101010101010101010101010101010101010101010101",
|
|
429
|
+
userIdentity: "did:entity-storage:0x0404040404040404040404040404040404040404040404040404040404040404"
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
]
|
|
293
433
|
}
|
|
294
434
|
]
|
|
295
435
|
}
|
|
@@ -300,27 +440,31 @@ function generateRestRoutesDocumentManagement(baseRouteName, componentName) {
|
|
|
300
440
|
]
|
|
301
441
|
};
|
|
302
442
|
return [
|
|
303
|
-
|
|
443
|
+
documentManagementCreateRoute,
|
|
444
|
+
documentManagementUpdateRoute,
|
|
304
445
|
documentManagementGetRoute,
|
|
305
|
-
|
|
446
|
+
documentManagementGetRevisionRoute,
|
|
447
|
+
documentManagementRemoveRevisionRoute,
|
|
306
448
|
documentManagementQueryRoute
|
|
307
449
|
];
|
|
308
450
|
}
|
|
309
451
|
/**
|
|
310
|
-
*
|
|
452
|
+
* Create a document as an auditable item graph vertex.
|
|
311
453
|
* @param httpRequestContext The request context for the API.
|
|
312
454
|
* @param componentName The name of the component to use in the routes.
|
|
313
455
|
* @param request The request.
|
|
314
456
|
* @returns The response object with additional http response properties.
|
|
315
457
|
*/
|
|
316
|
-
async function
|
|
458
|
+
async function documentManagementCreate(httpRequestContext, componentName, request) {
|
|
317
459
|
Guards.object(ROUTES_SOURCE, "request", request);
|
|
318
|
-
Guards.object(ROUTES_SOURCE, "request.pathParams", request.pathParams);
|
|
319
|
-
Guards.stringValue(ROUTES_SOURCE, "request.pathParams.auditableItemGraphId", request.pathParams.auditableItemGraphId);
|
|
320
460
|
Guards.object(ROUTES_SOURCE, "request.body", request.body);
|
|
321
461
|
Guards.stringBase64(ROUTES_SOURCE, "request.body.blob", request.body.blob);
|
|
322
462
|
const component = ComponentFactory.get(componentName);
|
|
323
|
-
const id = await component.
|
|
463
|
+
const id = await component.create(request.body.documentId, request.body.documentIdFormat, request.body.documentCode, Converter.base64ToBytes(request.body.blob), request.body.annotationObject, request.body.auditableItemGraphEdges, {
|
|
464
|
+
createAttestation: request.body.createAttestation,
|
|
465
|
+
addAlias: request.body.addAlias,
|
|
466
|
+
aliasAnnotationObject: request.body.aliasAnnotationObject
|
|
467
|
+
}, httpRequestContext.userIdentity, httpRequestContext.nodeIdentity);
|
|
324
468
|
return {
|
|
325
469
|
statusCode: HttpStatusCode.created,
|
|
326
470
|
headers: {
|
|
@@ -338,17 +482,46 @@ async function documentManagementSet(httpRequestContext, componentName, request)
|
|
|
338
482
|
async function documentManagementGet(httpRequestContext, componentName, request) {
|
|
339
483
|
Guards.object(ROUTES_SOURCE, "request", request);
|
|
340
484
|
Guards.object(ROUTES_SOURCE, "request.pathParams", request.pathParams);
|
|
341
|
-
Guards.stringValue(ROUTES_SOURCE, "request.pathParams.
|
|
342
|
-
Guards.stringValue(ROUTES_SOURCE, "request.pathParams.documentId", request.pathParams.documentId);
|
|
485
|
+
Guards.stringValue(ROUTES_SOURCE, "request.pathParams.auditableItemGraphDocumentId", request.pathParams.auditableItemGraphDocumentId);
|
|
343
486
|
const mimeType = request.headers?.[HeaderTypes.Accept] === MimeTypes.JsonLd ? "jsonld" : "json";
|
|
344
487
|
const component = ComponentFactory.get(componentName);
|
|
345
|
-
const result = await component.get(request.pathParams.
|
|
488
|
+
const result = await component.get(request.pathParams.auditableItemGraphDocumentId, {
|
|
346
489
|
includeBlobStorageMetadata: Coerce.boolean(request.query?.includeBlobStorageMetadata),
|
|
347
490
|
includeBlobStorageData: Coerce.boolean(request.query?.includeBlobStorageData),
|
|
348
491
|
includeAttestation: Coerce.boolean(request.query?.includeAttestation),
|
|
349
492
|
includeRemoved: Coerce.boolean(request.query?.includeRemoved),
|
|
350
|
-
|
|
351
|
-
|
|
493
|
+
extractRuleGroupId: request.query?.extractRuleGroupId,
|
|
494
|
+
extractMimeType: request.query?.extractMimeType
|
|
495
|
+
}, request.query?.cursor, Coerce.integer(request.query?.pageSize), httpRequestContext.userIdentity, httpRequestContext.nodeIdentity);
|
|
496
|
+
return {
|
|
497
|
+
headers: {
|
|
498
|
+
[HeaderTypes.ContentType]: mimeType === "json" ? MimeTypes.Json : MimeTypes.JsonLd
|
|
499
|
+
},
|
|
500
|
+
body: result
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* Get the document revision from the auditable item graph vertex.
|
|
505
|
+
* @param httpRequestContext The request context for the API.
|
|
506
|
+
* @param componentName The name of the component to use in the routes.
|
|
507
|
+
* @param request The request.
|
|
508
|
+
* @returns The response object with additional http response properties.
|
|
509
|
+
*/
|
|
510
|
+
async function documentManagementGetRevision(httpRequestContext, componentName, request) {
|
|
511
|
+
Guards.object(ROUTES_SOURCE, "request", request);
|
|
512
|
+
Guards.object(ROUTES_SOURCE, "request.pathParams", request.pathParams);
|
|
513
|
+
Guards.stringValue(ROUTES_SOURCE, "request.pathParams.auditableItemGraphDocumentId", request.pathParams.auditableItemGraphDocumentId);
|
|
514
|
+
const revision = Coerce.integer(request.pathParams.revision);
|
|
515
|
+
Guards.integer(ROUTES_SOURCE, "revision", revision);
|
|
516
|
+
const mimeType = request.headers?.[HeaderTypes.Accept] === MimeTypes.JsonLd ? "jsonld" : "json";
|
|
517
|
+
const component = ComponentFactory.get(componentName);
|
|
518
|
+
const result = await component.getRevision(request.pathParams.auditableItemGraphDocumentId, revision, {
|
|
519
|
+
includeBlobStorageMetadata: Coerce.boolean(request.query?.includeBlobStorageMetadata),
|
|
520
|
+
includeBlobStorageData: Coerce.boolean(request.query?.includeBlobStorageData),
|
|
521
|
+
includeAttestation: Coerce.boolean(request.query?.includeAttestation),
|
|
522
|
+
extractRuleGroupId: request.query?.extractRuleGroupId,
|
|
523
|
+
extractMimeType: request.query?.extractMimeType
|
|
524
|
+
}, httpRequestContext.userIdentity, httpRequestContext.nodeIdentity);
|
|
352
525
|
return {
|
|
353
526
|
headers: {
|
|
354
527
|
[HeaderTypes.ContentType]: mimeType === "json" ? MimeTypes.Json : MimeTypes.JsonLd
|
|
@@ -356,6 +529,23 @@ async function documentManagementGet(httpRequestContext, componentName, request)
|
|
|
356
529
|
body: result
|
|
357
530
|
};
|
|
358
531
|
}
|
|
532
|
+
/**
|
|
533
|
+
* Update the document from the auditable item graph vertex.
|
|
534
|
+
* @param httpRequestContext The request context for the API.
|
|
535
|
+
* @param componentName The name of the component to use in the routes.
|
|
536
|
+
* @param request The request.
|
|
537
|
+
* @returns The response object with additional http response properties.
|
|
538
|
+
*/
|
|
539
|
+
async function documentManagementUpdate(httpRequestContext, componentName, request) {
|
|
540
|
+
Guards.object(ROUTES_SOURCE, "request", request);
|
|
541
|
+
Guards.object(ROUTES_SOURCE, "request.pathParams", request.pathParams);
|
|
542
|
+
Guards.stringValue(ROUTES_SOURCE, "request.pathParams.auditableItemGraphDocumentId", request.pathParams.auditableItemGraphDocumentId);
|
|
543
|
+
const component = ComponentFactory.get(componentName);
|
|
544
|
+
await component.update(request.pathParams.auditableItemGraphDocumentId, Is.stringValue(request.body.blob) ? Converter.base64ToBytes(request.body.blob) : undefined, request.body.annotationObject, request.body.auditableItemGraphEdges, httpRequestContext.userIdentity, httpRequestContext.nodeIdentity);
|
|
545
|
+
return {
|
|
546
|
+
statusCode: HttpStatusCode.noContent
|
|
547
|
+
};
|
|
548
|
+
}
|
|
359
549
|
/**
|
|
360
550
|
* Remove the document from the auditable item graph vertex.
|
|
361
551
|
* @param httpRequestContext The request context for the API.
|
|
@@ -366,12 +556,11 @@ async function documentManagementGet(httpRequestContext, componentName, request)
|
|
|
366
556
|
async function documentManagementRemove(httpRequestContext, componentName, request) {
|
|
367
557
|
Guards.object(ROUTES_SOURCE, "request", request);
|
|
368
558
|
Guards.object(ROUTES_SOURCE, "request.pathParams", request.pathParams);
|
|
369
|
-
Guards.stringValue(ROUTES_SOURCE, "request.pathParams.
|
|
370
|
-
|
|
559
|
+
Guards.stringValue(ROUTES_SOURCE, "request.pathParams.auditableItemGraphDocumentId", request.pathParams.auditableItemGraphDocumentId);
|
|
560
|
+
const revision = Coerce.number(request.pathParams.revision);
|
|
561
|
+
Guards.integer(ROUTES_SOURCE, "request.pathParams.revision", revision);
|
|
371
562
|
const component = ComponentFactory.get(componentName);
|
|
372
|
-
await component.
|
|
373
|
-
removeAllRevisions: request.query?.removeAllRevisions
|
|
374
|
-
}, httpRequestContext.userIdentity, httpRequestContext.nodeIdentity);
|
|
563
|
+
await component.removeRevision(request.pathParams.auditableItemGraphDocumentId, revision, httpRequestContext.userIdentity, httpRequestContext.nodeIdentity);
|
|
375
564
|
return {
|
|
376
565
|
statusCode: HttpStatusCode.noContent
|
|
377
566
|
};
|
|
@@ -385,14 +574,11 @@ async function documentManagementRemove(httpRequestContext, componentName, reque
|
|
|
385
574
|
*/
|
|
386
575
|
async function documentManagementQuery(httpRequestContext, componentName, request) {
|
|
387
576
|
Guards.object(ROUTES_SOURCE, "request", request);
|
|
388
|
-
Guards.object(ROUTES_SOURCE, "request.
|
|
389
|
-
Guards.stringValue(ROUTES_SOURCE, "request.
|
|
577
|
+
Guards.object(ROUTES_SOURCE, "request.query", request.query);
|
|
578
|
+
Guards.stringValue(ROUTES_SOURCE, "request.query.documentId", request.query.documentId);
|
|
390
579
|
const mimeType = request.headers?.[HeaderTypes.Accept] === MimeTypes.JsonLd ? "jsonld" : "json";
|
|
391
580
|
const component = ComponentFactory.get(componentName);
|
|
392
|
-
const result = await component.query(request.
|
|
393
|
-
includeRemoved: Coerce.boolean(request.query?.includeRemoved),
|
|
394
|
-
includeMostRecentRevisions: Coerce.boolean(request.query?.includeMostRecentRevisions)
|
|
395
|
-
}, request.query?.cursor, httpRequestContext.userIdentity, httpRequestContext.nodeIdentity);
|
|
581
|
+
const result = await component.query(request.query.documentId, request.query?.cursor, Coerce.integer(request.query?.pageSize), httpRequestContext.userIdentity, httpRequestContext.nodeIdentity);
|
|
396
582
|
return {
|
|
397
583
|
headers: {
|
|
398
584
|
[HeaderTypes.ContentType]: mimeType === "json" ? MimeTypes.Json : MimeTypes.JsonLd
|
|
@@ -409,11 +595,6 @@ class DocumentManagementService {
|
|
|
409
595
|
* The namespace supported by the document management service.
|
|
410
596
|
*/
|
|
411
597
|
static NAMESPACE = "documents";
|
|
412
|
-
/**
|
|
413
|
-
* Default Page Size for cursor.
|
|
414
|
-
* @internal
|
|
415
|
-
*/
|
|
416
|
-
static _DEFAULT_PAGE_SIZE = 20;
|
|
417
598
|
/**
|
|
418
599
|
* Runtime name for the class.
|
|
419
600
|
*/
|
|
@@ -433,6 +614,11 @@ class DocumentManagementService {
|
|
|
433
614
|
* @internal
|
|
434
615
|
*/
|
|
435
616
|
_attestationComponent;
|
|
617
|
+
/**
|
|
618
|
+
* The connector for the data processing.
|
|
619
|
+
* @internal
|
|
620
|
+
*/
|
|
621
|
+
_dataProcessingComponent;
|
|
436
622
|
/**
|
|
437
623
|
* Create a new instance of DocumentManagementService.
|
|
438
624
|
* @param options The options for the service.
|
|
@@ -441,133 +627,218 @@ class DocumentManagementService {
|
|
|
441
627
|
this._auditableItemGraphComponent = ComponentFactory.get(options?.auditableItemGraphComponentType ?? "auditable-item-graph");
|
|
442
628
|
this._blobStorageComponent = ComponentFactory.get(options?.blobStorageComponentType ?? "blob-storage");
|
|
443
629
|
this._attestationComponent = ComponentFactory.get(options?.attestationComponentType ?? "attestation");
|
|
630
|
+
this._dataProcessingComponent = ComponentFactory.get(options?.dataProcessingComponentType ?? "data-processing");
|
|
444
631
|
SchemaOrgDataTypes.registerRedirects();
|
|
445
632
|
}
|
|
446
633
|
/**
|
|
447
|
-
* Store a document
|
|
634
|
+
* Store a document as an auditable item graph vertex and add its content to blob storage.
|
|
448
635
|
* If the document id already exists and the blob data is different a new revision will be created.
|
|
449
636
|
* For any other changes the current revision will be updated.
|
|
450
|
-
* @param auditableItemGraphId The auditable item graph vertex id to create the document on.
|
|
451
637
|
* @param documentId The document id to create.
|
|
452
638
|
* @param documentIdFormat The format of the document identifier.
|
|
453
639
|
* @param documentCode The code for the document type.
|
|
454
|
-
* @param blob The data to create the document.
|
|
640
|
+
* @param blob The data to create the document with.
|
|
455
641
|
* @param annotationObject Additional information to associate with the document.
|
|
456
|
-
* @param
|
|
642
|
+
* @param auditableItemGraphEdges The auditable item graph vertices to connect the document to.
|
|
643
|
+
* @param options Additional options for the set operation.
|
|
644
|
+
* @param options.createAttestation Flag to create an attestation for the document, defaults to false.
|
|
645
|
+
* @param options.addAlias Flag to add the document id as an alias to the aig vertex, defaults to true.
|
|
646
|
+
* @param options.aliasAnnotationObject Annotation object for the alias.
|
|
457
647
|
* @param userIdentity The identity to perform the auditable item graph operation with.
|
|
458
648
|
* @param nodeIdentity The node identity to use for vault operations.
|
|
459
|
-
* @returns The
|
|
649
|
+
* @returns The auditable item graph vertex created for the document including its revision.
|
|
460
650
|
*/
|
|
461
|
-
async
|
|
462
|
-
Guards.stringValue(this.CLASS_NAME, "auditableItemGraphId", auditableItemGraphId);
|
|
651
|
+
async create(documentId, documentIdFormat, documentCode, blob, annotationObject, auditableItemGraphEdges, options, userIdentity, nodeIdentity) {
|
|
463
652
|
Guards.stringValue(this.CLASS_NAME, "documentId", documentId);
|
|
464
653
|
Guards.arrayOneOf(this.CLASS_NAME, "documentCode", documentCode, Object.values(UneceDocumentCodes));
|
|
465
654
|
Guards.uint8Array(this.CLASS_NAME, "blob", blob);
|
|
655
|
+
Guards.stringValue(this.CLASS_NAME, "userIdentity", userIdentity);
|
|
656
|
+
Guards.stringValue(this.CLASS_NAME, "nodeIdentity", nodeIdentity);
|
|
466
657
|
try {
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
const matchingDocIds = this.findMatchingDocs(vertexDocs, documentId, documentCode, true);
|
|
473
|
-
// Calculate the hash for the blob.
|
|
474
|
-
const blobHash = this.generateBlobHash(blob);
|
|
475
|
-
let documentRevision;
|
|
476
|
-
if (Is.arrayValue(matchingDocIds) && matchingDocIds[0].blobHash === blobHash) {
|
|
477
|
-
documentRevision = matchingDocIds[0].documentRevision;
|
|
478
|
-
// If there is already a doc with the matching blob hash no need to create a new revision
|
|
479
|
-
// instead we just update the annotation object if it has changed.
|
|
480
|
-
if (!ObjectHelper.equal(matchingDocIds[0].annotationObject, annotationObject, false)) {
|
|
481
|
-
matchingDocIds[0].dateModified = new Date().toISOString();
|
|
482
|
-
matchingDocIds[0].annotationObject = annotationObject;
|
|
483
|
-
await this._auditableItemGraphComponent.update(vertex, userIdentity, nodeIdentity);
|
|
658
|
+
// Get the connected vertices first, if one fails we abort the create
|
|
659
|
+
const connectedVertices = {};
|
|
660
|
+
if (Is.arrayValue(auditableItemGraphEdges)) {
|
|
661
|
+
for (const edge of auditableItemGraphEdges) {
|
|
662
|
+
connectedVertices[edge.id] = await this._auditableItemGraphComponent.get(edge.id);
|
|
484
663
|
}
|
|
485
|
-
return matchingDocIds[0].id;
|
|
486
664
|
}
|
|
487
|
-
|
|
665
|
+
const documentVertex = {};
|
|
666
|
+
if (options?.addAlias ?? true) {
|
|
667
|
+
documentVertex.aliases ??= [];
|
|
668
|
+
documentVertex.aliases.push({
|
|
669
|
+
"@context": AuditableItemGraphContexts.ContextRoot,
|
|
670
|
+
type: AuditableItemGraphTypes.Alias,
|
|
671
|
+
id: documentId,
|
|
672
|
+
aliasFormat: documentIdFormat,
|
|
673
|
+
annotationObject: options?.aliasAnnotationObject
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
// Add the blob to blob storage
|
|
488
677
|
const blobStorageId = await this._blobStorageComponent.create(Converter.bytesToBase64(blob), undefined, undefined, undefined, undefined, userIdentity, nodeIdentity);
|
|
489
|
-
|
|
490
|
-
// We are creating a new document, if there is already docs with the same id and code we use the list length
|
|
491
|
-
// to determine the next revision number.
|
|
492
|
-
const document = {
|
|
678
|
+
const currentRevision = {
|
|
493
679
|
"@context": [
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
680
|
+
DocumentContexts.ContextRoot,
|
|
681
|
+
DocumentContexts.ContextRootCommon,
|
|
682
|
+
SchemaOrgContexts.ContextRoot
|
|
497
683
|
],
|
|
498
684
|
type: DocumentTypes.Document,
|
|
499
|
-
id: this.
|
|
685
|
+
id: this.createDocumentId(documentId, 0),
|
|
500
686
|
documentId,
|
|
501
687
|
documentIdFormat,
|
|
502
688
|
documentCode,
|
|
503
|
-
documentRevision,
|
|
689
|
+
documentRevision: 0,
|
|
690
|
+
annotationObject,
|
|
691
|
+
blobHash: this.generateBlobHash(blob),
|
|
504
692
|
blobStorageId,
|
|
505
|
-
|
|
506
|
-
|
|
693
|
+
dateCreated: new Date(Date.now()).toISOString(),
|
|
694
|
+
nodeIdentity,
|
|
695
|
+
userIdentity
|
|
507
696
|
};
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
// Add the new revision in to the AIG
|
|
516
|
-
vertex.resources.push({
|
|
517
|
-
"@context": AuditableItemGraphTypes.ContextRoot,
|
|
697
|
+
if (options?.createAttestation ?? false) {
|
|
698
|
+
currentRevision.attestationId = await this.createAttestation(currentRevision, userIdentity, nodeIdentity);
|
|
699
|
+
}
|
|
700
|
+
// Add the new revision in to the vertex
|
|
701
|
+
documentVertex.resources ??= [];
|
|
702
|
+
documentVertex.resources.push({
|
|
703
|
+
"@context": AuditableItemGraphContexts.ContextRoot,
|
|
518
704
|
type: AuditableItemGraphTypes.Resource,
|
|
519
|
-
resourceObject:
|
|
705
|
+
resourceObject: currentRevision
|
|
520
706
|
});
|
|
521
|
-
|
|
522
|
-
|
|
707
|
+
// Add the edges from the document to the items
|
|
708
|
+
this.updateEdges(documentVertex, auditableItemGraphEdges);
|
|
709
|
+
// And create the vertex
|
|
710
|
+
const vertexId = await this._auditableItemGraphComponent.create(documentVertex, userIdentity, nodeIdentity);
|
|
711
|
+
// Now add the edges to the connected vertices
|
|
712
|
+
await this.updateConnectedEdges(connectedVertices, vertexId, [], auditableItemGraphEdges, documentId, documentIdFormat, userIdentity, nodeIdentity);
|
|
713
|
+
return vertexId;
|
|
523
714
|
}
|
|
524
715
|
catch (error) {
|
|
525
716
|
if (BaseError.someErrorName(error, "NotFoundError")) {
|
|
526
717
|
throw error;
|
|
527
718
|
}
|
|
528
|
-
throw new GeneralError(this.CLASS_NAME, "
|
|
719
|
+
throw new GeneralError(this.CLASS_NAME, "createFailed", undefined, error);
|
|
529
720
|
}
|
|
530
721
|
}
|
|
531
722
|
/**
|
|
532
|
-
*
|
|
533
|
-
*
|
|
534
|
-
*
|
|
723
|
+
* Update a document as an auditable item graph vertex and add its content to blob storage.
|
|
724
|
+
* If the blob data is different a new revision will be created.
|
|
725
|
+
* For any other changes the current revision will be updated.
|
|
726
|
+
* @param auditableItemGraphDocumentId The auditable item graph vertex id which contains the document.
|
|
727
|
+
* @param blob The data to update the document with.
|
|
728
|
+
* @param annotationObject Additional information to associate with the document.
|
|
729
|
+
* @param auditableItemGraphEdges The auditable item graph vertices to connect the document to, if undefined retains current connections.
|
|
730
|
+
* @param userIdentity The identity to perform the auditable item graph operation with.
|
|
731
|
+
* @param nodeIdentity The node identity to use for vault operations.
|
|
732
|
+
* @returns Nothing.
|
|
733
|
+
*/
|
|
734
|
+
async update(auditableItemGraphDocumentId, blob, annotationObject, auditableItemGraphEdges, userIdentity, nodeIdentity) {
|
|
735
|
+
Urn.guard(this.CLASS_NAME, "auditableItemGraphDocumentId", auditableItemGraphDocumentId);
|
|
736
|
+
Guards.stringValue(this.CLASS_NAME, "userIdentity", userIdentity);
|
|
737
|
+
Guards.stringValue(this.CLASS_NAME, "nodeIdentity", nodeIdentity);
|
|
738
|
+
try {
|
|
739
|
+
const documentVertex = await this._auditableItemGraphComponent.get(auditableItemGraphDocumentId, { includeDeleted: true });
|
|
740
|
+
if (Is.empty(documentVertex.resources)) {
|
|
741
|
+
throw new NotFoundError(this.CLASS_NAME, "documentRevisionNone");
|
|
742
|
+
}
|
|
743
|
+
const documents = await this.getDocumentsFromVertex(documentVertex);
|
|
744
|
+
const latestRevision = documents.itemListElement[0];
|
|
745
|
+
documentVertex.resources = documentVertex.resources.filter(r => Is.empty(r.dateDeleted));
|
|
746
|
+
if (Is.empty(latestRevision)) {
|
|
747
|
+
throw new NotFoundError(this.CLASS_NAME, "documentRevisionNone");
|
|
748
|
+
}
|
|
749
|
+
// If auditableItemGraphEdges is undefined we are not updating the edges
|
|
750
|
+
// an empty array can be passed to remove all edges
|
|
751
|
+
const connectedVertices = {};
|
|
752
|
+
if (Is.array(auditableItemGraphEdges)) {
|
|
753
|
+
// Get the updated connected vertices first, if one fails we abort the update
|
|
754
|
+
for (const edge of auditableItemGraphEdges) {
|
|
755
|
+
connectedVertices[edge.id] = await this._auditableItemGraphComponent.get(edge.id);
|
|
756
|
+
}
|
|
757
|
+
// Also get the current edges in case some need disconnecting
|
|
758
|
+
if (Is.arrayValue(documents.edges)) {
|
|
759
|
+
for (const edgeId of documents.edges) {
|
|
760
|
+
// If we haven't retrieved the edge then it must be one that needs removing
|
|
761
|
+
if (Is.empty(connectedVertices[edgeId])) {
|
|
762
|
+
connectedVertices[edgeId] = await this._auditableItemGraphComponent.get(edgeId);
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
let updatedVertex = false;
|
|
768
|
+
// If the blob is set and its hash has changed then we create a new revision
|
|
769
|
+
if (Is.uint8Array(blob)) {
|
|
770
|
+
const newBlobHash = this.generateBlobHash(blob);
|
|
771
|
+
if (latestRevision.blobHash !== newBlobHash) {
|
|
772
|
+
// Add the blob to blob storage
|
|
773
|
+
const blobStorageId = await this._blobStorageComponent.create(Converter.bytesToBase64(blob), undefined, undefined, undefined, undefined, userIdentity, nodeIdentity);
|
|
774
|
+
const newRevision = ObjectHelper.clone(latestRevision);
|
|
775
|
+
newRevision.documentRevision++;
|
|
776
|
+
newRevision.id = this.createDocumentId(newRevision.documentId, newRevision.documentRevision);
|
|
777
|
+
newRevision.blobHash = newBlobHash;
|
|
778
|
+
newRevision.blobStorageId = blobStorageId;
|
|
779
|
+
newRevision.annotationObject = annotationObject;
|
|
780
|
+
if (Is.stringValue(latestRevision.attestationId)) {
|
|
781
|
+
newRevision.attestationId = await this.createAttestation(newRevision, userIdentity, nodeIdentity);
|
|
782
|
+
}
|
|
783
|
+
documentVertex.resources.push({
|
|
784
|
+
"@context": AuditableItemGraphContexts.ContextRoot,
|
|
785
|
+
type: AuditableItemGraphTypes.Resource,
|
|
786
|
+
resourceObject: newRevision
|
|
787
|
+
});
|
|
788
|
+
updatedVertex = true;
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
// If the blob wasn't updated but the annotation object has then update the current revision
|
|
792
|
+
// instead of creating a new one
|
|
793
|
+
if (!updatedVertex &&
|
|
794
|
+
!ObjectHelper.equal(latestRevision.annotationObject, annotationObject)) {
|
|
795
|
+
updatedVertex = true;
|
|
796
|
+
latestRevision.annotationObject = annotationObject;
|
|
797
|
+
latestRevision.dateModified = new Date(Date.now()).toISOString();
|
|
798
|
+
}
|
|
799
|
+
const existingEdgeIds = documentVertex.edges?.map(e => e.id) ?? [];
|
|
800
|
+
// Update the edges from the document to the items
|
|
801
|
+
const edgesUpdated = this.updateEdges(documentVertex, auditableItemGraphEdges);
|
|
802
|
+
if (edgesUpdated) {
|
|
803
|
+
updatedVertex = true;
|
|
804
|
+
}
|
|
805
|
+
if (updatedVertex) {
|
|
806
|
+
await this._auditableItemGraphComponent.update(documentVertex, userIdentity, nodeIdentity);
|
|
807
|
+
}
|
|
808
|
+
if (edgesUpdated) {
|
|
809
|
+
await this.updateConnectedEdges(connectedVertices, auditableItemGraphDocumentId, existingEdgeIds, auditableItemGraphEdges, latestRevision.documentId, latestRevision.documentIdFormat, userIdentity, nodeIdentity);
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
catch (error) {
|
|
813
|
+
if (BaseError.someErrorName(error, "NotFoundError")) {
|
|
814
|
+
throw error;
|
|
815
|
+
}
|
|
816
|
+
throw new GeneralError(this.CLASS_NAME, "updateFailed", undefined, error);
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
/**
|
|
820
|
+
* Get a document using it's auditable item graph vertex id and optional revision.
|
|
821
|
+
* @param auditableItemGraphDocumentId The auditable item graph vertex id which contains the document.
|
|
535
822
|
* @param options Additional options for the get operation.
|
|
536
823
|
* @param options.includeBlobStorageMetadata Flag to include the blob storage metadata for the document, defaults to false.
|
|
537
824
|
* @param options.includeBlobStorageData Flag to include the blob storage data for the document, defaults to false.
|
|
538
825
|
* @param options.includeAttestation Flag to include the attestation information for the document, defaults to false.
|
|
539
826
|
* @param options.includeRemoved Flag to include deleted documents, defaults to false.
|
|
540
|
-
* @param options.
|
|
541
|
-
* @param
|
|
827
|
+
* @param options.extractRuleGroupId If provided will extract data from the document using the specified rule group id.
|
|
828
|
+
* @param options.extractMimeType By default extraction will auto detect the mime type of the document, this can be used to override the detection.
|
|
829
|
+
* @param cursor The cursor to get the next chunk of revisions.
|
|
830
|
+
* @param pageSize Page size of items to return, defaults to 1 so only most recent is returned.
|
|
542
831
|
* @param userIdentity The identity to perform the auditable item graph operation with.
|
|
543
832
|
* @param nodeIdentity The node identity to use for vault operations.
|
|
544
833
|
* @returns The documents and revisions if requested, ordered by revision descending, cursor is set if there are more document revisions.
|
|
545
834
|
*/
|
|
546
|
-
async get(
|
|
547
|
-
Urn.guard(this.CLASS_NAME, "
|
|
548
|
-
Urn.guard(this.CLASS_NAME, "identifier", identifier);
|
|
835
|
+
async get(auditableItemGraphDocumentId, options, cursor, pageSize, userIdentity, nodeIdentity) {
|
|
836
|
+
Urn.guard(this.CLASS_NAME, "auditableItemGraphDocumentId", auditableItemGraphDocumentId);
|
|
549
837
|
try {
|
|
550
|
-
const
|
|
551
|
-
const includeBlobStorageData = options?.includeBlobStorageData ?? false;
|
|
552
|
-
const includeAttestation = options?.includeAttestation ?? false;
|
|
553
|
-
const includeRemoved = options?.includeRemoved ?? false;
|
|
554
|
-
const revCursor = Math.max(Coerce.integer(revisionCursor) ?? 0, 0);
|
|
555
|
-
const maxRevisionCount = Math.max(Coerce.integer(options?.maxRevisionCount) ?? 0);
|
|
556
|
-
const documentIdParts = this.parseDocumentId(identifier);
|
|
557
|
-
const vertex = await this._auditableItemGraphComponent.get(auditableItemGraphId);
|
|
558
|
-
// Get all the docs from the AIG vertex
|
|
559
|
-
const vertexDocs = this.filterDocumentsFromVertex(vertex);
|
|
560
|
-
// Reduce the list to those with a matching id and code
|
|
561
|
-
const matchingDocIds = this.findMatchingDocs(vertexDocs, documentIdParts.documentId, documentIdParts.documentCode, includeRemoved);
|
|
838
|
+
const documentVertex = await this._auditableItemGraphComponent.get(auditableItemGraphDocumentId, { includeDeleted: options?.includeRemoved });
|
|
562
839
|
// Populate the document and revisions with the options set
|
|
563
|
-
const
|
|
564
|
-
|
|
565
|
-
includeBlobStorageData,
|
|
566
|
-
includeAttestation,
|
|
567
|
-
includeRemoved,
|
|
568
|
-
maxRevisionCount
|
|
569
|
-
}, revCursor, userIdentity, nodeIdentity);
|
|
570
|
-
return JsonLdProcessor.compact(document, document["@context"]);
|
|
840
|
+
const documents = await this.getDocumentsFromVertex(documentVertex, options, cursor, pageSize, userIdentity, nodeIdentity);
|
|
841
|
+
return JsonLdProcessor.compact(documents, documents["@context"]);
|
|
571
842
|
}
|
|
572
843
|
catch (error) {
|
|
573
844
|
if (BaseError.someErrorName(error, "NotFoundError")) {
|
|
@@ -577,218 +848,237 @@ class DocumentManagementService {
|
|
|
577
848
|
}
|
|
578
849
|
}
|
|
579
850
|
/**
|
|
580
|
-
*
|
|
581
|
-
*
|
|
582
|
-
* @param
|
|
583
|
-
* @param
|
|
584
|
-
* @param options
|
|
585
|
-
* @param options.
|
|
851
|
+
* Get a document revision using it's auditable item graph vertex id.
|
|
852
|
+
* @param auditableItemGraphDocumentId The auditable item graph vertex id which contains the document.
|
|
853
|
+
* @param revision The revision id for the document.
|
|
854
|
+
* @param options Additional options for the get operation.
|
|
855
|
+
* @param options.includeBlobStorageMetadata Flag to include the blob storage metadata for the document, defaults to false.
|
|
856
|
+
* @param options.includeBlobStorageData Flag to include the blob storage data for the document, defaults to false.
|
|
857
|
+
* @param options.includeAttestation Flag to include the attestation information for the document, defaults to false.
|
|
858
|
+
* @param options.extractRuleGroupId If provided will extract data from the document using the specified rule group id.
|
|
859
|
+
* @param options.extractMimeType By default extraction will auto detect the mime type of the document, this can be used to override the detection.
|
|
586
860
|
* @param userIdentity The identity to perform the auditable item graph operation with.
|
|
587
861
|
* @param nodeIdentity The node identity to use for vault operations.
|
|
588
|
-
* @returns
|
|
862
|
+
* @returns The documents and revisions if requested, ordered by revision descending, cursor is set if there are more document revisions.
|
|
589
863
|
*/
|
|
590
|
-
async
|
|
591
|
-
Urn.guard(this.CLASS_NAME, "
|
|
592
|
-
|
|
864
|
+
async getRevision(auditableItemGraphDocumentId, revision, options, userIdentity, nodeIdentity) {
|
|
865
|
+
Urn.guard(this.CLASS_NAME, "auditableItemGraphDocumentId", auditableItemGraphDocumentId);
|
|
866
|
+
Guards.integer(this.CLASS_NAME, "revision", revision);
|
|
593
867
|
try {
|
|
594
|
-
const
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
const vertexDocs = this.filterDocumentsFromVertex(vertex);
|
|
598
|
-
// Reduce the list to those with a matching id and code
|
|
599
|
-
const matchingDocIds = this.findMatchingDocs(vertexDocs, documentIdParts.documentId, documentIdParts.documentCode, false);
|
|
600
|
-
const removeAllRevisions = options?.removeAllRevisions ?? false;
|
|
601
|
-
const now = Date.now();
|
|
602
|
-
if (removeAllRevisions) {
|
|
603
|
-
for (const doc of matchingDocIds) {
|
|
604
|
-
doc.dateDeleted = new Date(now).toISOString();
|
|
605
|
-
}
|
|
868
|
+
const documentVertex = await this._auditableItemGraphComponent.get(auditableItemGraphDocumentId, { includeDeleted: true });
|
|
869
|
+
if (Is.empty(documentVertex.resources)) {
|
|
870
|
+
throw new NotFoundError(this.CLASS_NAME, "documentRevisionNone");
|
|
606
871
|
}
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
matchingRevision.dateDeleted = new Date(now).toISOString();
|
|
611
|
-
}
|
|
612
|
-
else {
|
|
613
|
-
throw new NotFoundError(this.CLASS_NAME, "documentRevisionNotFound", identifier);
|
|
614
|
-
}
|
|
872
|
+
documentVertex.resources = documentVertex.resources.filter(d => d.resourceObject?.documentRevision === revision);
|
|
873
|
+
if (documentVertex.resources.length === 0) {
|
|
874
|
+
throw new NotFoundError(this.CLASS_NAME, "documentRevisionNotFound", revision.toString());
|
|
615
875
|
}
|
|
616
|
-
|
|
876
|
+
// Populate the document and revisions with the options set
|
|
877
|
+
const docList = await this.getDocumentsFromVertex(documentVertex, options, undefined, undefined, userIdentity, nodeIdentity);
|
|
878
|
+
return JsonLdProcessor.compact(docList.itemListElement[0], docList.itemListElement[0]["@context"]);
|
|
617
879
|
}
|
|
618
880
|
catch (error) {
|
|
619
881
|
if (BaseError.someErrorName(error, "NotFoundError")) {
|
|
620
882
|
throw error;
|
|
621
883
|
}
|
|
622
|
-
throw new GeneralError(this.CLASS_NAME, "
|
|
884
|
+
throw new GeneralError(this.CLASS_NAME, "getRevisionFailed", undefined, error);
|
|
623
885
|
}
|
|
624
886
|
}
|
|
625
887
|
/**
|
|
626
|
-
*
|
|
627
|
-
*
|
|
628
|
-
* @param
|
|
629
|
-
* @param
|
|
630
|
-
* @param options.includeMostRecentRevisions Include the most recent 5 revisions, use the individual get to retrieve more.
|
|
631
|
-
* @param options.includeRemoved Flag to include deleted documents, defaults to false.
|
|
632
|
-
* @param cursor The cursor to get the next chunk of documents.
|
|
888
|
+
* Remove an auditable item graph vertex using it's id.
|
|
889
|
+
* The document dateDeleted will be set, but can still be queried with the includeRemoved flag.
|
|
890
|
+
* @param auditableItemGraphDocumentId The auditable item graph vertex id which contains the document.
|
|
891
|
+
* @param revision The revision of the document to remove.
|
|
633
892
|
* @param userIdentity The identity to perform the auditable item graph operation with.
|
|
634
893
|
* @param nodeIdentity The node identity to use for vault operations.
|
|
635
|
-
* @returns
|
|
894
|
+
* @returns Nothing.
|
|
636
895
|
*/
|
|
637
|
-
async
|
|
638
|
-
Urn.guard(this.CLASS_NAME, "
|
|
896
|
+
async removeRevision(auditableItemGraphDocumentId, revision, userIdentity, nodeIdentity) {
|
|
897
|
+
Urn.guard(this.CLASS_NAME, "auditableItemGraphDocumentId", auditableItemGraphDocumentId);
|
|
898
|
+
Guards.number(this.CLASS_NAME, "revision", revision);
|
|
639
899
|
try {
|
|
640
|
-
const
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
const documentIdGroups = {};
|
|
651
|
-
let docGroupIds = [];
|
|
652
|
-
for (const doc of matchingDocIds) {
|
|
653
|
-
const docId = `${doc.documentId}:${doc.documentCode}`;
|
|
654
|
-
if (!docGroupIds.includes(docId)) {
|
|
655
|
-
docGroupIds.push(docId);
|
|
656
|
-
}
|
|
657
|
-
documentIdGroups[docId] ??= [];
|
|
658
|
-
documentIdGroups[docId].push(doc);
|
|
659
|
-
}
|
|
660
|
-
let nextDocCursor;
|
|
661
|
-
if (docGroupIds.length > docCursor + DocumentManagementService._DEFAULT_PAGE_SIZE) {
|
|
662
|
-
nextDocCursor = (docCursor + DocumentManagementService._DEFAULT_PAGE_SIZE).toString();
|
|
663
|
-
}
|
|
664
|
-
docGroupIds = docGroupIds.slice(docCursor, docCursor + DocumentManagementService._DEFAULT_PAGE_SIZE);
|
|
665
|
-
const finalDocs = [];
|
|
666
|
-
for (const docId of docGroupIds) {
|
|
667
|
-
finalDocs.push(await this.getDocumentAndRevisions(documentIdGroups[docId], docId, {
|
|
668
|
-
includeAttestation: false,
|
|
669
|
-
includeBlobStorageData: false,
|
|
670
|
-
includeBlobStorageMetadata: false,
|
|
671
|
-
includeRemoved,
|
|
672
|
-
maxRevisionCount: includeMostRecentRevisions ? 5 : 0
|
|
673
|
-
}, 0, userIdentity, nodeIdentity));
|
|
674
|
-
}
|
|
675
|
-
const docList = {
|
|
676
|
-
"@context": [
|
|
677
|
-
DocumentTypes.ContextRoot,
|
|
678
|
-
DocumentTypes.ContextRootCommon,
|
|
679
|
-
SchemaOrgTypes.ContextRoot
|
|
680
|
-
],
|
|
681
|
-
type: DocumentTypes.DocumentList,
|
|
682
|
-
documents: finalDocs,
|
|
683
|
-
cursor: nextDocCursor
|
|
684
|
-
};
|
|
685
|
-
return JsonLdProcessor.compact(docList, docList["@context"]);
|
|
900
|
+
const documentVertex = await this._auditableItemGraphComponent.get(auditableItemGraphDocumentId);
|
|
901
|
+
if (Is.empty(documentVertex.resources)) {
|
|
902
|
+
throw new NotFoundError(this.CLASS_NAME, "documentRevisionNone");
|
|
903
|
+
}
|
|
904
|
+
const docRevisionIndex = documentVertex.resources.findIndex(d => d.resourceObject?.documentRevision === revision);
|
|
905
|
+
if (docRevisionIndex === -1) {
|
|
906
|
+
throw new NotFoundError(this.CLASS_NAME, "documentRevisionNotFound", revision.toString());
|
|
907
|
+
}
|
|
908
|
+
documentVertex.resources.splice(docRevisionIndex, 1);
|
|
909
|
+
await this._auditableItemGraphComponent.update(documentVertex, userIdentity, nodeIdentity);
|
|
686
910
|
}
|
|
687
911
|
catch (error) {
|
|
688
912
|
if (BaseError.someErrorName(error, "NotFoundError")) {
|
|
689
913
|
throw error;
|
|
690
914
|
}
|
|
691
|
-
throw new GeneralError(this.CLASS_NAME, "
|
|
915
|
+
throw new GeneralError(this.CLASS_NAME, "removeRevisionFailed", undefined, error);
|
|
692
916
|
}
|
|
693
917
|
}
|
|
694
918
|
/**
|
|
695
|
-
*
|
|
696
|
-
* @param documentId The document
|
|
697
|
-
* @
|
|
698
|
-
* @
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
}
|
|
703
|
-
/**
|
|
704
|
-
* Decode the document id.
|
|
705
|
-
* @param documentId The document identifier.
|
|
706
|
-
* @returns The decoded identifier.
|
|
707
|
-
* @internal
|
|
919
|
+
* Find all the document with a specific id.
|
|
920
|
+
* @param documentId The document id to find in the graph.
|
|
921
|
+
* @param cursor The cursor to get the next chunk of documents.
|
|
922
|
+
* @param pageSize The page size to get the next chunk of documents.
|
|
923
|
+
* @param userIdentity The identity to perform the auditable item graph operation with.
|
|
924
|
+
* @param nodeIdentity The node identity to use for vault operations.
|
|
925
|
+
* @returns The graph vertices that contain documents referencing the specified document id.
|
|
708
926
|
*/
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
927
|
+
async query(documentId, cursor, pageSize, userIdentity, nodeIdentity) {
|
|
928
|
+
Guards.stringValue(this.CLASS_NAME, "documentId", documentId);
|
|
929
|
+
try {
|
|
930
|
+
return this._auditableItemGraphComponent.query({
|
|
931
|
+
id: documentId,
|
|
932
|
+
idMode: "both",
|
|
933
|
+
resourceTypes: [DocumentTypes.Document]
|
|
934
|
+
}, undefined, undefined, undefined, ["id", "dateCreated", "dateModified", "aliases", "annotationObject", "resources", "edges"], cursor, pageSize);
|
|
935
|
+
}
|
|
936
|
+
catch (error) {
|
|
937
|
+
if (BaseError.someErrorName(error, "NotFoundError")) {
|
|
938
|
+
throw error;
|
|
939
|
+
}
|
|
940
|
+
throw new GeneralError(this.CLASS_NAME, "queryFailed", undefined, error);
|
|
716
941
|
}
|
|
717
|
-
return { documentId: parts.join(":"), documentRevision: revision };
|
|
718
|
-
}
|
|
719
|
-
/**
|
|
720
|
-
* Create a full identifier for a document.
|
|
721
|
-
* @param documentCode The document code.
|
|
722
|
-
* @param documentId The document identifier.
|
|
723
|
-
* @param documentRevision The document revision.
|
|
724
|
-
* @returns The full identifier.
|
|
725
|
-
* @internal
|
|
726
|
-
*/
|
|
727
|
-
createIdentifier(documentCode, documentId, documentRevision) {
|
|
728
|
-
const docCode = this.parseDocumentCode(documentCode);
|
|
729
|
-
return `documents:${docCode}:${this.encodeDocumentIdentifier(documentId, documentRevision)}`;
|
|
730
942
|
}
|
|
731
943
|
/**
|
|
732
|
-
*
|
|
733
|
-
* @param
|
|
734
|
-
* @
|
|
944
|
+
* Update the edges of the document vertex.
|
|
945
|
+
* @param documentVertex The document vertex to update.
|
|
946
|
+
* @param auditableItemGraphEdges The list of edges to use.
|
|
947
|
+
* @returns True if the edges were updated.
|
|
735
948
|
* @internal
|
|
736
949
|
*/
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
const
|
|
740
|
-
if (
|
|
741
|
-
|
|
950
|
+
updateEdges(documentVertex, auditableItemGraphEdges) {
|
|
951
|
+
let changed = false;
|
|
952
|
+
const existingEdgeIds = documentVertex.edges?.map(e => e.id) ?? [];
|
|
953
|
+
if (Is.array(auditableItemGraphEdges)) {
|
|
954
|
+
for (const aigEdge of auditableItemGraphEdges) {
|
|
955
|
+
const existingIndex = existingEdgeIds.indexOf(aigEdge.id);
|
|
956
|
+
if (existingIndex !== -1) {
|
|
957
|
+
// If the edge already exists then we don't need to add it again
|
|
958
|
+
// We just need to remove it from the list of existing ids
|
|
959
|
+
// any remaining after this loop will be need to be removed
|
|
960
|
+
existingEdgeIds.splice(existingIndex, 1);
|
|
961
|
+
}
|
|
962
|
+
else {
|
|
963
|
+
const vertexEdge = {
|
|
964
|
+
"@context": AuditableItemGraphContexts.ContextRoot,
|
|
965
|
+
type: AuditableItemGraphTypes.Edge,
|
|
966
|
+
id: aigEdge.id,
|
|
967
|
+
edgeRelationships: ["document"]
|
|
968
|
+
};
|
|
969
|
+
documentVertex.edges ??= [];
|
|
970
|
+
documentVertex.edges?.push(vertexEdge);
|
|
971
|
+
changed = true;
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
// Anything left in the existingEdgeIds array means they need to be removed
|
|
975
|
+
if (existingEdgeIds.length > 0 && Is.array(documentVertex.edges)) {
|
|
976
|
+
for (const existingEdgeId of existingEdgeIds) {
|
|
977
|
+
const existingIndex = documentVertex.edges.findIndex(e => e.id === existingEdgeId);
|
|
978
|
+
if (existingIndex !== -1) {
|
|
979
|
+
documentVertex.edges.splice(existingIndex, 1);
|
|
980
|
+
changed = true;
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
}
|
|
742
984
|
}
|
|
743
|
-
|
|
744
|
-
const { documentId, documentRevision } = this.decodeDocumentIdentifier(urn.namespaceSpecific(1));
|
|
745
|
-
return { documentCode, documentId, documentRevision };
|
|
985
|
+
return changed;
|
|
746
986
|
}
|
|
747
987
|
/**
|
|
748
|
-
*
|
|
749
|
-
* @param
|
|
750
|
-
* @
|
|
988
|
+
* Update the edges.
|
|
989
|
+
* @param connectedVertices The connected vertices for the edges.
|
|
990
|
+
* @param auditableItemGraphDocumentId The document id to use.
|
|
991
|
+
* @param documentVertex The document vertex to update.
|
|
992
|
+
* @param auditableItemGraphEdges The list of edges to use.
|
|
993
|
+
* @param documentId The document identifier.
|
|
994
|
+
* @param documentIdFormat The format of the document identifier.
|
|
995
|
+
* @param userIdentity The identity to perform the auditable item graph operation with.
|
|
996
|
+
* @param nodeIdentity The node identity to use for vault operations.
|
|
751
997
|
* @internal
|
|
752
998
|
*/
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
999
|
+
async updateConnectedEdges(connectedVertices, auditableItemGraphDocumentId, existingEdgeIds, auditableItemGraphEdges, documentId, documentIdFormat, userIdentity, nodeIdentity) {
|
|
1000
|
+
if (Is.array(auditableItemGraphEdges)) {
|
|
1001
|
+
for (const aigEdge of auditableItemGraphEdges) {
|
|
1002
|
+
const connected = connectedVertices[aigEdge.id];
|
|
1003
|
+
if (!Is.empty(connected)) {
|
|
1004
|
+
let updatedConnected = false;
|
|
1005
|
+
const existingIndex = existingEdgeIds.indexOf(aigEdge.id);
|
|
1006
|
+
if (existingIndex !== -1) {
|
|
1007
|
+
// If the edge already exists we remove it from the list of existing ids
|
|
1008
|
+
// any remaining after this loop will be need to be disconnected
|
|
1009
|
+
existingEdgeIds.splice(existingIndex, 1);
|
|
1010
|
+
}
|
|
1011
|
+
// Add the edge with the document vertex id if it doesn't already exist
|
|
1012
|
+
const hasEdge = connected.edges?.some(e => e.id === auditableItemGraphDocumentId);
|
|
1013
|
+
if (!hasEdge) {
|
|
1014
|
+
const vertexEdge = {
|
|
1015
|
+
"@context": AuditableItemGraphContexts.ContextRoot,
|
|
1016
|
+
type: AuditableItemGraphTypes.Edge,
|
|
1017
|
+
id: auditableItemGraphDocumentId,
|
|
1018
|
+
edgeRelationships: ["document"]
|
|
1019
|
+
};
|
|
1020
|
+
connected.edges ??= [];
|
|
1021
|
+
connected.edges?.push(vertexEdge);
|
|
1022
|
+
updatedConnected = true;
|
|
1023
|
+
}
|
|
1024
|
+
// Add alias with the document id if option flag is set and it doesn't already exist
|
|
1025
|
+
if (aigEdge.addAlias) {
|
|
1026
|
+
const alias = connected.aliases?.find(a => a.id === documentId);
|
|
1027
|
+
if (Is.empty(alias)) {
|
|
1028
|
+
// No existing alias, so create one
|
|
1029
|
+
const vertexAlias = {
|
|
1030
|
+
"@context": AuditableItemGraphContexts.ContextRoot,
|
|
1031
|
+
type: AuditableItemGraphTypes.Alias,
|
|
1032
|
+
id: documentId,
|
|
1033
|
+
aliasFormat: documentIdFormat,
|
|
1034
|
+
annotationObject: aigEdge.aliasAnnotationObject
|
|
1035
|
+
};
|
|
1036
|
+
connected.aliases ??= [];
|
|
1037
|
+
connected.aliases?.push(vertexAlias);
|
|
1038
|
+
updatedConnected = true;
|
|
1039
|
+
}
|
|
1040
|
+
else if (!ObjectHelper.equal(alias.annotationObject, aigEdge.aliasAnnotationObject) ||
|
|
1041
|
+
documentIdFormat !== alias.aliasFormat) {
|
|
1042
|
+
// The alias already exists, but the format or annotation object has changed
|
|
1043
|
+
alias.annotationObject = aigEdge.aliasAnnotationObject;
|
|
1044
|
+
alias.aliasFormat = documentIdFormat;
|
|
1045
|
+
updatedConnected = true;
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
if (updatedConnected) {
|
|
1049
|
+
await this._auditableItemGraphComponent.update(connected, userIdentity, nodeIdentity);
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
758
1053
|
}
|
|
759
|
-
|
|
760
|
-
if (
|
|
761
|
-
|
|
1054
|
+
// Anything left in the existingEdgeIds array means they need to be removed
|
|
1055
|
+
if (existingEdgeIds.length > 0) {
|
|
1056
|
+
for (const existingEdgeId of existingEdgeIds) {
|
|
1057
|
+
const connected = connectedVertices[existingEdgeId];
|
|
1058
|
+
if (!Is.empty(connected)) {
|
|
1059
|
+
let updatedConnected = false;
|
|
1060
|
+
// Remove the edge from the connected vertex
|
|
1061
|
+
if (Is.arrayValue(connected.edges)) {
|
|
1062
|
+
const existingIndex = connected.edges.findIndex(e => e.id === auditableItemGraphDocumentId);
|
|
1063
|
+
if (existingIndex !== -1) {
|
|
1064
|
+
connected.edges.splice(existingIndex, 1);
|
|
1065
|
+
updatedConnected = true;
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
// Remove the alias from the connected vertex
|
|
1069
|
+
if (Is.arrayValue(connected.aliases)) {
|
|
1070
|
+
const existingIndex = connected.aliases.findIndex(e => e.id === documentId);
|
|
1071
|
+
if (existingIndex !== -1) {
|
|
1072
|
+
connected.aliases.splice(existingIndex, 1);
|
|
1073
|
+
updatedConnected = true;
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
if (updatedConnected) {
|
|
1077
|
+
await this._auditableItemGraphComponent.update(connected, userIdentity, nodeIdentity);
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
762
1081
|
}
|
|
763
|
-
return docCode;
|
|
764
|
-
}
|
|
765
|
-
/**
|
|
766
|
-
* Get the documents from a vertex.
|
|
767
|
-
* @param vertex The vertex to get the documents from.
|
|
768
|
-
* @returns The documents.
|
|
769
|
-
* @internal
|
|
770
|
-
*/
|
|
771
|
-
filterDocumentsFromVertex(vertex) {
|
|
772
|
-
return (vertex.resources
|
|
773
|
-
?.filter(resource => ObjectHelper.extractProperty(resource.resourceObject, ["@type", "type"], false) ===
|
|
774
|
-
DocumentTypes.Document)
|
|
775
|
-
.map(resource => resource.resourceObject) ?? []);
|
|
776
|
-
}
|
|
777
|
-
/**
|
|
778
|
-
* Find matching documents in the list of existing documents.
|
|
779
|
-
* @param documents The documents to search.
|
|
780
|
-
* @param documentId The document id.
|
|
781
|
-
* @param documentCode The document code.
|
|
782
|
-
* @param includeRemoved Include deleted documents.
|
|
783
|
-
* @returns The matching documents.
|
|
784
|
-
* @internal
|
|
785
|
-
*/
|
|
786
|
-
findMatchingDocs(documents, documentId, documentCode, includeRemoved) {
|
|
787
|
-
return documents
|
|
788
|
-
.filter(d => d.documentId === documentId &&
|
|
789
|
-
d.documentCode === documentCode &&
|
|
790
|
-
(includeRemoved || Is.empty(d.dateDeleted)))
|
|
791
|
-
.sort((a, b) => b.documentRevision - a.documentRevision);
|
|
792
1082
|
}
|
|
793
1083
|
/**
|
|
794
1084
|
* Generate a hash for the blob data.
|
|
@@ -801,57 +1091,136 @@ class DocumentManagementService {
|
|
|
801
1091
|
}
|
|
802
1092
|
/**
|
|
803
1093
|
* Get the documents from the auditable item graph vertex.
|
|
804
|
-
* @param
|
|
805
|
-
* @param identifier The full document identifier.
|
|
1094
|
+
* @param documentVertex The vertex containing the documents.
|
|
806
1095
|
* @param options Additional options for the get operation.
|
|
807
1096
|
* @param options.includeBlobStorageMetadata Flag to include the blob storage metadata for the document, defaults to false.
|
|
808
1097
|
* @param options.includeBlobStorageData Flag to include the blob storage data for the document, defaults to false.
|
|
809
1098
|
* @param options.includeAttestation Flag to include the attestation information for the document, defaults to false.
|
|
810
|
-
* @param options.
|
|
811
|
-
* @param options.
|
|
812
|
-
* @param
|
|
1099
|
+
* @param options.extractRuleGroupId If provided will extract data from the document using the specified rule group id.
|
|
1100
|
+
* @param options.extractMimeType By default extraction will auto detect the mime type of the document, this can be used to override the detection.
|
|
1101
|
+
* @param cursor The cursor to get the next chunk of revisions.
|
|
1102
|
+
* @param pageSize Page size of items to return, defaults to 1 so only most recent is returned.
|
|
813
1103
|
* @param userIdentity The identity to perform the auditable item graph operation with.
|
|
814
1104
|
* @param nodeIdentity The node identity to use for vault operations.
|
|
815
1105
|
* @returns The finalised list of documents.
|
|
816
1106
|
* @internal
|
|
817
1107
|
*/
|
|
818
|
-
async
|
|
819
|
-
const
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
const
|
|
834
|
-
|
|
835
|
-
|
|
1108
|
+
async getDocumentsFromVertex(documentVertex, options, cursor, pageSize, userIdentity, nodeIdentity) {
|
|
1109
|
+
const docList = {
|
|
1110
|
+
"@context": [
|
|
1111
|
+
SchemaOrgContexts.ContextRoot,
|
|
1112
|
+
DocumentContexts.ContextRoot,
|
|
1113
|
+
DocumentContexts.ContextRootCommon
|
|
1114
|
+
],
|
|
1115
|
+
type: SchemaOrgTypes.ItemList,
|
|
1116
|
+
[SchemaOrgTypes.ItemListElement]: []
|
|
1117
|
+
};
|
|
1118
|
+
if (Is.arrayValue(documentVertex.resources)) {
|
|
1119
|
+
// Sort by newest revision first
|
|
1120
|
+
documentVertex.resources.sort((a, b) => (Coerce.number(b.resourceObject?.documentRevision) ?? 0) -
|
|
1121
|
+
(Coerce.number(a.resourceObject?.documentRevision) ?? 0));
|
|
1122
|
+
const startIndex = Coerce.integer(cursor) ?? 0;
|
|
1123
|
+
const endIndex = Math.min(startIndex + (pageSize ?? 1), documentVertex.resources.length);
|
|
1124
|
+
const slicedResources = documentVertex.resources.slice(startIndex, endIndex);
|
|
1125
|
+
docList[SchemaOrgTypes.NextItem] =
|
|
1126
|
+
documentVertex.resources.length > endIndex ? (endIndex + 1).toString() : undefined;
|
|
1127
|
+
const includeBlobStorageMetadata = options?.includeBlobStorageMetadata ?? false;
|
|
1128
|
+
const includeBlobStorageData = options?.includeBlobStorageData ?? false;
|
|
1129
|
+
const includeAttestation = options?.includeAttestation ?? false;
|
|
1130
|
+
const extractData = Is.stringValue(options?.extractRuleGroupId);
|
|
1131
|
+
for (let i = 0; i < slicedResources.length; i++) {
|
|
1132
|
+
const document = slicedResources[i].resourceObject;
|
|
1133
|
+
if (Is.object(document)) {
|
|
1134
|
+
document.dateDeleted = slicedResources[i].dateDeleted;
|
|
1135
|
+
docList[SchemaOrgTypes.ItemListElement].push(document);
|
|
1136
|
+
const blobRequired = includeBlobStorageMetadata || includeBlobStorageData;
|
|
1137
|
+
if (blobRequired || extractData) {
|
|
1138
|
+
const blobEntry = await this._blobStorageComponent.get(document.blobStorageId, {
|
|
1139
|
+
includeContent: includeBlobStorageData || extractData
|
|
1140
|
+
}, userIdentity, nodeIdentity);
|
|
1141
|
+
if (blobRequired) {
|
|
1142
|
+
document.blobStorageEntry = blobEntry;
|
|
1143
|
+
if (!docList["@context"].includes(BlobStorageContexts.ContextRoot)) {
|
|
1144
|
+
docList["@context"].push(BlobStorageContexts.ContextRoot);
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
if (Is.stringValue(options?.extractRuleGroupId) && Is.stringValue(blobEntry.blob)) {
|
|
1148
|
+
const binaryBlob = Converter.base64ToBytes(blobEntry.blob);
|
|
1149
|
+
document.extractedData = await this._dataProcessingComponent.extract(options.extractRuleGroupId, binaryBlob, undefined, options?.extractMimeType);
|
|
1150
|
+
}
|
|
1151
|
+
// If we have the blob data due to extraction but we weren't asked for it
|
|
1152
|
+
// then we remove it from the document
|
|
1153
|
+
if (!blobRequired) {
|
|
1154
|
+
delete document.blobStorageEntry;
|
|
1155
|
+
}
|
|
1156
|
+
else if (!includeBlobStorageData) {
|
|
1157
|
+
delete document.blobStorageEntry?.blob;
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
if (includeAttestation && Is.stringValue(document.attestationId)) {
|
|
1161
|
+
const attestationInformation = await this._attestationComponent.get(document.attestationId);
|
|
1162
|
+
document.attestationInformation = attestationInformation;
|
|
1163
|
+
if (!docList["@context"].includes(AttestationContexts.ContextRoot)) {
|
|
1164
|
+
docList["@context"].push(AttestationContexts.ContextRoot);
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
836
1169
|
}
|
|
837
|
-
if (
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
1170
|
+
if (Is.arrayValue(documentVertex.edges)) {
|
|
1171
|
+
docList.edges ??= [];
|
|
1172
|
+
for (const edge of documentVertex.edges) {
|
|
1173
|
+
if (Is.object(edge)) {
|
|
1174
|
+
docList.edges.push(edge.id);
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
841
1177
|
}
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
1178
|
+
return docList;
|
|
1179
|
+
}
|
|
1180
|
+
/**
|
|
1181
|
+
* Create an attestation for the document.
|
|
1182
|
+
* @param document The document to create the attestation for.
|
|
1183
|
+
* @param userIdentity The identity to perform the attestation operation with.
|
|
1184
|
+
* @param nodeIdentity The node identity to perform attestation operation with.
|
|
1185
|
+
* @returns The attestation identifier.
|
|
1186
|
+
*/
|
|
1187
|
+
async createAttestation(document, userIdentity, nodeIdentity) {
|
|
1188
|
+
const documentAttestation = {
|
|
1189
|
+
"@context": [
|
|
1190
|
+
DocumentContexts.ContextRoot,
|
|
1191
|
+
DocumentContexts.ContextRootCommon,
|
|
1192
|
+
SchemaOrgContexts.ContextRoot
|
|
1193
|
+
],
|
|
1194
|
+
type: DocumentTypes.DocumentAttestation,
|
|
1195
|
+
id: document.id,
|
|
1196
|
+
documentId: document.documentId,
|
|
1197
|
+
documentCode: document.documentCode,
|
|
1198
|
+
documentRevision: document.documentRevision,
|
|
1199
|
+
dateCreated: document.dateCreated,
|
|
1200
|
+
blobHash: document.blobHash
|
|
1201
|
+
};
|
|
1202
|
+
return this._attestationComponent.create(documentAttestation, undefined, userIdentity, nodeIdentity);
|
|
1203
|
+
}
|
|
1204
|
+
/**
|
|
1205
|
+
* Create a document id from the document id and revision.
|
|
1206
|
+
* @param documentId The document id to create.
|
|
1207
|
+
* @param revision The revision of the document.
|
|
1208
|
+
* @returns The document id.
|
|
1209
|
+
* @internal
|
|
1210
|
+
*/
|
|
1211
|
+
createDocumentId(documentId, revision) {
|
|
1212
|
+
const documentIdHash = Converter.bytesToBase64Url(Sha256.sum256(Converter.utf8ToBytes(documentId)));
|
|
1213
|
+
return `document:${documentIdHash}:${revision}`;
|
|
845
1214
|
}
|
|
846
1215
|
}
|
|
847
1216
|
|
|
848
1217
|
const restEntryPoints = [
|
|
849
1218
|
{
|
|
850
|
-
name: "
|
|
851
|
-
defaultBaseRoute: "
|
|
1219
|
+
name: "document-management",
|
|
1220
|
+
defaultBaseRoute: "document-management",
|
|
852
1221
|
tags: tagsDocumentManagement,
|
|
853
1222
|
generateRoutes: generateRestRoutesDocumentManagement
|
|
854
1223
|
}
|
|
855
1224
|
];
|
|
856
1225
|
|
|
857
|
-
export { DocumentManagementService, documentManagementGet, documentManagementQuery, documentManagementRemove,
|
|
1226
|
+
export { DocumentManagementService, documentManagementCreate, documentManagementGet, documentManagementGetRevision, documentManagementQuery, documentManagementRemove, documentManagementUpdate, generateRestRoutesDocumentManagement, restEntryPoints, tagsDocumentManagement };
|