@twin.org/dataspace-control-plane-service 0.0.3-next.47 → 0.0.3-next.49

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,15 +1,16 @@
1
1
  // Copyright 2025 IOTA Stiftung.
2
2
  // SPDX-License-Identifier: Apache-2.0.
3
- import { HttpUrlHelper } from "@twin.org/api-models";
4
- import { ContextIdKeys, ContextIdStore, ContextIdHelper } from "@twin.org/context";
5
- import { ArrayHelper, AlreadyExistsError, BaseError, ComponentFactory, Converter, GeneralError, Guards, Is, NotFoundError, ObjectHelper, RandomHelper, StringHelper, UnauthorizedError, Url, Urn, ValidationError } from "@twin.org/core";
3
+ import { HttpContextIdKeys, HttpUrlHelper } from "@twin.org/api-models";
4
+ import { ContextIdHelper, ContextIdKeys, ContextIdStore } from "@twin.org/context";
5
+ import { AlreadyExistsError, ArrayHelper, BaseError, ComponentFactory, Converter, GeneralError, Guards, Is, NotFoundError, ObjectHelper, RandomHelper, StringHelper, UnauthorizedError, Url, Urn, ValidationError } from "@twin.org/core";
6
6
  import { JsonLdHelper } from "@twin.org/data-json-ld";
7
- import { DataspaceAppFactory, DataspaceTransferFormat, TransferProcessRole, getJsonLdId, getJsonLdType } from "@twin.org/dataspace-models";
7
+ import { DataspaceControlPlaneMetricIds, DataspaceControlPlaneMetrics, DataspaceTransferFormat, TransferProcessRole, getJsonLdId, getJsonLdType } from "@twin.org/dataspace-models";
8
8
  import { EngineCoreFactory } from "@twin.org/engine-models";
9
9
  import { ComparisonOperator } from "@twin.org/entity";
10
10
  import { EntityStorageConnectorFactory } from "@twin.org/entity-storage-models";
11
11
  import { OdrlPolicyHelper, PolicyRequesterFactory } from "@twin.org/rights-management-models";
12
12
  import { DataspaceProtocolContexts, DataspaceProtocolContractNegotiationTypes, DataspaceProtocolEndpointType, DataspaceProtocolHelper, DataspaceProtocolTransferProcessStateType, DataspaceProtocolTransferProcessTypes } from "@twin.org/standards-dataspace-protocol";
13
+ import { MetricHelper } from "@twin.org/telemetry-models";
13
14
  import { TrustHelper } from "@twin.org/trust-models";
14
15
  import { DataspaceControlPlanePolicyRequester } from "./dataspaceControlPlanePolicyRequester.js";
15
16
  import { EndpointProperties } from "./models/endpointProperties.js";
