@twin.org/auditable-item-graph-service 0.0.3-next.22 → 0.0.3-next.24

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.
@@ -1,7 +1,7 @@
1
1
  // Copyright 2024 IOTA Stiftung.
2
2
  // SPDX-License-Identifier: Apache-2.0.
3
3
  import { AuditableItemGraphContexts, AuditableItemGraphDataTypes, AuditableItemGraphMetricIds, AuditableItemGraphMetrics, AuditableItemGraphTopics, AuditableItemGraphTypes, VerifyDepth } from "@twin.org/auditable-item-graph-models";
4
- import { ContextIdKeys, ContextIdStore } from "@twin.org/context";
4
+ import { ContextIdHelper, ContextIdKeys, ContextIdStore } from "@twin.org/context";
5
5
  import { ArrayHelper, Coerce, ComponentFactory, GeneralError, Guards, Is, JsonHelper, Mutex, 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";
@@ -90,6 +90,7 @@ export class AuditableItemGraphService {
90
90
  }
91
91
  /**
92
92
  * Register all AIG metrics with the telemetry component.
93
+ * @returns A promise that resolves when all metrics have been registered.
93
94
  */
94
95
  async start() {
95
96
  if (Is.undefined(this._telemetryComponent)) {
@@ -109,6 +110,7 @@ export class AuditableItemGraphService {
109
110
  async create(vertex) {
110
111
  Guards.object(AuditableItemGraphService.CLASS_NAME, "vertex", vertex);
111
112
  const contextIds = await ContextIdStore.getContextIds();
113
+ ContextIdHelper.guard(contextIds, ContextIdKeys.Organization);
112
114
  try {
113
115
  const id = RandomHelper.generateUuidV7("compact");
114
116
  const schemaValidationFailures = [];
@@ -122,13 +124,15 @@ export class AuditableItemGraphService {
122
124
  await JsonLdHelper.validate(vertex.annotationObject, validationFailures);
123
125
  Validation.asValidationError(AuditableItemGraphService.CLASS_NAME, "vertex.annotationObject", validationFailures);
124
126
  }
127
+ const ownerOrganizationId = contextIds?.[ContextIdKeys.UserOrganization] ?? contextIds?.[ContextIdKeys.Organization];
125
128
  const context = {
126
129
  now: new Date(Date.now()).toISOString(),
127
- contextIds
130
+ organizationIdentity: ownerOrganizationId,
131
+ userIdentity: contextIds?.[ContextIdKeys.User]
128
132
  };
129
133
  const vertexModel = {
130
134
  id,
131
- organizationIdentity: contextIds?.[ContextIdKeys.Organization],
135
+ organizationIdentity: contextIds[ContextIdKeys.Organization],
132
136
  dateCreated: context.now
133
137
  };
134
138
  const originalEntity = ObjectHelper.clone(vertexModel);
@@ -157,7 +161,7 @@ export class AuditableItemGraphService {
157
161
  * Update a graph vertex (PUT — full replacement of vertex state).
158
162
  * Concurrent updates for the same vertex are serialized via `Mutex` on the vertex id.
159
163
  * @param vertex The vertex to update.
160
- * @returns Nothing.
164
+ * @returns A promise that resolves when the vertex has been updated.
161
165
  */
162
166
  async update(vertex) {
163
167
  Guards.object(AuditableItemGraphService.CLASS_NAME, "vertex", vertex);
@@ -165,7 +169,6 @@ export class AuditableItemGraphService {
165
169
  const vertexId = this.parseVertexId(vertex.id);
166
170
  await Mutex.lock(vertexId, { throwOnTimeout: true });
167
171
  try {
168
- const contextIds = await ContextIdStore.getContextIds();
169
172
  try {
170
173
  const schemaValidationFailures = [];
171
174
  await DataTypeHelper.validate("vertex", `${AuditableItemGraphContexts.Namespace}${AuditableItemGraphTypes.Vertex}`, vertex, schemaValidationFailures);
@@ -179,20 +182,18 @@ export class AuditableItemGraphService {
179
182
  await JsonLdHelper.validate(vertex.annotationObject, validationFailures);
180
183
  Validation.asValidationError(AuditableItemGraphService.CLASS_NAME, "vertex.annotationObject", validationFailures);
181
184
  }
185
+ const contextIds = await ContextIdStore.getContextIds();
186
+ const ownerOrganizationId = vertexEntity.organizationIdentity ??
187
+ contextIds?.[ContextIdKeys.UserOrganization] ??
188
+ contextIds?.[ContextIdKeys.Organization];
182
189
  const context = {
183
190
  now: new Date(Date.now()).toISOString(),
184
- contextIds
191
+ organizationIdentity: ownerOrganizationId,
192
+ userIdentity: contextIds?.[ContextIdKeys.User]
185
193
  };
186
194
  delete vertexEntity.aliasIndex;
187
195
  const originalEntity = ObjectHelper.clone(vertexEntity);
188
196
  const newEntity = ObjectHelper.clone(vertexEntity);
189
- // Capture org from context if vertex doesn't have one yet.
190
- // Enables ownership transition: when an authenticated user first
191
- // interacts with a vertex that was received without an org, the org
192
- // is set here and recorded as a patch in the changeset proof.
193
- if (!Is.stringValue(newEntity.organizationIdentity)) {
194
- newEntity.organizationIdentity = context.contextIds?.[ContextIdKeys.Organization];
195
- }
196
197
  newEntity.annotationObject = vertex.annotationObject;
197
198
  await this.updateAliasList(context, newEntity, vertex.aliases);
198
199
  await this.updateResourceList(context, newEntity, vertex.resources);
@@ -211,7 +212,7 @@ export class AuditableItemGraphService {
211
212
  * Partially update a graph vertex (PATCH — explicit list patches; only defined properties applied).
212
213
  * Serialized with `update` via `Mutex` on the same vertex id within this instance.
213
214
  * @param partial The partial vertex update.
214
- * @returns Nothing.
215
+ * @returns A promise that resolves when the partial update has been applied.
215
216
  */
216
217
  async updatePartial(partial) {
217
218
  Guards.object(AuditableItemGraphService.CLASS_NAME, "partial", partial);
@@ -219,7 +220,6 @@ export class AuditableItemGraphService {
219
220
  const vertexId = this.parseVertexId(partial.id);
220
221
  await Mutex.lock(vertexId, { throwOnTimeout: true });
221
222
  try {
222
- const contextIds = await ContextIdStore.getContextIds();
223
223
  try {
224
224
  const vertexEntity = await this._vertexStorage.get(vertexId);
225
225
  if (Is.empty(vertexEntity)) {
@@ -230,17 +230,18 @@ export class AuditableItemGraphService {
230
230
  await JsonLdHelper.validate(partial.annotationObject, validationFailures);
231
231
  Validation.asValidationError(AuditableItemGraphService.CLASS_NAME, "partial.annotationObject", validationFailures);
232
232
  }
233
+ const contextIds = await ContextIdStore.getContextIds();
234
+ const ownerOrganizationId = vertexEntity.organizationIdentity ??
235
+ contextIds?.[ContextIdKeys.UserOrganization] ??
236
+ contextIds?.[ContextIdKeys.Organization];
233
237
  const context = {
234
238
  now: new Date(Date.now()).toISOString(),
235
- contextIds
239
+ organizationIdentity: ownerOrganizationId,
240
+ userIdentity: contextIds?.[ContextIdKeys.User]
236
241
  };
237
242
  delete vertexEntity.aliasIndex;
238
243
  const originalEntity = ObjectHelper.clone(vertexEntity);
239
244
  const newEntity = ObjectHelper.clone(vertexEntity);
240
- // Capture org from context if vertex doesn't have one yet.
241
- if (!Is.stringValue(newEntity.organizationIdentity)) {
242
- newEntity.organizationIdentity = context.contextIds?.[ContextIdKeys.Organization];
243
- }
244
245
  if (partial.annotationObject !== undefined) {
245
246
  newEntity.annotationObject = partial.annotationObject;
246
247
  }
@@ -543,7 +544,7 @@ export class AuditableItemGraphService {
543
544
  /**
544
545
  * Remove the proof for an item.
545
546
  * @param id The id of the vertex to remove the proof from.
546
- * @returns Nothing.
547
+ * @returns A promise that resolves when the proof has been removed from all changesets.
547
548
  * @throws NotFoundError if the vertex is not found.
548
549
  */
549
550
  async removeProof(id) {
@@ -717,6 +718,7 @@ export class AuditableItemGraphService {
717
718
  * @param vertexUrn The vertex URN for events.
718
719
  * @param originalEntity The entity before changes.
719
720
  * @param newEntity The entity after changes.
721
+ * @returns A promise that resolves when the changes have been persisted and events published.
720
722
  * @internal
721
723
  */
722
724
  async persistVertexChanges(context, vertexId, vertexUrn, originalEntity, newEntity) {
@@ -900,6 +902,7 @@ export class AuditableItemGraphService {
900
902
  * @param context The context for the operation.
901
903
  * @param vertex The vertex.
902
904
  * @param aliases The new active alias set.
905
+ * @returns A promise that resolves when the alias list has been replaced.
903
906
  * @internal
904
907
  */
905
908
  async updateAliasList(context, vertex, aliases) {
@@ -923,6 +926,7 @@ export class AuditableItemGraphService {
923
926
  * @param context The context for the operation.
924
927
  * @param vertex The vertex.
925
928
  * @param patch The alias patch.
929
+ * @returns A promise that resolves when the alias patch has been applied.
926
930
  * @internal
927
931
  */
928
932
  async applyAliasPatch(context, vertex, patch) {
@@ -947,6 +951,7 @@ export class AuditableItemGraphService {
947
951
  * @param context The context for the operation.
948
952
  * @param vertex The vertex.
949
953
  * @param alias The alias.
954
+ * @returns A promise that resolves when the alias has been added or updated.
950
955
  * @internal
951
956
  */
952
957
  async updateAlias(context, vertex, alias) {
@@ -996,6 +1001,7 @@ export class AuditableItemGraphService {
996
1001
  * @param context The context for the operation.
997
1002
  * @param vertex The vertex.
998
1003
  * @param resources The new active resource set.
1004
+ * @returns A promise that resolves when the resource list has been replaced.
999
1005
  * @internal
1000
1006
  */
1001
1007
  async updateResourceList(context, vertex, resources) {
@@ -1029,6 +1035,7 @@ export class AuditableItemGraphService {
1029
1035
  * @param context The context for the operation.
1030
1036
  * @param vertex The vertex.
1031
1037
  * @param patch The resource patch.
1038
+ * @returns A promise that resolves when the resource patch has been applied.
1032
1039
  * @internal
1033
1040
  */
1034
1041
  async applyResourcePatch(context, vertex, patch) {
@@ -1053,6 +1060,7 @@ export class AuditableItemGraphService {
1053
1060
  * @param context The context for the operation.
1054
1061
  * @param vertex The vertex.
1055
1062
  * @param resource The resource.
1063
+ * @returns A promise that resolves when the resource has been added or updated.
1056
1064
  * @internal
1057
1065
  */
1058
1066
  async updateResource(context, vertex, resource) {
@@ -1087,6 +1095,7 @@ export class AuditableItemGraphService {
1087
1095
  * @param context The context for the operation.
1088
1096
  * @param vertex The vertex.
1089
1097
  * @param edges The new active edge set.
1098
+ * @returns A promise that resolves when the edge list has been replaced.
1090
1099
  * @internal
1091
1100
  */
1092
1101
  async updateEdgeList(context, vertex, edges) {
@@ -1112,6 +1121,7 @@ export class AuditableItemGraphService {
1112
1121
  * @param context The context for the operation.
1113
1122
  * @param vertex The vertex.
1114
1123
  * @param patch The edge patch.
1124
+ * @returns A promise that resolves when the edge patch has been applied.
1115
1125
  * @internal
1116
1126
  */
1117
1127
  async applyEdgePatch(context, vertex, patch) {
@@ -1136,6 +1146,7 @@ export class AuditableItemGraphService {
1136
1146
  * @param context The context for the operation.
1137
1147
  * @param vertex The vertex.
1138
1148
  * @param edge The edge.
1149
+ * @returns A promise that resolves when the edge has been added or updated.
1139
1150
  * @internal
1140
1151
  */
1141
1152
  async updateEdge(context, vertex, edge) {
@@ -1207,7 +1218,7 @@ export class AuditableItemGraphService {
1207
1218
  id: RandomHelper.generateUuidV7("compact"),
1208
1219
  vertexId: updated.id,
1209
1220
  dateCreated: context.now,
1210
- userIdentity: context.contextIds?.[ContextIdKeys.User],
1221
+ userIdentity: context.userIdentity,
1211
1222
  patches,
1212
1223
  version
1213
1224
  };