@twin.org/document-management-service 0.0.1-next.8 → 0.0.1-next.9
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 +574 -463
- package/dist/esm/index.mjs +574 -464
- package/dist/types/documentManagementRoutes.d.ts +12 -4
- package/dist/types/documentManagementService.d.ts +47 -37
- package/docs/changelog.md +14 -0
- package/docs/open-api/spec.json +247 -202
- package/docs/reference/classes/DocumentManagementService.md +106 -80
- package/docs/reference/functions/{documentManagementSet.md → documentManagementCreate.md} +4 -4
- package/docs/reference/functions/documentManagementUpdate.md +31 -0
- package/docs/reference/index.md +2 -1
- package/locales/en.json +5 -5
- package/package.json +2 -2
package/dist/esm/index.mjs
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Guards, ComponentFactory, Converter, Coerce, Is, ObjectHelper, BaseError, GeneralError, Urn, NotFoundError } from '@twin.org/core';
|
|
1
|
+
import { Guards, ComponentFactory, Converter, Coerce, Is, BaseError, GeneralError, Urn, NotFoundError, ObjectHelper } from '@twin.org/core';
|
|
3
2
|
import { DocumentTypes, DocumentContexts } from '@twin.org/document-management-models';
|
|
4
3
|
import { SchemaOrgContexts, SchemaOrgDataTypes } from '@twin.org/standards-schema-org';
|
|
5
4
|
import { UneceDocumentCodes } from '@twin.org/standards-unece';
|
|
@@ -10,8 +9,6 @@ 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,22 +75,29 @@ function generateRestRoutesDocumentManagement(baseRouteName, componentName) {
|
|
|
81
75
|
}
|
|
82
76
|
]
|
|
83
77
|
};
|
|
84
|
-
const
|
|
85
|
-
operationId: "
|
|
86
|
-
summary: "
|
|
78
|
+
const documentManagementUpdateRoute = {
|
|
79
|
+
operationId: "DocumentManagementUpdate",
|
|
80
|
+
summary: "Update a document in an auditable item graph vertex and add its content to blob storage.",
|
|
87
81
|
tag: tagsDocumentManagement[0].name,
|
|
88
|
-
method: "
|
|
89
|
-
path: `${baseRouteName}/:
|
|
90
|
-
handler: async (httpRequestContext, request) =>
|
|
82
|
+
method: "PUT",
|
|
83
|
+
path: `${baseRouteName}/:auditableItemGraphDocumentId`,
|
|
84
|
+
handler: async (httpRequestContext, request) => documentManagementUpdate(httpRequestContext, componentName, request),
|
|
91
85
|
requestType: {
|
|
92
|
-
type: "
|
|
86
|
+
type: "IDocumentManagementUpdateRequest",
|
|
93
87
|
examples: [
|
|
94
88
|
{
|
|
95
|
-
id: "
|
|
89
|
+
id: "DocumentManagementUpdateRequestExample",
|
|
96
90
|
request: {
|
|
97
91
|
pathParams: {
|
|
98
|
-
|
|
99
|
-
|
|
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
|
+
}
|
|
100
101
|
}
|
|
101
102
|
}
|
|
102
103
|
}
|
|
@@ -104,122 +105,33 @@ function generateRestRoutesDocumentManagement(baseRouteName, componentName) {
|
|
|
104
105
|
},
|
|
105
106
|
responseType: [
|
|
106
107
|
{
|
|
107
|
-
type: "
|
|
108
|
+
type: "INoContentResponse",
|
|
108
109
|
examples: [
|
|
109
110
|
{
|
|
110
|
-
id: "
|
|
111
|
+
id: "DocumentManagementCreateResponseExample",
|
|
111
112
|
response: {
|
|
112
|
-
|
|
113
|
-
"@context": [
|
|
114
|
-
DocumentContexts.ContextRoot,
|
|
115
|
-
DocumentContexts.ContextRootCommon,
|
|
116
|
-
SchemaOrgContexts.ContextRoot
|
|
117
|
-
],
|
|
118
|
-
type: DocumentTypes.Document,
|
|
119
|
-
id: "documents:705:2721000:rev-0",
|
|
120
|
-
documentId: "2721000",
|
|
121
|
-
documentIdFormat: "bol",
|
|
122
|
-
documentCode: UneceDocumentCodes.BillOfLading,
|
|
123
|
-
documentRevision: 0,
|
|
124
|
-
blobStorageId: "blob-memory:c57d94b088f4c6d2cb32ded014813d0c786aa00134c8ee22f84b1e2545602a70",
|
|
125
|
-
blobHash: "sha256:123456",
|
|
126
|
-
dateCreated: "2024-01-01T00:00:00Z",
|
|
127
|
-
annotationObject: {
|
|
128
|
-
"@context": "https://schema.org",
|
|
129
|
-
"@type": "DigitalDocument",
|
|
130
|
-
name: "myfile.pdf"
|
|
131
|
-
},
|
|
132
|
-
nodeIdentity: "did:entity-storage:0x6363636363636363636363636363636363636363636363636363636363636363",
|
|
133
|
-
userIdentity: "did:entity-storage:0x6363636363636363636363636363636363636363636363636363636363636363"
|
|
134
|
-
}
|
|
113
|
+
statusCode: HttpStatusCode.noContent
|
|
135
114
|
}
|
|
136
115
|
}
|
|
137
116
|
]
|
|
138
|
-
},
|
|
139
|
-
{
|
|
140
|
-
type: "IDocumentManagementGetResponse",
|
|
141
|
-
mimeType: MimeTypes.JsonLd,
|
|
142
|
-
examples: [
|
|
143
|
-
{
|
|
144
|
-
id: "DocumentManagementGetResponseExample",
|
|
145
|
-
response: {
|
|
146
|
-
body: {
|
|
147
|
-
"@context": [
|
|
148
|
-
DocumentContexts.ContextRoot,
|
|
149
|
-
DocumentContexts.ContextRootCommon,
|
|
150
|
-
SchemaOrgContexts.ContextRoot
|
|
151
|
-
],
|
|
152
|
-
type: DocumentTypes.Document,
|
|
153
|
-
id: "documents:705:2721000:rev-0",
|
|
154
|
-
documentId: "2721000",
|
|
155
|
-
documentIdFormat: "bol",
|
|
156
|
-
documentCode: UneceDocumentCodes.BillOfLading,
|
|
157
|
-
documentRevision: 0,
|
|
158
|
-
blobStorageId: "blob-memory:c57d94b088f4c6d2cb32ded014813d0c786aa00134c8ee22f84b1e2545602a70",
|
|
159
|
-
blobHash: "sha256:123456",
|
|
160
|
-
dateCreated: "2024-01-01T00:00:00Z",
|
|
161
|
-
annotationObject: {
|
|
162
|
-
"@context": "https://schema.org",
|
|
163
|
-
"@type": "DigitalDocument",
|
|
164
|
-
name: "myfile.pdf"
|
|
165
|
-
},
|
|
166
|
-
nodeIdentity: "did:entity-storage:0x6363636363636363636363636363636363636363636363636363636363636363",
|
|
167
|
-
userIdentity: "did:entity-storage:0x6363636363636363636363636363636363636363636363636363636363636363"
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
]
|
|
172
|
-
},
|
|
173
|
-
{
|
|
174
|
-
type: "INotFoundResponse"
|
|
175
117
|
}
|
|
176
118
|
]
|
|
177
119
|
};
|
|
178
|
-
const
|
|
179
|
-
operationId: "
|
|
180
|
-
summary: "
|
|
181
|
-
tag: tagsDocumentManagement[0].name,
|
|
182
|
-
method: "DELETE",
|
|
183
|
-
path: `${baseRouteName}/:auditableItemGraphId/:documentId`,
|
|
184
|
-
handler: async (httpRequestContext, request) => documentManagementRemove(httpRequestContext, componentName, request),
|
|
185
|
-
requestType: {
|
|
186
|
-
type: "IDocumentManagementRemoveRequest",
|
|
187
|
-
examples: [
|
|
188
|
-
{
|
|
189
|
-
id: "DocumentManagementRemoveRequestExample",
|
|
190
|
-
request: {
|
|
191
|
-
pathParams: {
|
|
192
|
-
auditableItemGraphId: "aig:1234",
|
|
193
|
-
documentId: "documents:123456:705:2721000"
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
]
|
|
198
|
-
},
|
|
199
|
-
responseType: [
|
|
200
|
-
{
|
|
201
|
-
type: "INoContentResponse"
|
|
202
|
-
},
|
|
203
|
-
{
|
|
204
|
-
type: "INotFoundResponse"
|
|
205
|
-
}
|
|
206
|
-
]
|
|
207
|
-
};
|
|
208
|
-
const documentManagementQueryRoute = {
|
|
209
|
-
operationId: "DocumentManagementQuery",
|
|
210
|
-
summary: "Query the items from an auditable item graph vertex",
|
|
120
|
+
const documentManagementGetRoute = {
|
|
121
|
+
operationId: "DocumentManagementGet",
|
|
122
|
+
summary: "Get the data for a document from document management",
|
|
211
123
|
tag: tagsDocumentManagement[0].name,
|
|
212
124
|
method: "GET",
|
|
213
|
-
path: `${baseRouteName}/:
|
|
214
|
-
handler: async (httpRequestContext, request) =>
|
|
125
|
+
path: `${baseRouteName}/:auditableItemGraphDocumentId`,
|
|
126
|
+
handler: async (httpRequestContext, request) => documentManagementGet(httpRequestContext, componentName, request),
|
|
215
127
|
requestType: {
|
|
216
|
-
type: "
|
|
128
|
+
type: "IDocumentManagementGetRequest",
|
|
217
129
|
examples: [
|
|
218
130
|
{
|
|
219
|
-
id: "
|
|
131
|
+
id: "DocumentManagementGetRequestExample",
|
|
220
132
|
request: {
|
|
221
133
|
pathParams: {
|
|
222
|
-
|
|
134
|
+
auditableItemGraphDocumentId: "aig:123456"
|
|
223
135
|
}
|
|
224
136
|
}
|
|
225
137
|
}
|
|
@@ -227,10 +139,10 @@ function generateRestRoutesDocumentManagement(baseRouteName, componentName) {
|
|
|
227
139
|
},
|
|
228
140
|
responseType: [
|
|
229
141
|
{
|
|
230
|
-
type: "
|
|
142
|
+
type: "IDocumentManagementGetResponse",
|
|
231
143
|
examples: [
|
|
232
144
|
{
|
|
233
|
-
id: "
|
|
145
|
+
id: "DocumentManagementGetResponseExample",
|
|
234
146
|
response: {
|
|
235
147
|
body: {
|
|
236
148
|
"@context": [DocumentContexts.ContextRoot, DocumentContexts.ContextRootCommon],
|
|
@@ -243,7 +155,7 @@ function generateRestRoutesDocumentManagement(baseRouteName, componentName) {
|
|
|
243
155
|
SchemaOrgContexts.ContextRoot
|
|
244
156
|
],
|
|
245
157
|
type: DocumentTypes.Document,
|
|
246
|
-
id: "
|
|
158
|
+
id: "2721000:0",
|
|
247
159
|
documentId: "2721000",
|
|
248
160
|
documentIdFormat: "bol",
|
|
249
161
|
documentCode: UneceDocumentCodes.BillOfLading,
|
|
@@ -266,11 +178,11 @@ function generateRestRoutesDocumentManagement(baseRouteName, componentName) {
|
|
|
266
178
|
]
|
|
267
179
|
},
|
|
268
180
|
{
|
|
269
|
-
type: "
|
|
181
|
+
type: "IDocumentManagementGetResponse",
|
|
270
182
|
mimeType: MimeTypes.JsonLd,
|
|
271
183
|
examples: [
|
|
272
184
|
{
|
|
273
|
-
id: "
|
|
185
|
+
id: "DocumentManagementGetResponseExample",
|
|
274
186
|
response: {
|
|
275
187
|
body: {
|
|
276
188
|
"@context": [DocumentContexts.ContextRoot, DocumentContexts.ContextRootCommon],
|
|
@@ -283,7 +195,7 @@ function generateRestRoutesDocumentManagement(baseRouteName, componentName) {
|
|
|
283
195
|
SchemaOrgContexts.ContextRoot
|
|
284
196
|
],
|
|
285
197
|
type: DocumentTypes.Document,
|
|
286
|
-
id: "
|
|
198
|
+
id: "2721000:0",
|
|
287
199
|
documentId: "2721000",
|
|
288
200
|
documentIdFormat: "bol",
|
|
289
201
|
documentCode: UneceDocumentCodes.BillOfLading,
|
|
@@ -304,33 +216,173 @@ function generateRestRoutesDocumentManagement(baseRouteName, componentName) {
|
|
|
304
216
|
}
|
|
305
217
|
}
|
|
306
218
|
]
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
type: "INotFoundResponse"
|
|
222
|
+
}
|
|
223
|
+
]
|
|
224
|
+
};
|
|
225
|
+
const documentManagementRemoveRoute = {
|
|
226
|
+
operationId: "DocumentManagementRemove",
|
|
227
|
+
summary: "Remove an document from an auditable item graph vertex",
|
|
228
|
+
tag: tagsDocumentManagement[0].name,
|
|
229
|
+
method: "DELETE",
|
|
230
|
+
path: `${baseRouteName}/:auditableItemGraphDocumentId/:revision`,
|
|
231
|
+
handler: async (httpRequestContext, request) => documentManagementRemove(httpRequestContext, componentName, request),
|
|
232
|
+
requestType: {
|
|
233
|
+
type: "IDocumentManagementRemoveRequest",
|
|
234
|
+
examples: [
|
|
235
|
+
{
|
|
236
|
+
id: "DocumentManagementRemoveRequestExample",
|
|
237
|
+
request: {
|
|
238
|
+
pathParams: {
|
|
239
|
+
auditableItemGraphDocumentId: "aig:1234",
|
|
240
|
+
revision: "1"
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
]
|
|
245
|
+
},
|
|
246
|
+
responseType: [
|
|
247
|
+
{
|
|
248
|
+
type: "INoContentResponse"
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
type: "INotFoundResponse"
|
|
307
252
|
}
|
|
308
253
|
]
|
|
309
254
|
};
|
|
255
|
+
const documentManagementQueryRoute = {
|
|
256
|
+
operationId: "DocumentManagementQuery",
|
|
257
|
+
summary: "Query the items from an auditable item graph vertex",
|
|
258
|
+
tag: tagsDocumentManagement[0].name,
|
|
259
|
+
method: "GET",
|
|
260
|
+
path: `${baseRouteName}/`,
|
|
261
|
+
handler: async (httpRequestContext, request) => documentManagementQuery(httpRequestContext, componentName, request),
|
|
262
|
+
requestType: {
|
|
263
|
+
type: "IDocumentManagementQueryRequest",
|
|
264
|
+
examples: [
|
|
265
|
+
{
|
|
266
|
+
id: "DocumentManagementQueryRequestExample",
|
|
267
|
+
request: {
|
|
268
|
+
query: {
|
|
269
|
+
documentId: "2721000"
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
]
|
|
274
|
+
},
|
|
275
|
+
responseType: [
|
|
276
|
+
// {
|
|
277
|
+
// type: nameof<IDocumentManagementQueryResponse>(),
|
|
278
|
+
// examples: [
|
|
279
|
+
// {
|
|
280
|
+
// id: "DocumentManagementQueryResponseExample",
|
|
281
|
+
// response: {
|
|
282
|
+
// body: {
|
|
283
|
+
// "@context": [DocumentContexts.ContextRoot, DocumentContexts.ContextRootCommon],
|
|
284
|
+
// type: DocumentTypes.DocumentList,
|
|
285
|
+
// documents: [
|
|
286
|
+
// {
|
|
287
|
+
// "@context": [
|
|
288
|
+
// DocumentContexts.ContextRoot,
|
|
289
|
+
// DocumentContexts.ContextRootCommon,
|
|
290
|
+
// SchemaOrgContexts.ContextRoot
|
|
291
|
+
// ],
|
|
292
|
+
// type: DocumentTypes.Document,
|
|
293
|
+
// id: "2721000:0",
|
|
294
|
+
// documentId: "2721000",
|
|
295
|
+
// documentIdFormat: "bol",
|
|
296
|
+
// documentCode: UneceDocumentCodes.BillOfLading,
|
|
297
|
+
// documentRevision: 0,
|
|
298
|
+
// blobStorageId:
|
|
299
|
+
// "blob-memory:c57d94b088f4c6d2cb32ded014813d0c786aa00134c8ee22f84b1e2545602a70",
|
|
300
|
+
// blobHash: "sha256:123456",
|
|
301
|
+
// dateCreated: "2024-01-01T00:00:00Z",
|
|
302
|
+
// annotationObject: {
|
|
303
|
+
// "@context": "https://schema.org",
|
|
304
|
+
// "@type": "DigitalDocument",
|
|
305
|
+
// name: "myfile.pdf"
|
|
306
|
+
// },
|
|
307
|
+
// nodeIdentity:
|
|
308
|
+
// "did:entity-storage:0x6363636363636363636363636363636363636363636363636363636363636363",
|
|
309
|
+
// userIdentity:
|
|
310
|
+
// "did:entity-storage:0x6363636363636363636363636363636363636363636363636363636363636363"
|
|
311
|
+
// }
|
|
312
|
+
// ]
|
|
313
|
+
// }
|
|
314
|
+
// }
|
|
315
|
+
// }
|
|
316
|
+
// ]
|
|
317
|
+
// },
|
|
318
|
+
// {
|
|
319
|
+
// type: nameof<IDocumentManagementQueryResponse>(),
|
|
320
|
+
// mimeType: MimeTypes.JsonLd,
|
|
321
|
+
// examples: [
|
|
322
|
+
// {
|
|
323
|
+
// id: "DocumentManagementListResponseJsonLdExample",
|
|
324
|
+
// response: {
|
|
325
|
+
// body: {
|
|
326
|
+
// "@context": [DocumentContexts.ContextRoot, DocumentContexts.ContextRootCommon],
|
|
327
|
+
// type: DocumentTypes.DocumentList,
|
|
328
|
+
// documents: [
|
|
329
|
+
// {
|
|
330
|
+
// "@context": [
|
|
331
|
+
// DocumentContexts.ContextRoot,
|
|
332
|
+
// DocumentContexts.ContextRootCommon,
|
|
333
|
+
// SchemaOrgContexts.ContextRoot
|
|
334
|
+
// ],
|
|
335
|
+
// type: DocumentTypes.Document,
|
|
336
|
+
// id: "2721000:0",
|
|
337
|
+
// documentId: "2721000",
|
|
338
|
+
// documentIdFormat: "bol",
|
|
339
|
+
// documentCode: UneceDocumentCodes.BillOfLading,
|
|
340
|
+
// documentRevision: 0,
|
|
341
|
+
// blobStorageId:
|
|
342
|
+
// "blob-memory:c57d94b088f4c6d2cb32ded014813d0c786aa00134c8ee22f84b1e2545602a70",
|
|
343
|
+
// blobHash: "sha256:123456",
|
|
344
|
+
// dateCreated: "2024-01-01T00:00:00Z",
|
|
345
|
+
// annotationObject: {
|
|
346
|
+
// "@context": "https://schema.org",
|
|
347
|
+
// "@type": "DigitalDocument",
|
|
348
|
+
// name: "myfile.pdf"
|
|
349
|
+
// },
|
|
350
|
+
// nodeIdentity:
|
|
351
|
+
// "did:entity-storage:0x6363636363636363636363636363636363636363636363636363636363636363",
|
|
352
|
+
// userIdentity:
|
|
353
|
+
// "did:entity-storage:0x6363636363636363636363636363636363636363636363636363636363636363"
|
|
354
|
+
// }
|
|
355
|
+
// ]
|
|
356
|
+
// }
|
|
357
|
+
// }
|
|
358
|
+
// }
|
|
359
|
+
// ]
|
|
360
|
+
// }
|
|
361
|
+
]
|
|
362
|
+
};
|
|
310
363
|
return [
|
|
311
|
-
|
|
364
|
+
documentManagementCreateRoute,
|
|
365
|
+
documentManagementUpdateRoute,
|
|
312
366
|
documentManagementGetRoute,
|
|
313
367
|
documentManagementRemoveRoute,
|
|
314
368
|
documentManagementQueryRoute
|
|
315
369
|
];
|
|
316
370
|
}
|
|
317
371
|
/**
|
|
318
|
-
*
|
|
372
|
+
* Create a document as an auditable item graph vertex.
|
|
319
373
|
* @param httpRequestContext The request context for the API.
|
|
320
374
|
* @param componentName The name of the component to use in the routes.
|
|
321
375
|
* @param request The request.
|
|
322
376
|
* @returns The response object with additional http response properties.
|
|
323
377
|
*/
|
|
324
|
-
async function
|
|
378
|
+
async function documentManagementCreate(httpRequestContext, componentName, request) {
|
|
325
379
|
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
380
|
Guards.object(ROUTES_SOURCE, "request.body", request.body);
|
|
329
381
|
Guards.stringBase64(ROUTES_SOURCE, "request.body.blob", request.body.blob);
|
|
330
382
|
const component = ComponentFactory.get(componentName);
|
|
331
|
-
const id = await component.
|
|
383
|
+
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
384
|
createAttestation: request.body.createAttestation,
|
|
333
|
-
|
|
385
|
+
addAlias: request.body.addAlias,
|
|
334
386
|
aliasAnnotationObject: request.body.aliasAnnotationObject
|
|
335
387
|
}, httpRequestContext.userIdentity, httpRequestContext.nodeIdentity);
|
|
336
388
|
return {
|
|
@@ -350,17 +402,15 @@ async function documentManagementSet(httpRequestContext, componentName, request)
|
|
|
350
402
|
async function documentManagementGet(httpRequestContext, componentName, request) {
|
|
351
403
|
Guards.object(ROUTES_SOURCE, "request", request);
|
|
352
404
|
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);
|
|
405
|
+
Guards.stringValue(ROUTES_SOURCE, "request.pathParams.auditableItemGraphDocumentId", request.pathParams.auditableItemGraphDocumentId);
|
|
355
406
|
const mimeType = request.headers?.[HeaderTypes.Accept] === MimeTypes.JsonLd ? "jsonld" : "json";
|
|
356
407
|
const component = ComponentFactory.get(componentName);
|
|
357
|
-
const result = await component.get(request.pathParams.
|
|
408
|
+
const result = await component.get(request.pathParams.auditableItemGraphDocumentId, {
|
|
358
409
|
includeBlobStorageMetadata: Coerce.boolean(request.query?.includeBlobStorageMetadata),
|
|
359
410
|
includeBlobStorageData: Coerce.boolean(request.query?.includeBlobStorageData),
|
|
360
411
|
includeAttestation: Coerce.boolean(request.query?.includeAttestation),
|
|
361
|
-
includeRemoved: Coerce.boolean(request.query?.includeRemoved)
|
|
362
|
-
|
|
363
|
-
}, request.query?.revisionCursor, httpRequestContext.userIdentity, httpRequestContext.nodeIdentity);
|
|
412
|
+
includeRemoved: Coerce.boolean(request.query?.includeRemoved)
|
|
413
|
+
}, request.query?.cursor, Coerce.integer(request.query?.pageSize), httpRequestContext.userIdentity, httpRequestContext.nodeIdentity);
|
|
364
414
|
return {
|
|
365
415
|
headers: {
|
|
366
416
|
[HeaderTypes.ContentType]: mimeType === "json" ? MimeTypes.Json : MimeTypes.JsonLd
|
|
@@ -368,6 +418,23 @@ async function documentManagementGet(httpRequestContext, componentName, request)
|
|
|
368
418
|
body: result
|
|
369
419
|
};
|
|
370
420
|
}
|
|
421
|
+
/**
|
|
422
|
+
* UPdate the document from the auditable item graph vertex.
|
|
423
|
+
* @param httpRequestContext The request context for the API.
|
|
424
|
+
* @param componentName The name of the component to use in the routes.
|
|
425
|
+
* @param request The request.
|
|
426
|
+
* @returns The response object with additional http response properties.
|
|
427
|
+
*/
|
|
428
|
+
async function documentManagementUpdate(httpRequestContext, componentName, request) {
|
|
429
|
+
Guards.object(ROUTES_SOURCE, "request", request);
|
|
430
|
+
Guards.object(ROUTES_SOURCE, "request.pathParams", request.pathParams);
|
|
431
|
+
Guards.stringValue(ROUTES_SOURCE, "request.pathParams.auditableItemGraphDocumentId", request.pathParams.auditableItemGraphDocumentId);
|
|
432
|
+
const component = ComponentFactory.get(componentName);
|
|
433
|
+
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);
|
|
434
|
+
return {
|
|
435
|
+
statusCode: HttpStatusCode.noContent
|
|
436
|
+
};
|
|
437
|
+
}
|
|
371
438
|
/**
|
|
372
439
|
* Remove the document from the auditable item graph vertex.
|
|
373
440
|
* @param httpRequestContext The request context for the API.
|
|
@@ -378,12 +445,11 @@ async function documentManagementGet(httpRequestContext, componentName, request)
|
|
|
378
445
|
async function documentManagementRemove(httpRequestContext, componentName, request) {
|
|
379
446
|
Guards.object(ROUTES_SOURCE, "request", request);
|
|
380
447
|
Guards.object(ROUTES_SOURCE, "request.pathParams", request.pathParams);
|
|
381
|
-
Guards.stringValue(ROUTES_SOURCE, "request.pathParams.
|
|
382
|
-
|
|
448
|
+
Guards.stringValue(ROUTES_SOURCE, "request.pathParams.auditableItemGraphDocumentId", request.pathParams.auditableItemGraphDocumentId);
|
|
449
|
+
const revision = Coerce.number(request.pathParams.revision);
|
|
450
|
+
Guards.integer(ROUTES_SOURCE, "request.pathParams.revision", revision);
|
|
383
451
|
const component = ComponentFactory.get(componentName);
|
|
384
|
-
await component.
|
|
385
|
-
removeAllRevisions: request.query?.removeAllRevisions
|
|
386
|
-
}, httpRequestContext.userIdentity, httpRequestContext.nodeIdentity);
|
|
452
|
+
await component.removeRevision(request.pathParams.auditableItemGraphDocumentId, revision, httpRequestContext.userIdentity, httpRequestContext.nodeIdentity);
|
|
387
453
|
return {
|
|
388
454
|
statusCode: HttpStatusCode.noContent
|
|
389
455
|
};
|
|
@@ -397,14 +463,11 @@ async function documentManagementRemove(httpRequestContext, componentName, reque
|
|
|
397
463
|
*/
|
|
398
464
|
async function documentManagementQuery(httpRequestContext, componentName, request) {
|
|
399
465
|
Guards.object(ROUTES_SOURCE, "request", request);
|
|
400
|
-
Guards.object(ROUTES_SOURCE, "request.
|
|
401
|
-
Guards.stringValue(ROUTES_SOURCE, "request.
|
|
466
|
+
Guards.object(ROUTES_SOURCE, "request.query", request.query);
|
|
467
|
+
Guards.stringValue(ROUTES_SOURCE, "request.query.documentId", request.query.documentId);
|
|
402
468
|
const mimeType = request.headers?.[HeaderTypes.Accept] === MimeTypes.JsonLd ? "jsonld" : "json";
|
|
403
469
|
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);
|
|
470
|
+
const result = await component.query(request.query.documentId, request.query?.cursor, Coerce.integer(request.query?.pageSize), httpRequestContext.userIdentity, httpRequestContext.nodeIdentity);
|
|
408
471
|
return {
|
|
409
472
|
headers: {
|
|
410
473
|
[HeaderTypes.ContentType]: mimeType === "json" ? MimeTypes.Json : MimeTypes.JsonLd
|
|
@@ -421,11 +484,6 @@ class DocumentManagementService {
|
|
|
421
484
|
* The namespace supported by the document management service.
|
|
422
485
|
*/
|
|
423
486
|
static NAMESPACE = "documents";
|
|
424
|
-
/**
|
|
425
|
-
* Default Page Size for cursor.
|
|
426
|
-
* @internal
|
|
427
|
-
*/
|
|
428
|
-
static _DEFAULT_PAGE_SIZE = 20;
|
|
429
487
|
/**
|
|
430
488
|
* Runtime name for the class.
|
|
431
489
|
*/
|
|
@@ -456,166 +514,211 @@ class DocumentManagementService {
|
|
|
456
514
|
SchemaOrgDataTypes.registerRedirects();
|
|
457
515
|
}
|
|
458
516
|
/**
|
|
459
|
-
* Store a document
|
|
517
|
+
* Store a document as an auditable item graph vertex and add its content to blob storage.
|
|
460
518
|
* If the document id already exists and the blob data is different a new revision will be created.
|
|
461
519
|
* 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
520
|
* @param documentId The document id to create.
|
|
464
521
|
* @param documentIdFormat The format of the document identifier.
|
|
465
522
|
* @param documentCode The code for the document type.
|
|
466
|
-
* @param blob The data to create the document.
|
|
523
|
+
* @param blob The data to create the document with.
|
|
467
524
|
* @param annotationObject Additional information to associate with the document.
|
|
525
|
+
* @param auditableItemGraphEdges The auditable item graph vertices to connect the document to.
|
|
468
526
|
* @param options Additional options for the set operation.
|
|
469
527
|
* @param options.createAttestation Flag to create an attestation for the document, defaults to false.
|
|
470
|
-
* @param options.
|
|
471
|
-
* @param options.aliasAnnotationObject
|
|
528
|
+
* @param options.addAlias Flag to add the document id as an alias to the aig vertex, defaults to true.
|
|
529
|
+
* @param options.aliasAnnotationObject Annotation object for the alias.
|
|
472
530
|
* @param userIdentity The identity to perform the auditable item graph operation with.
|
|
473
531
|
* @param nodeIdentity The node identity to use for vault operations.
|
|
474
|
-
* @returns The
|
|
532
|
+
* @returns The auditable item graph vertex created for the document including its revision.
|
|
475
533
|
*/
|
|
476
|
-
async
|
|
477
|
-
Guards.stringValue(this.CLASS_NAME, "auditableItemGraphId", auditableItemGraphId);
|
|
534
|
+
async create(documentId, documentIdFormat, documentCode, blob, annotationObject, auditableItemGraphEdges, options, userIdentity, nodeIdentity) {
|
|
478
535
|
Guards.stringValue(this.CLASS_NAME, "documentId", documentId);
|
|
479
536
|
Guards.arrayOneOf(this.CLASS_NAME, "documentCode", documentCode, Object.values(UneceDocumentCodes));
|
|
480
537
|
Guards.uint8Array(this.CLASS_NAME, "blob", blob);
|
|
481
538
|
Guards.stringValue(this.CLASS_NAME, "userIdentity", userIdentity);
|
|
482
539
|
Guards.stringValue(this.CLASS_NAME, "nodeIdentity", nodeIdentity);
|
|
483
540
|
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
|
-
});
|
|
541
|
+
// Get the connected vertices first, if one fails we abort the create
|
|
542
|
+
const connectedVertices = {};
|
|
543
|
+
if (Is.arrayValue(auditableItemGraphEdges)) {
|
|
544
|
+
for (const edge of auditableItemGraphEdges) {
|
|
545
|
+
connectedVertices[edge.id] = await this._auditableItemGraphComponent.get(edge.id);
|
|
501
546
|
}
|
|
502
547
|
}
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
548
|
+
const documentVertex = {};
|
|
549
|
+
if (options?.addAlias ?? true) {
|
|
550
|
+
documentVertex.aliases ??= [];
|
|
551
|
+
documentVertex.aliases.push({
|
|
552
|
+
"@context": AuditableItemGraphContexts.ContextRoot,
|
|
553
|
+
type: AuditableItemGraphTypes.Alias,
|
|
554
|
+
id: documentId,
|
|
555
|
+
aliasFormat: documentIdFormat,
|
|
556
|
+
annotationObject: options?.aliasAnnotationObject
|
|
557
|
+
});
|
|
513
558
|
}
|
|
514
|
-
//
|
|
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;
|
|
534
|
-
}
|
|
535
|
-
// Nothing matches the current blob hash so upload it to blob storage
|
|
559
|
+
// Add the blob to blob storage
|
|
536
560
|
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 = {
|
|
561
|
+
const currentRevision = {
|
|
541
562
|
"@context": [
|
|
542
563
|
DocumentContexts.ContextRoot,
|
|
543
564
|
DocumentContexts.ContextRootCommon,
|
|
544
565
|
SchemaOrgContexts.ContextRoot
|
|
545
566
|
],
|
|
546
567
|
type: DocumentTypes.Document,
|
|
547
|
-
id:
|
|
568
|
+
id: `${documentId}:0`,
|
|
548
569
|
documentId,
|
|
549
570
|
documentIdFormat,
|
|
550
571
|
documentCode,
|
|
551
|
-
documentRevision,
|
|
552
|
-
blobStorageId,
|
|
553
|
-
blobHash,
|
|
572
|
+
documentRevision: 0,
|
|
554
573
|
annotationObject,
|
|
574
|
+
blobHash: this.generateBlobHash(blob),
|
|
575
|
+
blobStorageId,
|
|
555
576
|
dateCreated: new Date(Date.now()).toISOString(),
|
|
556
577
|
nodeIdentity,
|
|
557
578
|
userIdentity
|
|
558
579
|
};
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
document.attestationId = await this.createAttestation(document, userIdentity, nodeIdentity);
|
|
580
|
+
if (options?.createAttestation ?? false) {
|
|
581
|
+
currentRevision.attestationId = await this.createAttestation(currentRevision, userIdentity, nodeIdentity);
|
|
562
582
|
}
|
|
563
|
-
// Add the new revision in to the
|
|
564
|
-
|
|
583
|
+
// Add the new revision in to the vertex
|
|
584
|
+
documentVertex.resources ??= [];
|
|
585
|
+
documentVertex.resources.push({
|
|
565
586
|
"@context": AuditableItemGraphContexts.ContextRoot,
|
|
566
587
|
type: AuditableItemGraphTypes.Resource,
|
|
567
|
-
resourceObject:
|
|
588
|
+
resourceObject: currentRevision
|
|
568
589
|
});
|
|
569
|
-
|
|
570
|
-
|
|
590
|
+
// Add the edges from the document to the items
|
|
591
|
+
this.updateEdges(documentVertex, auditableItemGraphEdges);
|
|
592
|
+
// And create the vertex
|
|
593
|
+
const vertexId = await this._auditableItemGraphComponent.create(documentVertex, userIdentity, nodeIdentity);
|
|
594
|
+
// Now add the edges to the connected vertices
|
|
595
|
+
await this.updateConnectedEdges(connectedVertices, vertexId, [], auditableItemGraphEdges, documentId, documentIdFormat, userIdentity, nodeIdentity);
|
|
596
|
+
return vertexId;
|
|
597
|
+
}
|
|
598
|
+
catch (error) {
|
|
599
|
+
if (BaseError.someErrorName(error, "NotFoundError")) {
|
|
600
|
+
throw error;
|
|
601
|
+
}
|
|
602
|
+
throw new GeneralError(this.CLASS_NAME, "createFailed", undefined, error);
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
/**
|
|
606
|
+
* Update a document as an auditable item graph vertex and add its content to blob storage.
|
|
607
|
+
* If the blob data is different a new revision will be created.
|
|
608
|
+
* For any other changes the current revision will be updated.
|
|
609
|
+
* @param auditableItemGraphDocumentId The auditable item graph vertex id which contains the document.
|
|
610
|
+
* @param blob The data to update the document with.
|
|
611
|
+
* @param annotationObject Additional information to associate with the document.
|
|
612
|
+
* @param auditableItemGraphEdges The auditable item graph vertices to connect the document to, if undefined retains current connections.
|
|
613
|
+
* @param userIdentity The identity to perform the auditable item graph operation with.
|
|
614
|
+
* @param nodeIdentity The node identity to use for vault operations.
|
|
615
|
+
* @returns Nothing.
|
|
616
|
+
*/
|
|
617
|
+
async update(auditableItemGraphDocumentId, blob, annotationObject, auditableItemGraphEdges, userIdentity, nodeIdentity) {
|
|
618
|
+
Urn.guard(this.CLASS_NAME, "auditableItemGraphDocumentId", auditableItemGraphDocumentId);
|
|
619
|
+
Guards.stringValue(this.CLASS_NAME, "userIdentity", userIdentity);
|
|
620
|
+
Guards.stringValue(this.CLASS_NAME, "nodeIdentity", nodeIdentity);
|
|
621
|
+
try {
|
|
622
|
+
const documentVertex = await this._auditableItemGraphComponent.get(auditableItemGraphDocumentId);
|
|
623
|
+
if (Is.empty(documentVertex.resources)) {
|
|
624
|
+
throw new NotFoundError(this.CLASS_NAME, "documentRevisionNone");
|
|
625
|
+
}
|
|
626
|
+
const documents = await this.getDocumentsFromVertex(documentVertex);
|
|
627
|
+
const latestRevision = documents.documents[0];
|
|
628
|
+
if (Is.empty(latestRevision)) {
|
|
629
|
+
throw new NotFoundError(this.CLASS_NAME, "documentRevisionNone");
|
|
630
|
+
}
|
|
631
|
+
// If auditableItemGraphEdges is undefined we are not updating the edges
|
|
632
|
+
// an empty array can be passed to remove all edges
|
|
633
|
+
const connectedVertices = {};
|
|
634
|
+
if (Is.array(auditableItemGraphEdges)) {
|
|
635
|
+
// Get the updated connected vertices first, if one fails we abort the update
|
|
636
|
+
for (const edge of auditableItemGraphEdges) {
|
|
637
|
+
connectedVertices[edge.id] = await this._auditableItemGraphComponent.get(edge.id);
|
|
638
|
+
}
|
|
639
|
+
// Also get the current edges in case some need disconnecting
|
|
640
|
+
if (Is.arrayValue(documents.edges)) {
|
|
641
|
+
for (const edgeId of documents.edges) {
|
|
642
|
+
// If we haven't retrieved the edge then it must be one that needs removing
|
|
643
|
+
if (Is.empty(connectedVertices[edgeId])) {
|
|
644
|
+
connectedVertices[edgeId] = await this._auditableItemGraphComponent.get(edgeId);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
let updatedVertex = false;
|
|
650
|
+
// If the blob is set and its hash has changed then we create a new revision
|
|
651
|
+
if (Is.uint8Array(blob)) {
|
|
652
|
+
const newBlobHash = this.generateBlobHash(blob);
|
|
653
|
+
if (latestRevision.blobHash !== newBlobHash) {
|
|
654
|
+
// Add the blob to blob storage
|
|
655
|
+
const blobStorageId = await this._blobStorageComponent.create(Converter.bytesToBase64(blob), undefined, undefined, undefined, undefined, userIdentity, nodeIdentity);
|
|
656
|
+
const newRevision = ObjectHelper.clone(latestRevision);
|
|
657
|
+
newRevision.documentRevision++;
|
|
658
|
+
newRevision.id = `${newRevision.documentId}:${newRevision.documentRevision}`;
|
|
659
|
+
newRevision.blobHash = newBlobHash;
|
|
660
|
+
newRevision.blobStorageId = blobStorageId;
|
|
661
|
+
newRevision.annotationObject = annotationObject;
|
|
662
|
+
if (Is.stringValue(latestRevision.attestationId)) {
|
|
663
|
+
newRevision.attestationId = await this.createAttestation(newRevision, userIdentity, nodeIdentity);
|
|
664
|
+
}
|
|
665
|
+
documentVertex.resources.push({
|
|
666
|
+
"@context": AuditableItemGraphContexts.ContextRoot,
|
|
667
|
+
type: AuditableItemGraphTypes.Resource,
|
|
668
|
+
resourceObject: newRevision
|
|
669
|
+
});
|
|
670
|
+
updatedVertex = true;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
// If the blob wasn't updated but the annotation object has then update the current revision
|
|
674
|
+
// instead of creating a new one
|
|
675
|
+
if (!updatedVertex &&
|
|
676
|
+
!ObjectHelper.equal(latestRevision.annotationObject, annotationObject)) {
|
|
677
|
+
updatedVertex = true;
|
|
678
|
+
latestRevision.annotationObject = annotationObject;
|
|
679
|
+
latestRevision.dateModified = new Date(Date.now()).toISOString();
|
|
680
|
+
}
|
|
681
|
+
const existingEdgeIds = documentVertex.edges?.map(e => e.id) ?? [];
|
|
682
|
+
// Update the edges from the document to the items
|
|
683
|
+
const edgesUpdated = this.updateEdges(documentVertex, auditableItemGraphEdges);
|
|
684
|
+
if (edgesUpdated) {
|
|
685
|
+
updatedVertex = true;
|
|
686
|
+
}
|
|
687
|
+
if (updatedVertex) {
|
|
688
|
+
await this._auditableItemGraphComponent.update(documentVertex, userIdentity, nodeIdentity);
|
|
689
|
+
}
|
|
690
|
+
if (edgesUpdated) {
|
|
691
|
+
await this.updateConnectedEdges(connectedVertices, auditableItemGraphDocumentId, existingEdgeIds, auditableItemGraphEdges, latestRevision.documentId, latestRevision.documentIdFormat, userIdentity, nodeIdentity);
|
|
692
|
+
}
|
|
571
693
|
}
|
|
572
694
|
catch (error) {
|
|
573
695
|
if (BaseError.someErrorName(error, "NotFoundError")) {
|
|
574
696
|
throw error;
|
|
575
697
|
}
|
|
576
|
-
throw new GeneralError(this.CLASS_NAME, "
|
|
698
|
+
throw new GeneralError(this.CLASS_NAME, "updateFailed", undefined, error);
|
|
577
699
|
}
|
|
578
700
|
}
|
|
579
701
|
/**
|
|
580
|
-
* Get a
|
|
581
|
-
* @param
|
|
582
|
-
* @param identifier The identifier of the document to get.
|
|
702
|
+
* Get a document using it's auditable item graph vertex id and optional revision.
|
|
703
|
+
* @param auditableItemGraphDocumentId The auditable item graph vertex id which contains the document.
|
|
583
704
|
* @param options Additional options for the get operation.
|
|
584
705
|
* @param options.includeBlobStorageMetadata Flag to include the blob storage metadata for the document, defaults to false.
|
|
585
706
|
* @param options.includeBlobStorageData Flag to include the blob storage data for the document, defaults to false.
|
|
586
707
|
* @param options.includeAttestation Flag to include the attestation information for the document, defaults to false.
|
|
587
708
|
* @param options.includeRemoved Flag to include deleted documents, defaults to false.
|
|
588
|
-
* @param
|
|
589
|
-
* @param
|
|
709
|
+
* @param cursor The cursor to get the next chunk of revisions.
|
|
710
|
+
* @param pageSize Page size of items to return, defaults to 1 so only most recent is returned.
|
|
590
711
|
* @param userIdentity The identity to perform the auditable item graph operation with.
|
|
591
712
|
* @param nodeIdentity The node identity to use for vault operations.
|
|
592
713
|
* @returns The documents and revisions if requested, ordered by revision descending, cursor is set if there are more document revisions.
|
|
593
714
|
*/
|
|
594
|
-
async get(
|
|
595
|
-
Urn.guard(this.CLASS_NAME, "
|
|
596
|
-
Urn.guard(this.CLASS_NAME, "identifier", identifier);
|
|
715
|
+
async get(auditableItemGraphDocumentId, options, cursor, pageSize, userIdentity, nodeIdentity) {
|
|
716
|
+
Urn.guard(this.CLASS_NAME, "auditableItemGraphDocumentId", auditableItemGraphDocumentId);
|
|
597
717
|
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);
|
|
718
|
+
const documentVertex = await this._auditableItemGraphComponent.get(auditableItemGraphDocumentId, { includeDeleted: options?.includeRemoved });
|
|
610
719
|
// 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"]);
|
|
720
|
+
const documents = await this.getDocumentsFromVertex(documentVertex, options, cursor, pageSize, userIdentity, nodeIdentity);
|
|
721
|
+
return JsonLdProcessor.compact(documents, documents["@context"]);
|
|
619
722
|
}
|
|
620
723
|
catch (error) {
|
|
621
724
|
if (BaseError.someErrorName(error, "NotFoundError")) {
|
|
@@ -625,112 +728,53 @@ class DocumentManagementService {
|
|
|
625
728
|
}
|
|
626
729
|
}
|
|
627
730
|
/**
|
|
628
|
-
* Remove
|
|
629
|
-
* The
|
|
630
|
-
* @param
|
|
631
|
-
* @param
|
|
632
|
-
* @param options Additional options for the remove operation.
|
|
633
|
-
* @param options.removeAllRevisions Flag to remove all revisions of the document, defaults to false.
|
|
731
|
+
* Remove an auditable item graph vertex using it's id.
|
|
732
|
+
* The document dateDeleted will be set, but can still be queried with the includeRemoved flag.
|
|
733
|
+
* @param auditableItemGraphDocumentId The auditable item graph vertex id which contains the document.
|
|
734
|
+
* @param revision The revision of the document to remove.
|
|
634
735
|
* @param userIdentity The identity to perform the auditable item graph operation with.
|
|
635
736
|
* @param nodeIdentity The node identity to use for vault operations.
|
|
636
737
|
* @returns Nothing.
|
|
637
738
|
*/
|
|
638
|
-
async
|
|
639
|
-
Urn.guard(this.CLASS_NAME, "
|
|
640
|
-
|
|
739
|
+
async removeRevision(auditableItemGraphDocumentId, revision, userIdentity, nodeIdentity) {
|
|
740
|
+
Urn.guard(this.CLASS_NAME, "auditableItemGraphDocumentId", auditableItemGraphDocumentId);
|
|
741
|
+
Guards.number(this.CLASS_NAME, "revision", revision);
|
|
641
742
|
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
|
-
}
|
|
743
|
+
const documentVertex = await this._auditableItemGraphComponent.get(auditableItemGraphDocumentId);
|
|
744
|
+
if (Is.empty(documentVertex.resources)) {
|
|
745
|
+
throw new NotFoundError(this.CLASS_NAME, "documentRevisionNone");
|
|
654
746
|
}
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
matchingRevision.dateDeleted = new Date(now).toISOString();
|
|
659
|
-
}
|
|
660
|
-
else {
|
|
661
|
-
throw new NotFoundError(this.CLASS_NAME, "documentRevisionNotFound", identifier);
|
|
662
|
-
}
|
|
747
|
+
const docRevisionIndex = documentVertex.resources.findIndex(d => d.resourceObject?.documentRevision === revision);
|
|
748
|
+
if (docRevisionIndex === -1) {
|
|
749
|
+
throw new NotFoundError(this.CLASS_NAME, "documentRevisionNotFound", revision.toString());
|
|
663
750
|
}
|
|
664
|
-
|
|
751
|
+
documentVertex.resources.splice(docRevisionIndex, 1);
|
|
752
|
+
await this._auditableItemGraphComponent.update(documentVertex, userIdentity, nodeIdentity);
|
|
665
753
|
}
|
|
666
754
|
catch (error) {
|
|
667
755
|
if (BaseError.someErrorName(error, "NotFoundError")) {
|
|
668
756
|
throw error;
|
|
669
757
|
}
|
|
670
|
-
throw new GeneralError(this.CLASS_NAME, "
|
|
758
|
+
throw new GeneralError(this.CLASS_NAME, "removeRevisionFailed", undefined, error);
|
|
671
759
|
}
|
|
672
760
|
}
|
|
673
761
|
/**
|
|
674
|
-
*
|
|
675
|
-
* @param
|
|
676
|
-
* @param documentCodes The document codes to query for, if undefined gets all document codes.
|
|
677
|
-
* @param options Additional options for the query operation.
|
|
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.
|
|
762
|
+
* Find all the document with a specific id.
|
|
763
|
+
* @param documentId The document id to find in the graph.
|
|
680
764
|
* @param cursor The cursor to get the next chunk of documents.
|
|
765
|
+
* @param pageSize The page size to get the next chunk of documents.
|
|
681
766
|
* @param userIdentity The identity to perform the auditable item graph operation with.
|
|
682
767
|
* @param nodeIdentity The node identity to use for vault operations.
|
|
683
|
-
* @returns The
|
|
768
|
+
* @returns The graph vertices that contain documents referencing the specified document id.
|
|
684
769
|
*/
|
|
685
|
-
async query(
|
|
686
|
-
|
|
770
|
+
async query(documentId, cursor, pageSize, userIdentity, nodeIdentity) {
|
|
771
|
+
Guards.stringValue(this.CLASS_NAME, "documentId", documentId);
|
|
687
772
|
try {
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
const vertexDocs = this.filterDocumentsFromVertex(vertex);
|
|
694
|
-
let matchingDocIds = vertexDocs;
|
|
695
|
-
if (Is.arrayValue(documentCodes)) {
|
|
696
|
-
matchingDocIds = vertexDocs.filter(d => documentCodes.includes(d.documentCode));
|
|
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"]);
|
|
773
|
+
return this._auditableItemGraphComponent.query({
|
|
774
|
+
id: documentId,
|
|
775
|
+
idMode: "both",
|
|
776
|
+
resourceTypes: [DocumentTypes.Document]
|
|
777
|
+
}, undefined, undefined, undefined, ["id", "dateCreated", "dateModified", "aliases", "annotationObject", "resources", "edges"], cursor, pageSize);
|
|
734
778
|
}
|
|
735
779
|
catch (error) {
|
|
736
780
|
if (BaseError.someErrorName(error, "NotFoundError")) {
|
|
@@ -740,103 +784,144 @@ class DocumentManagementService {
|
|
|
740
784
|
}
|
|
741
785
|
}
|
|
742
786
|
/**
|
|
743
|
-
*
|
|
744
|
-
* @param
|
|
745
|
-
* @
|
|
787
|
+
* Update the edges of the document vertex.
|
|
788
|
+
* @param documentVertex The document vertex to update.
|
|
789
|
+
* @param auditableItemGraphEdges The list of edges to use.
|
|
790
|
+
* @returns True if the edges were updated.
|
|
746
791
|
* @internal
|
|
747
792
|
*/
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
793
|
+
updateEdges(documentVertex, auditableItemGraphEdges) {
|
|
794
|
+
let changed = false;
|
|
795
|
+
const existingEdgeIds = documentVertex.edges?.map(e => e.id) ?? [];
|
|
796
|
+
if (Is.array(auditableItemGraphEdges)) {
|
|
797
|
+
for (const aigEdge of auditableItemGraphEdges) {
|
|
798
|
+
const existingIndex = existingEdgeIds.indexOf(aigEdge.id);
|
|
799
|
+
if (existingIndex !== -1) {
|
|
800
|
+
// If the edge already exists then we don't need to add it again
|
|
801
|
+
// We just need to remove it from the list of existing ids
|
|
802
|
+
// any remaining after this loop will be need to be removed
|
|
803
|
+
existingEdgeIds.splice(existingIndex, 1);
|
|
804
|
+
}
|
|
805
|
+
else {
|
|
806
|
+
const vertexEdge = {
|
|
807
|
+
"@context": AuditableItemGraphContexts.ContextRoot,
|
|
808
|
+
type: AuditableItemGraphTypes.Edge,
|
|
809
|
+
id: aigEdge.id,
|
|
810
|
+
edgeRelationships: ["document"]
|
|
811
|
+
};
|
|
812
|
+
documentVertex.edges ??= [];
|
|
813
|
+
documentVertex.edges?.push(vertexEdge);
|
|
814
|
+
changed = true;
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
// Anything left in the existingEdgeIds array means they need to be removed
|
|
818
|
+
if (existingEdgeIds.length > 0 && Is.array(documentVertex.edges)) {
|
|
819
|
+
for (const existingEdgeId of existingEdgeIds) {
|
|
820
|
+
const existingIndex = documentVertex.edges.findIndex(e => e.id === existingEdgeId);
|
|
821
|
+
if (existingIndex !== -1) {
|
|
822
|
+
documentVertex.edges.splice(existingIndex, 1);
|
|
823
|
+
changed = true;
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
}
|
|
764
827
|
}
|
|
765
|
-
return
|
|
828
|
+
return changed;
|
|
766
829
|
}
|
|
767
830
|
/**
|
|
768
|
-
*
|
|
769
|
-
* @param
|
|
831
|
+
* Update the edges.
|
|
832
|
+
* @param connectedVertices The connected vertices for the edges.
|
|
833
|
+
* @param auditableItemGraphDocumentId The document id to use.
|
|
834
|
+
* @param documentVertex The document vertex to update.
|
|
835
|
+
* @param auditableItemGraphEdges The list of edges to use.
|
|
770
836
|
* @param documentId The document identifier.
|
|
771
|
-
* @param
|
|
772
|
-
* @
|
|
773
|
-
* @
|
|
774
|
-
*/
|
|
775
|
-
createIdentifier(documentCode, documentId, documentRevision) {
|
|
776
|
-
const docCode = this.parseDocumentCode(documentCode);
|
|
777
|
-
return `documents:${docCode}:${this.encodeDocumentIdentifier(documentId, documentRevision)}`;
|
|
778
|
-
}
|
|
779
|
-
/**
|
|
780
|
-
* Parse the document identifier from the full identifier.
|
|
781
|
-
* @param identifier The full identifier to parse.
|
|
782
|
-
* @returns The document identifier.
|
|
783
|
-
* @internal
|
|
784
|
-
*/
|
|
785
|
-
parseDocumentId(identifier) {
|
|
786
|
-
const urn = Urn.fromValidString(identifier);
|
|
787
|
-
const remainingParts = urn.namespaceSpecificParts();
|
|
788
|
-
if (remainingParts.length < 2) {
|
|
789
|
-
throw new GeneralError(this.CLASS_NAME, "invalidDocumentId", { identifier });
|
|
790
|
-
}
|
|
791
|
-
const documentCode = `unece:DocumentCodeList#${remainingParts[0]}`;
|
|
792
|
-
const { documentId, documentRevision } = this.decodeDocumentIdentifier(urn.namespaceSpecific(1));
|
|
793
|
-
return { documentCode, documentId, documentRevision };
|
|
794
|
-
}
|
|
795
|
-
/**
|
|
796
|
-
* Parse the document code from the full identifier.
|
|
797
|
-
* @param documentCode The document code to parse.
|
|
798
|
-
* @returns The document code.
|
|
837
|
+
* @param documentIdFormat The format of the document identifier.
|
|
838
|
+
* @param userIdentity The identity to perform the auditable item graph operation with.
|
|
839
|
+
* @param nodeIdentity The node identity to use for vault operations.
|
|
799
840
|
* @internal
|
|
800
841
|
*/
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
842
|
+
async updateConnectedEdges(connectedVertices, auditableItemGraphDocumentId, existingEdgeIds, auditableItemGraphEdges, documentId, documentIdFormat, userIdentity, nodeIdentity) {
|
|
843
|
+
if (Is.array(auditableItemGraphEdges)) {
|
|
844
|
+
for (const aigEdge of auditableItemGraphEdges) {
|
|
845
|
+
const connected = connectedVertices[aigEdge.id];
|
|
846
|
+
if (!Is.empty(connected)) {
|
|
847
|
+
let updatedConnected = false;
|
|
848
|
+
const existingIndex = existingEdgeIds.indexOf(aigEdge.id);
|
|
849
|
+
if (existingIndex !== -1) {
|
|
850
|
+
// If the edge already exists we remove it from the list of existing ids
|
|
851
|
+
// any remaining after this loop will be need to be disconnected
|
|
852
|
+
existingEdgeIds.splice(existingIndex, 1);
|
|
853
|
+
}
|
|
854
|
+
// Add the edge with the document vertex id if it doesn't already exist
|
|
855
|
+
const hasEdge = connected.edges?.some(e => e.id === auditableItemGraphDocumentId);
|
|
856
|
+
if (!hasEdge) {
|
|
857
|
+
const vertexEdge = {
|
|
858
|
+
"@context": AuditableItemGraphContexts.ContextRoot,
|
|
859
|
+
type: AuditableItemGraphTypes.Edge,
|
|
860
|
+
id: auditableItemGraphDocumentId,
|
|
861
|
+
edgeRelationships: ["document"]
|
|
862
|
+
};
|
|
863
|
+
connected.edges ??= [];
|
|
864
|
+
connected.edges?.push(vertexEdge);
|
|
865
|
+
updatedConnected = true;
|
|
866
|
+
}
|
|
867
|
+
// Add alias with the document id if option flag is set and it doesn't already exist
|
|
868
|
+
if (aigEdge.addAlias) {
|
|
869
|
+
const alias = connected.aliases?.find(a => a.id === documentId);
|
|
870
|
+
if (Is.empty(alias)) {
|
|
871
|
+
// No existing alias, so create one
|
|
872
|
+
const vertexAlias = {
|
|
873
|
+
"@context": AuditableItemGraphContexts.ContextRoot,
|
|
874
|
+
type: AuditableItemGraphTypes.Alias,
|
|
875
|
+
id: documentId,
|
|
876
|
+
aliasFormat: documentIdFormat,
|
|
877
|
+
annotationObject: aigEdge.aliasAnnotationObject
|
|
878
|
+
};
|
|
879
|
+
connected.aliases ??= [];
|
|
880
|
+
connected.aliases?.push(vertexAlias);
|
|
881
|
+
updatedConnected = true;
|
|
882
|
+
}
|
|
883
|
+
else if (!ObjectHelper.equal(alias.annotationObject, aigEdge.aliasAnnotationObject) ||
|
|
884
|
+
documentIdFormat !== alias.aliasFormat) {
|
|
885
|
+
// The alias already exists, but the format or annotation object has changed
|
|
886
|
+
alias.annotationObject = aigEdge.aliasAnnotationObject;
|
|
887
|
+
alias.aliasFormat = documentIdFormat;
|
|
888
|
+
updatedConnected = true;
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
if (updatedConnected) {
|
|
892
|
+
await this._auditableItemGraphComponent.update(connected, userIdentity, nodeIdentity);
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
}
|
|
806
896
|
}
|
|
807
|
-
|
|
808
|
-
if (
|
|
809
|
-
|
|
897
|
+
// Anything left in the existingEdgeIds array means they need to be removed
|
|
898
|
+
if (existingEdgeIds.length > 0) {
|
|
899
|
+
for (const existingEdgeId of existingEdgeIds) {
|
|
900
|
+
const connected = connectedVertices[existingEdgeId];
|
|
901
|
+
if (!Is.empty(connected)) {
|
|
902
|
+
let updatedConnected = false;
|
|
903
|
+
// Remove the edge from the connected vertex
|
|
904
|
+
if (Is.arrayValue(connected.edges)) {
|
|
905
|
+
const existingIndex = connected.edges.findIndex(e => e.id === auditableItemGraphDocumentId);
|
|
906
|
+
if (existingIndex !== -1) {
|
|
907
|
+
connected.edges.splice(existingIndex, 1);
|
|
908
|
+
updatedConnected = true;
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
// Remove the alias from the connected vertex
|
|
912
|
+
if (Is.arrayValue(connected.aliases)) {
|
|
913
|
+
const existingIndex = connected.aliases.findIndex(e => e.id === documentId);
|
|
914
|
+
if (existingIndex !== -1) {
|
|
915
|
+
connected.aliases.splice(existingIndex, 1);
|
|
916
|
+
updatedConnected = true;
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
if (updatedConnected) {
|
|
920
|
+
await this._auditableItemGraphComponent.update(connected, userIdentity, nodeIdentity);
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
}
|
|
810
924
|
}
|
|
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
925
|
}
|
|
841
926
|
/**
|
|
842
927
|
* Generate a hash for the blob data.
|
|
@@ -849,47 +934,71 @@ class DocumentManagementService {
|
|
|
849
934
|
}
|
|
850
935
|
/**
|
|
851
936
|
* Get the documents from the auditable item graph vertex.
|
|
852
|
-
* @param
|
|
853
|
-
* @param identifier The full document identifier.
|
|
937
|
+
* @param documentVertex The vertex containing the documents.
|
|
854
938
|
* @param options Additional options for the get operation.
|
|
855
939
|
* @param options.includeBlobStorageMetadata Flag to include the blob storage metadata for the document, defaults to false.
|
|
856
940
|
* @param options.includeBlobStorageData Flag to include the blob storage data for the document, defaults to false.
|
|
857
941
|
* @param options.includeAttestation Flag to include the attestation information for the document, defaults to false.
|
|
858
942
|
* @param options.includeRemoved Flag to include deleted documents, defaults to false.
|
|
859
|
-
* @param
|
|
860
|
-
* @param
|
|
943
|
+
* @param cursor The cursor to get the next chunk of revisions.
|
|
944
|
+
* @param pageSize Page size of items to return, defaults to 1 so only most recent is returned.
|
|
861
945
|
* @param userIdentity The identity to perform the auditable item graph operation with.
|
|
862
946
|
* @param nodeIdentity The node identity to use for vault operations.
|
|
863
947
|
* @returns The finalised list of documents.
|
|
864
948
|
* @internal
|
|
865
949
|
*/
|
|
866
|
-
async
|
|
867
|
-
const
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
const
|
|
882
|
-
|
|
883
|
-
|
|
950
|
+
async getDocumentsFromVertex(documentVertex, options, cursor, pageSize, userIdentity, nodeIdentity) {
|
|
951
|
+
const docList = {
|
|
952
|
+
"@context": [
|
|
953
|
+
DocumentContexts.ContextRoot,
|
|
954
|
+
DocumentContexts.ContextRootCommon,
|
|
955
|
+
SchemaOrgContexts.ContextRoot
|
|
956
|
+
],
|
|
957
|
+
type: DocumentTypes.DocumentList,
|
|
958
|
+
documents: []
|
|
959
|
+
};
|
|
960
|
+
if (Is.arrayValue(documentVertex.resources)) {
|
|
961
|
+
// Sort by newest revision first
|
|
962
|
+
documentVertex.resources.sort((a, b) => (Coerce.number(b.resourceObject?.documentRevision) ?? 0) -
|
|
963
|
+
(Coerce.number(a.resourceObject?.documentRevision) ?? 0));
|
|
964
|
+
const startIndex = Coerce.integer(cursor) ?? 0;
|
|
965
|
+
const endIndex = Math.min(startIndex + (pageSize ?? 1), documentVertex.resources.length);
|
|
966
|
+
const slicedResources = documentVertex.resources.slice(startIndex, endIndex);
|
|
967
|
+
docList.cursor =
|
|
968
|
+
documentVertex.resources.length > endIndex ? (endIndex + 1).toString() : undefined;
|
|
969
|
+
const includeBlobStorageMetadata = options?.includeBlobStorageMetadata ?? false;
|
|
970
|
+
const includeBlobStorageData = options?.includeBlobStorageData ?? false;
|
|
971
|
+
const includeAttestation = options?.includeAttestation ?? false;
|
|
972
|
+
for (let i = 0; i < slicedResources.length; i++) {
|
|
973
|
+
const document = slicedResources[i].resourceObject;
|
|
974
|
+
if (Is.object(document)) {
|
|
975
|
+
docList.documents.push(document);
|
|
976
|
+
if (includeBlobStorageMetadata || includeBlobStorageData) {
|
|
977
|
+
const blobEntry = await this._blobStorageComponent.get(document.blobStorageId, includeBlobStorageData, userIdentity, nodeIdentity);
|
|
978
|
+
document.blobStorageEntry = blobEntry;
|
|
979
|
+
if (!docList["@context"].includes(BlobStorageContexts.ContextRoot)) {
|
|
980
|
+
docList["@context"].push(BlobStorageContexts.ContextRoot);
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
if (includeAttestation && Is.stringValue(document.attestationId)) {
|
|
984
|
+
const attestationInformation = await this._attestationComponent.get(document.attestationId);
|
|
985
|
+
document.attestationInformation = attestationInformation;
|
|
986
|
+
if (!docList["@context"].includes(AttestationContexts.ContextRoot)) {
|
|
987
|
+
docList["@context"].push(AttestationContexts.ContextRoot);
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
}
|
|
884
992
|
}
|
|
885
|
-
if (
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
993
|
+
if (Is.arrayValue(documentVertex.edges)) {
|
|
994
|
+
docList.edges ??= [];
|
|
995
|
+
for (const edge of documentVertex.edges) {
|
|
996
|
+
if (Is.object(edge)) {
|
|
997
|
+
docList.edges.push(edge.id);
|
|
998
|
+
}
|
|
999
|
+
}
|
|
889
1000
|
}
|
|
890
|
-
|
|
891
|
-
document.revisionCursor = nextRevisionCursor;
|
|
892
|
-
return document;
|
|
1001
|
+
return docList;
|
|
893
1002
|
}
|
|
894
1003
|
/**
|
|
895
1004
|
* Create an attestation for the document.
|
|
@@ -906,6 +1015,7 @@ class DocumentManagementService {
|
|
|
906
1015
|
SchemaOrgContexts.ContextRoot
|
|
907
1016
|
],
|
|
908
1017
|
type: DocumentTypes.DocumentAttestation,
|
|
1018
|
+
id: document.id,
|
|
909
1019
|
documentId: document.documentId,
|
|
910
1020
|
documentCode: document.documentCode,
|
|
911
1021
|
documentRevision: document.documentRevision,
|
|
@@ -925,4 +1035,4 @@ const restEntryPoints = [
|
|
|
925
1035
|
}
|
|
926
1036
|
];
|
|
927
1037
|
|
|
928
|
-
export { DocumentManagementService, documentManagementGet, documentManagementQuery, documentManagementRemove,
|
|
1038
|
+
export { DocumentManagementService, documentManagementCreate, documentManagementGet, documentManagementQuery, documentManagementRemove, documentManagementUpdate, generateRestRoutesDocumentManagement, restEntryPoints, tagsDocumentManagement };
|