@twin.org/document-management-service 0.0.1-next.8 → 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/index.cjs +728 -428
- package/dist/esm/index.mjs +728 -430
- package/dist/types/documentManagementRoutes.d.ts +20 -4
- package/dist/types/documentManagementService.d.ts +71 -36
- package/dist/types/models/IDocumentManagementStorageServiceConstructorOptions.d.ts +5 -0
- package/docs/changelog.md +195 -0
- package/docs/open-api/spec.json +609 -1470
- package/docs/reference/classes/DocumentManagementService.md +179 -63
- 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 +73 -15
package/dist/esm/index.mjs
CHANGED
|
@@ -1,17 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Guards, ComponentFactory, Converter, Coerce, Is,
|
|
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
3
|
import { DocumentTypes, DocumentContexts } from '@twin.org/document-management-models';
|
|
4
|
-
import { SchemaOrgContexts, SchemaOrgDataTypes } from '@twin.org/standards-schema-org';
|
|
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
7
|
import { AttestationContexts } from '@twin.org/attestation-models';
|
|
8
|
-
import { AuditableItemGraphTypes, AuditableItemGraphContexts } from '@twin.org/auditable-item-graph-models';
|
|
9
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,14 +63,56 @@ 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"
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
]
|
|
75
|
+
}
|
|
76
|
+
]
|
|
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"
|
|
77
100
|
}
|
|
78
101
|
}
|
|
79
102
|
}
|
|
103
|
+
}
|
|
104
|
+
]
|
|
105
|
+
},
|
|
106
|
+
responseType: [
|
|
107
|
+
{
|
|
108
|
+
type: "INoContentResponse",
|
|
109
|
+
examples: [
|
|
110
|
+
{
|
|
111
|
+
id: "DocumentManagementCreateResponseExample",
|
|
112
|
+
response: {
|
|
113
|
+
statusCode: HttpStatusCode.noContent
|
|
114
|
+
}
|
|
115
|
+
}
|
|
80
116
|
]
|
|
81
117
|
}
|
|
82
118
|
]
|
|
@@ -86,7 +122,7 @@ function generateRestRoutesDocumentManagement(baseRouteName, componentName) {
|
|
|
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
|
}
|
|
@@ -108,6 +143,120 @@ function generateRestRoutesDocumentManagement(baseRouteName, componentName) {
|
|
|
108
143
|
examples: [
|
|
109
144
|
{
|
|
110
145
|
id: "DocumentManagementGetResponseExample",
|
|
146
|
+
response: {
|
|
147
|
+
body: {
|
|
148
|
+
"@context": [
|
|
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",
|
|
111
260
|
response: {
|
|
112
261
|
body: {
|
|
113
262
|
"@context": [
|
|
@@ -116,11 +265,11 @@ function generateRestRoutesDocumentManagement(baseRouteName, componentName) {
|
|
|
116
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",
|
|
@@ -137,11 +286,11 @@ function generateRestRoutesDocumentManagement(baseRouteName, componentName) {
|
|
|
137
286
|
]
|
|
138
287
|
},
|
|
139
288
|
{
|
|
140
|
-
type: "
|
|
289
|
+
type: "IDocumentManagementGetRevisionResponse",
|
|
141
290
|
mimeType: MimeTypes.JsonLd,
|
|
142
291
|
examples: [
|
|
143
292
|
{
|
|
144
|
-
id: "
|
|
293
|
+
id: "DocumentManagementGetRevisionResponseExample",
|
|
145
294
|
response: {
|
|
146
295
|
body: {
|
|
147
296
|
"@context": [
|
|
@@ -150,11 +299,11 @@ function generateRestRoutesDocumentManagement(baseRouteName, componentName) {
|
|
|
150
299
|
SchemaOrgContexts.ContextRoot
|
|
151
300
|
],
|
|
152
301
|
type: DocumentTypes.Document,
|
|
153
|
-
id: "
|
|
302
|
+
id: "2721000:0",
|
|
154
303
|
documentId: "2721000",
|
|
155
304
|
documentIdFormat: "bol",
|
|
156
305
|
documentCode: UneceDocumentCodes.BillOfLading,
|
|
157
|
-
documentRevision:
|
|
306
|
+
documentRevision: 1,
|
|
158
307
|
blobStorageId: "blob-memory:c57d94b088f4c6d2cb32ded014813d0c786aa00134c8ee22f84b1e2545602a70",
|
|
159
308
|
blobHash: "sha256:123456",
|
|
160
309
|
dateCreated: "2024-01-01T00:00:00Z",
|
|
@@ -175,12 +324,12 @@ function generateRestRoutesDocumentManagement(baseRouteName, componentName) {
|
|
|
175
324
|
}
|
|
176
325
|
]
|
|
177
326
|
};
|
|
178
|
-
const
|
|
327
|
+
const documentManagementRemoveRevisionRoute = {
|
|
179
328
|
operationId: "DocumentManagementRemove",
|
|
180
329
|
summary: "Remove an document from an auditable item graph vertex",
|
|
181
330
|
tag: tagsDocumentManagement[0].name,
|
|
182
331
|
method: "DELETE",
|
|
183
|
-
path: `${baseRouteName}/:
|
|
332
|
+
path: `${baseRouteName}/:auditableItemGraphDocumentId/:revision`,
|
|
184
333
|
handler: async (httpRequestContext, request) => documentManagementRemove(httpRequestContext, componentName, request),
|
|
185
334
|
requestType: {
|
|
186
335
|
type: "IDocumentManagementRemoveRequest",
|
|
@@ -189,8 +338,8 @@ function generateRestRoutesDocumentManagement(baseRouteName, componentName) {
|
|
|
189
338
|
id: "DocumentManagementRemoveRequestExample",
|
|
190
339
|
request: {
|
|
191
340
|
pathParams: {
|
|
192
|
-
|
|
193
|
-
|
|
341
|
+
auditableItemGraphDocumentId: "aig:1234",
|
|
342
|
+
revision: "1"
|
|
194
343
|
}
|
|
195
344
|
}
|
|
196
345
|
}
|
|
@@ -210,7 +359,7 @@ function generateRestRoutesDocumentManagement(baseRouteName, componentName) {
|
|
|
210
359
|
summary: "Query the items from an auditable item graph vertex",
|
|
211
360
|
tag: tagsDocumentManagement[0].name,
|
|
212
361
|
method: "GET",
|
|
213
|
-
path: `${baseRouteName}
|
|
362
|
+
path: `${baseRouteName}/`,
|
|
214
363
|
handler: async (httpRequestContext, request) => documentManagementQuery(httpRequestContext, componentName, request),
|
|
215
364
|
requestType: {
|
|
216
365
|
type: "IDocumentManagementQueryRequest",
|
|
@@ -218,8 +367,8 @@ function generateRestRoutesDocumentManagement(baseRouteName, componentName) {
|
|
|
218
367
|
{
|
|
219
368
|
id: "DocumentManagementQueryRequestExample",
|
|
220
369
|
request: {
|
|
221
|
-
|
|
222
|
-
|
|
370
|
+
query: {
|
|
371
|
+
documentId: "2721000"
|
|
223
372
|
}
|
|
224
373
|
}
|
|
225
374
|
}
|
|
@@ -233,71 +382,54 @@ function generateRestRoutesDocumentManagement(baseRouteName, componentName) {
|
|
|
233
382
|
id: "DocumentManagementQueryResponseExample",
|
|
234
383
|
response: {
|
|
235
384
|
body: {
|
|
236
|
-
"@context": [
|
|
237
|
-
type:
|
|
238
|
-
|
|
385
|
+
"@context": [SchemaOrgContexts.ContextRoot, AuditableItemGraphContexts.ContextRoot],
|
|
386
|
+
type: [SchemaOrgTypes.ItemList, AuditableItemGraphTypes.VertexList],
|
|
387
|
+
[SchemaOrgTypes.ItemListElement]: [
|
|
239
388
|
{
|
|
240
389
|
"@context": [
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
SchemaOrgContexts.ContextRoot
|
|
390
|
+
AuditableItemGraphContexts.ContextRoot,
|
|
391
|
+
AuditableItemGraphContexts.ContextRootCommon
|
|
244
392
|
],
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
"@context": "https://schema.org",
|
|
256
|
-
"@type": "DigitalDocument",
|
|
257
|
-
name: "myfile.pdf"
|
|
258
|
-
},
|
|
259
|
-
nodeIdentity: "did:entity-storage:0x6363636363636363636363636363636363636363636363636363636363636363",
|
|
260
|
-
userIdentity: "did:entity-storage:0x6363636363636363636363636363636363636363636363636363636363636363"
|
|
261
|
-
}
|
|
262
|
-
]
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
]
|
|
267
|
-
},
|
|
268
|
-
{
|
|
269
|
-
type: "IDocumentManagementQueryResponse",
|
|
270
|
-
mimeType: MimeTypes.JsonLd,
|
|
271
|
-
examples: [
|
|
272
|
-
{
|
|
273
|
-
id: "DocumentManagementListResponseJsonLdExample",
|
|
274
|
-
response: {
|
|
275
|
-
body: {
|
|
276
|
-
"@context": [DocumentContexts.ContextRoot, DocumentContexts.ContextRootCommon],
|
|
277
|
-
type: DocumentTypes.DocumentList,
|
|
278
|
-
documents: [
|
|
279
|
-
{
|
|
280
|
-
"@context": [
|
|
281
|
-
DocumentContexts.ContextRoot,
|
|
282
|
-
DocumentContexts.ContextRootCommon,
|
|
283
|
-
SchemaOrgContexts.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
|
+
}
|
|
284
403
|
],
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
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
|
+
]
|
|
301
433
|
}
|
|
302
434
|
]
|
|
303
435
|
}
|
|
@@ -308,29 +440,29 @@ function generateRestRoutesDocumentManagement(baseRouteName, componentName) {
|
|
|
308
440
|
]
|
|
309
441
|
};
|
|
310
442
|
return [
|
|
311
|
-
|
|
443
|
+
documentManagementCreateRoute,
|
|
444
|
+
documentManagementUpdateRoute,
|
|
312
445
|
documentManagementGetRoute,
|
|
313
|
-
|
|
446
|
+
documentManagementGetRevisionRoute,
|
|
447
|
+
documentManagementRemoveRevisionRoute,
|
|
314
448
|
documentManagementQueryRoute
|
|
315
449
|
];
|
|
316
450
|
}
|
|
317
451
|
/**
|
|
318
|
-
*
|
|
452
|
+
* Create a document as an auditable item graph vertex.
|
|
319
453
|
* @param httpRequestContext The request context for the API.
|
|
320
454
|
* @param componentName The name of the component to use in the routes.
|
|
321
455
|
* @param request The request.
|
|
322
456
|
* @returns The response object with additional http response properties.
|
|
323
457
|
*/
|
|
324
|
-
async function
|
|
458
|
+
async function documentManagementCreate(httpRequestContext, componentName, request) {
|
|
325
459
|
Guards.object(ROUTES_SOURCE, "request", request);
|
|
326
|
-
Guards.object(ROUTES_SOURCE, "request.pathParams", request.pathParams);
|
|
327
|
-
Guards.stringValue(ROUTES_SOURCE, "request.pathParams.auditableItemGraphId", request.pathParams.auditableItemGraphId);
|
|
328
460
|
Guards.object(ROUTES_SOURCE, "request.body", request.body);
|
|
329
461
|
Guards.stringBase64(ROUTES_SOURCE, "request.body.blob", request.body.blob);
|
|
330
462
|
const component = ComponentFactory.get(componentName);
|
|
331
|
-
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, {
|
|
332
464
|
createAttestation: request.body.createAttestation,
|
|
333
|
-
|
|
465
|
+
addAlias: request.body.addAlias,
|
|
334
466
|
aliasAnnotationObject: request.body.aliasAnnotationObject
|
|
335
467
|
}, httpRequestContext.userIdentity, httpRequestContext.nodeIdentity);
|
|
336
468
|
return {
|
|
@@ -350,17 +482,17 @@ async function documentManagementSet(httpRequestContext, componentName, request)
|
|
|
350
482
|
async function documentManagementGet(httpRequestContext, componentName, request) {
|
|
351
483
|
Guards.object(ROUTES_SOURCE, "request", request);
|
|
352
484
|
Guards.object(ROUTES_SOURCE, "request.pathParams", request.pathParams);
|
|
353
|
-
Guards.stringValue(ROUTES_SOURCE, "request.pathParams.
|
|
354
|
-
Guards.stringValue(ROUTES_SOURCE, "request.pathParams.documentId", request.pathParams.documentId);
|
|
485
|
+
Guards.stringValue(ROUTES_SOURCE, "request.pathParams.auditableItemGraphDocumentId", request.pathParams.auditableItemGraphDocumentId);
|
|
355
486
|
const mimeType = request.headers?.[HeaderTypes.Accept] === MimeTypes.JsonLd ? "jsonld" : "json";
|
|
356
487
|
const component = ComponentFactory.get(componentName);
|
|
357
|
-
const result = await component.get(request.pathParams.
|
|
488
|
+
const result = await component.get(request.pathParams.auditableItemGraphDocumentId, {
|
|
358
489
|
includeBlobStorageMetadata: Coerce.boolean(request.query?.includeBlobStorageMetadata),
|
|
359
490
|
includeBlobStorageData: Coerce.boolean(request.query?.includeBlobStorageData),
|
|
360
491
|
includeAttestation: Coerce.boolean(request.query?.includeAttestation),
|
|
361
492
|
includeRemoved: Coerce.boolean(request.query?.includeRemoved),
|
|
362
|
-
|
|
363
|
-
|
|
493
|
+
extractRuleGroupId: request.query?.extractRuleGroupId,
|
|
494
|
+
extractMimeType: request.query?.extractMimeType
|
|
495
|
+
}, request.query?.cursor, Coerce.integer(request.query?.pageSize), httpRequestContext.userIdentity, httpRequestContext.nodeIdentity);
|
|
364
496
|
return {
|
|
365
497
|
headers: {
|
|
366
498
|
[HeaderTypes.ContentType]: mimeType === "json" ? MimeTypes.Json : MimeTypes.JsonLd
|
|
@@ -368,6 +500,52 @@ async function documentManagementGet(httpRequestContext, componentName, request)
|
|
|
368
500
|
body: result
|
|
369
501
|
};
|
|
370
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);
|
|
525
|
+
return {
|
|
526
|
+
headers: {
|
|
527
|
+
[HeaderTypes.ContentType]: mimeType === "json" ? MimeTypes.Json : MimeTypes.JsonLd
|
|
528
|
+
},
|
|
529
|
+
body: result
|
|
530
|
+
};
|
|
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
|
+
}
|
|
371
549
|
/**
|
|
372
550
|
* Remove the document from the auditable item graph vertex.
|
|
373
551
|
* @param httpRequestContext The request context for the API.
|
|
@@ -378,12 +556,11 @@ async function documentManagementGet(httpRequestContext, componentName, request)
|
|
|
378
556
|
async function documentManagementRemove(httpRequestContext, componentName, request) {
|
|
379
557
|
Guards.object(ROUTES_SOURCE, "request", request);
|
|
380
558
|
Guards.object(ROUTES_SOURCE, "request.pathParams", request.pathParams);
|
|
381
|
-
Guards.stringValue(ROUTES_SOURCE, "request.pathParams.
|
|
382
|
-
|
|
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);
|
|
383
562
|
const component = ComponentFactory.get(componentName);
|
|
384
|
-
await component.
|
|
385
|
-
removeAllRevisions: request.query?.removeAllRevisions
|
|
386
|
-
}, httpRequestContext.userIdentity, httpRequestContext.nodeIdentity);
|
|
563
|
+
await component.removeRevision(request.pathParams.auditableItemGraphDocumentId, revision, httpRequestContext.userIdentity, httpRequestContext.nodeIdentity);
|
|
387
564
|
return {
|
|
388
565
|
statusCode: HttpStatusCode.noContent
|
|
389
566
|
};
|
|
@@ -397,14 +574,11 @@ async function documentManagementRemove(httpRequestContext, componentName, reque
|
|
|
397
574
|
*/
|
|
398
575
|
async function documentManagementQuery(httpRequestContext, componentName, request) {
|
|
399
576
|
Guards.object(ROUTES_SOURCE, "request", request);
|
|
400
|
-
Guards.object(ROUTES_SOURCE, "request.
|
|
401
|
-
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);
|
|
402
579
|
const mimeType = request.headers?.[HeaderTypes.Accept] === MimeTypes.JsonLd ? "jsonld" : "json";
|
|
403
580
|
const component = ComponentFactory.get(componentName);
|
|
404
|
-
const result = await component.query(request.
|
|
405
|
-
includeRemoved: Coerce.boolean(request.query?.includeRemoved),
|
|
406
|
-
includeMostRecentRevisions: Coerce.boolean(request.query?.includeMostRecentRevisions)
|
|
407
|
-
}, 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);
|
|
408
582
|
return {
|
|
409
583
|
headers: {
|
|
410
584
|
[HeaderTypes.ContentType]: mimeType === "json" ? MimeTypes.Json : MimeTypes.JsonLd
|
|
@@ -421,11 +595,6 @@ class DocumentManagementService {
|
|
|
421
595
|
* The namespace supported by the document management service.
|
|
422
596
|
*/
|
|
423
597
|
static NAMESPACE = "documents";
|
|
424
|
-
/**
|
|
425
|
-
* Default Page Size for cursor.
|
|
426
|
-
* @internal
|
|
427
|
-
*/
|
|
428
|
-
static _DEFAULT_PAGE_SIZE = 20;
|
|
429
598
|
/**
|
|
430
599
|
* Runtime name for the class.
|
|
431
600
|
*/
|
|
@@ -445,6 +614,11 @@ class DocumentManagementService {
|
|
|
445
614
|
* @internal
|
|
446
615
|
*/
|
|
447
616
|
_attestationComponent;
|
|
617
|
+
/**
|
|
618
|
+
* The connector for the data processing.
|
|
619
|
+
* @internal
|
|
620
|
+
*/
|
|
621
|
+
_dataProcessingComponent;
|
|
448
622
|
/**
|
|
449
623
|
* Create a new instance of DocumentManagementService.
|
|
450
624
|
* @param options The options for the service.
|
|
@@ -453,169 +627,218 @@ class DocumentManagementService {
|
|
|
453
627
|
this._auditableItemGraphComponent = ComponentFactory.get(options?.auditableItemGraphComponentType ?? "auditable-item-graph");
|
|
454
628
|
this._blobStorageComponent = ComponentFactory.get(options?.blobStorageComponentType ?? "blob-storage");
|
|
455
629
|
this._attestationComponent = ComponentFactory.get(options?.attestationComponentType ?? "attestation");
|
|
630
|
+
this._dataProcessingComponent = ComponentFactory.get(options?.dataProcessingComponentType ?? "data-processing");
|
|
456
631
|
SchemaOrgDataTypes.registerRedirects();
|
|
457
632
|
}
|
|
458
633
|
/**
|
|
459
|
-
* Store a document
|
|
634
|
+
* Store a document as an auditable item graph vertex and add its content to blob storage.
|
|
460
635
|
* If the document id already exists and the blob data is different a new revision will be created.
|
|
461
636
|
* For any other changes the current revision will be updated.
|
|
462
|
-
* @param auditableItemGraphId The auditable item graph vertex id to create the document on.
|
|
463
637
|
* @param documentId The document id to create.
|
|
464
638
|
* @param documentIdFormat The format of the document identifier.
|
|
465
639
|
* @param documentCode The code for the document type.
|
|
466
|
-
* @param blob The data to create the document.
|
|
640
|
+
* @param blob The data to create the document with.
|
|
467
641
|
* @param annotationObject Additional information to associate with the document.
|
|
642
|
+
* @param auditableItemGraphEdges The auditable item graph vertices to connect the document to.
|
|
468
643
|
* @param options Additional options for the set operation.
|
|
469
644
|
* @param options.createAttestation Flag to create an attestation for the document, defaults to false.
|
|
470
|
-
* @param options.
|
|
471
|
-
* @param options.aliasAnnotationObject
|
|
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.
|
|
472
647
|
* @param userIdentity The identity to perform the auditable item graph operation with.
|
|
473
648
|
* @param nodeIdentity The node identity to use for vault operations.
|
|
474
|
-
* @returns The
|
|
649
|
+
* @returns The auditable item graph vertex created for the document including its revision.
|
|
475
650
|
*/
|
|
476
|
-
async
|
|
477
|
-
Guards.stringValue(this.CLASS_NAME, "auditableItemGraphId", auditableItemGraphId);
|
|
651
|
+
async create(documentId, documentIdFormat, documentCode, blob, annotationObject, auditableItemGraphEdges, options, userIdentity, nodeIdentity) {
|
|
478
652
|
Guards.stringValue(this.CLASS_NAME, "documentId", documentId);
|
|
479
653
|
Guards.arrayOneOf(this.CLASS_NAME, "documentCode", documentCode, Object.values(UneceDocumentCodes));
|
|
480
654
|
Guards.uint8Array(this.CLASS_NAME, "blob", blob);
|
|
481
655
|
Guards.stringValue(this.CLASS_NAME, "userIdentity", userIdentity);
|
|
482
656
|
Guards.stringValue(this.CLASS_NAME, "nodeIdentity", nodeIdentity);
|
|
483
657
|
try {
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
if (
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
if (found) {
|
|
490
|
-
found.annotationObject = options?.aliasAnnotationObject ?? found.annotationObject;
|
|
491
|
-
found.aliasFormat = documentIdFormat ?? found.aliasFormat;
|
|
492
|
-
}
|
|
493
|
-
else {
|
|
494
|
-
vertex.aliases.push({
|
|
495
|
-
"@context": AuditableItemGraphContexts.ContextRoot,
|
|
496
|
-
type: AuditableItemGraphTypes.Alias,
|
|
497
|
-
id: documentId,
|
|
498
|
-
aliasFormat: documentIdFormat,
|
|
499
|
-
annotationObject: options?.aliasAnnotationObject
|
|
500
|
-
});
|
|
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);
|
|
501
663
|
}
|
|
502
664
|
}
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
}
|
|
514
|
-
// Calculate the hash for the blob.
|
|
515
|
-
const blobHash = this.generateBlobHash(blob);
|
|
516
|
-
// Is the blob data the same as the current revision ?
|
|
517
|
-
if (currentRevision?.blobHash === blobHash) {
|
|
518
|
-
// Blob data matches so no need to create a new revision
|
|
519
|
-
// We update the current object if the annotation or createAttestation flag has changed.
|
|
520
|
-
let updated = false;
|
|
521
|
-
if (!ObjectHelper.equal(currentRevision.annotationObject, annotationObject, false)) {
|
|
522
|
-
currentRevision.annotationObject = annotationObject;
|
|
523
|
-
updated = true;
|
|
524
|
-
}
|
|
525
|
-
if (createAttestation && Is.empty(currentRevision.attestationId)) {
|
|
526
|
-
currentRevision.attestationId = await this.createAttestation(currentRevision, userIdentity, nodeIdentity);
|
|
527
|
-
updated = true;
|
|
528
|
-
}
|
|
529
|
-
if (updated) {
|
|
530
|
-
currentRevision.dateModified = new Date(Date.now()).toISOString();
|
|
531
|
-
await this._auditableItemGraphComponent.update(vertex, userIdentity, nodeIdentity);
|
|
532
|
-
}
|
|
533
|
-
return currentRevision.id;
|
|
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
|
+
});
|
|
534
675
|
}
|
|
535
|
-
//
|
|
676
|
+
// Add the blob to blob storage
|
|
536
677
|
const blobStorageId = await this._blobStorageComponent.create(Converter.bytesToBase64(blob), undefined, undefined, undefined, undefined, userIdentity, nodeIdentity);
|
|
537
|
-
const
|
|
538
|
-
// We are creating a new document, if there is already docs with the same id and code we use the list length
|
|
539
|
-
// to determine the next revision number.
|
|
540
|
-
const document = {
|
|
678
|
+
const currentRevision = {
|
|
541
679
|
"@context": [
|
|
542
680
|
DocumentContexts.ContextRoot,
|
|
543
681
|
DocumentContexts.ContextRootCommon,
|
|
544
682
|
SchemaOrgContexts.ContextRoot
|
|
545
683
|
],
|
|
546
684
|
type: DocumentTypes.Document,
|
|
547
|
-
id: this.
|
|
685
|
+
id: this.createDocumentId(documentId, 0),
|
|
548
686
|
documentId,
|
|
549
687
|
documentIdFormat,
|
|
550
688
|
documentCode,
|
|
551
|
-
documentRevision,
|
|
552
|
-
blobStorageId,
|
|
553
|
-
blobHash,
|
|
689
|
+
documentRevision: 0,
|
|
554
690
|
annotationObject,
|
|
691
|
+
blobHash: this.generateBlobHash(blob),
|
|
692
|
+
blobStorageId,
|
|
555
693
|
dateCreated: new Date(Date.now()).toISOString(),
|
|
556
694
|
nodeIdentity,
|
|
557
695
|
userIdentity
|
|
558
696
|
};
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
document.attestationId = await this.createAttestation(document, userIdentity, nodeIdentity);
|
|
697
|
+
if (options?.createAttestation ?? false) {
|
|
698
|
+
currentRevision.attestationId = await this.createAttestation(currentRevision, userIdentity, nodeIdentity);
|
|
562
699
|
}
|
|
563
|
-
// Add the new revision in to the
|
|
564
|
-
|
|
700
|
+
// Add the new revision in to the vertex
|
|
701
|
+
documentVertex.resources ??= [];
|
|
702
|
+
documentVertex.resources.push({
|
|
565
703
|
"@context": AuditableItemGraphContexts.ContextRoot,
|
|
566
704
|
type: AuditableItemGraphTypes.Resource,
|
|
567
|
-
resourceObject:
|
|
705
|
+
resourceObject: currentRevision
|
|
568
706
|
});
|
|
569
|
-
|
|
570
|
-
|
|
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;
|
|
571
714
|
}
|
|
572
715
|
catch (error) {
|
|
573
716
|
if (BaseError.someErrorName(error, "NotFoundError")) {
|
|
574
717
|
throw error;
|
|
575
718
|
}
|
|
576
|
-
throw new GeneralError(this.CLASS_NAME, "
|
|
719
|
+
throw new GeneralError(this.CLASS_NAME, "createFailed", undefined, error);
|
|
577
720
|
}
|
|
578
721
|
}
|
|
579
722
|
/**
|
|
580
|
-
*
|
|
581
|
-
*
|
|
582
|
-
*
|
|
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.
|
|
583
822
|
* @param options Additional options for the get operation.
|
|
584
823
|
* @param options.includeBlobStorageMetadata Flag to include the blob storage metadata for the document, defaults to false.
|
|
585
824
|
* @param options.includeBlobStorageData Flag to include the blob storage data for the document, defaults to false.
|
|
586
825
|
* @param options.includeAttestation Flag to include the attestation information for the document, defaults to false.
|
|
587
826
|
* @param options.includeRemoved Flag to include deleted documents, defaults to false.
|
|
588
|
-
* @param options.
|
|
589
|
-
* @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.
|
|
590
831
|
* @param userIdentity The identity to perform the auditable item graph operation with.
|
|
591
832
|
* @param nodeIdentity The node identity to use for vault operations.
|
|
592
833
|
* @returns The documents and revisions if requested, ordered by revision descending, cursor is set if there are more document revisions.
|
|
593
834
|
*/
|
|
594
|
-
async get(
|
|
595
|
-
Urn.guard(this.CLASS_NAME, "
|
|
596
|
-
Urn.guard(this.CLASS_NAME, "identifier", identifier);
|
|
835
|
+
async get(auditableItemGraphDocumentId, options, cursor, pageSize, userIdentity, nodeIdentity) {
|
|
836
|
+
Urn.guard(this.CLASS_NAME, "auditableItemGraphDocumentId", auditableItemGraphDocumentId);
|
|
597
837
|
try {
|
|
598
|
-
const
|
|
599
|
-
const includeBlobStorageData = options?.includeBlobStorageData ?? false;
|
|
600
|
-
const includeAttestation = options?.includeAttestation ?? false;
|
|
601
|
-
const includeRemoved = options?.includeRemoved ?? false;
|
|
602
|
-
const revCursor = Math.max(Coerce.integer(revisionCursor) ?? 0, 0);
|
|
603
|
-
const maxRevisionCount = Math.max(Coerce.integer(options?.maxRevisionCount) ?? 0);
|
|
604
|
-
const documentIdParts = this.parseDocumentId(identifier);
|
|
605
|
-
const vertex = await this._auditableItemGraphComponent.get(auditableItemGraphId);
|
|
606
|
-
// Get all the docs from the AIG vertex
|
|
607
|
-
const vertexDocs = this.filterDocumentsFromVertex(vertex);
|
|
608
|
-
// Reduce the list to those with a matching id and code
|
|
609
|
-
const matchingDocIds = this.findMatchingDocs(vertexDocs, documentIdParts.documentId, documentIdParts.documentCode, includeRemoved);
|
|
838
|
+
const documentVertex = await this._auditableItemGraphComponent.get(auditableItemGraphDocumentId, { includeDeleted: options?.includeRemoved });
|
|
610
839
|
// Populate the document and revisions with the options set
|
|
611
|
-
const
|
|
612
|
-
|
|
613
|
-
includeBlobStorageData,
|
|
614
|
-
includeAttestation,
|
|
615
|
-
includeRemoved,
|
|
616
|
-
maxRevisionCount
|
|
617
|
-
}, revCursor, userIdentity, nodeIdentity);
|
|
618
|
-
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"]);
|
|
619
842
|
}
|
|
620
843
|
catch (error) {
|
|
621
844
|
if (BaseError.someErrorName(error, "NotFoundError")) {
|
|
@@ -625,218 +848,237 @@ class DocumentManagementService {
|
|
|
625
848
|
}
|
|
626
849
|
}
|
|
627
850
|
/**
|
|
628
|
-
*
|
|
629
|
-
*
|
|
630
|
-
* @param
|
|
631
|
-
* @param
|
|
632
|
-
* @param options
|
|
633
|
-
* @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.
|
|
634
860
|
* @param userIdentity The identity to perform the auditable item graph operation with.
|
|
635
861
|
* @param nodeIdentity The node identity to use for vault operations.
|
|
636
|
-
* @returns
|
|
862
|
+
* @returns The documents and revisions if requested, ordered by revision descending, cursor is set if there are more document revisions.
|
|
637
863
|
*/
|
|
638
|
-
async
|
|
639
|
-
Urn.guard(this.CLASS_NAME, "
|
|
640
|
-
|
|
864
|
+
async getRevision(auditableItemGraphDocumentId, revision, options, userIdentity, nodeIdentity) {
|
|
865
|
+
Urn.guard(this.CLASS_NAME, "auditableItemGraphDocumentId", auditableItemGraphDocumentId);
|
|
866
|
+
Guards.integer(this.CLASS_NAME, "revision", revision);
|
|
641
867
|
try {
|
|
642
|
-
const
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
const vertexDocs = this.filterDocumentsFromVertex(vertex);
|
|
646
|
-
// Reduce the list to those with a matching id and code
|
|
647
|
-
const matchingDocIds = this.findMatchingDocs(vertexDocs, documentIdParts.documentId, documentIdParts.documentCode, false);
|
|
648
|
-
const removeAllRevisions = options?.removeAllRevisions ?? false;
|
|
649
|
-
const now = Date.now();
|
|
650
|
-
if (removeAllRevisions) {
|
|
651
|
-
for (const doc of matchingDocIds) {
|
|
652
|
-
doc.dateDeleted = new Date(now).toISOString();
|
|
653
|
-
}
|
|
868
|
+
const documentVertex = await this._auditableItemGraphComponent.get(auditableItemGraphDocumentId, { includeDeleted: true });
|
|
869
|
+
if (Is.empty(documentVertex.resources)) {
|
|
870
|
+
throw new NotFoundError(this.CLASS_NAME, "documentRevisionNone");
|
|
654
871
|
}
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
matchingRevision.dateDeleted = new Date(now).toISOString();
|
|
659
|
-
}
|
|
660
|
-
else {
|
|
661
|
-
throw new NotFoundError(this.CLASS_NAME, "documentRevisionNotFound", identifier);
|
|
662
|
-
}
|
|
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());
|
|
663
875
|
}
|
|
664
|
-
|
|
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"]);
|
|
665
879
|
}
|
|
666
880
|
catch (error) {
|
|
667
881
|
if (BaseError.someErrorName(error, "NotFoundError")) {
|
|
668
882
|
throw error;
|
|
669
883
|
}
|
|
670
|
-
throw new GeneralError(this.CLASS_NAME, "
|
|
884
|
+
throw new GeneralError(this.CLASS_NAME, "getRevisionFailed", undefined, error);
|
|
671
885
|
}
|
|
672
886
|
}
|
|
673
887
|
/**
|
|
674
|
-
*
|
|
675
|
-
*
|
|
676
|
-
* @param
|
|
677
|
-
* @param
|
|
678
|
-
* @param options.includeMostRecentRevisions Include the most recent 5 revisions, use the individual get to retrieve more.
|
|
679
|
-
* @param options.includeRemoved Flag to include deleted documents, defaults to false.
|
|
680
|
-
* @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.
|
|
681
892
|
* @param userIdentity The identity to perform the auditable item graph operation with.
|
|
682
893
|
* @param nodeIdentity The node identity to use for vault operations.
|
|
683
|
-
* @returns
|
|
894
|
+
* @returns Nothing.
|
|
684
895
|
*/
|
|
685
|
-
async
|
|
686
|
-
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);
|
|
687
899
|
try {
|
|
688
|
-
const
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
const documentIdGroups = {};
|
|
699
|
-
let docGroupIds = [];
|
|
700
|
-
for (const doc of matchingDocIds) {
|
|
701
|
-
const docId = `${doc.documentId}:${doc.documentCode}`;
|
|
702
|
-
if (!docGroupIds.includes(docId)) {
|
|
703
|
-
docGroupIds.push(docId);
|
|
704
|
-
}
|
|
705
|
-
documentIdGroups[docId] ??= [];
|
|
706
|
-
documentIdGroups[docId].push(doc);
|
|
707
|
-
}
|
|
708
|
-
let nextDocCursor;
|
|
709
|
-
if (docGroupIds.length > docCursor + DocumentManagementService._DEFAULT_PAGE_SIZE) {
|
|
710
|
-
nextDocCursor = (docCursor + DocumentManagementService._DEFAULT_PAGE_SIZE).toString();
|
|
711
|
-
}
|
|
712
|
-
docGroupIds = docGroupIds.slice(docCursor, docCursor + DocumentManagementService._DEFAULT_PAGE_SIZE);
|
|
713
|
-
const finalDocs = [];
|
|
714
|
-
for (const docId of docGroupIds) {
|
|
715
|
-
finalDocs.push(await this.getDocumentAndRevisions(documentIdGroups[docId], docId, {
|
|
716
|
-
includeAttestation: false,
|
|
717
|
-
includeBlobStorageData: false,
|
|
718
|
-
includeBlobStorageMetadata: false,
|
|
719
|
-
includeRemoved,
|
|
720
|
-
maxRevisionCount: includeMostRecentRevisions ? 5 : 0
|
|
721
|
-
}, 0, userIdentity, nodeIdentity));
|
|
722
|
-
}
|
|
723
|
-
const docList = {
|
|
724
|
-
"@context": [
|
|
725
|
-
DocumentContexts.ContextRoot,
|
|
726
|
-
DocumentContexts.ContextRootCommon,
|
|
727
|
-
SchemaOrgContexts.ContextRoot
|
|
728
|
-
],
|
|
729
|
-
type: DocumentTypes.DocumentList,
|
|
730
|
-
documents: finalDocs,
|
|
731
|
-
cursor: nextDocCursor
|
|
732
|
-
};
|
|
733
|
-
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);
|
|
734
910
|
}
|
|
735
911
|
catch (error) {
|
|
736
912
|
if (BaseError.someErrorName(error, "NotFoundError")) {
|
|
737
913
|
throw error;
|
|
738
914
|
}
|
|
739
|
-
throw new GeneralError(this.CLASS_NAME, "
|
|
915
|
+
throw new GeneralError(this.CLASS_NAME, "removeRevisionFailed", undefined, error);
|
|
740
916
|
}
|
|
741
917
|
}
|
|
742
918
|
/**
|
|
743
|
-
*
|
|
744
|
-
* @param documentId The document
|
|
745
|
-
* @
|
|
746
|
-
* @
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
}
|
|
751
|
-
/**
|
|
752
|
-
* Decode the document id.
|
|
753
|
-
* @param documentId The document identifier.
|
|
754
|
-
* @returns The decoded identifier.
|
|
755
|
-
* @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.
|
|
756
926
|
*/
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
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);
|
|
764
941
|
}
|
|
765
|
-
return { documentId: parts.join(":"), documentRevision: revision };
|
|
766
|
-
}
|
|
767
|
-
/**
|
|
768
|
-
* Create a full identifier for a document.
|
|
769
|
-
* @param documentCode The document code.
|
|
770
|
-
* @param documentId The document identifier.
|
|
771
|
-
* @param documentRevision The document revision.
|
|
772
|
-
* @returns The full identifier.
|
|
773
|
-
* @internal
|
|
774
|
-
*/
|
|
775
|
-
createIdentifier(documentCode, documentId, documentRevision) {
|
|
776
|
-
const docCode = this.parseDocumentCode(documentCode);
|
|
777
|
-
return `documents:${docCode}:${this.encodeDocumentIdentifier(documentId, documentRevision)}`;
|
|
778
942
|
}
|
|
779
943
|
/**
|
|
780
|
-
*
|
|
781
|
-
* @param
|
|
782
|
-
* @
|
|
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.
|
|
783
948
|
* @internal
|
|
784
949
|
*/
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
const
|
|
788
|
-
if (
|
|
789
|
-
|
|
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
|
+
}
|
|
790
984
|
}
|
|
791
|
-
|
|
792
|
-
const { documentId, documentRevision } = this.decodeDocumentIdentifier(urn.namespaceSpecific(1));
|
|
793
|
-
return { documentCode, documentId, documentRevision };
|
|
985
|
+
return changed;
|
|
794
986
|
}
|
|
795
987
|
/**
|
|
796
|
-
*
|
|
797
|
-
* @param
|
|
798
|
-
* @
|
|
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.
|
|
799
997
|
* @internal
|
|
800
998
|
*/
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
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
|
+
}
|
|
806
1053
|
}
|
|
807
|
-
|
|
808
|
-
if (
|
|
809
|
-
|
|
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
|
+
}
|
|
810
1081
|
}
|
|
811
|
-
return docCode;
|
|
812
|
-
}
|
|
813
|
-
/**
|
|
814
|
-
* Get the documents from a vertex.
|
|
815
|
-
* @param vertex The vertex to get the documents from.
|
|
816
|
-
* @returns The documents.
|
|
817
|
-
* @internal
|
|
818
|
-
*/
|
|
819
|
-
filterDocumentsFromVertex(vertex) {
|
|
820
|
-
return (vertex.resources
|
|
821
|
-
?.filter(resource => ObjectHelper.extractProperty(resource.resourceObject, ["@type", "type"], false) ===
|
|
822
|
-
DocumentTypes.Document)
|
|
823
|
-
.map(resource => resource.resourceObject) ?? []);
|
|
824
|
-
}
|
|
825
|
-
/**
|
|
826
|
-
* Find matching documents in the list of existing documents.
|
|
827
|
-
* @param documents The documents to search.
|
|
828
|
-
* @param documentId The document id.
|
|
829
|
-
* @param documentCode The document code.
|
|
830
|
-
* @param includeRemoved Include deleted documents.
|
|
831
|
-
* @returns The matching documents.
|
|
832
|
-
* @internal
|
|
833
|
-
*/
|
|
834
|
-
findMatchingDocs(documents, documentId, documentCode, includeRemoved) {
|
|
835
|
-
return documents
|
|
836
|
-
.filter(d => d.documentId === documentId &&
|
|
837
|
-
d.documentCode === documentCode &&
|
|
838
|
-
(includeRemoved || Is.empty(d.dateDeleted)))
|
|
839
|
-
.sort((a, b) => b.documentRevision - a.documentRevision);
|
|
840
1082
|
}
|
|
841
1083
|
/**
|
|
842
1084
|
* Generate a hash for the blob data.
|
|
@@ -849,47 +1091,91 @@ class DocumentManagementService {
|
|
|
849
1091
|
}
|
|
850
1092
|
/**
|
|
851
1093
|
* Get the documents from the auditable item graph vertex.
|
|
852
|
-
* @param
|
|
853
|
-
* @param identifier The full document identifier.
|
|
1094
|
+
* @param documentVertex The vertex containing the documents.
|
|
854
1095
|
* @param options Additional options for the get operation.
|
|
855
1096
|
* @param options.includeBlobStorageMetadata Flag to include the blob storage metadata for the document, defaults to false.
|
|
856
1097
|
* @param options.includeBlobStorageData Flag to include the blob storage data for the document, defaults to false.
|
|
857
1098
|
* @param options.includeAttestation Flag to include the attestation information for the document, defaults to false.
|
|
858
|
-
* @param options.
|
|
859
|
-
* @param options.
|
|
860
|
-
* @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.
|
|
861
1103
|
* @param userIdentity The identity to perform the auditable item graph operation with.
|
|
862
1104
|
* @param nodeIdentity The node identity to use for vault operations.
|
|
863
1105
|
* @returns The finalised list of documents.
|
|
864
1106
|
* @internal
|
|
865
1107
|
*/
|
|
866
|
-
async
|
|
867
|
-
const
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
const
|
|
882
|
-
|
|
883
|
-
|
|
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
|
+
}
|
|
884
1169
|
}
|
|
885
|
-
if (
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
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
|
+
}
|
|
889
1177
|
}
|
|
890
|
-
|
|
891
|
-
document.revisionCursor = nextRevisionCursor;
|
|
892
|
-
return document;
|
|
1178
|
+
return docList;
|
|
893
1179
|
}
|
|
894
1180
|
/**
|
|
895
1181
|
* Create an attestation for the document.
|
|
@@ -906,6 +1192,7 @@ class DocumentManagementService {
|
|
|
906
1192
|
SchemaOrgContexts.ContextRoot
|
|
907
1193
|
],
|
|
908
1194
|
type: DocumentTypes.DocumentAttestation,
|
|
1195
|
+
id: document.id,
|
|
909
1196
|
documentId: document.documentId,
|
|
910
1197
|
documentCode: document.documentCode,
|
|
911
1198
|
documentRevision: document.documentRevision,
|
|
@@ -914,15 +1201,26 @@ class DocumentManagementService {
|
|
|
914
1201
|
};
|
|
915
1202
|
return this._attestationComponent.create(documentAttestation, undefined, userIdentity, nodeIdentity);
|
|
916
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}`;
|
|
1214
|
+
}
|
|
917
1215
|
}
|
|
918
1216
|
|
|
919
1217
|
const restEntryPoints = [
|
|
920
1218
|
{
|
|
921
|
-
name: "
|
|
922
|
-
defaultBaseRoute: "
|
|
1219
|
+
name: "document-management",
|
|
1220
|
+
defaultBaseRoute: "document-management",
|
|
923
1221
|
tags: tagsDocumentManagement,
|
|
924
1222
|
generateRoutes: generateRestRoutesDocumentManagement
|
|
925
1223
|
}
|
|
926
1224
|
];
|
|
927
1225
|
|
|
928
|
-
export { DocumentManagementService, documentManagementGet, documentManagementQuery, documentManagementRemove,
|
|
1226
|
+
export { DocumentManagementService, documentManagementCreate, documentManagementGet, documentManagementGetRevision, documentManagementQuery, documentManagementRemove, documentManagementUpdate, generateRestRoutesDocumentManagement, restEntryPoints, tagsDocumentManagement };
|