@twin.org/dataspace-control-plane-service 0.0.3-next.48 → 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.
@@ -4,12 +4,13 @@ import { HttpContextIdKeys, HttpUrlHelper } from "@twin.org/api-models";
4
4
  import { ContextIdHelper, ContextIdKeys, ContextIdStore } from "@twin.org/context";
5
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";
@@ -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()) {
@@ -344,7 +352,7 @@ export class DataspaceControlPlaneService {
344
352
  * @param options Request options.
345
353
  * @param options.autoStart When true, the provider immediately starts the requested transfer (scheduled
346
354
  * on the next tick); when omitted/false the provider start must be triggered explicitly.
347
- * @param trustPayload Trust payload containing authorization information (Base64-encoded token).
355
+ * @param trustPayload Trust payload containing authorization information.
348
356
  * @returns Transfer Process (DSP compliant) with state REQUESTED, or TransferError if the operation fails.
349
357
  *
350
358
  * Role Performed: Provider
@@ -377,14 +385,10 @@ export class DataspaceControlPlaneService {
377
385
  ts: Date.now(),
378
386
  message: "agreementNotFound",
379
387
  data: {
380
- agreementId: request.agreementId,
381
- hint: "Agreement must exist before transfer. Use contract negotiation to create agreement first."
388
+ notFoundId: request.agreementId
382
389
  }
383
390
  });
384
- throw new NotFoundError(DataspaceControlPlaneService.CLASS_NAME, "agreementNotFound", request.agreementId, {
385
- agreementId: request.agreementId,
386
- hint: "Perform contract negotiation first to create an agreement"
387
- });
391
+ throw new NotFoundError(DataspaceControlPlaneService.CLASS_NAME, "agreementNotFound", request.agreementId);
388
392
  }
389
393
  const assigneeIdentity = OdrlPolicyHelper.extractAssigneeIdentity(agreement);
390
394
  const assigneeIds = ArrayHelper.fromObjectOrArray(assigneeIdentity);
@@ -453,6 +457,7 @@ export class DataspaceControlPlaneService {
453
457
  await this.runProviderStart(consumerPid, contextIds?.[HttpContextIdKeys.PublicOrigin]);
454
458
  }, 0);
455
459
  }
460
+ await MetricHelper.metricIncrement(this._telemetryComponent, DataspaceControlPlaneMetricIds.TransfersRequested);
456
461
  return {
457
462
  "@context": [DataspaceProtocolContexts.Context],
458
463
  "@type": DataspaceProtocolTransferProcessTypes.TransferProcess,
@@ -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,7 +616,7 @@ 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 trustPayload Trust payload containing authorization information (Base64-encoded token).
619
+ * @param trustPayload Trust payload containing authorization information.
614
620
  * @returns Transfer Start Message (DSP compliant) with dataAddress for PULL transfers, or TransferError if the operation fails.
615
621
  *
616
622
  * Role Performed: Provider / Consumer
@@ -668,6 +674,7 @@ export class DataspaceControlPlaneService {
668
674
  });
669
675
  await this._internalTransferCallback.onStateChanged(entity.consumerPid, DataspaceProtocolTransferProcessStateType.STARTED);
670
676
  await this._internalTransferCallback.onStarted(entity.consumerPid, message);
677
+ await MetricHelper.metricIncrement(this._telemetryComponent, DataspaceControlPlaneMetricIds.TransfersStarted);
671
678
  return message;
672
679
  }
673
680
  // PROVIDER-ACT (role === Provider): build the dataAddress, transition to STARTED, deliver below.
@@ -890,6 +897,7 @@ export class DataspaceControlPlaneService {
890
897
  // Notify the consumer by POSTing the TransferStart (with its dataAddress) to the callback.
891
898
  // Best-effort: a delivery failure must not roll back the STARTED transition.
892
899
  await this.deliverToConsumerCallback(entity, "start", async (remoteControlPlane, token) => remoteControlPlane.startTransfer(response, token));
900
+ await MetricHelper.metricIncrement(this._telemetryComponent, DataspaceControlPlaneMetricIds.TransfersStarted);
893
901
  return response;
894
902
  }
