@twin.org/dataspace-control-plane-service 0.0.3-next.32 → 0.0.3-next.34
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/dataspaceControlPlanePolicyRequester.js +2 -1
- package/dist/es/dataspaceControlPlanePolicyRequester.js.map +1 -1
- package/dist/es/dataspaceControlPlaneService.js +305 -3
- package/dist/es/dataspaceControlPlaneService.js.map +1 -1
- package/dist/es/models/IDataspaceControlPlaneServiceConstructorOptions.js.map +1 -1
- package/dist/types/dataspaceControlPlaneService.d.ts +41 -1
- package/dist/types/models/IDataspaceControlPlaneServiceConstructorOptions.d.ts +6 -0
- package/docs/changelog.md +33 -0
- package/docs/reference/classes/DataspaceControlPlaneService.md +114 -0
- package/docs/reference/interfaces/IDataspaceControlPlaneServiceConstructorOptions.md +15 -0
- package/locales/en.json +7 -1
- package/package.json +4 -4
|
@@ -168,7 +168,8 @@ export class DataspaceControlPlanePolicyRequester {
|
|
|
168
168
|
}
|
|
169
169
|
});
|
|
170
170
|
if (agreementId) {
|
|
171
|
-
await this._callback?.
|
|
171
|
+
await this._callback?.onStateChanged(negotiationId, DataspaceProtocolContractNegotiationStateType.FINALIZED, { agreement: negotiation.agreement });
|
|
172
|
+
await this._callback?.onFinalized(negotiationId, agreementId);
|
|
172
173
|
}
|
|
173
174
|
else {
|
|
174
175
|
await this._callback?.onFailed(negotiationId, "negotiationFinalizedNoAgreement");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dataspaceControlPlanePolicyRequester.js","sourceRoot":"","sources":["../../src/dataspaceControlPlanePolicyRequester.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2025 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { ComponentFactory } from \"@twin.org/core\";\nimport type { INegotiationCallback } from \"@twin.org/dataspace-models\";\nimport type { ILoggingComponent } from \"@twin.org/logging-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport { OdrlPolicyHelper, type IPolicyRequester } from \"@twin.org/rights-management-models\";\nimport {\n\tDataspaceProtocolContractNegotiationStateType,\n\ttype IDataspaceProtocolAgreement,\n\ttype IDataspaceProtocolOffer\n} from \"@twin.org/standards-dataspace-protocol\";\nimport type { INegotiationState } from \"./models/INegotiationState.js\";\n\n/**\n * Policy Requester for Dataspace Control Plane.\n *\n * Handles contract negotiation callbacks from PNP and forwards state changes\n * to the control plane service via the INegotiationCallback interface.\n *\n * Callback Flow:\n * 1. Control Plane initiates negotiation via PNP.sendRequestToProvider()\n * 2. PNP calls offer() → auto-accept, notify control plane\n * 3. PNP calls agreement() → store agreement, notify control plane\n * 4. PNP calls finalised() → notify control plane with agreementId\n * 5. Control plane notifies upstream caller (e.g. supply-chain)\n */\nexport class DataspaceControlPlanePolicyRequester implements IPolicyRequester {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<DataspaceControlPlanePolicyRequester>();\n\n\t/**\n\t * The logging component.\n\t * @internal\n\t */\n\tprivate readonly _loggingComponent?: ILoggingComponent;\n\n\t/**\n\t * Callback interface for notifying the control plane of negotiation state changes.\n\t * @internal\n\t */\n\tprivate readonly _callback?: INegotiationCallback;\n\n\t/**\n\t * Active negotiations tracked in memory.\n\t * Key: negotiationId\n\t * Value: Negotiation state\n\t * @internal\n\t */\n\tprivate readonly _negotiations: Map<string, INegotiationState>;\n\n\t/**\n\t * Create a new instance of DataspaceControlPlanePolicyRequester.\n\t * @param loggingComponentType Optional logging component type.\n\t * @param callback Optional callback interface for state change notifications.\n\t */\n\tconstructor(loggingComponentType?: string, callback?: INegotiationCallback) {\n\t\tthis._loggingComponent = ComponentFactory.getIfExists<ILoggingComponent>(\n\t\t\tloggingComponentType ?? \"logging\"\n\t\t);\n\t\tthis._callback = callback;\n\t\tthis._negotiations = new Map();\n\t}\n\n\t/**\n\t * Returns the class name of the component.\n\t * @returns The class name of the component.\n\t */\n\tpublic className(): string {\n\t\treturn DataspaceControlPlanePolicyRequester.CLASS_NAME;\n\t}\n\n\t/**\n\t * Register a negotiation for tracking.\n\t * Called by the control plane service after initiating a negotiation via PNP.\n\t * @param negotiationId The negotiation ID returned by PNP.sendRequestToProvider().\n\t */\n\tpublic trackNegotiation(negotiationId: string): void {\n\t\tconst now = Date.now();\n\t\tthis._negotiations.set(negotiationId, {\n\t\t\tnegotiationId,\n\t\t\tstate: DataspaceProtocolContractNegotiationStateType.REQUESTED,\n\t\t\tstartedAt: now,\n\t\t\tupdatedAt: now\n\t\t});\n\t}\n\n\t/**\n\t * Get all active negotiations (for stalled cleanup).\n\t * @returns Map of negotiationId to negotiation state.\n\t */\n\tpublic getActiveNegotiations(): Map<string, INegotiationState> {\n\t\treturn this._negotiations;\n\t}\n\n\t/**\n\t * Remove a negotiation from tracking (for cleanup).\n\t * @param negotiationId The negotiation ID to remove.\n\t */\n\tpublic removeNegotiation(negotiationId: string): void {\n\t\tthis._negotiations.delete(negotiationId);\n\t}\n\n\t/**\n\t * A policy has been offered by a provider.\n\t * Called by PNP when provider sends an OfferMessage.\n\t * @param negotiationId The id of the negotiation.\n\t * @param offer The offer sent by the provider.\n\t * @returns True if the offer was accepted, false otherwise.\n\t */\n\tpublic async offer(negotiationId: string, offer: IDataspaceProtocolOffer): Promise<boolean> {\n\t\tawait this._loggingComponent?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: DataspaceControlPlanePolicyRequester.CLASS_NAME,\n\t\t\tts: Date.now(),\n\t\t\tmessage: \"offerReceived\",\n\t\t\tdata: {\n\t\t\t\tnegotiationId,\n\t\t\t\tofferId: offer[\"@id\"]\n\t\t\t}\n\t\t});\n\n\t\tconst negotiation = this._negotiations.get(negotiationId);\n\t\tif (negotiation) {\n\t\t\tnegotiation.state = DataspaceProtocolContractNegotiationStateType.OFFERED;\n\t\t\tnegotiation.updatedAt = Date.now();\n\t\t}\n\n\t\tawait this._callback?.onStateChanged(\n\t\t\tnegotiationId,\n\t\t\tDataspaceProtocolContractNegotiationStateType.OFFERED,\n\t\t\t{ offer }\n\t\t);\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * A policy agreement has been sent by a provider.\n\t * Called by PNP when provider sends an AgreementMessage.\n\t * @param negotiationId The id of the negotiation.\n\t * @param agreement The agreement sent by the provider.\n\t * @returns True if the agreement was accepted, false otherwise.\n\t */\n\tpublic async agreement(\n\t\tnegotiationId: string,\n\t\tagreement: IDataspaceProtocolAgreement\n\t): Promise<boolean> {\n\t\tawait this._loggingComponent?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: DataspaceControlPlanePolicyRequester.CLASS_NAME,\n\t\t\tts: Date.now(),\n\t\t\tmessage: \"agreementReceived\",\n\t\t\tdata: {\n\t\t\t\tnegotiationId,\n\t\t\t\tagreementId: OdrlPolicyHelper.getUid(agreement) ?? \"\"\n\t\t\t}\n\t\t});\n\n\t\tconst negotiation = this._negotiations.get(negotiationId);\n\t\tif (negotiation) {\n\t\t\tnegotiation.state = DataspaceProtocolContractNegotiationStateType.AGREED;\n\t\t\tnegotiation.agreement = agreement;\n\t\t\tnegotiation.updatedAt = Date.now();\n\t\t}\n\n\t\tawait this._callback?.onStateChanged(\n\t\t\tnegotiationId,\n\t\t\tDataspaceProtocolContractNegotiationStateType.AGREED,\n\t\t\t{ agreement }\n\t\t);\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * A policy finalisation has been sent by a provider.\n\t * Called by PNP when provider sends a FinalizedEvent.\n\t * @param negotiationId The id of the negotiation.\n\t * @returns Nothing.\n\t */\n\tpublic async finalised(negotiationId: string): Promise<void> {\n\t\tawait this._loggingComponent?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: DataspaceControlPlanePolicyRequester.CLASS_NAME,\n\t\t\tts: Date.now(),\n\t\t\tmessage: \"negotiationFinalized\",\n\t\t\tdata: { negotiationId }\n\t\t});\n\n\t\tconst negotiation = this._negotiations.get(negotiationId);\n\t\tif (negotiation) {\n\t\t\tnegotiation.state = DataspaceProtocolContractNegotiationStateType.FINALIZED;\n\t\t\tnegotiation.updatedAt = Date.now();\n\n\t\t\tconst agreementId = OdrlPolicyHelper.getUid(negotiation.agreement);\n\t\t\tthis._negotiations.delete(negotiationId);\n\n\t\t\tawait this._loggingComponent?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: DataspaceControlPlanePolicyRequester.CLASS_NAME,\n\t\t\t\tts: Date.now(),\n\t\t\t\tmessage: \"negotiationCompleted\",\n\t\t\t\tdata: {\n\t\t\t\t\tnegotiationId,\n\t\t\t\t\tagreementId,\n\t\t\t\t\tdurationMs: Date.now() - negotiation.startedAt\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tif (agreementId) {\n\t\t\t\tawait this._callback?.onCompleted(negotiationId, agreementId);\n\t\t\t} else {\n\t\t\t\tawait this._callback?.onFailed(negotiationId, \"negotiationFinalizedNoAgreement\");\n\t\t\t}\n\t\t} else {\n\t\t\tawait this._loggingComponent?.log({\n\t\t\t\tlevel: \"warn\",\n\t\t\t\tsource: DataspaceControlPlanePolicyRequester.CLASS_NAME,\n\t\t\t\tts: Date.now(),\n\t\t\t\tmessage: \"unknownNegotiationFinalized\",\n\t\t\t\tdata: { negotiationId }\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * A policy termination has been sent by a provider.\n\t * Called by PNP when provider sends a TerminatedMessage or negotiation fails.\n\t * @param negotiationId The id of the negotiation.\n\t * @returns Nothing.\n\t */\n\tpublic async terminated(negotiationId: string): Promise<void> {\n\t\tawait this._loggingComponent?.log({\n\t\t\tlevel: \"warn\",\n\t\t\tsource: DataspaceControlPlanePolicyRequester.CLASS_NAME,\n\t\t\tts: Date.now(),\n\t\t\tmessage: \"negotiationTerminated\",\n\t\t\tdata: { negotiationId }\n\t\t});\n\n\t\tconst negotiation = this._negotiations.get(negotiationId);\n\t\tif (negotiation) {\n\t\t\tnegotiation.state = DataspaceProtocolContractNegotiationStateType.TERMINATED;\n\t\t\tnegotiation.updatedAt = Date.now();\n\n\t\t\tthis._negotiations.delete(negotiationId);\n\n\t\t\tawait this._loggingComponent?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: DataspaceControlPlanePolicyRequester.CLASS_NAME,\n\t\t\t\tts: Date.now(),\n\t\t\t\tmessage: \"negotiationRejected\",\n\t\t\t\tdata: {\n\t\t\t\t\tnegotiationId,\n\t\t\t\t\tdurationMs: Date.now() - negotiation.startedAt\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tawait this._callback?.onFailed(negotiationId, \"negotiationTerminatedByProvider\");\n\t\t} else {\n\t\t\tawait this._loggingComponent?.log({\n\t\t\t\tlevel: \"warn\",\n\t\t\t\tsource: DataspaceControlPlanePolicyRequester.CLASS_NAME,\n\t\t\t\tts: Date.now(),\n\t\t\t\tmessage: \"unknownNegotiationTerminated\",\n\t\t\t\tdata: { negotiationId }\n\t\t\t});\n\t\t}\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"dataspaceControlPlanePolicyRequester.js","sourceRoot":"","sources":["../../src/dataspaceControlPlanePolicyRequester.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2025 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { ComponentFactory } from \"@twin.org/core\";\nimport type { INegotiationCallback } from \"@twin.org/dataspace-models\";\nimport type { ILoggingComponent } from \"@twin.org/logging-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport { OdrlPolicyHelper, type IPolicyRequester } from \"@twin.org/rights-management-models\";\nimport {\n\tDataspaceProtocolContractNegotiationStateType,\n\ttype IDataspaceProtocolAgreement,\n\ttype IDataspaceProtocolOffer\n} from \"@twin.org/standards-dataspace-protocol\";\nimport type { INegotiationState } from \"./models/INegotiationState.js\";\n\n/**\n * Policy Requester for Dataspace Control Plane.\n *\n * Handles contract negotiation callbacks from PNP and forwards state changes\n * to the control plane service via the INegotiationCallback interface.\n *\n * Callback Flow:\n * 1. Control Plane initiates negotiation via PNP.sendRequestToProvider()\n * 2. PNP calls offer() → auto-accept, notify control plane\n * 3. PNP calls agreement() → store agreement, notify control plane\n * 4. PNP calls finalised() → notify control plane with agreementId\n * 5. Control plane notifies upstream caller (e.g. supply-chain)\n */\nexport class DataspaceControlPlanePolicyRequester implements IPolicyRequester {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<DataspaceControlPlanePolicyRequester>();\n\n\t/**\n\t * The logging component.\n\t * @internal\n\t */\n\tprivate readonly _loggingComponent?: ILoggingComponent;\n\n\t/**\n\t * Callback interface for notifying the control plane of negotiation state changes.\n\t * @internal\n\t */\n\tprivate readonly _callback?: INegotiationCallback;\n\n\t/**\n\t * Active negotiations tracked in memory.\n\t * Key: negotiationId\n\t * Value: Negotiation state\n\t * @internal\n\t */\n\tprivate readonly _negotiations: Map<string, INegotiationState>;\n\n\t/**\n\t * Create a new instance of DataspaceControlPlanePolicyRequester.\n\t * @param loggingComponentType Optional logging component type.\n\t * @param callback Optional callback interface for state change notifications.\n\t */\n\tconstructor(loggingComponentType?: string, callback?: INegotiationCallback) {\n\t\tthis._loggingComponent = ComponentFactory.getIfExists<ILoggingComponent>(\n\t\t\tloggingComponentType ?? \"logging\"\n\t\t);\n\t\tthis._callback = callback;\n\t\tthis._negotiations = new Map();\n\t}\n\n\t/**\n\t * Returns the class name of the component.\n\t * @returns The class name of the component.\n\t */\n\tpublic className(): string {\n\t\treturn DataspaceControlPlanePolicyRequester.CLASS_NAME;\n\t}\n\n\t/**\n\t * Register a negotiation for tracking.\n\t * Called by the control plane service after initiating a negotiation via PNP.\n\t * @param negotiationId The negotiation ID returned by PNP.sendRequestToProvider().\n\t */\n\tpublic trackNegotiation(negotiationId: string): void {\n\t\tconst now = Date.now();\n\t\tthis._negotiations.set(negotiationId, {\n\t\t\tnegotiationId,\n\t\t\tstate: DataspaceProtocolContractNegotiationStateType.REQUESTED,\n\t\t\tstartedAt: now,\n\t\t\tupdatedAt: now\n\t\t});\n\t}\n\n\t/**\n\t * Get all active negotiations (for stalled cleanup).\n\t * @returns Map of negotiationId to negotiation state.\n\t */\n\tpublic getActiveNegotiations(): Map<string, INegotiationState> {\n\t\treturn this._negotiations;\n\t}\n\n\t/**\n\t * Remove a negotiation from tracking (for cleanup).\n\t * @param negotiationId The negotiation ID to remove.\n\t */\n\tpublic removeNegotiation(negotiationId: string): void {\n\t\tthis._negotiations.delete(negotiationId);\n\t}\n\n\t/**\n\t * A policy has been offered by a provider.\n\t * Called by PNP when provider sends an OfferMessage.\n\t * @param negotiationId The id of the negotiation.\n\t * @param offer The offer sent by the provider.\n\t * @returns True if the offer was accepted, false otherwise.\n\t */\n\tpublic async offer(negotiationId: string, offer: IDataspaceProtocolOffer): Promise<boolean> {\n\t\tawait this._loggingComponent?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: DataspaceControlPlanePolicyRequester.CLASS_NAME,\n\t\t\tts: Date.now(),\n\t\t\tmessage: \"offerReceived\",\n\t\t\tdata: {\n\t\t\t\tnegotiationId,\n\t\t\t\tofferId: offer[\"@id\"]\n\t\t\t}\n\t\t});\n\n\t\tconst negotiation = this._negotiations.get(negotiationId);\n\t\tif (negotiation) {\n\t\t\tnegotiation.state = DataspaceProtocolContractNegotiationStateType.OFFERED;\n\t\t\tnegotiation.updatedAt = Date.now();\n\t\t}\n\n\t\tawait this._callback?.onStateChanged(\n\t\t\tnegotiationId,\n\t\t\tDataspaceProtocolContractNegotiationStateType.OFFERED,\n\t\t\t{ offer }\n\t\t);\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * A policy agreement has been sent by a provider.\n\t * Called by PNP when provider sends an AgreementMessage.\n\t * @param negotiationId The id of the negotiation.\n\t * @param agreement The agreement sent by the provider.\n\t * @returns True if the agreement was accepted, false otherwise.\n\t */\n\tpublic async agreement(\n\t\tnegotiationId: string,\n\t\tagreement: IDataspaceProtocolAgreement\n\t): Promise<boolean> {\n\t\tawait this._loggingComponent?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: DataspaceControlPlanePolicyRequester.CLASS_NAME,\n\t\t\tts: Date.now(),\n\t\t\tmessage: \"agreementReceived\",\n\t\t\tdata: {\n\t\t\t\tnegotiationId,\n\t\t\t\tagreementId: OdrlPolicyHelper.getUid(agreement) ?? \"\"\n\t\t\t}\n\t\t});\n\n\t\tconst negotiation = this._negotiations.get(negotiationId);\n\t\tif (negotiation) {\n\t\t\tnegotiation.state = DataspaceProtocolContractNegotiationStateType.AGREED;\n\t\t\tnegotiation.agreement = agreement;\n\t\t\tnegotiation.updatedAt = Date.now();\n\t\t}\n\n\t\tawait this._callback?.onStateChanged(\n\t\t\tnegotiationId,\n\t\t\tDataspaceProtocolContractNegotiationStateType.AGREED,\n\t\t\t{ agreement }\n\t\t);\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * A policy finalisation has been sent by a provider.\n\t * Called by PNP when provider sends a FinalizedEvent.\n\t * @param negotiationId The id of the negotiation.\n\t * @returns Nothing.\n\t */\n\tpublic async finalised(negotiationId: string): Promise<void> {\n\t\tawait this._loggingComponent?.log({\n\t\t\tlevel: \"info\",\n\t\t\tsource: DataspaceControlPlanePolicyRequester.CLASS_NAME,\n\t\t\tts: Date.now(),\n\t\t\tmessage: \"negotiationFinalized\",\n\t\t\tdata: { negotiationId }\n\t\t});\n\n\t\tconst negotiation = this._negotiations.get(negotiationId);\n\t\tif (negotiation) {\n\t\t\tnegotiation.state = DataspaceProtocolContractNegotiationStateType.FINALIZED;\n\t\t\tnegotiation.updatedAt = Date.now();\n\n\t\t\tconst agreementId = OdrlPolicyHelper.getUid(negotiation.agreement);\n\t\t\tthis._negotiations.delete(negotiationId);\n\n\t\t\tawait this._loggingComponent?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: DataspaceControlPlanePolicyRequester.CLASS_NAME,\n\t\t\t\tts: Date.now(),\n\t\t\t\tmessage: \"negotiationCompleted\",\n\t\t\t\tdata: {\n\t\t\t\t\tnegotiationId,\n\t\t\t\t\tagreementId,\n\t\t\t\t\tdurationMs: Date.now() - negotiation.startedAt\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tif (agreementId) {\n\t\t\t\tawait this._callback?.onStateChanged(\n\t\t\t\t\tnegotiationId,\n\t\t\t\t\tDataspaceProtocolContractNegotiationStateType.FINALIZED,\n\t\t\t\t\t{ agreement: negotiation.agreement }\n\t\t\t\t);\n\n\t\t\t\tawait this._callback?.onFinalized(negotiationId, agreementId);\n\t\t\t} else {\n\t\t\t\tawait this._callback?.onFailed(negotiationId, \"negotiationFinalizedNoAgreement\");\n\t\t\t}\n\t\t} else {\n\t\t\tawait this._loggingComponent?.log({\n\t\t\t\tlevel: \"warn\",\n\t\t\t\tsource: DataspaceControlPlanePolicyRequester.CLASS_NAME,\n\t\t\t\tts: Date.now(),\n\t\t\t\tmessage: \"unknownNegotiationFinalized\",\n\t\t\t\tdata: { negotiationId }\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * A policy termination has been sent by a provider.\n\t * Called by PNP when provider sends a TerminatedMessage or negotiation fails.\n\t * @param negotiationId The id of the negotiation.\n\t * @returns Nothing.\n\t */\n\tpublic async terminated(negotiationId: string): Promise<void> {\n\t\tawait this._loggingComponent?.log({\n\t\t\tlevel: \"warn\",\n\t\t\tsource: DataspaceControlPlanePolicyRequester.CLASS_NAME,\n\t\t\tts: Date.now(),\n\t\t\tmessage: \"negotiationTerminated\",\n\t\t\tdata: { negotiationId }\n\t\t});\n\n\t\tconst negotiation = this._negotiations.get(negotiationId);\n\t\tif (negotiation) {\n\t\t\tnegotiation.state = DataspaceProtocolContractNegotiationStateType.TERMINATED;\n\t\t\tnegotiation.updatedAt = Date.now();\n\n\t\t\tthis._negotiations.delete(negotiationId);\n\n\t\t\tawait this._loggingComponent?.log({\n\t\t\t\tlevel: \"info\",\n\t\t\t\tsource: DataspaceControlPlanePolicyRequester.CLASS_NAME,\n\t\t\t\tts: Date.now(),\n\t\t\t\tmessage: \"negotiationRejected\",\n\t\t\t\tdata: {\n\t\t\t\t\tnegotiationId,\n\t\t\t\t\tdurationMs: Date.now() - negotiation.startedAt\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tawait this._callback?.onFailed(negotiationId, \"negotiationTerminatedByProvider\");\n\t\t} else {\n\t\t\tawait this._loggingComponent?.log({\n\t\t\t\tlevel: \"warn\",\n\t\t\t\tsource: DataspaceControlPlanePolicyRequester.CLASS_NAME,\n\t\t\t\tts: Date.now(),\n\t\t\t\tmessage: \"unknownNegotiationTerminated\",\n\t\t\t\tdata: { negotiationId }\n\t\t\t});\n\t\t}\n\t}\n}\n"]}
|
|
@@ -120,6 +120,22 @@ export class DataspaceControlPlaneService {
|
|
|
120
120
|
* @internal
|
|
121
121
|
*/
|
|
122
122
|
_negotiationCallbacks;
|
|
123
|
+
/**
|
|
124
|
+
* Registered transfer callbacks from upstream callers, keyed by registration key.
|
|
125
|
+
* @internal
|
|
126
|
+
*/
|
|
127
|
+
_transferCallbacks;
|
|
128
|
+
/**
|
|
129
|
+
* Internal transfer callback that fans out to all registered transfer callbacks.
|
|
130
|
+
* @internal
|
|
131
|
+
*/
|
|
132
|
+
_internalTransferCallback;
|
|
133
|
+
/**
|
|
134
|
+
* Factory key used to create remote control plane REST client instances for outbound
|
|
135
|
+
* DSP transfer requests. Resolved via ComponentFactory.create() at call time.
|
|
136
|
+
* @internal
|
|
137
|
+
*/
|
|
138
|
+
_remoteControlPlaneComponentType;
|
|
123
139
|
/**
|
|
124
140
|
* The component type name for the hosting component.
|
|
125
141
|
* @internal
|
|
@@ -159,6 +175,10 @@ export class DataspaceControlPlaneService {
|
|
|
159
175
|
options?.dataPlaneComponentType ?? "dataspace-data-plane-service";
|
|
160
176
|
this._urlTransformerComponent = ComponentFactory.get(options?.urlTransformerComponentType ?? "url-transformer");
|
|
161
177
|
this._negotiationCallbacks = new Map();
|
|
178
|
+
this._transferCallbacks = new Map();
|
|
179
|
+
this._internalTransferCallback = this.createInternalTransferCallback();
|
|
180
|
+
this._remoteControlPlaneComponentType =
|
|
181
|
+
options?.remoteControlPlaneComponentType ?? "dataspace-control-plane-rest-client";
|
|
162
182
|
const internalCallback = this.createInternalCallback();
|
|
163
183
|
this._policyRequester = new DataspaceControlPlanePolicyRequester(options?.loggingComponentType ?? "logging", internalCallback);
|
|
164
184
|
PolicyRequesterFactory.register(DataspaceControlPlaneService._REQUESTER_TYPE, () => this._policyRequester);
|
|
@@ -188,6 +208,24 @@ export class DataspaceControlPlaneService {
|
|
|
188
208
|
Guards.stringValue(DataspaceControlPlaneService.CLASS_NAME, "key", key);
|
|
189
209
|
this._negotiationCallbacks.delete(key);
|
|
190
210
|
}
|
|
211
|
+
/**
|
|
212
|
+
* Register a callback to receive transfer process state change notifications.
|
|
213
|
+
* @param key A unique key identifying this callback registration.
|
|
214
|
+
* @param callback The callback interface to register.
|
|
215
|
+
*/
|
|
216
|
+
registerTransferCallback(key, callback) {
|
|
217
|
+
Guards.stringValue(DataspaceControlPlaneService.CLASS_NAME, "key", key);
|
|
218
|
+
Guards.object(DataspaceControlPlaneService.CLASS_NAME, "callback", callback);
|
|
219
|
+
this._transferCallbacks.set(key, callback);
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Unregister a previously registered transfer callback.
|
|
223
|
+
* @param key The key used when registering the callback.
|
|
224
|
+
*/
|
|
225
|
+
unregisterTransferCallback(key) {
|
|
226
|
+
Guards.stringValue(DataspaceControlPlaneService.CLASS_NAME, "key", key);
|
|
227
|
+
this._transferCallbacks.delete(key);
|
|
228
|
+
}
|
|
191
229
|
/**
|
|
192
230
|
* The service needs to be started when the application is initialized.
|
|
193
231
|
* Populates the Federated Catalogue with datasets from registered apps
|
|
@@ -406,6 +444,142 @@ export class DataspaceControlPlaneService {
|
|
|
406
444
|
state: entity.state
|
|
407
445
|
};
|
|
408
446
|
}
|
|
447
|
+
/**
|
|
448
|
+
* Start a data transfer as a Consumer.
|
|
449
|
+
* Generates a consumerPid, POSTs a TransferRequestMessage to the provider's DSP endpoint,
|
|
450
|
+
* and (only if the provider accepts) persists a local TransferProcess in REQUESTED state.
|
|
451
|
+
* @param agreementId The finalized agreement ID from contract negotiation.
|
|
452
|
+
* @param providerEndpoint The provider's DSP control plane base URL.
|
|
453
|
+
* @param publicOrigin The public origin URL of this control plane (used as callbackAddress).
|
|
454
|
+
* @param format The transfer format (e.g. "HttpProxy-PULL", "HttpProxy-PUSH").
|
|
455
|
+
* @param trustPayload Trust payload for authenticating this call.
|
|
456
|
+
* @returns The consumerPid of the newly created TransferProcess.
|
|
457
|
+
*
|
|
458
|
+
* **Engine configuration requirement:** The outbound call to the provider uses
|
|
459
|
+
* `ComponentFactory.create(remoteControlPlaneComponentType, { endpoint, pathPrefix })`.
|
|
460
|
+
* For the runtime `providerEndpoint` to be forwarded correctly, the engine **must** register
|
|
461
|
+
* the component type (default: `dataspace-control-plane-rest-client`) as a
|
|
462
|
+
* **multi-instance** component (`isMultiInstance: true` in engine config). A singleton
|
|
463
|
+
* registration ignores the runtime `endpoint` arg and silently POSTs to its
|
|
464
|
+
* static endpoint instead.
|
|
465
|
+
*/
|
|
466
|
+
async startDataTransfer(agreementId, providerEndpoint, publicOrigin, format, trustPayload) {
|
|
467
|
+
Guards.stringValue(DataspaceControlPlaneService.CLASS_NAME, "agreementId", agreementId);
|
|
468
|
+
Guards.stringValue(DataspaceControlPlaneService.CLASS_NAME, "providerEndpoint", providerEndpoint);
|
|
469
|
+
Guards.stringValue(DataspaceControlPlaneService.CLASS_NAME, "publicOrigin", publicOrigin);
|
|
470
|
+
Guards.stringValue(DataspaceControlPlaneService.CLASS_NAME, "format", format);
|
|
471
|
+
if (!Object.values(DataspaceTransferFormat).includes(format)) {
|
|
472
|
+
throw new GeneralError(DataspaceControlPlaneService.CLASS_NAME, "unsupportedTransferFormat", {
|
|
473
|
+
format,
|
|
474
|
+
supported: Object.values(DataspaceTransferFormat)
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
const trustInfo = await TrustHelper.verifyTrust(this._trustComponent, trustPayload, "startDataTransfer");
|
|
478
|
+
await this._loggingComponent?.log({
|
|
479
|
+
level: "info",
|
|
480
|
+
source: DataspaceControlPlaneService.CLASS_NAME,
|
|
481
|
+
ts: Date.now(),
|
|
482
|
+
message: "startingDataTransfer",
|
|
483
|
+
data: { agreementId, providerEndpoint, format, identity: trustInfo.identity }
|
|
484
|
+
});
|
|
485
|
+
const agreement = await this.lookupAgreement(agreementId);
|
|
486
|
+
const assigneeIdentity = OdrlPolicyHelper.extractAssigneeIdentity(agreement);
|
|
487
|
+
const assigneeIds = ArrayHelper.fromObjectOrArray(assigneeIdentity);
|
|
488
|
+
const callerComposite = this.buildCallerComposite(trustInfo);
|
|
489
|
+
if (!assigneeIds.includes(callerComposite)) {
|
|
490
|
+
throw new UnauthorizedError(DataspaceControlPlaneService.CLASS_NAME, "callerNotAuthorizedForAgreement");
|
|
491
|
+
}
|
|
492
|
+
const assignerIdentity = OdrlPolicyHelper.extractAssignerIdentity(agreement);
|
|
493
|
+
const assignerIds = ArrayHelper.fromObjectOrArray(assignerIdentity);
|
|
494
|
+
if (assignerIds.length > 1) {
|
|
495
|
+
throw new GeneralError(DataspaceControlPlaneService.CLASS_NAME, "multipleAssignersNotSupported");
|
|
496
|
+
}
|
|
497
|
+
const providerIdentity = assignerIds[0];
|
|
498
|
+
const datasetId = this.extractDatasetId(agreement);
|
|
499
|
+
const consumerPid = `urn:uuid:${RandomHelper.generateUuidV7()}`;
|
|
500
|
+
const callbackAddress = StringHelper.trimTrailingSlashes(publicOrigin);
|
|
501
|
+
// Fetch context once and reuse across the PUSH dataAddress, trust token, and storage entity.
|
|
502
|
+
const contextIds = await ContextIdStore.getContextIds();
|
|
503
|
+
const nodeIdentity = contextIds?.[ContextIdKeys.Node];
|
|
504
|
+
Guards.stringValue(DataspaceControlPlaneService.CLASS_NAME, "nodeIdentity", nodeIdentity);
|
|
505
|
+
const tenantId = contextIds?.[ContextIdKeys.Tenant];
|
|
506
|
+
const organizationIdentity = contextIds?.[ContextIdKeys.Organization];
|
|
507
|
+
const transferRequestMessage = {
|
|
508
|
+
"@context": [DataspaceProtocolContexts.Context],
|
|
509
|
+
"@type": DataspaceProtocolTransferProcessTypes.TransferRequestMessage,
|
|
510
|
+
consumerPid,
|
|
511
|
+
agreementId,
|
|
512
|
+
callbackAddress,
|
|
513
|
+
format
|
|
514
|
+
};
|
|
515
|
+
// For consumer-initiated PUSH transfers the consumer must supply its /inbox endpoint as
|
|
516
|
+
// dataAddress so the provider knows where to push ActivityStreams objects. Without it the
|
|
517
|
+
// provider silently falls through to PULL mode on startTransfer.
|
|
518
|
+
if (format === DataspaceTransferFormat.HttpProxyPush) {
|
|
519
|
+
if (!Is.stringValue(this._dataPlanePath)) {
|
|
520
|
+
throw new GeneralError(DataspaceControlPlaneService.CLASS_NAME, "pushTransferDataPathNotConfigured", { consumerPid });
|
|
521
|
+
}
|
|
522
|
+
let inboxEndpoint = `${callbackAddress}/${this._dataPlanePath}/inbox`;
|
|
523
|
+
if (Is.stringValue(tenantId)) {
|
|
524
|
+
inboxEndpoint = await this._urlTransformerComponent.addEncryptedQueryParamToUrl(inboxEndpoint, "tenant", tenantId);
|
|
525
|
+
}
|
|
526
|
+
transferRequestMessage.dataAddress = {
|
|
527
|
+
"@type": DataspaceProtocolTransferProcessTypes.DataAddress,
|
|
528
|
+
endpointType: DataspaceProtocolEndpointType.HttpsActivityStreamEndpoint,
|
|
529
|
+
endpoint: inboxEndpoint
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
// Generate outbound trust token to authenticate this node to the provider.
|
|
533
|
+
const outboundToken = await this._trustComponent.generate(nodeIdentity, this._overrideTrustGeneratorType, { subject: { consumerPid, agreementId } }, Is.stringValue(tenantId) ? TrustHelper.hashTenantId(tenantId) : undefined, organizationIdentity);
|
|
534
|
+
// Create a remote REST client pointed at the provider endpoint and call requestTransfer.
|
|
535
|
+
const remoteControlPlane = ComponentFactory.create(this._remoteControlPlaneComponentType, { endpoint: providerEndpoint, pathPrefix: "" });
|
|
536
|
+
const result = await remoteControlPlane.requestTransfer(transferRequestMessage, outboundToken);
|
|
537
|
+
if (getJsonLdType(result) === DataspaceProtocolTransferProcessTypes.TransferError) {
|
|
538
|
+
const transferError = result;
|
|
539
|
+
throw new GeneralError(DataspaceControlPlaneService.CLASS_NAME, "transferRequestRejectedByProvider", {
|
|
540
|
+
agreementId,
|
|
541
|
+
providerEndpoint,
|
|
542
|
+
code: transferError.code
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
const transferProcess = result;
|
|
546
|
+
if (!Is.stringValue(transferProcess.providerPid)) {
|
|
547
|
+
throw new GeneralError(DataspaceControlPlaneService.CLASS_NAME, "providerPidMissingInResponse", { consumerPid });
|
|
548
|
+
}
|
|
549
|
+
const now = new Date();
|
|
550
|
+
const storageEntity = {
|
|
551
|
+
id: Converter.bytesToHex(RandomHelper.generate(32)),
|
|
552
|
+
consumerPid,
|
|
553
|
+
providerPid: transferProcess.providerPid,
|
|
554
|
+
state: DataspaceProtocolTransferProcessStateType.REQUESTED,
|
|
555
|
+
agreementId,
|
|
556
|
+
datasetId,
|
|
557
|
+
consumerIdentity: callerComposite,
|
|
558
|
+
providerIdentity,
|
|
559
|
+
offerId: agreementId,
|
|
560
|
+
policies: [agreement],
|
|
561
|
+
callbackAddress,
|
|
562
|
+
format,
|
|
563
|
+
dataAddress: transferRequestMessage.dataAddress,
|
|
564
|
+
tenantId: Is.stringValue(tenantId) ? tenantId : undefined,
|
|
565
|
+
dateCreated: now.toISOString(),
|
|
566
|
+
dateModified: now.toISOString()
|
|
567
|
+
};
|
|
568
|
+
await this._transferProcessStorage.set(storageEntity);
|
|
569
|
+
await this._loggingComponent?.log({
|
|
570
|
+
level: "info",
|
|
571
|
+
source: DataspaceControlPlaneService.CLASS_NAME,
|
|
572
|
+
ts: Date.now(),
|
|
573
|
+
message: "dataTransferStarted",
|
|
574
|
+
data: {
|
|
575
|
+
consumerPid,
|
|
576
|
+
providerPid: storageEntity.providerPid,
|
|
577
|
+
agreementId,
|
|
578
|
+
format
|
|
579
|
+
}
|
|
580
|
+
});
|
|
581
|
+
return { consumerPid };
|
|
582
|
+
}
|
|
409
583
|
// ----------------------------------------------------------------------------
|
|
410
584
|
// PROVIDER SIDE OPERATIONS
|
|
411
585
|
// ----------------------------------------------------------------------------
|
|
@@ -670,6 +844,10 @@ export class DataspaceControlPlaneService {
|
|
|
670
844
|
throw setupError;
|
|
671
845
|
}
|
|
672
846
|
}
|
|
847
|
+
if (role === TransferProcessRole.Consumer) {
|
|
848
|
+
await this._internalTransferCallback.onStateChanged(entity.consumerPid, DataspaceProtocolTransferProcessStateType.STARTED);
|
|
849
|
+
await this._internalTransferCallback.onStarted(entity.consumerPid, response);
|
|
850
|
+
}
|
|
673
851
|
return response;
|
|
674
852
|
}
|
|
675
853
|
catch (error) {
|
|
@@ -757,6 +935,10 @@ export class DataspaceControlPlaneService {
|
|
|
757
935
|
throw teardownError;
|
|
758
936
|
}
|
|
759
937
|
}
|
|
938
|
+
if (role === TransferProcessRole.Consumer) {
|
|
939
|
+
await this._internalTransferCallback.onStateChanged(entity.consumerPid, DataspaceProtocolTransferProcessStateType.COMPLETED);
|
|
940
|
+
await this._internalTransferCallback.onCompleted(entity.consumerPid);
|
|
941
|
+
}
|
|
760
942
|
return {
|
|
761
943
|
"@context": [DataspaceProtocolContexts.Context],
|
|
762
944
|
"@type": DataspaceProtocolTransferProcessTypes.TransferProcess,
|
|
@@ -846,6 +1028,15 @@ export class DataspaceControlPlaneService {
|
|
|
846
1028
|
throw suspendError;
|
|
847
1029
|
}
|
|
848
1030
|
}
|
|
1031
|
+
if (role === TransferProcessRole.Consumer) {
|
|
1032
|
+
await this._internalTransferCallback.onStateChanged(entity.consumerPid, DataspaceProtocolTransferProcessStateType.SUSPENDED);
|
|
1033
|
+
// DSP reason is typed any[] — forward only the first entry since the callback
|
|
1034
|
+
// contract is reason?: string. Additional entries are intentionally dropped.
|
|
1035
|
+
const suspendReason = Array.isArray(message.reason)
|
|
1036
|
+
? message.reason[0]
|
|
1037
|
+
: message.reason;
|
|
1038
|
+
await this._internalTransferCallback.onSuspended(entity.consumerPid, suspendReason);
|
|
1039
|
+
}
|
|
849
1040
|
return {
|
|
850
1041
|
"@context": [DataspaceProtocolContexts.Context],
|
|
851
1042
|
"@type": DataspaceProtocolTransferProcessTypes.TransferProcess,
|
|
@@ -929,6 +1120,15 @@ export class DataspaceControlPlaneService {
|
|
|
929
1120
|
throw teardownError;
|
|
930
1121
|
}
|
|
931
1122
|
}
|
|
1123
|
+
if (role === TransferProcessRole.Consumer) {
|
|
1124
|
+
await this._internalTransferCallback.onStateChanged(entity.consumerPid, DataspaceProtocolTransferProcessStateType.TERMINATED);
|
|
1125
|
+
// DSP reason is typed any[] — forward only the first entry since the callback
|
|
1126
|
+
// contract is reason?: string. Additional entries are intentionally dropped.
|
|
1127
|
+
const terminateReason = Array.isArray(message.reason)
|
|
1128
|
+
? message.reason[0]
|
|
1129
|
+
: message.reason;
|
|
1130
|
+
await this._internalTransferCallback.onTerminated(entity.consumerPid, terminateReason);
|
|
1131
|
+
}
|
|
932
1132
|
return {
|
|
933
1133
|
"@context": [DataspaceProtocolContexts.Context],
|
|
934
1134
|
"@type": DataspaceProtocolTransferProcessTypes.TransferProcess,
|
|
@@ -995,12 +1195,19 @@ export class DataspaceControlPlaneService {
|
|
|
995
1195
|
Guards.stringValue(DataspaceControlPlaneService.CLASS_NAME, "offerId", offerId);
|
|
996
1196
|
Guards.stringValue(DataspaceControlPlaneService.CLASS_NAME, "providerEndpoint", providerEndpoint);
|
|
997
1197
|
Guards.stringValue(DataspaceControlPlaneService.CLASS_NAME, "publicOrigin", publicOrigin);
|
|
1198
|
+
const trustInfo = await TrustHelper.verifyTrust(this._trustComponent, trustPayload, "negotiateAgreement");
|
|
998
1199
|
await this._loggingComponent?.log({
|
|
999
1200
|
level: "info",
|
|
1000
1201
|
source: DataspaceControlPlaneService.CLASS_NAME,
|
|
1001
1202
|
ts: Date.now(),
|
|
1002
1203
|
message: "startingContractNegotiation",
|
|
1003
|
-
data: {
|
|
1204
|
+
data: {
|
|
1205
|
+
datasetId,
|
|
1206
|
+
offerId,
|
|
1207
|
+
providerEndpoint,
|
|
1208
|
+
publicOrigin,
|
|
1209
|
+
verifiedIdentity: trustInfo.identity
|
|
1210
|
+
}
|
|
1004
1211
|
});
|
|
1005
1212
|
const catalogResult = await this._federatedCatalogueComponent.get(datasetId);
|
|
1006
1213
|
if (isCatalogError(catalogResult)) {
|
|
@@ -1955,10 +2162,10 @@ export class DataspaceControlPlaneService {
|
|
|
1955
2162
|
}
|
|
1956
2163
|
}
|
|
1957
2164
|
},
|
|
1958
|
-
|
|
2165
|
+
onFinalized: async (negotiationId, agreementId) => {
|
|
1959
2166
|
for (const [key, cb] of this._negotiationCallbacks.entries()) {
|
|
1960
2167
|
try {
|
|
1961
|
-
await cb.
|
|
2168
|
+
await cb.onFinalized(negotiationId, agreementId);
|
|
1962
2169
|
}
|
|
1963
2170
|
catch (error) {
|
|
1964
2171
|
await this._loggingComponent?.log({
|
|
@@ -1989,6 +2196,101 @@ export class DataspaceControlPlaneService {
|
|
|
1989
2196
|
}
|
|
1990
2197
|
};
|
|
1991
2198
|
}
|
|
2199
|
+
/**
|
|
2200
|
+
* Creates the internal ITransferCallback that fans out to all registered transfer callbacks.
|
|
2201
|
+
* Errors thrown by individual callbacks are logged and swallowed so they cannot affect
|
|
2202
|
+
* the DSP protocol state machine or other registrants.
|
|
2203
|
+
* @returns The internal transfer callback.
|
|
2204
|
+
*/
|
|
2205
|
+
createInternalTransferCallback() {
|
|
2206
|
+
return {
|
|
2207
|
+
onStateChanged: async (consumerPid, state) => {
|
|
2208
|
+
for (const [key, cb] of this._transferCallbacks.entries()) {
|
|
2209
|
+
try {
|
|
2210
|
+
await cb.onStateChanged(consumerPid, state);
|
|
2211
|
+
}
|
|
2212
|
+
catch (error) {
|
|
2213
|
+
await this._loggingComponent?.log({
|
|
2214
|
+
level: "error",
|
|
2215
|
+
source: DataspaceControlPlaneService.CLASS_NAME,
|
|
2216
|
+
ts: Date.now(),
|
|
2217
|
+
message: "transferCallbackError",
|
|
2218
|
+
error: BaseError.fromError(error),
|
|
2219
|
+
data: { key, consumerPid, state, method: "onStateChanged" }
|
|
2220
|
+
});
|
|
2221
|
+
}
|
|
2222
|
+
}
|
|
2223
|
+
},
|
|
2224
|
+
onStarted: async (consumerPid, message) => {
|
|
2225
|
+
for (const [key, cb] of this._transferCallbacks.entries()) {
|
|
2226
|
+
try {
|
|
2227
|
+
await cb.onStarted(consumerPid, message);
|
|
2228
|
+
}
|
|
2229
|
+
catch (error) {
|
|
2230
|
+
await this._loggingComponent?.log({
|
|
2231
|
+
level: "error",
|
|
2232
|
+
source: DataspaceControlPlaneService.CLASS_NAME,
|
|
2233
|
+
ts: Date.now(),
|
|
2234
|
+
message: "transferCallbackError",
|
|
2235
|
+
error: BaseError.fromError(error),
|
|
2236
|
+
data: { key, consumerPid, method: "onStarted" }
|
|
2237
|
+
});
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
2240
|
+
},
|
|
2241
|
+
onCompleted: async (consumerPid) => {
|
|
2242
|
+
for (const [key, cb] of this._transferCallbacks.entries()) {
|
|
2243
|
+
try {
|
|
2244
|
+
await cb.onCompleted(consumerPid);
|
|
2245
|
+
}
|
|
2246
|
+
catch (error) {
|
|
2247
|
+
await this._loggingComponent?.log({
|
|
2248
|
+
level: "error",
|
|
2249
|
+
source: DataspaceControlPlaneService.CLASS_NAME,
|
|
2250
|
+
ts: Date.now(),
|
|
2251
|
+
message: "transferCallbackError",
|
|
2252
|
+
error: BaseError.fromError(error),
|
|
2253
|
+
data: { key, consumerPid, method: "onCompleted" }
|
|
2254
|
+
});
|
|
2255
|
+
}
|
|
2256
|
+
}
|
|
2257
|
+
},
|
|
2258
|
+
onSuspended: async (consumerPid, reason) => {
|
|
2259
|
+
for (const [key, cb] of this._transferCallbacks.entries()) {
|
|
2260
|
+
try {
|
|
2261
|
+
await cb.onSuspended(consumerPid, reason);
|
|
2262
|
+
}
|
|
2263
|
+
catch (error) {
|
|
2264
|
+
await this._loggingComponent?.log({
|
|
2265
|
+
level: "error",
|
|
2266
|
+
source: DataspaceControlPlaneService.CLASS_NAME,
|
|
2267
|
+
ts: Date.now(),
|
|
2268
|
+
message: "transferCallbackError",
|
|
2269
|
+
error: BaseError.fromError(error),
|
|
2270
|
+
data: { key, consumerPid, method: "onSuspended" }
|
|
2271
|
+
});
|
|
2272
|
+
}
|
|
2273
|
+
}
|
|
2274
|
+
},
|
|
2275
|
+
onTerminated: async (consumerPid, reason) => {
|
|
2276
|
+
for (const [key, cb] of this._transferCallbacks.entries()) {
|
|
2277
|
+
try {
|
|
2278
|
+
await cb.onTerminated(consumerPid, reason);
|
|
2279
|
+
}
|
|
2280
|
+
catch (error) {
|
|
2281
|
+
await this._loggingComponent?.log({
|
|
2282
|
+
level: "error",
|
|
2283
|
+
source: DataspaceControlPlaneService.CLASS_NAME,
|
|
2284
|
+
ts: Date.now(),
|
|
2285
|
+
message: "transferCallbackError",
|
|
2286
|
+
error: BaseError.fromError(error),
|
|
2287
|
+
data: { key, consumerPid, method: "onTerminated" }
|
|
2288
|
+
});
|
|
2289
|
+
}
|
|
2290
|
+
}
|
|
2291
|
+
}
|
|
2292
|
+
};
|
|
2293
|
+
}
|
|
1992
2294
|
/**
|
|
1993
2295
|
* Re-stamp `@id` onto a stored payload blob using the entity primary key.
|
|
1994
2296
|
* @param payload The stored payload blob (without `@id`).
|