@twin.org/document-management-service 0.0.1-next.2 → 0.0.1-next.3

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.
@@ -130,7 +130,9 @@ function generateRestRoutesDocumentManagement(baseRouteName, componentName) {
130
130
  "@context": "https://schema.org",
131
131
  "@type": "DigitalDocument",
132
132
  name: "myfile.pdf"
133
- }
133
+ },
134
+ nodeIdentity: "did:entity-storage:0x6363636363636363636363636363636363636363636363636363636363636363",
135
+ userIdentity: "did:entity-storage:0x6363636363636363636363636363636363636363636363636363636363636363"
134
136
  }
135
137
  }
136
138
  }
@@ -162,7 +164,9 @@ function generateRestRoutesDocumentManagement(baseRouteName, componentName) {
162
164
  "@context": "https://schema.org",
163
165
  "@type": "DigitalDocument",
164
166
  name: "myfile.pdf"
165
- }
167
+ },
168
+ nodeIdentity: "did:entity-storage:0x6363636363636363636363636363636363636363636363636363636363636363",
169
+ userIdentity: "did:entity-storage:0x6363636363636363636363636363636363636363636363636363636363636363"
166
170
  }
167
171
  }
168
172
  }
@@ -253,7 +257,9 @@ function generateRestRoutesDocumentManagement(baseRouteName, componentName) {
253
257
  "@context": "https://schema.org",
254
258
  "@type": "DigitalDocument",
255
259
  name: "myfile.pdf"
256
- }
260
+ },
261
+ nodeIdentity: "did:entity-storage:0x6363636363636363636363636363636363636363636363636363636363636363",
262
+ userIdentity: "did:entity-storage:0x6363636363636363636363636363636363636363636363636363636363636363"
257
263
  }
258
264
  ]
259
265
  }
@@ -291,7 +297,9 @@ function generateRestRoutesDocumentManagement(baseRouteName, componentName) {
291
297
  "@context": "https://schema.org",
292
298
  "@type": "DigitalDocument",
293
299
  name: "myfile.pdf"
294
- }
300
+ },
301
+ nodeIdentity: "did:entity-storage:0x6363636363636363636363636363636363636363636363636363636363636363",
302
+ userIdentity: "did:entity-storage:0x6363636363636363636363636363636363636363636363636363636363636363"
295
303
  }
296
304
  ]
297
305
  }