895
903
  catch (error) {
@@ -945,7 +953,7 @@ export class DataspaceControlPlaneService {
945
953
  /**
946
954
  * Complete a Transfer Process.
947
955
  * @param message Transfer completion message (DSP compliant).
948
- * @param trustPayload Trust payload containing authorization information (Base64-encoded token).
956
+ * @param trustPayload Trust payload containing authorization information.
949
957
  * @returns Transfer Process (DSP compliant) with state COMPLETED, or TransferError if the operation fails.
950
958
  */
951
959
  async completeTransfer(message, trustPayload) {
@@ -1025,6 +1033,7 @@ export class DataspaceControlPlaneService {
1025
1033
  await this._internalTransferCallback.onStateChanged(entity.consumerPid, DataspaceProtocolTransferProcessStateType.COMPLETED);
1026
1034
  await this._internalTransferCallback.onCompleted(entity.consumerPid);
1027
1035
  }
1036
+ await MetricHelper.metricIncrement(this._telemetryComponent, DataspaceControlPlaneMetricIds.TransfersCompleted);
1028
1037
  return {
1029
1038
  "@context": [DataspaceProtocolContexts.Context],
1030
1039
  "@type": DataspaceProtocolTransferProcessTypes.TransferProcess,
@@ -1040,7 +1049,7 @@ export class DataspaceControlPlaneService {
1040
1049
  /**
1041
1050
  * Suspend a Transfer Process.
1042
1051
  * @param message Transfer suspension message (DSP compliant).
1043
- * @param trustPayload Trust payload containing authorization information (Base64-encoded token).
1052
+ * @param trustPayload Trust payload containing authorization information.
1044
1053
  * @returns Transfer Process (DSP compliant) with state SUSPENDED, or TransferError if the operation fails.
1045
1054
  */
1046
1055
  async suspendTransfer(message, trustPayload) {
@@ -1127,6 +1136,7 @@ export class DataspaceControlPlaneService {
1127
1136
  // PROVIDER-ACT: forward the suspension (with its reason) to the consumer callback.
1128
1137
  await this.deliverToConsumerCallback(entity, "suspension", async (remoteControlPlane, token) => remoteControlPlane.suspendTransfer(message, token));
1129
1138
  }
1139
+ await MetricHelper.metricIncrement(this._telemetryComponent, DataspaceControlPlaneMetricIds.TransfersSuspended);
1130
1140
  return {
1131
1141
  "@context": [DataspaceProtocolContexts.Context],
1132
1142
  "@type": DataspaceProtocolTransferProcessTypes.TransferProcess,
@@ -1142,7 +1152,7 @@ export class DataspaceControlPlaneService {
1142
1152
  /**
1143
1153
  * Terminate a Transfer Process.
1144
1154
  * @param message Transfer termination message (DSP compliant).
1145
- * @param trustPayload Trust payload containing authorization information (Base64-encoded token).
1155
+ * @param trustPayload Trust payload containing authorization information.
1146
1156
  * @returns Transfer Process (DSP compliant) with state TERMINATED, or TransferError if the operation fails.
1147
1157
  */
1148
1158
  async terminateTransfer(message, trustPayload) {
@@ -1223,6 +1233,7 @@ export class DataspaceControlPlaneService {
1223
1233
  // PROVIDER-ACT: forward the termination (with its reason) to the consumer callback.
1224
1234
  await this.deliverToConsumerCallback(entity, "termination", async (remoteControlPlane, token) => remoteControlPlane.terminateTransfer(message, token));
1225
1235
  }
1236
+ await MetricHelper.metricIncrement(this._telemetryComponent, DataspaceControlPlaneMetricIds.TransfersTerminated);
1226
1237
  return {
1227
1238
  "@context": [DataspaceProtocolContexts.Context],
1228
1239
  "@type": DataspaceProtocolTransferProcessTypes.TransferProcess,
@@ -1238,7 +1249,7 @@ export class DataspaceControlPlaneService {
1238
1249
  /**
1239
1250
  * Get Transfer Process state.
1240
1251
  * @param pid Process ID (consumerPid or providerPid).
1241
- * @param trustPayload Trust payload containing authorization information (Base64-encoded token).
1252
+ * @param trustPayload Trust payload containing authorization information.
1242
1253
  * @returns Transfer Process (DSP compliant) with current state, or TransferError if the operation fails.
1243
1254
  */
1244
1255
  async getTransferProcess(pid, trustPayload) {
@@ -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
@@ -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);