@@ -88,7 +89,7 @@ export class DataspaceControlPlaneService {
88
89
  _overrideTrustGeneratorType;
89
90
  /**
90
91
  * Data plane endpoint path (path only, not full URL).
91
- * Will be combined with public origin from hosting component.
92
+ * Will be combined with public origin.
92
93
  * If not configured, PULL transfers are not supported.
93
94
  * @internal
94
95
  */
@@ -120,6 +121,11 @@ export class DataspaceControlPlaneService {
120
121
  * @internal
121
122
  */
122
123
  _platformComponent;
124
+ /**
125
+ * The optional telemetry component for metrics.
126
+ * @internal
127
+ */
128
+ _telemetryComponent;
123
129
  /**
124
130
  * Registered negotiation callbacks from upstream callers, keyed by registration key.
125
131
  * @internal
@@ -169,6 +175,7 @@ export class DataspaceControlPlaneService {
169
175
  : undefined;
170
176
  this._taskScheduler = ComponentFactory.getIfExists(options?.taskSchedulerComponentType ?? "task-scheduler");
171
177
  this._platformComponent = ComponentFactory.getIfExists(options?.platformComponentType ?? "platform");
178
+ this._telemetryComponent = ComponentFactory.getIfExists(options?.telemetryComponentType);
172
179
  // Data plane component is optional and resolved lazily. The control plane is initialised
173
180
  // BEFORE the data plane in engine startup order, so `getRegisteredInstanceTypeOptional`
174
181
  // returns undefined when the engine constructs us — the wiring override can't fire here.
@@ -235,6 +242,7 @@ export class DataspaceControlPlaneService {
235
242
  * @returns A promise that resolves when the federated catalogue is populated and the cleanup task is scheduled.
236
243
  */
237
244
  async start(nodeLoggingComponentType) {
245
+ await MetricHelper.createMetrics(this._telemetryComponent, DataspaceControlPlaneMetrics);
238
246
  const engine = EngineCoreFactory.getIfExists("engine");
239
247
  // Skip if no engine exists OR if this is a clone instance
240
248
  if (Is.empty(engine) || engine.isClone()) {
@@ -256,7 +264,7 @@ export class DataspaceControlPlaneService {
256
264
  let errorCount = 0;
257
265
  let totalDatasets = 0;
258
266
  // The platform component execute is used so that the dataset publication runs in the
259
- // tenant context of the hosting component, also works in single tenant mode
267
+ // tenant context, also works in single tenant mode
260
268
  await this._platformComponent?.execute(async () => {
261
269
  // The tenant context id is set here for each system tenant
262
270
  let cursor;
@@ -341,18 +349,16 @@ export class DataspaceControlPlaneService {
341
349
  * Request a Transfer Process.
342
350
  * Creates a new Transfer Process in REQUESTED state.
343
351
  * @param request Transfer request message (DSP compliant).
344
- * @param publicOrigin The public origin of this provider node, resolved by the REST route from the
345
- * hosting component; used to build the data-plane endpoint when auto-starting.
346
352
  * @param options Request options.
347
353
  * @param options.autoStart When true, the provider immediately starts the requested transfer (scheduled
348
354
  * on the next tick); when omitted/false the provider start must be triggered explicitly.
349
- * @param trustPayload Trust payload containing authorization information (Base64-encoded token).
355
+ * @param trustPayload Trust payload containing authorization information.
350
356
  * @returns Transfer Process (DSP compliant) with state REQUESTED, or TransferError if the operation fails.
351
357
  *
352
358
  * Role Performed: Provider
353
359
  * Called by: Consumer when it wants to request a new Transfer Process
354
360
  */
355
- async requestTransfer(request, publicOrigin, options, trustPayload) {
361
+ async requestTransfer(request, options, trustPayload) {
356
362
  const trustInfo = await TrustHelper.verifyTrust(this._trustComponent, trustPayload, "requestTransfer");
357
363
  const validationFailures = await DataspaceProtocolHelper.validate(JsonLdHelper.toNodeObject(request));
358
364
  if (Is.arrayValue(validationFailures)) {
@@ -379,14 +385,10 @@ export class DataspaceControlPlaneService {
379
385
  ts: Date.now(),
380
386
  message: "agreementNotFound",
381
387
  data: {
382
- agreementId: request.agreementId,
383
- hint: "Agreement must exist before transfer. Use contract negotiation to create agreement first."
388
+ notFoundId: request.agreementId
384
389
  }
385
390
  });
386
- throw new NotFoundError(DataspaceControlPlaneService.CLASS_NAME, "agreementNotFound", request.agreementId, {
387
- agreementId: request.agreementId,
388
- hint: "Perform contract negotiation first to create an agreement"
389
- });
391
+ throw new NotFoundError(DataspaceControlPlaneService.CLASS_NAME, "agreementNotFound", request.agreementId);
390
392
  }
391
393
  const assigneeIdentity = OdrlPolicyHelper.extractAssigneeIdentity(agreement);
392
394
  const assigneeIds = ArrayHelper.fromObjectOrArray(assigneeIdentity);
@@ -448,12 +450,14 @@ export class DataspaceControlPlaneService {
448
450
  // setTimeout follow-up). The timer runs inside the request's ALS context, so the [Node, Tenant] + org
449
451
  // partition propagates to the deferred start.
450
452
  if (options?.autoStart) {
453
+ const contextIds = await ContextIdStore.getContextIds();
451
454
  const consumerPid = request.consumerPid;
452
455
  setTimeout(async () => {
453
456
  // runProviderStart catches internally and can't reject; awaiting satisfies no-floating-promises.
454
- await this.runProviderStart(consumerPid, publicOrigin);
457
+ await this.runProviderStart(consumerPid, contextIds?.[HttpContextIdKeys.PublicOrigin]);
455
458
  }, 0);
456
459
  }
460
+ await MetricHelper.metricIncrement(this._telemetryComponent, DataspaceControlPlaneMetricIds.TransfersRequested);
457
461
  return {
458
462
  "@context": [DataspaceProtocolContexts.Context],
459
463
  "@type": DataspaceProtocolTransferProcessTypes.TransferProcess,
@@ -468,7 +472,6 @@ export class DataspaceControlPlaneService {
468
472
  * and (only if the provider accepts) persists a local TransferProcess in REQUESTED state.
469
473
  * @param agreementId The finalized agreement ID from contract negotiation.
470
474
  * @param providerEndpoint The provider's DSP control plane base URL.
471
- * @param publicOrigin The public origin URL of this control plane (used as callbackAddress).
472
475
  * @param format The transfer format (e.g. "HttpData-PULL", "HttpData-PUSH").
473
476
  * @param trustPayload Trust payload for authenticating this call.
474
477
  * @returns The consumerPid of the newly created TransferProcess.
@@ -481,11 +484,13 @@ export class DataspaceControlPlaneService {
481
484
  * registration ignores the runtime `endpoint` arg and silently POSTs to its
482
485
  * static endpoint instead.
483
486
  */
484
- async prepareTransfer(agreementId, providerEndpoint, publicOrigin, format, trustPayload) {
487
+ async prepareTransfer(agreementId, providerEndpoint, format, trustPayload) {
485
488
  Guards.stringValue(DataspaceControlPlaneService.CLASS_NAME, "agreementId", agreementId);
486
489
  Guards.stringValue(DataspaceControlPlaneService.CLASS_NAME, "providerEndpoint", providerEndpoint);
487
- Guards.stringValue(DataspaceControlPlaneService.CLASS_NAME, "publicOrigin", publicOrigin);
488
490
  Guards.stringValue(DataspaceControlPlaneService.CLASS_NAME, "format", format);
491
+ const contextIds = await ContextIdStore.getContextIds();
492
+ ContextIdHelper.guard(contextIds, HttpContextIdKeys.PublicOrigin);
493
+ const publicOrigin = contextIds[HttpContextIdKeys.PublicOrigin];
489
494
  if (!Object.values(DataspaceTransferFormat).includes(format)) {
490
495
  throw new GeneralError(DataspaceControlPlaneService.CLASS_NAME, "unsupportedTransferFormat", {
491
496
  format,
@@ -555,7 +560,7 @@ export class DataspaceControlPlaneService {
555
560
  const outboundToken = await this._trustComponent.generate(organizationIdentity, this._overrideTrustGeneratorType, { subject: { consumerPid, agreementId } });
556
561
  // Create a remote REST client pointed at the provider endpoint and call requestTransfer.
557
562
  const remoteControlPlane = ComponentFactory.create(this._remoteControlPlaneComponentType, { endpoint: providerEndpoint, pathPrefix: "" });
558
- const result = await remoteControlPlane.requestTransfer(transferRequestMessage, "", undefined, outboundToken);
563
+ const result = await remoteControlPlane.requestTransfer(transferRequestMessage, undefined, outboundToken);
559
564
  if (getJsonLdType(result) === DataspaceProtocolTransferProcessTypes.TransferError) {
560
565
  const transferError = result;
561
566
  throw new GeneralError(DataspaceControlPlaneService.CLASS_NAME, "transferRequestRejectedByProvider", {
@@ -601,6 +606,7 @@ export class DataspaceControlPlaneService {
601
606
  format
602
607
  }
603
608
  });
609
+ await MetricHelper.metricIncrement(this._telemetryComponent, DataspaceControlPlaneMetricIds.TransfersPrepared);
604
610
  return { consumerPid };
605
611
  }
606
612
  // ----------------------------------------------------------------------------
@@ -610,14 +616,15 @@ export class DataspaceControlPlaneService {
610
616
  * Start a Transfer Process.
611
617
  * Transitions Transfer Process from REQUESTED to STARTED state or resumes from SUSPENDED state.
612
618
  * @param message Transfer start message (DSP compliant).
613
- * @param publicOrigin The public origin URL of this service.
614
- * @param trustPayload Trust payload containing authorization information (Base64-encoded token).
619
+ * @param trustPayload Trust payload containing authorization information.
615
620
  * @returns Transfer Start Message (DSP compliant) with dataAddress for PULL transfers, or TransferError if the operation fails.
616
621
  *
617
622
  * Role Performed: Provider / Consumer
618
623
  */
619
- async startTransfer(message, publicOrigin, trustPayload) {
620
- publicOrigin = StringHelper.trimTrailingSlashes(publicOrigin);
624
+ async startTransfer(message, trustPayload) {
625
+ const contextIds = await ContextIdStore.getContextIds();
626
+ ContextIdHelper.guard(contextIds, HttpContextIdKeys.PublicOrigin);
627
+ const publicOrigin = contextIds[HttpContextIdKeys.PublicOrigin];
621
628
  const trustInfo = await TrustHelper.verifyTrust(this._trustComponent, trustPayload, "startTransfer");
622
629
  const validationFailures = await DataspaceProtocolHelper.validate(JsonLdHelper.toNodeObject(message));
623
630
  if (Is.arrayValue(validationFailures)) {
@@ -667,6 +674,7 @@ export class DataspaceControlPlaneService {
667
674
  });
668
675
  await this._internalTransferCallback.onStateChanged(entity.consumerPid, DataspaceProtocolTransferProcessStateType.STARTED);
669
676
  await this._internalTransferCallback.onStarted(entity.consumerPid, message);
677
+ await MetricHelper.metricIncrement(this._telemetryComponent, DataspaceControlPlaneMetricIds.TransfersStarted);
670
678
  return message;
671
679
  }
672
680
  // PROVIDER-ACT (role === Provider): build the dataAddress, transition to STARTED, deliver below.
@@ -888,7 +896,8 @@ export class DataspaceControlPlaneService {
888
896
  });
889
897
  // Notify the consumer by POSTing the TransferStart (with its dataAddress) to the callback.
890
898
  // Best-effort: a delivery failure must not roll back the STARTED transition.
891
- await this.deliverToConsumerCallback(entity, "start", async (remoteControlPlane, token) => remoteControlPlane.startTransfer(response, "", token));
899
+ await this.deliverToConsumerCallback(entity, "start", async (remoteControlPlane, token) => remoteControlPlane.startTransfer(response, token));
900
+ await MetricHelper.metricIncrement(this._telemetryComponent, DataspaceControlPlaneMetricIds.TransfersStarted);
892
901
  return response;
893
902
  }
894
903
  catch (error) {
@@ -904,16 +913,14 @@ export class DataspaceControlPlaneService {
904
913
  * the provider, builds the dataAddress for PULL, persists STARTED, and delivers to the consumer).
905
914
  * This is the provider-side mirror of prepareTransfer.
906
915
  * @param pid The Process ID (consumerPid or providerPid) identifying the transfer to start.
907
- * @param publicOrigin The public origin URL of this provider node (used to build the data plane endpoint for PULL transfers).
908
916
  * @param trustPayload Trust payload proving the caller is the provider.
909
917
  * @returns Transfer Start Message (DSP compliant) with dataAddress for PULL transfers, or TransferError if the operation fails.
910
918
  */
911
- async transferStarted(pid, publicOrigin, trustPayload) {
919
+ async transferStarted(pid, trustPayload) {
912
920
  let consumerPid;
913
921
  let providerPid;
914
922
  try {
915
923
  Guards.stringValue(DataspaceControlPlaneService.CLASS_NAME, "pid", pid);
916
- Guards.stringValue(DataspaceControlPlaneService.CLASS_NAME, "publicOrigin", publicOrigin);
917
924
  const { entity, role } = await this.lookupTransferByPid(pid);
918
925
  consumerPid = entity.consumerPid;
919
926
  providerPid = entity.providerPid;
@@ -934,7 +941,7 @@ export class DataspaceControlPlaneService {
934
941
  // startTransfer verifies trustPayload is the provider, checks org ownership, applies the
935
942
  // REQUESTED|SUSPENDED state guard, builds the dataAddress, persists STARTED, and delivers the
936
943
  // TransferStartMessage to the consumer callback.
937
- return await this.startTransfer(startMessage, publicOrigin, trustPayload);
944
+ return await this.startTransfer(startMessage, trustPayload);
938
945
  }
939
946
  catch (error) {
940
947
  return transformToTransferError(error, { consumerPid, providerPid });
@@ -946,7 +953,7 @@ export class DataspaceControlPlaneService {
946
953
  /**
947
954
  * Complete a Transfer Process.
948
955
  * @param message Transfer completion message (DSP compliant).
949
- * @param trustPayload Trust payload containing authorization information (Base64-encoded token).
956
+ * @param trustPayload Trust payload containing authorization information.
950
957
  * @returns Transfer Process (DSP compliant) with state COMPLETED, or TransferError if the operation fails.
951
958
  */
952
959
  async completeTransfer(message, trustPayload) {
@@ -1026,6 +1033,7 @@ export class DataspaceControlPlaneService {
1026
1033
  await this._internalTransferCallback.onStateChanged(entity.consumerPid, DataspaceProtocolTransferProcessStateType.COMPLETED);
1027
1034
  await this._internalTransferCallback.onCompleted(entity.consumerPid);
1028
1035
  }
1036
+ await MetricHelper.metricIncrement(this._telemetryComponent, DataspaceControlPlaneMetricIds.TransfersCompleted);
1029
1037
  return {
1030
1038
  "@context": [DataspaceProtocolContexts.Context],
1031
1039
  "@type": DataspaceProtocolTransferProcessTypes.TransferProcess,
@@ -1041,7 +1049,7 @@ export class DataspaceControlPlaneService {
1041
1049
  /**
1042
1050
  * Suspend a Transfer Process.
1043
1051
  * @param message Transfer suspension message (DSP compliant).
1044
- * @param trustPayload Trust payload containing authorization information (Base64-encoded token).
1052
+ * @param trustPayload Trust payload containing authorization information.
1045
1053
  * @returns Transfer Process (DSP compliant) with state SUSPENDED, or TransferError if the operation fails.
1046
1054
  */
1047
1055
  async suspendTransfer(message, trustPayload) {
@@ -1128,6 +1136,7 @@ export class DataspaceControlPlaneService {
1128
1136
  // PROVIDER-ACT: forward the suspension (with its reason) to the consumer callback.
1129
1137
  await this.deliverToConsumerCallback(entity, "suspension", async (remoteControlPlane, token) => remoteControlPlane.suspendTransfer(message, token));
1130
1138
  }
1139
+ await MetricHelper.metricIncrement(this._telemetryComponent, DataspaceControlPlaneMetricIds.TransfersSuspended);
1131
1140
  return {
1132
1141
  "@context": [DataspaceProtocolContexts.Context],
1133
1142
  "@type": DataspaceProtocolTransferProcessTypes.TransferProcess,
@@ -1143,7 +1152,7 @@ export class DataspaceControlPlaneService {
1143
1152
  /**
1144
1153
  * Terminate a Transfer Process.
1145
1154
  * @param message Transfer termination message (DSP compliant).
1146
- * @param trustPayload Trust payload containing authorization information (Base64-encoded token).
1155
+ * @param trustPayload Trust payload containing authorization information.
1147
1156
  * @returns Transfer Process (DSP compliant) with state TERMINATED, or TransferError if the operation fails.
1148
1157
  */
1149
1158
  async terminateTransfer(message, trustPayload) {
@@ -1224,6 +1233,7 @@ export class DataspaceControlPlaneService {
1224
1233
  // PROVIDER-ACT: forward the termination (with its reason) to the consumer callback.
1225
1234
  await this.deliverToConsumerCallback(entity, "termination", async (remoteControlPlane, token) => remoteControlPlane.terminateTransfer(message, token));
1226
1235
  }
1236
+ await MetricHelper.metricIncrement(this._telemetryComponent, DataspaceControlPlaneMetricIds.TransfersTerminated);
1227
1237
  return {
1228
1238
  "@context": [DataspaceProtocolContexts.Context],
1229
1239
  "@type": DataspaceProtocolTransferProcessTypes.TransferProcess,
@@ -1239,7 +1249,7 @@ export class DataspaceControlPlaneService {
1239
1249
  /**
1240
1250
  * Get Transfer Process state.
1241
1251
  * @param pid Process ID (consumerPid or providerPid).
1242
- * @param trustPayload Trust payload containing authorization information (Base64-encoded token).
1252
+ * @param trustPayload Trust payload containing authorization information.
1243
1253
  * @returns Transfer Process (DSP compliant) with current state, or TransferError if the operation fails.
1244
1254
  */
1245
1255
  async getTransferProcess(pid, trustPayload) {
@@ -1284,15 +1294,16 @@ export class DataspaceControlPlaneService {
1284
1294
  * @param datasetId The dataset ID from the provider's catalog.
1285
1295
  * @param offerId The offer ID from the provider's catalog.
1286
1296
  * @param providerEndpoint The provider's contract negotiation endpoint URL.
1287
- * @param publicOrigin The public origin URL of this control plane (for callbacks).
1288
1297
  * @param trustPayload The trust payload for authentication.
1289
1298
  * @returns The negotiation ID. Use the registered callback for completion notification.
1290
1299
  */
1291
- async negotiateAgreement(datasetId, offerId, providerEndpoint, publicOrigin, trustPayload) {
1300
+ async negotiateAgreement(datasetId, offerId, providerEndpoint, trustPayload) {
1292
1301
  Guards.stringValue(DataspaceControlPlaneService.CLASS_NAME, "datasetId", datasetId);
1293
1302
  Guards.stringValue(DataspaceControlPlaneService.CLASS_NAME, "offerId", offerId);
1294
1303
  Guards.stringValue(DataspaceControlPlaneService.CLASS_NAME, "providerEndpoint", providerEndpoint);
1295
- Guards.stringValue(DataspaceControlPlaneService.CLASS_NAME, "publicOrigin", publicOrigin);
1304
+ const contextIds = await ContextIdStore.getContextIds();
1305
+ ContextIdHelper.guard(contextIds, HttpContextIdKeys.PublicOrigin);
1306
+ const publicOrigin = contextIds[HttpContextIdKeys.PublicOrigin];
1296
1307
  const trustInfo = await TrustHelper.verifyTrust(this._trustComponent, trustPayload, "negotiateAgreement");
1297
1308
  await this._loggingComponent?.log({
1298
1309
  level: "info",
@@ -1372,6 +1383,7 @@ export class DataspaceControlPlaneService {
1372
1383
  });
1373
1384
  }
1374
1385
  this._policyRequester.trackNegotiation(negotiationId);
1386
+ await MetricHelper.metricIncrement(this._telemetryComponent, DataspaceControlPlaneMetricIds.NegotiationsInitiated);
1375
1387
  await this._loggingComponent?.log({
1376
1388
  level: "info",
1377
1389
  source: DataspaceControlPlaneService.CLASS_NAME,
@@ -1464,7 +1476,7 @@ export class DataspaceControlPlaneService {
1464
1476
  /**
1465
1477
  * Resolve consumerPid to Transfer Context.
1466
1478
  * @param consumerPid Consumer Process ID.
1467
- * @param trustPayload Trust payload containing authorization information (Base64-encoded token).
1479
+ * @param trustPayload Trust payload containing authorization information.
1468
1480
  * @returns Transfer Context with Agreement, datasetId, and Transfer Process metadata.
1469
1481
  */
1470
1482
  async resolveConsumerPid(consumerPid, trustPayload) {
@@ -1521,7 +1533,7 @@ export class DataspaceControlPlaneService {
1521
1533
  /**
1522
1534
  * Resolve providerPid to Transfer Context.
1523
1535
  * @param providerPid Provider Process ID.
1524
- * @param trustPayload Trust payload containing authorization information (Base64-encoded token).
1536
+ * @param trustPayload Trust payload containing authorization information.
1525
1537
  * @returns Transfer Context with Agreement, datasetId, and Transfer Process metadata.
1526
1538
  */
1527
1539
  async resolveProviderPid(providerPid, trustPayload) {
@@ -1631,6 +1643,7 @@ export class DataspaceControlPlaneService {
1631
1643
  // Side effect first, primary storage last
1632
1644
  await this.publishAppDataset(entity);
1633
1645
  await this._dataspaceAppDatasetStorage.set(entity);
1646
+ await MetricHelper.metricIncrement(this._telemetryComponent, DataspaceControlPlaneMetricIds.AppDatasetsCreated);
1634
1647
  return resolvedId;
1635
1648
  }
1636
1649
  /**
@@ -1709,6 +1722,7 @@ export class DataspaceControlPlaneService {
1709
1722
  // Side effect first, primary storage last
1710
1723
  await this.publishAppDataset(updated);
1711
1724
  await this._dataspaceAppDatasetStorage.set(updated);
1725
+ await MetricHelper.metricIncrement(this._telemetryComponent, DataspaceControlPlaneMetricIds.AppDatasetsUpdated);
1712
1726
  }
1713
1727
  /**
1714
1728
  * Delete a dataspace app dataset owned by the calling organization.
@@ -1735,6 +1749,7 @@ export class DataspaceControlPlaneService {
1735
1749
  });
1736
1750
  }
1737
1751
  await this._dataspaceAppDatasetStorage.remove(id);
1752
+ await MetricHelper.metricIncrement(this._telemetryComponent, DataspaceControlPlaneMetricIds.AppDatasetsDeleted);
1738
1753
  }
1739
1754
  // ============================================================================
1740
1755
  // PRIVATE HELPER METHODS
@@ -1829,7 +1844,7 @@ export class DataspaceControlPlaneService {
1829
1844
  agreementId: entity.agreementId
1830
1845
  }
1831
1846
  });
1832
- const result = await this.transferStarted(consumerPid, publicOrigin, selfToken);
1847
+ const result = await this.transferStarted(consumerPid, selfToken);
1833
1848
  if (getJsonLdType(result) === DataspaceProtocolTransferProcessTypes.TransferError) {
1834
1849
  await this._loggingComponent?.log({
1835
1850
  level: "error",
@@ -2438,12 +2453,8 @@ export class DataspaceControlPlaneService {
2438
2453
  const contextIds = await ContextIdStore.getContextIds();
2439
2454
  const tenantContextIds = { ...contextIds, [ContextIdKeys.Tenant]: appDataset.tenantId };
2440
2455
  await ContextIdStore.run(tenantContextIds, async () => {
2441
- const app = DataspaceAppFactory.get(appDataset.appId);
2442
2456
  const datasetPayload = this.restampDatasetId(appDataset.dataset, appDataset.id);
2443
- const overrideHandler = app.datasetsHandled?.bind(app);
2444
- const rawDatasets = overrideHandler
2445
- ? await overrideHandler(datasetPayload)
2446
- : [datasetPayload];
2457
+ const rawDatasets = [datasetPayload];
2447
2458
  const datasets = await Promise.all(rawDatasets.map(async (d) => this.populateDefaults(d)));
2448
2459
  for (const dataset of datasets) {
2449
2460
  const publishResult = await this._federatedCatalogueComponent.set(dataset, localTrustPayload);