@@ -465,6 +473,8 @@ class DocumentManagementService {
465
473
  core.Guards.stringValue(this.CLASS_NAME, "documentId", documentId);
466
474
  core.Guards.arrayOneOf(this.CLASS_NAME, "documentCode", documentCode, Object.values(standardsUnece.UneceDocumentCodes));
467
475
  core.Guards.uint8Array(this.CLASS_NAME, "blob", blob);
476
+ core.Guards.stringValue(this.CLASS_NAME, "userIdentity", userIdentity);
477
+ core.Guards.stringValue(this.CLASS_NAME, "nodeIdentity", nodeIdentity);
468
478
  try {
469
479
  const vertex = await this._auditableItemGraphComponent.get(auditableItemGraphId);
470
480
  vertex.resources = vertex.resources ?? [];
@@ -472,23 +482,36 @@ class DocumentManagementService {
472
482
  const vertexDocs = this.filterDocumentsFromVertex(vertex);
473
483
  // Reduce the list to those with a matching id and code
474
484
  const matchingDocIds = this.findMatchingDocs(vertexDocs, documentId, documentCode, true);
485
+ const currentRevision = matchingDocIds[0];
486
+ // If the create attestation flag is not defined we check to see if any previous
487
+ // revisions have an attestation and if so we create one for the new revision.
488
+ if (core.Is.undefined(createAttestation)) {
489
+ createAttestation = matchingDocIds.some(d => core.Is.stringValue(d.attestationId));
490
+ }
475
491
  // Calculate the hash for the blob.
476
492
  const blobHash = this.generateBlobHash(blob);
477
- let documentRevision;
478
- if (core.Is.arrayValue(matchingDocIds) && matchingDocIds[0].blobHash === blobHash) {
479
- documentRevision = matchingDocIds[0].documentRevision;
480
- // If there is already a doc with the matching blob hash no need to create a new revision
481
- // instead we just update the annotation object if it has changed.
482
- if (!core.ObjectHelper.equal(matchingDocIds[0].annotationObject, annotationObject, false)) {
483
- matchingDocIds[0].dateModified = new Date().toISOString();
484
- matchingDocIds[0].annotationObject = annotationObject;
493
+ // Is the blob data the same as the current revision ?
494
+ if (currentRevision?.blobHash === blobHash) {
495
+ // Blob data matches so no need to create a new revision
496
+ // We update the current object if the annotation or createAttestation flag has changed.
497
+ let updated = false;
498
+ if (!core.ObjectHelper.equal(currentRevision.annotationObject, annotationObject, false)) {
499
+ currentRevision.annotationObject = annotationObject;
500
+ updated = true;
501
+ }
502
+ if (createAttestation && core.Is.empty(currentRevision.attestationId)) {
503
+ currentRevision.attestationId = await this.createAttestation(currentRevision, userIdentity, nodeIdentity);
504
+ updated = true;
505
+ }
506
+ if (updated) {
507
+ currentRevision.dateModified = new Date().toISOString();
485
508
  await this._auditableItemGraphComponent.update(vertex, userIdentity, nodeIdentity);
486
509
  }
487
- return matchingDocIds[0].id;
510
+ return currentRevision.id;
488
511
  }
489
512
  // Nothing matches the current blob hash so upload it to blob storage
490
513
  const blobStorageId = await this._blobStorageComponent.create(core.Converter.bytesToBase64(blob), undefined, undefined, undefined, undefined, userIdentity, nodeIdentity);
491
- documentRevision = matchingDocIds.length;
514
+ const documentRevision = matchingDocIds.length;
492
515
  // We are creating a new document, if there is already docs with the same id and code we use the list length
493
516
  // to determine the next revision number.
494
517
  const document = {
@@ -505,15 +528,15 @@ class DocumentManagementService {
505
528
  documentRevision,
506
529
  blobStorageId,
507
530
  blobHash,
508
- dateCreated: new Date(Date.now()).toISOString()
531
+ annotationObject,
532
+ dateCreated: new Date(Date.now()).toISOString(),
533
+ nodeIdentity,
534
+ userIdentity
509
535
  };
510
536
  // If the attestation flag is set then create it
511
537
  if (createAttestation ?? false) {
512
- document.attestationId = await this._attestationComponent.create(document, undefined, userIdentity, nodeIdentity);
538
+ document.attestationId = await this.createAttestation(document, userIdentity, nodeIdentity);
513
539
  }
514
- // We assign the annotation object after the attestation was created
515
- // as we don't want to include it in the attestation
516
- document.annotationObject = annotationObject;
517
540
  // Add the new revision in to the AIG
518
541
  vertex.resources.push({
519
542
  "@context": auditableItemGraphModels.AuditableItemGraphTypes.ContextRoot,
@@ -845,6 +868,29 @@ class DocumentManagementService {
845
868
  document.revisionCursor = nextRevisionCursor;
846
869
  return document;
847
870
  }
871
+ /**
872
+ * Create an attestation for the document.
873
+ * @param document The document to create the attestation for.
874
+ * @param userIdentity The identity to perform the attestation operation with.
875
+ * @param nodeIdentity The node identity to perform attestation operation with.
876
+ * @returns The attestation identifier.
877
+ */
878
+ async createAttestation(document, userIdentity, nodeIdentity) {
879
+ const documentAttestation = {
880
+ "@context": [
881
+ documentManagementModels.DocumentTypes.ContextRoot,
882
+ documentManagementModels.DocumentTypes.ContextRootCommon,
883
+ standardsSchemaOrg.SchemaOrgTypes.ContextRoot
884
+ ],
885
+ type: documentManagementModels.DocumentTypes.DocumentAttestation,
886
+ documentId: document.documentId,
887
+ documentCode: document.documentCode,
888
+ documentRevision: document.documentRevision,
889
+ dateCreated: document.dateCreated,
890
+ blobHash: document.blobHash
891
+ };
892
+ return this._attestationComponent.create(documentAttestation, undefined, userIdentity, nodeIdentity);
893
+ }
848
894
  }
849
895
 
850
896
  const restEntryPoints = [
@@ -128,7 +128,9 @@ function generateRestRoutesDocumentManagement(baseRouteName, componentName) {
128
128
  "@context": "https://schema.org",
129
129
  "@type": "DigitalDocument",
130
130
  name: "myfile.pdf"
131
- }
131
+ },
132
+ nodeIdentity: "did:entity-storage:0x6363636363636363636363636363636363636363636363636363636363636363",
133
+ userIdentity: "did:entity-storage:0x6363636363636363636363636363636363636363636363636363636363636363"
132
134
  }
133
135
  }
134
136
  }
@@ -160,7 +162,9 @@ function generateRestRoutesDocumentManagement(baseRouteName, componentName) {
160
162
  "@context": "https://schema.org",
161
163
  "@type": "DigitalDocument",
162
164
  name: "myfile.pdf"
163
- }
165
+ },
166
+ nodeIdentity: "did:entity-storage:0x6363636363636363636363636363636363636363636363636363636363636363",
167
+ userIdentity: "did:entity-storage:0x6363636363636363636363636363636363636363636363636363636363636363"
164
168
  }
165
169
  }
166
170
  }
