@twin.org/auditable-item-stream-service 0.0.3-next.2 → 0.0.3-next.21

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.
Files changed (29) hide show
  1. package/README.md +2 -2
  2. package/dist/es/auditableItemStreamRoutes.js +471 -130
  3. package/dist/es/auditableItemStreamRoutes.js.map +1 -1
  4. package/dist/es/auditableItemStreamService.js +330 -138
  5. package/dist/es/auditableItemStreamService.js.map +1 -1
  6. package/dist/es/entities/auditableItemStream.js +20 -6
  7. package/dist/es/entities/auditableItemStream.js.map +1 -1
  8. package/dist/es/models/IAuditableItemStreamServiceConstructorOptions.js.map +1 -1
  9. package/dist/es/models/IAuditableItemStreamServiceContext.js.map +1 -1
  10. package/dist/types/auditableItemStreamRoutes.d.ts +33 -1
  11. package/dist/types/auditableItemStreamService.d.ts +44 -37
  12. package/dist/types/entities/auditableItemStream.d.ts +12 -3
  13. package/dist/types/models/IAuditableItemStreamServiceConstructorOptions.d.ts +4 -0
  14. package/dist/types/models/IAuditableItemStreamServiceContext.d.ts +4 -0
  15. package/docs/changelog.md +355 -77
  16. package/docs/examples.md +211 -1
  17. package/docs/open-api/spec.json +839 -177
  18. package/docs/reference/classes/AuditableItemStream.md +32 -16
  19. package/docs/reference/classes/AuditableItemStreamEntry.md +13 -13
  20. package/docs/reference/classes/AuditableItemStreamService.md +107 -84
  21. package/docs/reference/functions/auditableItemStreamClose.md +31 -0
  22. package/docs/reference/functions/auditableItemStreamListEntriesNoStream.md +31 -0
  23. package/docs/reference/functions/auditableItemStreamListEntryObjectsNoStream.md +31 -0
  24. package/docs/reference/functions/auditableItemStreamRemoveProof.md +31 -0
  25. package/docs/reference/index.md +4 -0
  26. package/docs/reference/interfaces/IAuditableItemStreamServiceConfig.md +2 -2
  27. package/docs/reference/interfaces/IAuditableItemStreamServiceConstructorOptions.md +18 -10
  28. package/locales/en.json +6 -1
  29. package/package.json +6 -5
@@ -1,13 +1,15 @@
1
1
  // Copyright 2024 IOTA Stiftung.
2
2
  // SPDX-License-Identifier: Apache-2.0.
3
- import { AuditableItemStreamContexts, AuditableItemStreamTopics, AuditableItemStreamTypes } from "@twin.org/auditable-item-stream-models";
4
- import { ContextIdKeys, ContextIdStore } from "@twin.org/context";
5
- import { Coerce, ComponentFactory, Converter, GeneralError, Guards, Is, NotFoundError, ObjectHelper, RandomHelper, Urn, Validation } from "@twin.org/core";
6
- import { JsonLdHelper, JsonLdProcessor } from "@twin.org/data-json-ld";
3
+ import { AuditableItemStreamContexts, AuditableItemStreamDataTypes, AuditableItemStreamMetricIds, AuditableItemStreamMetrics, AuditableItemStreamModes, AuditableItemStreamTopics, AuditableItemStreamTypes } from "@twin.org/auditable-item-stream-models";
4
+ import { ContextIdHelper, ContextIdKeys, ContextIdStore } from "@twin.org/context";
5
+ import { Coerce, ComponentFactory, GeneralError, Guards, Is, Mutex, NotFoundError, ObjectHelper, RandomHelper, Urn, Validation } from "@twin.org/core";
6
+ import { DataTypeHelper } from "@twin.org/data-core";
7
+ import { JsonLdDataTypes, JsonLdHelper, JsonLdProcessor } from "@twin.org/data-json-ld";
7
8
  import { ComparisonOperator, LogicalOperator, SortDirection } from "@twin.org/entity";
8
9
  import { EntityStorageConnectorFactory } from "@twin.org/entity-storage-models";
9
- import { ImmutableProofContexts } from "@twin.org/immutable-proof-models";
10
+ import { ImmutableProofContexts, ImmutableProofDataTypes } from "@twin.org/immutable-proof-models";
10
11
  import { SchemaOrgContexts, SchemaOrgDataTypes, SchemaOrgTypes } from "@twin.org/standards-schema-org";
12
+ import { MetricHelper } from "@twin.org/telemetry-models";
11
13
  /**
12
14
  * Class for performing auditable item stream operations.
13
15
  */
