@twin.org/dataspace-control-plane-service 0.0.3-next.49 → 0.0.3-next.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/es/dataspaceControlPlaneService.js +93 -5
- package/dist/es/dataspaceControlPlaneService.js.map +1 -1
- package/dist/types/dataspaceControlPlaneService.d.ts +3 -2
- package/docs/changelog.md +33 -0
- package/docs/reference/classes/DataspaceControlPlaneService.md +11 -11
- package/locales/en.json +2 -0
- package/package.json +2 -2
|
@@ -10,6 +10,7 @@ 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 { OdrlPolicyType } from "@twin.org/standards-w3c-odrl";
|
|
13
14
|
import { MetricHelper } from "@twin.org/telemetry-models";
|
|
14
15
|
import { TrustHelper } from "@twin.org/trust-models";
|
|
15
16
|
import { DataspaceControlPlanePolicyRequester } from "./dataspaceControlPlanePolicyRequester.js";
|
|
@@ -174,7 +175,7 @@ export class DataspaceControlPlaneService {
|
|
|
174
175
|
? StringHelper.trimLeadingAndTrailingSlashes(options.config.callbackPath)
|
|
175
176
|
: undefined;
|
|
176
177
|
this._taskScheduler = ComponentFactory.getIfExists(options?.taskSchedulerComponentType ?? "task-scheduler");
|
|
177
|
-
this._platformComponent = ComponentFactory.
|
|
178
|
+
this._platformComponent = ComponentFactory.get(options?.platformComponentType ?? "platform");
|
|
178
179
|
this._telemetryComponent = ComponentFactory.getIfExists(options?.telemetryComponentType);
|
|
179
180
|
// Data plane component is optional and resolved lazily. The control plane is initialised
|
|
180
181
|
// BEFORE the data plane in engine startup order, so `getRegisteredInstanceTypeOptional`
|
|
@@ -265,7 +266,7 @@ export class DataspaceControlPlaneService {
|
|
|
265
266
|
let totalDatasets = 0;
|
|
266
267
|
// The platform component execute is used so that the dataset publication runs in the
|
|
267
268
|
// tenant context, also works in single tenant mode
|
|
268
|
-
await this._platformComponent
|
|
269
|
+
await this._platformComponent.execute(async () => {
|
|
269
270
|
// The tenant context id is set here for each system tenant
|
|
270
271
|
let cursor;
|
|
271
272
|
do {
|
|
@@ -1295,7 +1296,7 @@ export class DataspaceControlPlaneService {
|
|
|
1295
1296
|
* @param offerId The offer ID from the provider's catalog.
|
|
1296
1297
|
* @param providerEndpoint The provider's contract negotiation endpoint URL.
|
|
1297
1298
|
* @param trustPayload The trust payload for authentication.
|
|
1298
|
-
* @returns
|
|
1299
|
+
* @returns For implicit trust: `{ agreementId }`. For external negotiation: `{ negotiationId }`.
|
|
1299
1300
|
*/
|
|
1300
1301
|
async negotiateAgreement(datasetId, offerId, providerEndpoint, trustPayload) {
|
|
1301
1302
|
Guards.stringValue(DataspaceControlPlaneService.CLASS_NAME, "datasetId", datasetId);
|
|
@@ -1375,6 +1376,9 @@ export class DataspaceControlPlaneService {
|
|
|
1375
1376
|
offerType: getJsonLdType(matchingOffer)
|
|
1376
1377
|
}
|
|
1377
1378
|
});
|
|
1379
|
+
if (trustInfo.identity === organizationId) {
|
|
1380
|
+
return this.negotiateImplicitTrustAgreement(organizationId, datasetId);
|
|
1381
|
+
}
|
|
1378
1382
|
const negotiationId = await this._policyNegotiationPointComponent.sendRequestToProvider(providerEndpoint, DataspaceControlPlaneService._REQUESTER_TYPE, offerId, publicOrigin);
|
|
1379
1383
|
if (!negotiationId) {
|
|
1380
1384
|
throw new GeneralError(DataspaceControlPlaneService.CLASS_NAME, "negotiationInitiationFailed", {
|
|
@@ -1866,6 +1870,29 @@ export class DataspaceControlPlaneService {
|
|
|
1866
1870
|
});
|
|
1867
1871
|
}
|
|
1868
1872
|
}
|
|
1873
|
+
/**
|
|
1874
|
+
* Resolve the control-plane component for the given URL and invoke an action with it. When the
|
|
1875
|
+
* URL maps to a local origin the action runs against this instance inside that origin's context
|
|
1876
|
+
* (avoiding HTTP serialisation). Otherwise the action runs against a remote REST client. Falls
|
|
1877
|
+
* back to remote when no platform component is available or the locality check throws.
|
|
1878
|
+
* @param url The endpoint URL to resolve.
|
|
1879
|
+
* @param action The action to run with the resolved component.
|
|
1880
|
+
* @returns The result of the action.
|
|
1881
|
+
* @internal
|
|
1882
|
+
*/
|
|
1883
|
+
async withControlPlaneComponent(url, action) {
|
|
1884
|
+
try {
|
|
1885
|
+
const localContext = await this._platformComponent.getLocalOriginContext(url);
|
|
1886
|
+
if (!Is.empty(localContext)) {
|
|
1887
|
+
return await ContextIdStore.run(localContext, async () => action(this));
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1890
|
+
catch {
|
|
1891
|
+
// Fall back to remote component if locality check throws
|
|
1892
|
+
}
|
|
1893
|
+
const remoteComponent = ComponentFactory.create(this._remoteControlPlaneComponentType, { endpoint: url, pathPrefix: "" });
|
|
1894
|
+
return action(remoteComponent);
|
|
1895
|
+
}
|
|
1869
1896
|
/**
|
|
1870
1897
|
* Deliver a transfer state-change DSP message to the consumer's callback (provider → consumer), via a
|
|
1871
1898
|
* remote control-plane client and the supplied sender. Mirrors negotiation's sendOfferToConsumer.
|
|
@@ -1891,8 +1918,7 @@ export class DataspaceControlPlaneService {
|
|
|
1891
1918
|
agreementId: entity.agreementId
|
|
1892
1919
|
}
|
|
1893
1920
|
});
|
|
1894
|
-
const
|
|
1895
|
-
const result = await send(remoteControlPlane, outboundToken);
|
|
1921
|
+
const result = await this.withControlPlaneComponent(entity.callbackAddress, async (component) => send(component, outboundToken));
|
|
1896
1922
|
if (getJsonLdType(result) === DataspaceProtocolTransferProcessTypes.TransferError) {
|
|
1897
1923
|
await this._loggingComponent?.log({
|
|
1898
1924
|
level: "error",
|
|
@@ -2488,5 +2514,67 @@ export class DataspaceControlPlaneService {
|
|
|
2488
2514
|
const contextIds = await ContextIdStore.getContextIds();
|
|
2489
2515
|
return contextIds?.[ContextIdKeys.Tenant];
|
|
2490
2516
|
}
|
|
2517
|
+
/**
|
|
2518
|
+
* Return an existing full-access agreement for the same-organization (implicit trust) case,
|
|
2519
|
+
* or create and store one if none exists. Fires onFinalized on all registered callbacks in
|
|
2520
|
+
* both cases.
|
|
2521
|
+
* @param organizationId The local organization ID (both assigner and assignee).
|
|
2522
|
+
* @param datasetId The dataset being granted access to.
|
|
2523
|
+
* @returns The agreement ID.
|
|
2524
|
+
* @internal
|
|
2525
|
+
*/
|
|
2526
|
+
async negotiateImplicitTrustAgreement(organizationId, datasetId) {
|
|
2527
|
+
const { policies } = await this._policyAdministrationPointComponent.query({
|
|
2528
|
+
type: OdrlPolicyType.Agreement,
|
|
2529
|
+
assigner: organizationId,
|
|
2530
|
+
assignee: organizationId,
|
|
2531
|
+
target: datasetId
|
|
2532
|
+
});
|
|
2533
|
+
let agreementId;
|
|
2534
|
+
if (policies.length > 0) {
|
|
2535
|
+
agreementId = OdrlPolicyHelper.getUid(policies[0]);
|
|
2536
|
+
await this._loggingComponent?.log({
|
|
2537
|
+
level: "info",
|
|
2538
|
+
source: DataspaceControlPlaneService.CLASS_NAME,
|
|
2539
|
+
ts: Date.now(),
|
|
2540
|
+
message: "implicitTrustAgreementReused",
|
|
2541
|
+
data: { agreementId, datasetId, organizationId }
|
|
2542
|
+
});
|
|
2543
|
+
}
|
|
2544
|
+
else {
|
|
2545
|
+
const implicitAgreement = {
|
|
2546
|
+
"@context": "http://www.w3.org/ns/odrl.jsonld",
|
|
2547
|
+
"@type": "Agreement",
|
|
2548
|
+
assigner: organizationId,
|
|
2549
|
+
assignee: organizationId,
|
|
2550
|
+
target: datasetId,
|
|
2551
|
+
permission: [{ action: "use" }]
|
|
2552
|
+
};
|
|
2553
|
+
agreementId = await this._policyAdministrationPointComponent.create(implicitAgreement);
|
|
2554
|
+
await MetricHelper.metricIncrement(this._telemetryComponent, DataspaceControlPlaneMetricIds.NegotiationsInitiated);
|
|
2555
|
+
await this._loggingComponent?.log({
|
|
2556
|
+
level: "info",
|
|
2557
|
+
source: DataspaceControlPlaneService.CLASS_NAME,
|
|
2558
|
+
ts: Date.now(),
|
|
2559
|
+
message: "implicitTrustAgreementCreated",
|
|
2560
|
+
data: { agreementId, datasetId, organizationId }
|
|
2561
|
+
});
|
|
2562
|
+
}
|
|
2563
|
+
for (const [key, cb] of this._negotiationCallbacks.entries()) {
|
|
2564
|
+
try {
|
|
2565
|
+
await cb.onFinalized(undefined, agreementId);
|
|
2566
|
+
}
|
|
2567
|
+
catch (error) {
|
|
2568
|
+
await this._loggingComponent?.log({
|
|
2569
|
+
level: "error",
|
|
2570
|
+
source: DataspaceControlPlaneService.CLASS_NAME,
|
|
2571
|
+
ts: Date.now(),
|
|
2572
|
+
message: "negotiationCallbackError",
|
|
2573
|
+
data: { key, negotiationId: agreementId, method: "onFinalized", error }
|
|
2574
|
+
});
|
|
2575
|
+
}
|
|
2576
|
+
}
|
|
2577
|
+
return { agreementId };
|
|
2578
|
+
}
|
|
2491
2579
|
}
|
|
2492
2580
|
//# sourceMappingURL=dataspaceControlPlaneService.js.map
|