@@ -251,7 +255,9 @@ function generateRestRoutesDocumentManagement(baseRouteName, componentName) {
251
255
  "@context": "https://schema.org",
252
256
  "@type": "DigitalDocument",
253
257
  name: "myfile.pdf"
254
- }
258
+ },
259
+ nodeIdentity: "did:entity-storage:0x6363636363636363636363636363636363636363636363636363636363636363",
260
+ userIdentity: "did:entity-storage:0x6363636363636363636363636363636363636363636363636363636363636363"
255
261
  }
256
262
  ]
257
263
  }
@@ -289,7 +295,9 @@ function generateRestRoutesDocumentManagement(baseRouteName, componentName) {
289
295
  "@context": "https://schema.org",
290
296
  "@type": "DigitalDocument",
291
297
  name: "myfile.pdf"
292
- }
298
+ },
299
+ nodeIdentity: "did:entity-storage:0x6363636363636363636363636363636363636363636363636363636363636363",
300
+ userIdentity: "did:entity-storage:0x6363636363636363636363636363636363636363636363636363636363636363"
293
301
  }
294
302
  ]
295
303
  }
@@ -463,6 +471,8 @@ class DocumentManagementService {
463
471
  Guards.stringValue(this.CLASS_NAME, "documentId", documentId);
464
472
  Guards.arrayOneOf(this.CLASS_NAME, "documentCode", documentCode, Object.values(UneceDocumentCodes));
465
473
  Guards.uint8Array(this.CLASS_NAME, "blob", blob);
474
+ Guards.stringValue(this.CLASS_NAME, "userIdentity", userIdentity);
475
+ Guards.stringValue(this.CLASS_NAME, "nodeIdentity", nodeIdentity);
466
476
  try {
467
477
  const vertex = await this._auditableItemGraphComponent.get(auditableItemGraphId);
468
478
  vertex.resources = vertex.resources ?? [];
@@ -470,23 +480,36 @@ class DocumentManagementService {
470
480
  const vertexDocs = this.filterDocumentsFromVertex(vertex);
471
481
  // Reduce the list to those with a matching id and code
472
482
  const matchingDocIds = this.findMatchingDocs(vertexDocs, documentId, documentCode, true);
483
+ const currentRevision = matchingDocIds[0];
484
+ // If the create attestation flag is not defined we check to see if any previous
485
+ // revisions have an attestation and if so we create one for the new revision.
486
+ if (Is.undefined(createAttestation)) {
487
+ createAttestation = matchingDocIds.some(d => Is.stringValue(d.attestationId));
488
+ }
473
489
  // Calculate the hash for the blob.
474
490
  const blobHash = this.generateBlobHash(blob);
475
- let documentRevision;
476
- if (Is.arrayValue(matchingDocIds) && matchingDocIds[0].blobHash === blobHash) {
477
- documentRevision = matchingDocIds[0].documentRevision;
478
- // If there is already a doc with the matching blob hash no need to create a new revision
479
- // instead we just update the annotation object if it has changed.
480
- if (!ObjectHelper.equal(matchingDocIds[0].annotationObject, annotationObject, false)) {
481
- matchingDocIds[0].dateModified = new Date().toISOString();
482
- matchingDocIds[0].annotationObject = annotationObject;
491
+ // Is the blob data the same as the current revision ?
492
+ if (currentRevision?.blobHash === blobHash) {
493
+ // Blob data matches so no need to create a new revision
494
+ // We update the current object if the annotation or createAttestation flag has changed.
495
+ let updated = false;
496
+ if (!ObjectHelper.equal(currentRevision.annotationObject, annotationObject, false)) {
497
+ currentRevision.annotationObject = annotationObject;
498
+ updated = true;
499
+ }
500
+ if (createAttestation && Is.empty(currentRevision.attestationId)) {
501
+ currentRevision.attestationId = await this.createAttestation(currentRevision, userIdentity, nodeIdentity);
502
+ updated = true;
503
+ }
504
+ if (updated) {
505
+ currentRevision.dateModified = new Date().toISOString();
483
506
  await this._auditableItemGraphComponent.update(vertex, userIdentity, nodeIdentity);
484
507
  }
485
- return matchingDocIds[0].id;
508
+ return currentRevision.id;
486
509
  }
487
510
  // Nothing matches the current blob hash so upload it to blob storage
488
511
  const blobStorageId = await this._blobStorageComponent.create(Converter.bytesToBase64(blob), undefined, undefined, undefined, undefined, userIdentity, nodeIdentity);
489
- documentRevision = matchingDocIds.length;
512
+ const documentRevision = matchingDocIds.length;
490
513
  // We are creating a new document, if there is already docs with the same id and code we use the list length
491
514
  // to determine the next revision number.
492
515
  const document = {
@@ -503,15 +526,15 @@ class DocumentManagementService {
503
526
  documentRevision,
504
527
  blobStorageId,
505
528
  blobHash,
506
- dateCreated: new Date(Date.now()).toISOString()
529
+ annotationObject,
530
+ dateCreated: new Date(Date.now()).toISOString(),
531
+ nodeIdentity,
532
+ userIdentity
507
533
  };
508
534
  // If the attestation flag is set then create it
509
535
  if (createAttestation ?? false) {
510
- document.attestationId = await this._attestationComponent.create(document, undefined, userIdentity, nodeIdentity);
536
+ document.attestationId = await this.createAttestation(document, userIdentity, nodeIdentity);
511
537
  }
512
- // We assign the annotation object after the attestation was created
513
- // as we don't want to include it in the attestation
514
- document.annotationObject = annotationObject;
515
538
  // Add the new revision in to the AIG
516
539
  vertex.resources.push({
517
540
  "@context": AuditableItemGraphTypes.ContextRoot,
@@ -843,6 +866,29 @@ class DocumentManagementService {
843
866
  document.revisionCursor = nextRevisionCursor;
844
867
  return document;
845
868
  }
869
+ /**
870
+ * Create an attestation for the document.
871
+ * @param document The document to create the attestation for.
872
+ * @param userIdentity The identity to perform the attestation operation with.
873
+ * @param nodeIdentity The node identity to perform attestation operation with.
874
+ * @returns The attestation identifier.
875
+ */
876
+ async createAttestation(document, userIdentity, nodeIdentity) {
877
+ const documentAttestation = {
878
+ "@context": [
879
+ DocumentTypes.ContextRoot,
880
+ DocumentTypes.ContextRootCommon,
881
+ SchemaOrgTypes.ContextRoot
882
+ ],
883
+ type: DocumentTypes.DocumentAttestation,
884
+ documentId: document.documentId,
885
+ documentCode: document.documentCode,
886
+ documentRevision: document.documentRevision,
887
+ dateCreated: document.dateCreated,
888
+ blobHash: document.blobHash
889
+ };
890
+ return this._attestationComponent.create(documentAttestation, undefined, userIdentity, nodeIdentity);
891
+ }
846
892
  }
847
893
 
848
894
  const restEntryPoints = [
@@ -87,4 +87,12 @@ export declare class DocumentManagementService implements IDocumentManagementCom
87
87
  includeMostRecentRevisions?: boolean;
88
88
  includeRemoved?: boolean;
89
89
  }, cursor?: string, userIdentity?: string, nodeIdentity?: string): Promise<IDocumentList>;
90
+ /**
91
+ * Create an attestation for the document.
92
+ * @param document The document to create the attestation for.
93
+ * @param userIdentity The identity to perform the attestation operation with.
94
+ * @param nodeIdentity The node identity to perform attestation operation with.
95
+ * @returns The attestation identifier.
96
+ */
97
+ private createAttestation;
90
98
  }
package/docs/changelog.md CHANGED
@@ -1,5 +1,5 @@
1
1
  # @twin.org/document-management-service - Changelog
2
2
 
3
- ## v0.0.1-next.2
3
+ ## v0.0.1-next.3
4
4
 
5
5
  - Initial Release
@@ -249,7 +249,9 @@
249
249
  "@context": "https://schema.org",
250
250
  "@type": "DigitalDocument",
251
251
  "name": "myfile.pdf"
252
- }
252
+ },
253
+ "nodeIdentity": "did:entity-storage:0x6363636363636363636363636363636363636363636363636363636363636363",
254
+ "userIdentity": "did:entity-storage:0x6363636363636363636363636363636363636363636363636363636363636363"
253
255
  }
254
256
  ]
255
257
  }