@@ -68,6 +70,11 @@ export class AuditableItemStreamService {
68
70
  * @internal
69
71
  */
70
72
  _eventBusComponent;
73
+ /**
74
+ * The telemetry component.
75
+ * @internal
76
+ */
77
+ _telemetryComponent;
71
78
  /**
72
79
  * The default interval for the integrity checks.
73
80
  * @internal
@@ -84,9 +91,13 @@ export class AuditableItemStreamService {
84
91
  if (Is.stringValue(options?.eventBusComponentType)) {
85
92
  this._eventBusComponent = ComponentFactory.get(options.eventBusComponentType);
86
93
  }
94
+ this._telemetryComponent = ComponentFactory.getIfExists(options?.telemetryComponentType);
87
95
  this._config = options?.config ?? {};
88
96
  this._defaultImmutableInterval = this._config.defaultImmutableInterval ?? 10;
89
97
  SchemaOrgDataTypes.registerRedirects();
98
+ AuditableItemStreamDataTypes.registerTypes();
99
+ JsonLdDataTypes.registerTypes();
100
+ ImmutableProofDataTypes.registerTypes();
90
101
  }
91
102
  /**
92
103
  * Returns the class name of the component.
@@ -95,75 +106,83 @@ export class AuditableItemStreamService {
95
106
  className() {
96
107
  return AuditableItemStreamService.CLASS_NAME;
97
108
  }
109
+ /**
110
+ * Register all AIS metrics with the telemetry component.
111
+ */
112
+ async start() {
113
+ if (Is.undefined(this._telemetryComponent)) {
114
+ return;
115
+ }
116
+ await MetricHelper.createMetrics(this._telemetryComponent, AuditableItemStreamMetrics);
117
+ }
98
118
  /**
99
119
  * Create a new stream.
100
120
  * @param stream The stream to create.
101
- * @param stream.annotationObject The object for the stream as JSON-LD.
102
- * @param stream.entries Entries to store in the stream.
103
- * @param options Options for creating the stream.
104
- * @param options.immutableInterval After how many entries do we add immutable checks, defaults to service configured value.
105
- * A value of 0 will disable integrity checks, 1 will be every item, or any other integer for an interval.
106
121
  * @returns The id of the new stream item.
107
122
  */
108
- async create(stream, options) {
123
+ async create(stream) {
109
124
  Guards.object(AuditableItemStreamService.CLASS_NAME, "stream", stream);
110
125
  const contextIds = await ContextIdStore.getContextIds();
126
+ ContextIdHelper.guard(contextIds, ContextIdKeys.Organization);
111
127
  try {
128
+ const ownerOrganizationId = contextIds[ContextIdKeys.UserOrganization] ?? contextIds[ContextIdKeys.Organization];
129
+ const id = RandomHelper.generateUuidV7("compact");
130
+ const schemaValidationFailures = [];
131
+ await DataTypeHelper.validate("stream", `${AuditableItemStreamContexts.Namespace}${AuditableItemStreamTypes.Stream}Base`, stream, schemaValidationFailures);
132
+ Validation.asValidationError(AuditableItemStreamService.CLASS_NAME, "stream", schemaValidationFailures);
133
+ if (stream.closed && !Is.arrayValue(stream.entries?.[SchemaOrgTypes.ItemListElement])) {
134
+ throw new GeneralError(AuditableItemStreamService.CLASS_NAME, "closedRequiresEntries");
135
+ }
112
136
  if (Is.object(stream.annotationObject)) {
113
137
  const validationFailures = [];
114
138
  await JsonLdHelper.validate(stream.annotationObject, validationFailures);
115
139
  Validation.asValidationError(AuditableItemStreamService.CLASS_NAME, "stream.annotationObject", validationFailures);
116
140
  }
117
- const id = Converter.bytesToHex(RandomHelper.generate(32), false);
118
141
  const context = {
119
142
  now: new Date(Date.now()).toISOString(),
120
143
  contextIds,
121
144
  indexCounter: 0,
122
- immutableInterval: options?.immutableInterval ?? this._defaultImmutableInterval
145
+ immutableInterval: stream?.immutableInterval ?? this._defaultImmutableInterval,
146
+ organizationIdentity: ownerOrganizationId
123
147
  };
124
148
  const streamEntity = {
125
149
  id,
126
- organizationIdentity: contextIds?.[ContextIdKeys.Organization],
150
+ organizationIdentity: ownerOrganizationId,
127
151
  userIdentity: contextIds?.[ContextIdKeys.User],
128
152
  dateCreated: context.now,
129
153
  immutableInterval: context.immutableInterval,
130
- indexCounter: 0,
131
- proofId: ""
154
+ closed: stream.closed,
155
+ mode: stream.mode,
156
+ numberOfItems: 0
132
157
  };
133
- // Create the JSON-LD object we want to use for the proof
134
- // this is a subset of fixed properties from the stream object.
135
- const streamModel = this.streamEntityToJsonLd(ObjectHelper.pick(streamEntity, AuditableItemStreamService._PROOF_KEYS_STREAM));
136
- // Create the proof for the stream object
137
- streamEntity.proofId = await this._immutableProofComponent.create(streamModel);
138
- if (Is.arrayValue(stream.entries)) {
139
- for (const entry of stream.entries) {
158
+ const streamUrn = await this.createStreamProof(streamEntity, context.immutableInterval);
159
+ if (Is.arrayValue(stream.entries?.[SchemaOrgTypes.ItemListElement])) {
160
+ for (const entry of stream.entries[SchemaOrgTypes.ItemListElement]) {
140
161
  await this.setEntry(context, id, entry);
141
162
  }
142
163
  }
143
164
  // Add these dynamic properties to the stream object after the proof has been created.
144
165
  streamEntity.dateModified = context.now;
145
166
  streamEntity.annotationObject = stream.annotationObject;
146
- streamEntity.indexCounter = context.indexCounter;
167
+ streamEntity.numberOfItems = context.indexCounter;
147
168
  await this._streamStorage.set(streamEntity);
148
- await this._eventBusComponent?.publish(AuditableItemStreamTopics.StreamCreated, { id: streamModel.id });
149
- return streamModel.id;
169
+ await MetricHelper.metricIncrement(this._telemetryComponent, AuditableItemStreamMetricIds.StreamsCreated, {
170
+ mode: streamEntity.mode ?? AuditableItemStreamModes.Default,
171
+ immutableInterval: context.immutableInterval
172
+ });
173
+ await this._eventBusComponent?.publish(AuditableItemStreamTopics.StreamCreated, { id: streamUrn });
174
+ return streamUrn;
150
175
  }
151
176
  catch (error) {
152
177
  throw new GeneralError(AuditableItemStreamService.CLASS_NAME, "createFailed", undefined, error);
153
178
  }
154
179
  }
155
180
  /**
156
- * Get a stream header without the entries.
157
- * @param id The id of the stream to get.
158
- * @param options Additional options for the get operation.
159
- * @param options.includeEntries Whether to include the entries, defaults to false.
160
- * @param options.includeDeleted Whether to include deleted entries, defaults to false.
161
- * @param options.verifyStream Should the stream be verified, defaults to false.
162
- * @param options.verifyEntries Should the entries be verified, defaults to false.
163
- * @returns The stream and entries if found.
164
- * @throws NotFoundError if the stream is not found
181
+ * Close a stream.
182
+ * @param id The id of the stream to close.
183
+ * @returns Nothing.
165
184
  */
166
- async get(id, options) {
185
+ async close(id) {
167
186
  Guards.stringValue(AuditableItemStreamService.CLASS_NAME, "id", id);
168
187
  const urnParsed = Urn.fromValidString(id);
169
188
  if (urnParsed.namespaceIdentifier() !== AuditableItemStreamService._NAMESPACE) {
@@ -172,38 +191,31 @@ export class AuditableItemStreamService {
172
191
  id
173
192
  });
174
193
  }
194
+ const streamId = urnParsed.namespaceSpecific(0);
195
+ await Mutex.lock(streamId, { throwOnTimeout: true });
175
196
  try {
176
- const streamId = urnParsed.namespaceSpecific(0);
177
197
  const streamEntity = await this._streamStorage.get(streamId);
178
198
  if (Is.empty(streamEntity)) {
179
199
  throw new NotFoundError(AuditableItemStreamService.CLASS_NAME, "streamNotFound", id);
180
200
  }
181
- const verifyStream = options?.verifyStream ?? false;
182
- const verifyEntries = options?.verifyEntries ?? false;
183
- const streamModel = this.streamEntityToJsonLd(streamEntity);
184
- if (options?.includeEntries) {
185
- const result = await this.findEntries(streamId, options?.includeDeleted, verifyEntries);
186
- streamModel.entries = result.entries;
187
- streamModel.cursor = result.cursor;
188
- }
189
- if (verifyStream && Is.stringValue(streamEntity.proofId)) {
190
- streamModel.verification = await this._immutableProofComponent.verify(streamEntity.proofId);
191
- }
192
- if (verifyStream || verifyEntries) {
193
- streamModel["@context"].push(ImmutableProofContexts.ContextRoot);
201
+ if (!streamEntity.closed) {
202
+ streamEntity.closed = true;
203
+ streamEntity.dateModified = new Date(Date.now()).toISOString();
204
+ await this._streamStorage.set(streamEntity);
205
+ await MetricHelper.metricIncrement(this._telemetryComponent, AuditableItemStreamMetricIds.StreamsClosed);
206
+ await this._eventBusComponent?.publish(AuditableItemStreamTopics.StreamUpdated, { id });
194
207
  }
195
- const result = await JsonLdProcessor.compact(streamModel, streamModel["@context"]);
196
- return result;
197
208
  }
198
209
  catch (error) {
199
- throw new GeneralError(AuditableItemStreamService.CLASS_NAME, "getFailed", undefined, error);
210
+ throw new GeneralError(AuditableItemStreamService.CLASS_NAME, "closeFailed", undefined, error);
211
+ }
212
+ finally {
213
+ Mutex.unlock(streamId);
200
214
  }
201
215
  }
202
216
  /**
203
217
  * Update a stream.
204
- * @param stream The stream to update.
205
- * @param stream.id The id of the stream to update.
206
- * @param stream.annotationObject The object for the stream as JSON-LD.
218
+ * @param stream The stream to update, does not update entries.
207
219
  * @returns Nothing.
208
220
  */
209
221
  async update(stream) {
@@ -216,8 +228,12 @@ export class AuditableItemStreamService {
216
228
  id: stream.id
217
229
  });
218
230
  }
231
+ const streamId = urnParsed.namespaceSpecific(0);
232
+ await Mutex.lock(streamId, { throwOnTimeout: true });
219
233
  try {
220
- const streamId = urnParsed.namespaceSpecific(0);
234
+ const schemaValidationFailures = [];
235
+ await DataTypeHelper.validate("stream", `${AuditableItemStreamContexts.Namespace}${AuditableItemStreamTypes.Stream}`, stream, schemaValidationFailures);
236
+ Validation.asValidationError(AuditableItemStreamService.CLASS_NAME, "stream", schemaValidationFailures);
221
237
  const streamEntity = await this._streamStorage.get(streamId);
222
238
  if (Is.empty(streamEntity)) {
223
239
  throw new NotFoundError(AuditableItemStreamService.CLASS_NAME, "streamNotFound", stream.id);
@@ -227,16 +243,94 @@ export class AuditableItemStreamService {
227
243
  await JsonLdHelper.validate(stream.annotationObject, validationFailures);
228
244
  Validation.asValidationError(AuditableItemStreamService.CLASS_NAME, "stream.annotationObject", validationFailures);
229
245
  }
246
+ let changed = false;
230
247
  if (!ObjectHelper.equal(streamEntity.annotationObject, stream.annotationObject, false)) {
231
248
  streamEntity.annotationObject = stream.annotationObject;
249
+ changed = true;
250
+ }
251
+ const contextIds = await ContextIdStore.getContextIds();
252
+ const ownerOrganizationId = contextIds?.[ContextIdKeys.UserOrganization] ?? contextIds?.[ContextIdKeys.Organization];
253
+ if (!Is.stringValue(streamEntity.organizationIdentity) &&
254
+ Is.stringValue(ownerOrganizationId)) {
255
+ streamEntity.organizationIdentity = ownerOrganizationId;
256
+ changed = true;
257
+ }
258
+ if (!Is.stringValue(streamEntity.proofId) &&
259
+ Is.stringValue(streamEntity.organizationIdentity)) {
260
+ await this.createStreamProof(streamEntity, streamEntity.immutableInterval ?? this._defaultImmutableInterval);
261
+ if (Is.stringValue(streamEntity.proofId)) {
262
+ changed = true;
263
+ }
264
+ }
265
+ if (changed) {
232
266
  streamEntity.dateModified = new Date(Date.now()).toISOString();
233
267
  await this._streamStorage.set(streamEntity);
268
+ await MetricHelper.metricIncrement(this._telemetryComponent, AuditableItemStreamMetricIds.StreamsUpdated);
234
269
  await this._eventBusComponent?.publish(AuditableItemStreamTopics.StreamUpdated, { id: stream.id });
235
270
  }
236
271
  }
237
272
  catch (error) {
238
273
  throw new GeneralError(AuditableItemStreamService.CLASS_NAME, "updatingFailed", undefined, error);
239
274
  }
275
+ finally {
276
+ Mutex.unlock(streamId);
277
+ }
278
+ }
279
+ /**
280
+ * Get a stream header without the entries.
281
+ * @param id The id of the stream to get.
282
+ * @param cursor Cursor to use for next chunk of entries.
283
+ * @param limit Limit the number of entries to return, only applicable if includeEntries is true.
284
+ * @param options Additional options for the get operation.
285
+ * @param options.includeEntries Whether to include the entries, defaults to false.
286
+ * @param options.includeDeleted Whether to include deleted entries, defaults to false.
287
+ * @param options.verifyStream Should the stream be verified, defaults to false.
288
+ * @param options.verifyEntries Should the entries be verified, defaults to false.
289
+ * @returns The stream and entries if found.
290
+ * @throws NotFoundError if the stream is not found
291
+ */
292
+ async get(id, cursor, limit, options) {
293
+ Guards.stringValue(AuditableItemStreamService.CLASS_NAME, "id", id);
294
+ const urnParsed = Urn.fromValidString(id);
295
+ if (urnParsed.namespaceIdentifier() !== AuditableItemStreamService._NAMESPACE) {
296
+ throw new GeneralError(AuditableItemStreamService.CLASS_NAME, "namespaceMismatch", {
297
+ namespace: AuditableItemStreamService._NAMESPACE,
298
+ id
299
+ });
300
+ }
301
+ try {
302
+ const streamId = urnParsed.namespaceSpecific(0);
303
+ const streamEntity = await this._streamStorage.get(streamId);
304
+ if (Is.empty(streamEntity)) {
305
+ throw new NotFoundError(AuditableItemStreamService.CLASS_NAME, "streamNotFound", id);
306
+ }
307
+ const verifyStream = options?.verifyStream ?? false;
308
+ const verifyEntries = options?.verifyEntries ?? false;
309
+ const streamModel = this.streamEntityToJsonLd(streamEntity);
310
+ let returnCursor;
311
+ if (options?.includeEntries) {
312
+ const result = await this.findEntries(streamId, options?.includeDeleted, verifyEntries, undefined, undefined, undefined, limit, cursor);
313
+ streamModel.entries = {
314
+ type: SchemaOrgTypes.ItemList,
315
+ [SchemaOrgTypes.ItemListElement]: result.entries
316
+ };
317
+ returnCursor = result.cursor;
318
+ }
319
+ if (verifyStream && Is.stringValue(streamEntity.proofId)) {
320
+ streamModel.verification = await this._immutableProofComponent.verify(streamEntity.proofId);
321
+ }
322
+ if (verifyStream || verifyEntries) {
323
+ streamModel["@context"].push(ImmutableProofContexts.Context);
324
+ }
325
+ const result = await JsonLdProcessor.compact(streamModel, streamModel["@context"]);
326
+ return {
327
+ stream: result,
328
+ cursor: returnCursor
329
+ };
330
+ }
331
+ catch (error) {
332
+ throw new GeneralError(AuditableItemStreamService.CLASS_NAME, "getFailed", undefined, error);
333
+ }
240
334
  }
241
335
  /**
242
336
  * Delete the stream.
@@ -252,19 +346,24 @@ export class AuditableItemStreamService {
252
346
  id
253
347
  });
254
348
  }
349
+ const streamId = urnParsed.namespaceSpecific(0);
350
+ await Mutex.lock(streamId, { throwOnTimeout: true });
255
351
  try {
256
- const streamId = urnParsed.namespaceSpecific(0);
257
352
  const streamEntity = await this._streamStorage.get(streamId);
258
353
  if (Is.empty(streamEntity)) {
259
354
  throw new NotFoundError(AuditableItemStreamService.CLASS_NAME, "streamNotFound", id);
260
355
  }
261
356
  await this.internalRemoveEntries(streamEntity, false);
262
357
  await this._streamStorage.remove(streamEntity.id);
358
+ await MetricHelper.metricIncrement(this._telemetryComponent, AuditableItemStreamMetricIds.StreamsDeleted);
263
359
  await this._eventBusComponent?.publish(AuditableItemStreamTopics.StreamDeleted, { id });
264
360
  }
265
361
  catch (error) {
266
362
  throw new GeneralError(AuditableItemStreamService.CLASS_NAME, "removingFailed", undefined, error);
267
363
  }
364
+ finally {
365
+ Mutex.unlock(streamId);
366
+ }
268
367
  }
269
368
  /**
270
369
  * Query all the streams, will not return entries.
@@ -305,16 +404,18 @@ export class AuditableItemStreamService {
305
404
  ], propertiesToReturn, cursor, limit);
306
405
  const list = {
307
406
  "@context": [
308
- SchemaOrgContexts.ContextRoot,
309
- AuditableItemStreamContexts.ContextRoot,
310
- AuditableItemStreamContexts.ContextRootCommon
407
+ SchemaOrgContexts.Context,
408
+ AuditableItemStreamContexts.Context,
409
+ AuditableItemStreamContexts.ContextCommon
311
410
  ],
312
411
  type: [SchemaOrgTypes.ItemList, AuditableItemStreamTypes.StreamList],
313
- [SchemaOrgTypes.ItemListElement]: results.entities.map(e => this.streamEntityToJsonLd(e)),
314
- [SchemaOrgTypes.NextItem]: results.cursor
412
+ [SchemaOrgTypes.ItemListElement]: results.entities.map(e => this.streamEntityToJsonLd(e))
315
413
  };
316
414
  const result = await JsonLdProcessor.compact(list, list["@context"]);
317
- return result;
415
+ return {
416
+ entries: result,
417
+ cursor: results.cursor
418
+ };
318
419
  }
319
420
  catch (error) {
320
421
  throw new GeneralError(AuditableItemStreamService.CLASS_NAME, "queryingFailed", undefined, error);
@@ -336,24 +437,36 @@ export class AuditableItemStreamService {
336
437
  id: streamId
337
438
  });
338
439
  }
440
+ const streamIdParts = urnParsed.namespaceSpecific(0);
441
+ await Mutex.lock(streamIdParts, { throwOnTimeout: true });
339
442
  try {
340
- const streamIdParts = urnParsed.namespaceSpecific(0);
341
443
  const streamEntity = await this._streamStorage.get(streamIdParts);
342
444
  if (Is.empty(streamEntity)) {
343
445
  throw new NotFoundError(AuditableItemStreamService.CLASS_NAME, "streamNotFound", streamIdParts);
344
446
  }
447
+ if (streamEntity.closed) {
448
+ await MetricHelper.metricIncrement(this._telemetryComponent, AuditableItemStreamMetricIds.ClosedStreamRejections, { operation: "createEntry" });
449
+ throw new GeneralError(AuditableItemStreamService.CLASS_NAME, "streamClosed", {
450
+ id: streamId
451
+ });
452
+ }
345
453
  const context = {
346
454
  now: new Date(Date.now()).toISOString(),
347
455
  contextIds,
348
- indexCounter: streamEntity.indexCounter,
349
- immutableInterval: streamEntity.immutableInterval
456
+ indexCounter: streamEntity.numberOfItems,
457
+ immutableInterval: streamEntity.immutableInterval,
458
+ organizationIdentity: streamEntity.organizationIdentity ?? contextIds?.[ContextIdKeys.Organization]
350
459
  };
351
460
  const createdId = await this.setEntry(context, streamEntity.id, {
352
461
  entryObject
353
462
  });
354
463
  streamEntity.dateModified = context.now;
355
- streamEntity.indexCounter = context.indexCounter;
464
+ streamEntity.numberOfItems = context.indexCounter;
356
465
  await this._streamStorage.set(streamEntity);
466
+ await MetricHelper.metricIncrement(this._telemetryComponent, AuditableItemStreamMetricIds.EntriesCreated, {
467
+ hasProof: context.immutableInterval > 0 &&
468
+ (context.indexCounter - 1) % context.immutableInterval === 0
469
+ });
357
470
  const fullId = new Urn(AuditableItemStreamService._NAMESPACE, [
358
471
  streamEntity.id,
359
472
  createdId
@@ -364,6 +477,9 @@ export class AuditableItemStreamService {
364
477
  catch (error) {
365
478
  throw new GeneralError(AuditableItemStreamService.CLASS_NAME, "creatingEntryFailed", undefined, error);
366
479
  }
480
+ finally {
481
+ Mutex.unlock(streamIdParts);
482
+ }
367
483
  }
368
484
  /**
369
485
  * Get the entry from the stream.
@@ -405,7 +521,7 @@ export class AuditableItemStreamService {
405
521
  }
406
522
  const entry = this.streamEntryEntityToJsonLd(result.entity);
407
523
  if (verifyEntry) {
408
- entry["@context"].push(ImmutableProofContexts.ContextRoot);
524
+ entry["@context"] = JsonLdProcessor.combineContexts(entry["@context"], ImmutableProofContexts.Context);
409
525
  entry.verification = result.verification;
410
526
  }
411
527
  const result2 = await JsonLdProcessor.compact(entry, entry["@context"]);
@@ -466,7 +582,6 @@ export class AuditableItemStreamService {
466
582
  async updateEntry(streamId, entryId, entryObject) {
467
583
  Guards.stringValue(AuditableItemStreamService.CLASS_NAME, "streamId", streamId);
468
584
  Guards.stringValue(AuditableItemStreamService.CLASS_NAME, "entryId", entryId);
469
- const contextIds = await ContextIdStore.getContextIds();
470
585
  const urnParsed = Urn.fromValidString(streamId);
471
586
  if (urnParsed.namespaceIdentifier() !== AuditableItemStreamService._NAMESPACE) {
472
587
  throw new GeneralError(AuditableItemStreamService.CLASS_NAME, "namespaceMismatch", {
@@ -481,8 +596,9 @@ export class AuditableItemStreamService {
481
596
  id: entryId
482
597
  });
483
598
  }
599
+ const streamNamespaceId = urnParsed.namespaceSpecific(0);
600
+ await Mutex.lock(streamNamespaceId, { throwOnTimeout: true });
484
601
  try {
485
- const streamNamespaceId = urnParsed.namespaceMethod();
486
602
  const streamEntryNamespaceId = urnParsedEntry.namespaceMethod();
487
603
  if (streamNamespaceId !== streamEntryNamespaceId) {
488
604
  throw new GeneralError(AuditableItemStreamService.CLASS_NAME, "namespaceMismatch", {
@@ -494,29 +610,48 @@ export class AuditableItemStreamService {
494
610
  if (Is.empty(streamEntity)) {
495
611
  throw new NotFoundError(AuditableItemStreamService.CLASS_NAME, "streamNotFound", streamId);
496
612
  }
613
+ if (streamEntity.closed) {
614
+ await MetricHelper.metricIncrement(this._telemetryComponent, AuditableItemStreamMetricIds.ClosedStreamRejections, { operation: "updateEntry" });
615
+ throw new GeneralError(AuditableItemStreamService.CLASS_NAME, "streamClosed", {
616
+ id: streamId
617
+ });
618
+ }
619
+ if (streamEntity.mode === AuditableItemStreamModes.AppendOnly) {
620
+ await MetricHelper.metricIncrement(this._telemetryComponent, AuditableItemStreamMetricIds.AppendOnlyRejections, { operation: "updateEntry" });
621
+ throw new GeneralError(AuditableItemStreamService.CLASS_NAME, "appendOnlyNoEntryUpdates", {
622
+ id: streamId
623
+ });
624
+ }
497
625
  const entryNamespaceId = urnParsedEntry.namespaceSpecific(1);
498
626
  const existing = await this.findEntry(streamEntity.id, entryNamespaceId);
499
627
  if (Is.empty(existing)) {
500
628
  throw new NotFoundError(AuditableItemStreamService.CLASS_NAME, "streamEntryNotFound", entryId);
501
629
  }
630
+ const contextIds = await ContextIdStore.getContextIds();
631
+ const ownerOrganizationId = contextIds?.[ContextIdKeys.UserOrganization] ?? contextIds?.[ContextIdKeys.Organization];
502
632
  const context = {
503
633
  now: new Date(Date.now()).toISOString(),
504
634
  contextIds,
505
- indexCounter: streamEntity.indexCounter,
506
- immutableInterval: streamEntity.immutableInterval
635
+ indexCounter: streamEntity.numberOfItems,
636
+ immutableInterval: streamEntity.immutableInterval,
637
+ organizationIdentity: streamEntity.organizationIdentity ?? ownerOrganizationId
507
638
  };
508
639
  await this.setEntry(context, streamEntity.id, {
509
640
  ...existing.entity,
510
641
  entryObject
511
642
  });
512
643
  streamEntity.dateModified = context.now;
513
- streamEntity.indexCounter = context.indexCounter;
644
+ streamEntity.numberOfItems = context.indexCounter;
514
645
  await this._streamStorage.set(streamEntity);
646
+ await MetricHelper.metricIncrement(this._telemetryComponent, AuditableItemStreamMetricIds.EntriesUpdated);
515
647
  await this._eventBusComponent?.publish(AuditableItemStreamTopics.StreamEntryUpdated, { id: streamId, entryId });
516
648
  }
517
649
  catch (error) {
518
650
  throw new GeneralError(AuditableItemStreamService.CLASS_NAME, "updatingEntryFailed", undefined, error);
519
651
  }
652
+ finally {
653
+ Mutex.unlock(streamNamespaceId);
654
+ }
520
655
  }
521
656
  /**
522
657
  * Delete from the stream.
@@ -527,7 +662,6 @@ export class AuditableItemStreamService {
527
662
  async removeEntry(streamId, entryId) {
528
663
  Guards.stringValue(AuditableItemStreamService.CLASS_NAME, "streamId", streamId);
529
664
  Guards.stringValue(AuditableItemStreamService.CLASS_NAME, "entryId", entryId);
530
- const contextIds = await ContextIdStore.getContextIds();
531
665
  const urnParsed = Urn.fromValidString(streamId);
532
666
  if (urnParsed.namespaceIdentifier() !== AuditableItemStreamService._NAMESPACE) {
533
667
  throw new GeneralError(AuditableItemStreamService.CLASS_NAME, "namespaceMismatch", {
@@ -542,8 +676,9 @@ export class AuditableItemStreamService {
542
676
  id: entryId
543
677
  });
544
678
  }
679
+ const streamNamespaceId = urnParsed.namespaceSpecific(0);
680
+ await Mutex.lock(streamNamespaceId, { throwOnTimeout: true });
545
681
  try {
546
- const streamNamespaceId = urnParsed.namespaceMethod();
547
682
  const streamEntryNamespaceId = urnParsedEntry.namespaceMethod();
548
683
  if (streamNamespaceId !== streamEntryNamespaceId) {
549
684
  throw new GeneralError(AuditableItemStreamService.CLASS_NAME, "namespaceMismatch", {
@@ -555,35 +690,48 @@ export class AuditableItemStreamService {
555
690
  if (Is.empty(streamEntity)) {
556
691
  throw new NotFoundError(AuditableItemStreamService.CLASS_NAME, "streamNotFound", streamId);
557
692
  }
693
+ if (streamEntity.mode === AuditableItemStreamModes.AppendOnly) {
694
+ await MetricHelper.metricIncrement(this._telemetryComponent, AuditableItemStreamMetricIds.AppendOnlyRejections, { operation: "removeEntry" });
695
+ throw new GeneralError(AuditableItemStreamService.CLASS_NAME, "appendOnlyNoEntryRemovals", {
696
+ id: streamId
697
+ });
698
+ }
558
699
  const entryNamespaceId = urnParsedEntry.namespaceSpecific(1);
559
700
  const result = await this.findEntry(streamNamespaceId, entryNamespaceId);
560
701
  if (Is.empty(result)) {
561
702
  throw new NotFoundError(AuditableItemStreamService.CLASS_NAME, "streamEntryNotFound", entryId);
562
703
  }
563
704
  if (Is.empty(result.entity.dateDeleted)) {
705
+ const contextIds = await ContextIdStore.getContextIds();
706
+ const ownerOrganizationId = contextIds?.[ContextIdKeys.UserOrganization] ?? contextIds?.[ContextIdKeys.Organization];
564
707
  const context = {
565
708
  now: new Date(Date.now()).toISOString(),
566
709
  contextIds,
567
- indexCounter: streamEntity.indexCounter,
568
- immutableInterval: streamEntity.immutableInterval
710
+ indexCounter: streamEntity.numberOfItems,
711
+ immutableInterval: streamEntity.immutableInterval,
712
+ organizationIdentity: streamEntity.organizationIdentity ?? ownerOrganizationId
569
713
  };
570
714
  await this.setEntry(context, streamEntity.id, {
571
715
  ...result.entity,
572
716
  dateDeleted: context.now
573
717
  });
574
718
  streamEntity.dateModified = context.now;
575
- streamEntity.indexCounter = context.indexCounter;
719
+ streamEntity.numberOfItems = context.indexCounter;
576
720
  await this._streamStorage.set(streamEntity);
721
+ await MetricHelper.metricIncrement(this._telemetryComponent, AuditableItemStreamMetricIds.EntriesDeleted);
577
722
  await this._eventBusComponent?.publish(AuditableItemStreamTopics.StreamEntryDeleted, { id: streamId, entryId });
578
723
  }
579
724
  }
580
725
  catch (error) {
581
726
  throw new GeneralError(AuditableItemStreamService.CLASS_NAME, "removingEntryFailed", undefined, error);
582
727
  }
728
+ finally {
729
+ Mutex.unlock(streamNamespaceId);
730
+ }
583
731
  }
584
732
  /**
585
733
  * Get the entries for the stream.
586
- * @param streamId The id of the stream to get.
734
+ * @param streamId The id of the stream to get, if undefined returns all matching entries.
587
735
  * @param options Additional options for the get operation.
588
736
  * @param options.conditions The conditions to filter the stream.
589
737
  * @param options.includeDeleted Whether to include deleted entries, defaults to false.
@@ -595,37 +743,44 @@ export class AuditableItemStreamService {
595
743
  * @throws NotFoundError if the stream is not found.
596
744
  */
597
745
  async getEntries(streamId, options) {
598
- Guards.stringValue(AuditableItemStreamService.CLASS_NAME, "streamId", streamId);
599
- const urnParsed = Urn.fromValidString(streamId);
600
- if (urnParsed.namespaceIdentifier() !== AuditableItemStreamService._NAMESPACE) {
601
- throw new GeneralError(AuditableItemStreamService.CLASS_NAME, "namespaceMismatch", {
602
- namespace: AuditableItemStreamService._NAMESPACE,
603
- id: streamId
604
- });
746
+ let streamNamespaceId;
747
+ if (!Is.empty(streamId)) {
748
+ Guards.stringValue(AuditableItemStreamService.CLASS_NAME, "streamId", streamId);
749
+ const urnParsed = Urn.fromValidString(streamId);
750
+ if (urnParsed.namespaceIdentifier() !== AuditableItemStreamService._NAMESPACE) {
751
+ throw new GeneralError(AuditableItemStreamService.CLASS_NAME, "namespaceMismatch", {
752
+ namespace: AuditableItemStreamService._NAMESPACE,
753
+ id: streamId
754
+ });
755
+ }
756
+ streamNamespaceId = urnParsed.namespaceSpecific(0);
605
757
  }
606
758
  try {
607
- const streamNamespaceId = urnParsed.namespaceSpecific(0);
608
- const streamEntity = await this._streamStorage.get(streamNamespaceId);
609
- if (Is.empty(streamEntity)) {
610
- throw new NotFoundError(AuditableItemStreamService.CLASS_NAME, "streamNotFound", streamId);
759
+ if (Is.stringValue(streamNamespaceId)) {
760
+ const streamEntity = await this._streamStorage.get(streamNamespaceId);
761
+ if (Is.empty(streamEntity)) {
762
+ throw new NotFoundError(AuditableItemStreamService.CLASS_NAME, "streamNotFound", streamId);
763
+ }
611
764
  }
612
765
  const verifyEntries = options?.verifyEntries ?? false;
613
766
  const result = await this.findEntries(streamNamespaceId, options?.includeDeleted, verifyEntries, options?.conditions, options?.order, undefined, options?.limit, options?.cursor);
614
767
  const list = {
615
768
  "@context": [
616
- SchemaOrgContexts.ContextRoot,
617
- AuditableItemStreamContexts.ContextRoot,
618
- AuditableItemStreamContexts.ContextRootCommon
769
+ SchemaOrgContexts.Context,
770
+ AuditableItemStreamContexts.Context,
771
+ AuditableItemStreamContexts.ContextCommon
619
772
  ],
620
773
  type: [SchemaOrgTypes.ItemList, AuditableItemStreamTypes.StreamEntryList],
621
- [SchemaOrgTypes.ItemListElement]: result.entries,
622
- [SchemaOrgTypes.NextItem]: result.cursor
774
+ [SchemaOrgTypes.ItemListElement]: result.entries
623
775
  };
624
776
  if (verifyEntries) {
625
- list["@context"].push(ImmutableProofContexts.ContextRoot);
777
+ list["@context"].push(ImmutableProofContexts.Context);
626
778
  }
627
779
  const result2 = await JsonLdProcessor.compact(list, list["@context"]);
628
- return result2;
780
+ return {
781
+ entries: result2,
782
+ cursor: result.cursor
783
+ };
629
784
  }
630
785
  catch (error) {
631
786
  throw new GeneralError(AuditableItemStreamService.CLASS_NAME, "gettingEntriesFailed", undefined, error);
@@ -633,7 +788,7 @@ export class AuditableItemStreamService {
633
788
  }
634
789
  /**
635
790
  * Get the entry objects for the stream.
636
- * @param streamId The id of the stream to get.
791
+ * @param streamId The id of the stream to get, if undefined returns all matching entries.
637
792
  * @param options Additional options for the get operation.
638
793
  * @param options.conditions The conditions to filter the stream.
639
794
  * @param options.includeDeleted Whether to include deleted entries, defaults to false.
@@ -644,45 +799,52 @@ export class AuditableItemStreamService {
644
799
  * @throws NotFoundError if the stream is not found.
645
800
  */
646
801
  async getEntryObjects(streamId, options) {
647
- Guards.stringValue(AuditableItemStreamService.CLASS_NAME, "streamId", streamId);
648
- const urnParsed = Urn.fromValidString(streamId);
649
- if (urnParsed.namespaceIdentifier() !== AuditableItemStreamService._NAMESPACE) {
650
- throw new GeneralError(AuditableItemStreamService.CLASS_NAME, "namespaceMismatch", {
651
- namespace: AuditableItemStreamService._NAMESPACE,
652
- id: streamId
653
- });
802
+ let streamNamespaceId;
803
+ if (!Is.empty(streamId)) {
804
+ Guards.stringValue(AuditableItemStreamService.CLASS_NAME, "streamId", streamId);
805
+ const urnParsed = Urn.fromValidString(streamId);
806
+ if (urnParsed.namespaceIdentifier() !== AuditableItemStreamService._NAMESPACE) {
807
+ throw new GeneralError(AuditableItemStreamService.CLASS_NAME, "namespaceMismatch", {
808
+ namespace: AuditableItemStreamService._NAMESPACE,
809
+ id: streamId
810
+ });
811
+ }
812
+ streamNamespaceId = urnParsed.namespaceSpecific(0);
654
813
  }
655
814
  try {
656
- const streamNamespaceId = urnParsed.namespaceSpecific(0);
657
- const streamEntity = await this._streamStorage.get(streamNamespaceId);
658
- if (Is.empty(streamEntity)) {
659
- throw new NotFoundError(AuditableItemStreamService.CLASS_NAME, "streamNotFound", streamId);
815
+ if (Is.stringValue(streamNamespaceId)) {
816
+ const streamEntity = await this._streamStorage.get(streamNamespaceId);
817
+ if (Is.empty(streamEntity)) {
818
+ throw new NotFoundError(AuditableItemStreamService.CLASS_NAME, "streamNotFound", streamId);
819
+ }
660
820
  }
661
821
  const result = await this.findEntries(streamNamespaceId, options?.includeDeleted, false, options?.conditions, options?.order, undefined, options?.limit, options?.cursor);
662
822
  const list = {
663
823
  "@context": [
664
- SchemaOrgContexts.ContextRoot,
665
- AuditableItemStreamContexts.ContextRoot,
666
- AuditableItemStreamContexts.ContextRootCommon
824
+ SchemaOrgContexts.Context,
825
+ AuditableItemStreamContexts.Context,
826
+ AuditableItemStreamContexts.ContextCommon
667
827
  ],
668
828
  type: [SchemaOrgTypes.ItemList, AuditableItemStreamTypes.StreamEntryObjectList],
669
- [SchemaOrgTypes.ItemListElement]: result.entries.map(m => m.entryObject),
670
- [SchemaOrgTypes.NextItem]: result.cursor
829
+ [SchemaOrgTypes.ItemListElement]: result.entries.map(m => m.entryObject)
671
830
  };
672
831
  const result2 = await JsonLdProcessor.compact(list, list["@context"]);
673
- return result2;
832
+ return {
833
+ entries: result2,
834
+ cursor: result.cursor
835
+ };
674
836
  }
675
837
  catch (error) {
676
838
  throw new GeneralError(AuditableItemStreamService.CLASS_NAME, "gettingEntryObjectsFailed", undefined, error);
677
839
  }
678
840
  }
679
841
  /**
680
- * Remove the verifiable storage for the stream and entries.
681
- * @param streamId The id of the stream to remove the storage from.
842
+ * Remove the proof for the stream and entries.
843
+ * @param streamId The id of the stream to remove the proof from.
682
844
  * @returns Nothing.
683
845
  * @throws NotFoundError if the vertex is not found.
684
846
  */
685
- async removeVerifiable(streamId) {
847
+ async removeProof(streamId) {
686
848
  Guards.stringValue(AuditableItemStreamService.CLASS_NAME, "streamId", streamId);
687
849
  const urnParsed = Urn.fromValidString(streamId);
688
850
  if (urnParsed.namespaceIdentifier() !== AuditableItemStreamService._NAMESPACE) {
@@ -691,8 +853,9 @@ export class AuditableItemStreamService {
691
853
  id: streamId
692
854
  });
693
855
  }
856
+ const streamIdParts = urnParsed.namespaceSpecific(0);
857
+ await Mutex.lock(streamIdParts, { throwOnTimeout: true });
694
858
  try {
695
- const streamIdParts = urnParsed.namespaceSpecific(0);
696
859
  const streamEntity = await this._streamStorage.get(streamIdParts);
697
860
  if (Is.empty(streamEntity)) {
698
861
  throw new NotFoundError(AuditableItemStreamService.CLASS_NAME, "streamNotFound", streamIdParts);
@@ -700,9 +863,27 @@ export class AuditableItemStreamService {
700
863
  await this.internalRemoveEntries(streamEntity, true);
701
864
  }
702
865
  catch (error) {
703
- throw new GeneralError(AuditableItemStreamService.CLASS_NAME, "removeVerifiableFailed", undefined, error);
866
+ throw new GeneralError(AuditableItemStreamService.CLASS_NAME, "removeProofFailed", undefined, error);
867
+ }
868
+ finally {
869
+ Mutex.unlock(streamIdParts);
704
870
  }
705
871
  }
872
+ /**
873
+ * Create an immutable proof for the stream entity if the conditions are met.
874
+ * @param streamEntity The stream entity to create the proof for.
875
+ * @param immutableInterval The immutable interval for the stream.
876
+ * @returns The proof id.
877
+ * @internal
878
+ */
879
+ async createStreamProof(streamEntity, immutableInterval) {
880
+ const streamModel = this.streamEntityToJsonLd(ObjectHelper.pick(streamEntity, AuditableItemStreamService._PROOF_KEYS_STREAM));
881
+ if (immutableInterval > 0 && Is.stringValue(streamModel.organizationIdentity)) {
882
+ streamEntity.proofId = await this._immutableProofComponent.create(streamModel);
883
+ await MetricHelper.metricIncrement(this._telemetryComponent, AuditableItemStreamMetricIds.ProofsCreatedStream);
884
+ }
885
+ return streamModel.id;
886
+ }
706
887
  /**
707
888
  * Map the stream entity to a JSON-LD model.
708
889
  * @param streamEntity The stream entity.
@@ -712,9 +893,9 @@ export class AuditableItemStreamService {
712
893
  streamEntityToJsonLd(streamEntity) {
713
894
  const model = {
714
895
  "@context": [
715
- AuditableItemStreamContexts.ContextRoot,
716
- AuditableItemStreamContexts.ContextRootCommon,
717
- SchemaOrgContexts.ContextRoot
896
+ SchemaOrgContexts.Context,
897
+ AuditableItemStreamContexts.Context,
898
+ AuditableItemStreamContexts.ContextCommon
718
899
  ],
719
900
  type: AuditableItemStreamTypes.Stream,
720
901
  id: `${AuditableItemStreamService._NAMESPACE}:${streamEntity.id}`,
@@ -724,7 +905,10 @@ export class AuditableItemStreamService {
724
905
  userIdentity: streamEntity.userIdentity,
725
906
  annotationObject: streamEntity.annotationObject,
726
907
  immutableInterval: streamEntity.immutableInterval,
727
- proofId: streamEntity.proofId
908
+ proofId: streamEntity.proofId,
909
+ numberOfItems: streamEntity.numberOfItems,
910
+ closed: streamEntity.closed,
911
+ mode: streamEntity.mode
728
912
  };
729
913
  return model;
730
914
  }
@@ -737,9 +921,9 @@ export class AuditableItemStreamService {
737
921
  streamEntryEntityToJsonLd(streamEntryEntity) {
738
922
  const streamEntryModel = {
739
923
  "@context": [
740
- AuditableItemStreamContexts.ContextRoot,
741
- AuditableItemStreamContexts.ContextRootCommon,
742
- SchemaOrgContexts.ContextRoot
924
+ AuditableItemStreamContexts.Context,
925
+ AuditableItemStreamContexts.ContextCommon,
926
+ SchemaOrgContexts.Context
743
927
  ],
744
928
  type: AuditableItemStreamTypes.StreamEntry,
745
929
  id: `${AuditableItemStreamService._NAMESPACE}:${streamEntryEntity.streamId}:${streamEntryEntity.id}`,
@@ -770,7 +954,7 @@ export class AuditableItemStreamService {
770
954
  Validation.asValidationError(AuditableItemStreamService.CLASS_NAME, "entry.entryObject", validationFailures);
771
955
  }
772
956
  const entity = {
773
- id: entry.id ?? Converter.bytesToHex(RandomHelper.generate(32), false),
957
+ id: entry.id ?? RandomHelper.generateUuidV7("compact"),
774
958
  streamId,
775
959
  dateCreated: entry.dateCreated ?? context.now,
776
960
  dateDeleted: entry.dateDeleted,
@@ -786,8 +970,12 @@ export class AuditableItemStreamService {
786
970
  // Create the JSON-LD object we want to use for the proof
787
971
  // this is a subset of fixed properties from the stream entry object.
788
972
  const streamEntryModel = this.streamEntryEntityToJsonLd(ObjectHelper.pick(entity, AuditableItemStreamService._PROOF_KEYS_STREAM_ENTRY));
789
- // Create the proof for the stream object
790
- entity.proofId = await this._immutableProofComponent.create(streamEntryModel);
973
+ // Create the proof for the stream object but only if we have an organization identity,
974
+ // either from the stream or the context, as this is needed for the proof creation and immutability.
975
+ if (Is.stringValue(context.organizationIdentity)) {
976
+ entity.proofId = await this._immutableProofComponent.create(JsonLdHelper.toNodeObject(streamEntryModel));
977
+ await MetricHelper.metricIncrement(this._telemetryComponent, AuditableItemStreamMetricIds.ProofsCreatedEntry, { index: entity.index });
978
+ }
791
979
  }
792
980
  await this._streamEntryStorage.set(entity);
793
981
  return entity.id;
@@ -797,6 +985,7 @@ export class AuditableItemStreamService {
797
985
  * @param streamId The stream id.
798
986
  * @param entryId The entry id.
799
987
  * @param verifyEntry Should the entry be verified.
988
+ * @returns The entry entity and optional verification result, or undefined if not found.
800
989
  * @internal
801
990
  */
802
991
  async findEntry(streamId, entryId, verifyEntry) {
@@ -843,18 +1032,19 @@ export class AuditableItemStreamService {
843
1032
  * @param propertiesToReturn The properties to return.
844
1033
  * @param limit Limit the number of entities when finding.
845
1034
  * @param cursor The cursor.
846
- * @param contextIds The context ids to perform the operation with.
1035
+ * @returns The stream entries and optional next cursor.
847
1036
  * @internal
848
1037
  */
849
1038
  async findEntries(streamId, includeDeleted, verifyEntries, conditions, sortDirection, propertiesToReturn, limit, cursor) {
850
1039
  const needToVerify = verifyEntries ?? false;
851
- const combinedConditions = [
852
- {
1040
+ const combinedConditions = [];
1041
+ if (Is.stringValue(streamId)) {
1042
+ combinedConditions.push({
853
1043
  property: "streamId",
854
1044
  comparison: ComparisonOperator.Equals,
855
1045
  value: streamId
856
- }
857
- ];
1046
+ });
1047
+ }
858
1048
  if (Is.stringValue(cursor)) {
859
1049
  const parts = cursor.split("|");
860
1050
  if (parts.length > 1) {
@@ -920,9 +1110,10 @@ export class AuditableItemStreamService {
920
1110
  */
921
1111
  async internalRemoveEntries(streamEntity, removeOnlyProof) {
922
1112
  if (Is.stringValue(streamEntity.proofId)) {
923
- await this._immutableProofComponent.removeVerifiable(streamEntity.proofId);
1113
+ await this._immutableProofComponent.removeNotarization(streamEntity.proofId);
924
1114
  delete streamEntity.proofId;
925
1115
  await this._streamStorage.set(streamEntity);
1116
+ await MetricHelper.metricIncrement(this._telemetryComponent, AuditableItemStreamMetricIds.ProofsRemovedStream);
926
1117
  }
927
1118
  const entryIds = [];
928
1119
  let entriesResult;
@@ -940,8 +1131,9 @@ export class AuditableItemStreamService {
940
1131
  for (const streamEntry of entriesResult.entities) {
941
1132
  entryIds.push(streamEntry.id);
942
1133
  if (Is.stringValue(streamEntry.proofId)) {
943
- await this._immutableProofComponent.removeVerifiable(streamEntry.proofId);
1134
+ await this._immutableProofComponent.removeNotarization(streamEntry.proofId);
944
1135
  delete streamEntry.proofId;
1136
+ await MetricHelper.metricIncrement(this._telemetryComponent, AuditableItemStreamMetricIds.ProofsRemovedEntry);
945
1137
  // If we are only removing the proof, we need to set the entry
946
1138
  // otherwise the entry is going to be removed later anyway.
947
1139
  if (removeOnlyProof) {