@twin.org/auditable-item-graph-service 0.0.3-next.13 → 0.0.3-next.14
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/es/auditableItemGraphRoutes.js +162 -1
- package/dist/es/auditableItemGraphRoutes.js.map +1 -1
- package/dist/es/auditableItemGraphService.js +150 -7
- package/dist/es/auditableItemGraphService.js.map +1 -1
- package/dist/es/entities/auditableItemGraphChangeset.js +8 -0
- package/dist/es/entities/auditableItemGraphChangeset.js.map +1 -1
- package/dist/es/entities/auditableItemGraphVertex.js +8 -0
- package/dist/es/entities/auditableItemGraphVertex.js.map +1 -1
- package/dist/types/auditableItemGraphRoutes.d.ts +17 -1
- package/dist/types/auditableItemGraphService.d.ts +22 -1
- package/dist/types/entities/auditableItemGraphChangeset.d.ts +4 -0
- package/dist/types/entities/auditableItemGraphVertex.d.ts +4 -0
- package/docs/changelog.md +14 -0
- package/docs/open-api/spec.json +284 -0
- package/docs/reference/classes/AuditableItemGraphChangeset.md +8 -0
- package/docs/reference/classes/AuditableItemGraphService.md +82 -0
- package/docs/reference/classes/AuditableItemGraphVertex.md +8 -0
- package/docs/reference/functions/auditableItemGraphVersionGet.md +31 -0
- package/docs/reference/functions/auditableItemGraphVersionList.md +31 -0
- package/docs/reference/index.md +2 -0
- package/locales/en.json +3 -0
- package/package.json +2 -2
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0.
|
|
3
3
|
import { AuditableItemGraphContexts, AuditableItemGraphDataTypes, AuditableItemGraphTopics, AuditableItemGraphTypes, VerifyDepth } from "@twin.org/auditable-item-graph-models";
|
|
4
4
|
import { ContextIdKeys, ContextIdStore } from "@twin.org/context";
|
|
5
|
-
import { ArrayHelper, ComponentFactory, GeneralError, Guards, Is, JsonHelper, NotFoundError, ObjectHelper, RandomHelper, StringHelper, Urn, Validation } from "@twin.org/core";
|
|
5
|
+
import { ArrayHelper, Coerce, ComponentFactory, GeneralError, Guards, Is, JsonHelper, NotFoundError, ObjectHelper, RandomHelper, StringHelper, Urn, Validation } from "@twin.org/core";
|
|
6
6
|
import { DataTypeHelper } from "@twin.org/data-core";
|
|
7
7
|
import { JsonLdDataTypes, JsonLdHelper, JsonLdProcessor } from "@twin.org/data-json-ld";
|
|
8
8
|
import { ComparisonOperator, LogicalOperator, SortDirection } from "@twin.org/entity";
|
|
@@ -124,7 +124,8 @@ export class AuditableItemGraphService {
|
|
|
124
124
|
await this.updateEdgeList(context, vertexModel, vertex.edges);
|
|
125
125
|
delete originalEntity.aliasIndex;
|
|
126
126
|
delete originalEntity.resourceTypeIndex;
|
|
127
|
-
await this.addChangeset(context, originalEntity, vertexModel, true);
|
|
127
|
+
await this.addChangeset(context, originalEntity, vertexModel, true, 0);
|
|
128
|
+
vertexModel.version = 0;
|
|
128
129
|
await this._vertexStorage.set({
|
|
129
130
|
...vertexModel,
|
|
130
131
|
...this.buildIndexes(vertexModel)
|
|
@@ -183,9 +184,13 @@ export class AuditableItemGraphService {
|
|
|
183
184
|
await this.updateAliasList(context, newEntity, vertex.aliases);
|
|
184
185
|
await this.updateResourceList(context, newEntity, vertex.resources);
|
|
185
186
|
await this.updateEdgeList(context, newEntity, vertex.edges);
|
|
186
|
-
const
|
|
187
|
+
const nextVersion = Is.empty(vertexEntity.version)
|
|
188
|
+
? (await this.internalGetChangesets(vertexId)).length
|
|
189
|
+
: vertexEntity.version + 1;
|
|
190
|
+
const patches = await this.addChangeset(context, originalEntity, newEntity, false, nextVersion);
|
|
187
191
|
if (patches.length > 0) {
|
|
188
192
|
newEntity.dateModified = context.now;
|
|
193
|
+
newEntity.version = nextVersion;
|
|
189
194
|
const indexes = this.buildIndexes(newEntity);
|
|
190
195
|
await this._vertexStorage.set({
|
|
191
196
|
...newEntity,
|
|
@@ -348,6 +353,105 @@ export class AuditableItemGraphService {
|
|
|
348
353
|
throw new GeneralError(AuditableItemGraphService.CLASS_NAME, "getFailed", undefined, error);
|
|
349
354
|
}
|
|
350
355
|
}
|
|
356
|
+
/**
|
|
357
|
+
* Get a graph vertex at a specific version.
|
|
358
|
+
* @param id The id of the vertex.
|
|
359
|
+
* @param versionId The id of the version (changeset id) to retrieve.
|
|
360
|
+
* @returns The vertex reconstructed at that version.
|
|
361
|
+
* @throws NotFoundError if the vertex or version is not found.
|
|
362
|
+
*/
|
|
363
|
+
async getVersion(id, versionId) {
|
|
364
|
+
Guards.stringValue(AuditableItemGraphService.CLASS_NAME, "id", id);
|
|
365
|
+
Guards.stringValue(AuditableItemGraphService.CLASS_NAME, "versionId", versionId);
|
|
366
|
+
const urnParsed = Urn.fromValidString(id);
|
|
367
|
+
if (urnParsed.namespaceIdentifier() !== AuditableItemGraphService.NAMESPACE) {
|
|
368
|
+
throw new GeneralError(AuditableItemGraphService.CLASS_NAME, "namespaceMismatch", {
|
|
369
|
+
namespace: AuditableItemGraphService.NAMESPACE,
|
|
370
|
+
id
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
try {
|
|
374
|
+
const vertexId = urnParsed.namespaceSpecific(0);
|
|
375
|
+
const vertexEntity = await this._vertexStorage.get(vertexId);
|
|
376
|
+
if (Is.empty(vertexEntity)) {
|
|
377
|
+
throw new NotFoundError(AuditableItemGraphService.CLASS_NAME, "vertexNotFound", id);
|
|
378
|
+
}
|
|
379
|
+
const targetChangeset = await this._changesetStorage.get(versionId);
|
|
380
|
+
if (Is.empty(targetChangeset) || targetChangeset.vertexId !== vertexId) {
|
|
381
|
+
throw new NotFoundError(AuditableItemGraphService.CLASS_NAME, "versionNotFound", versionId);
|
|
382
|
+
}
|
|
383
|
+
const changesets = await this.internalGetChangesets(vertexId, {
|
|
384
|
+
maxVersion: targetChangeset.version
|
|
385
|
+
});
|
|
386
|
+
let entityState = {
|
|
387
|
+
id: vertexEntity.id,
|
|
388
|
+
dateCreated: vertexEntity.dateCreated,
|
|
389
|
+
organizationIdentity: vertexEntity.organizationIdentity
|
|
390
|
+
};
|
|
391
|
+
for (const changeset of changesets) {
|
|
392
|
+
entityState = JsonHelper.patch(entityState, changeset.patches);
|
|
393
|
+
}
|
|
394
|
+
const vertexModel = this.vertexEntityToJsonLd(entityState);
|
|
395
|
+
vertexModel.version = targetChangeset.version;
|
|
396
|
+
const result = await JsonLdProcessor.compact(vertexModel, vertexModel["@context"]);
|
|
397
|
+
return result;
|
|
398
|
+
}
|
|
399
|
+
catch (error) {
|
|
400
|
+
throw new GeneralError(AuditableItemGraphService.CLASS_NAME, "getVersionFailed", undefined, error);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Get all versions of a graph vertex.
|
|
405
|
+
* @param id The id of the vertex.
|
|
406
|
+
* @param options Additional options for the operation.
|
|
407
|
+
* @param options.after Only return versions created after this ISO 8601 timestamp (exclusive).
|
|
408
|
+
* @param options.before Only return versions created before this ISO 8601 timestamp (exclusive).
|
|
409
|
+
* @returns The list of vertex versions.
|
|
410
|
+
* @throws NotFoundError if the vertex is not found.
|
|
411
|
+
*/
|
|
412
|
+
async getVersions(id, options) {
|
|
413
|
+
Guards.stringValue(AuditableItemGraphService.CLASS_NAME, "id", id);
|
|
414
|
+
const urnParsed = Urn.fromValidString(id);
|
|
415
|
+
if (urnParsed.namespaceIdentifier() !== AuditableItemGraphService.NAMESPACE) {
|
|
416
|
+
throw new GeneralError(AuditableItemGraphService.CLASS_NAME, "namespaceMismatch", {
|
|
417
|
+
namespace: AuditableItemGraphService.NAMESPACE,
|
|
418
|
+
id
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
try {
|
|
422
|
+
const vertexId = urnParsed.namespaceSpecific(0);
|
|
423
|
+
const vertexEntity = await this._vertexStorage.get(vertexId);
|
|
424
|
+
if (Is.empty(vertexEntity)) {
|
|
425
|
+
throw new NotFoundError(AuditableItemGraphService.CLASS_NAME, "vertexNotFound", id);
|
|
426
|
+
}
|
|
427
|
+
const beforeDate = Coerce.dateTime(options?.before);
|
|
428
|
+
const afterDate = Coerce.dateTime(options?.after);
|
|
429
|
+
const allChangesets = await this.internalGetChangesets(vertexId, {
|
|
430
|
+
before: beforeDate?.toISOString()
|
|
431
|
+
});
|
|
432
|
+
const versions = [];
|
|
433
|
+
for (const changeset of allChangesets) {
|
|
434
|
+
const changesetDate = Coerce.dateTime(changeset.dateCreated);
|
|
435
|
+
const afterExcluded = !Is.empty(afterDate) && !Is.empty(changesetDate) && changesetDate <= afterDate;
|
|
436
|
+
if (!afterExcluded) {
|
|
437
|
+
versions.push(changeset.version ?? 0);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
const versionList = {
|
|
441
|
+
"@context": [
|
|
442
|
+
SchemaOrgContexts.Context,
|
|
443
|
+
AuditableItemGraphContexts.Context,
|
|
444
|
+
AuditableItemGraphContexts.ContextCommon
|
|
445
|
+
],
|
|
446
|
+
type: [SchemaOrgTypes.ItemList, AuditableItemGraphTypes.VertexVersionList],
|
|
447
|
+
[SchemaOrgTypes.ItemListElement]: versions
|
|
448
|
+
};
|
|
449
|
+
return await JsonLdProcessor.compact(versionList, versionList["@context"]);
|
|
450
|
+
}
|
|
451
|
+
catch (error) {
|
|
452
|
+
throw new GeneralError(AuditableItemGraphService.CLASS_NAME, "getVersionsFailed", undefined, error);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
351
455
|
/**
|
|
352
456
|
* Remove the verifiable storage for an item.
|
|
353
457
|
* @param id The id of the vertex to get.
|
|
@@ -599,10 +703,47 @@ export class AuditableItemGraphService {
|
|
|
599
703
|
patchFrom: p.from,
|
|
600
704
|
patchValue: p.value
|
|
601
705
|
})),
|
|
602
|
-
proofId: changesetEntity.proofId
|
|
706
|
+
proofId: changesetEntity.proofId,
|
|
707
|
+
version: changesetEntity.version
|
|
603
708
|
};
|
|
604
709
|
return model;
|
|
605
710
|
}
|
|
711
|
+
/**
|
|
712
|
+
* Fetch all changesets for a vertex in ascending date order.
|
|
713
|
+
* @param vertexId The internal vertex id.
|
|
714
|
+
* @param options Optional filtering options.
|
|
715
|
+
* @param options.before Only fetch changesets created strictly before this ISO 8601 timestamp.
|
|
716
|
+
* @param options.maxVersion Only fetch changesets with version <= this value.
|
|
717
|
+
* @returns All changeset entities sorted ascending by dateCreated.
|
|
718
|
+
* @internal
|
|
719
|
+
*/
|
|
720
|
+
async internalGetChangesets(vertexId, options) {
|
|
721
|
+
const all = [];
|
|
722
|
+
let cursor;
|
|
723
|
+
const conditions = [
|
|
724
|
+
{ property: "vertexId", value: vertexId, comparison: ComparisonOperator.Equals }
|
|
725
|
+
];
|
|
726
|
+
if (Is.stringValue(options?.before)) {
|
|
727
|
+
conditions.push({
|
|
728
|
+
property: "dateCreated",
|
|
729
|
+
value: options.before,
|
|
730
|
+
comparison: ComparisonOperator.LessThan
|
|
731
|
+
});
|
|
732
|
+
}
|
|
733
|
+
if (!Is.empty(options?.maxVersion)) {
|
|
734
|
+
conditions.push({
|
|
735
|
+
property: "version",
|
|
736
|
+
value: options.maxVersion,
|
|
737
|
+
comparison: ComparisonOperator.LessThanOrEqual
|
|
738
|
+
});
|
|
739
|
+
}
|
|
740
|
+
do {
|
|
741
|
+
const result = await this._changesetStorage.query({ conditions, logicalOperator: LogicalOperator.And }, [{ property: "dateCreated", sortDirection: SortDirection.Ascending }], undefined, cursor);
|
|
742
|
+
all.push(...result.entities);
|
|
743
|
+
cursor = result.cursor;
|
|
744
|
+
} while (Is.stringValue(cursor));
|
|
745
|
+
return all;
|
|
746
|
+
}
|
|
606
747
|
/**
|
|
607
748
|
* Update the aliases of a vertex model.
|
|
608
749
|
* @param context The context for the operation.
|
|
@@ -820,10 +961,11 @@ export class AuditableItemGraphService {
|
|
|
820
961
|
* @param original The original vertex.
|
|
821
962
|
* @param updated The updated vertex.
|
|
822
963
|
* @param isNew Whether this is a new item.
|
|
964
|
+
* @param version The version number of the vertex after this changeset.
|
|
823
965
|
* @returns True if there were changes.
|
|
824
966
|
* @internal
|
|
825
967
|
*/
|
|
826
|
-
async addChangeset(context, original, updated, isNew) {
|
|
968
|
+
async addChangeset(context, original, updated, isNew, version) {
|
|
827
969
|
const patches = JsonHelper.diff(original, updated);
|
|
828
970
|
// If there is a diff set or this is the first time the item is created.
|
|
829
971
|
if (patches.length > 0 || isNew) {
|
|
@@ -832,13 +974,14 @@ export class AuditableItemGraphService {
|
|
|
832
974
|
vertexId: updated.id,
|
|
833
975
|
dateCreated: context.now,
|
|
834
976
|
userIdentity: context.contextIds?.[ContextIdKeys.User],
|
|
835
|
-
patches
|
|
977
|
+
patches,
|
|
978
|
+
version
|
|
836
979
|
};
|
|
837
980
|
// Create the JSON-LD object we want to use for the proof
|
|
838
981
|
// this is a subset of fixed properties from the changeset object.
|
|
839
982
|
const reducedChangesetJsonLd = this.changesetEntityToJsonLd(original.id, ObjectHelper.pick(changesetEntity, AuditableItemGraphService._PROOF_KEYS_CHANGESET));
|
|
840
983
|
// Create the proof for the changeset object
|
|
841
|
-
changesetEntity.proofId = await this._immutableProofComponent.create(reducedChangesetJsonLd);
|
|
984
|
+
changesetEntity.proofId = await this._immutableProofComponent.create(JsonLdHelper.toNodeObject(reducedChangesetJsonLd));
|
|
842
985
|
// Link the verifiable storage id to the changeset
|
|
843
986
|
await this._changesetStorage.set(changesetEntity);
|
|
844
987
|
return patches;
|