@@ -288,7 +290,9 @@
288
290
  "@context": "https://schema.org",
289
291
  "@type": "DigitalDocument",
290
292
  "name": "myfile.pdf"
291
- }
293
+ },
294
+ "nodeIdentity": "did:entity-storage:0x6363636363636363636363636363636363636363636363636363636363636363",
295
+ "userIdentity": "did:entity-storage:0x6363636363636363636363636363636363636363636363636363636363636363"
292
296
  }
293
297
  ]
294
298
  }
@@ -485,7 +489,9 @@
485
489
  "@context": "https://schema.org",
486
490
  "@type": "DigitalDocument",
487
491
  "name": "myfile.pdf"
488
- }
492
+ },
493
+ "nodeIdentity": "did:entity-storage:0x6363636363636363636363636363636363636363636363636363636363636363",
494
+ "userIdentity": "did:entity-storage:0x6363636363636363636363636363636363636363636363636363636363636363"
489
495
  }
490
496
  }
491
497
  }
@@ -515,7 +521,9 @@
515
521
  "@context": "https://schema.org",
516
522
  "@type": "DigitalDocument",
517
523
  "name": "myfile.pdf"
518
- }
524
+ },
525
+ "nodeIdentity": "did:entity-storage:0x6363636363636363636363636363636363636363636363636363636363636363",
526
+ "userIdentity": "did:entity-storage:0x6363636363636363636363636363636363636363636363636363636363636363"
519
527
  }
520
528
  }
521
529
  }
@@ -965,6 +973,14 @@
965
973
  "type": "string",
966
974
  "description": "The date/time of when the document was deleted, as we never actually remove items."
967
975
  },
976
+ "nodeIdentity": {
977
+ "type": "string",
978
+ "description": "The node which added the document to the graph."
979
+ },
980
+ "userIdentity": {
981
+ "type": "string",
982
+ "description": "The user who added the document to the graph."
983
+ },
968
984
  "revisions": {
969
985
  "type": "array",
970
986
  "items": {
@@ -986,7 +1002,9 @@
986
1002
  "documentRevision",
987
1003
  "blobStorageId",
988
1004
  "blobHash",
989
- "dateCreated"
1005
+ "dateCreated",
1006
+ "nodeIdentity",
1007
+ "userIdentity"
990
1008
  ],
991
1009
  "additionalProperties": false,
992
1010
  "description": "Interface describing a document."
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@twin.org/document-management-service",
3
- "version": "0.0.1-next.2",
3
+ "version": "0.0.1-next.3",
4
4
  "description": "Document management contract implementation and REST endpoint definitions",
5
5
  "repository": {
6
6
  "type": "git",
@@ -21,7 +21,7 @@
21
21
  "@twin.org/core": "next",
22
22
  "@twin.org/crypto": "next",
23
23
  "@twin.org/data-json-ld": "next",
24
- "@twin.org/document-management-models": "0.0.1-next.2",
24
+ "@twin.org/document-management-models": "0.0.1-next.3",
25
25
  "@twin.org/entity": "next",
26
26
  "@twin.org/entity-storage-models": "next",
27
27
  "@twin.org/nameof": "next",