Write an adapter¶
Implement or extend a TF-SDK adapter to connect platform operations to a backend. The adapter translates requests into backend calls and returns results or errors in the SDK contract format. SRM remains responsible for target selection, placement and lifecycle state.
Before you start¶
Read the Release 2 execution and discovery design, the implementation guidance (P.2/P.3), and the SDK contribution guide. Use the specification for required behaviour and the release source for concrete Python interfaces. The ADAPTERS_BY_TYPE dictionary maps adapter type names to Python classes. TF-SDK uses the bootstrap’s adapter_type value to select the class.
Reference the existing specification when implementing an already defined capability. If your change requires a specification update, follow the project’s architecture change process.
Steps¶
1. Start from the released contract¶
Use Python 3.10–3.13 and a TF-SDK checkout. Inspect v2.0.0 for the release baseline, then work on your contribution branch following the module's contribution policy:
git clone https://labs.etsi.org/rep/oop/code/tf-sdk.git
cd tf-sdk
git checkout v2.0.0
git switch -c feature/my-backend-adapter
python3 -m venv .venv
. .venv/bin/activate
python -m pip install -e . pytest
The contract models live in src/openop_tfsdk/contract/. Adapter implementations and their registration code live in src/openop_tfsdk/contract/adapters/.
2. Define the functionality and its contract¶
Define the capabilities and operations the adapter will implement, together with their request and result types. Map those operations to the backend’s APIs and declare the implemented support through capability_surface.
If the functionality requires a new SDK capability or operation, extend the SDK contract and its SRM integration. Integrate with OEG when exposing a new public API.
3. Implement the backend operations¶
Create the adapter module under src/openop_tfsdk/contract/adapters/ and give its class a unique adapter_type. Reuse or implement a backend client for the required operations. Implement the methods and data types defined by the functionality’s SDK contract.
Construct ControlPathScope with the configured control_path_ref, and use its check(request) at the start of each execution method.
Use the released adapter implementations as examples of SDK integration.
- Validate configuration in
__init__. Read credential files and open backend connections when executing a request. - Start each execution method with
self._scope.check(request)to verify that the request belongs to this control path. - Use the backend configured for that control path.
- Return SDK result objects.
- Report failures using SDK error types, and keep credentials out of errors and logs.
Use InvalidTargetRef for invalid target references and distinguish these from backend connection failures.
SRM may call the same adapter instance from multiple worker threads. Keep request-specific data in local variables and make shared client state safe for concurrent use. Set explicit timeouts for backend calls and report timeout failures using SDK error types.
4. Register the adapter and declare its capabilities¶
Import your adapter class in src/openop_tfsdk/contract/adapters/__init__.py and add it to the existing tuple that builds ADAPTERS_BY_TYPE. Keep the existing adapters registered.
Both build_adapter and capability_surface use this mapping. The bootstrap’s adapter_type value selects the adapter class.
Declare support through the adapter class's static capability_surface(capability_kind, config=None). Advertise only the actions implemented by the adapter. Return ([], []) for capabilities it does not support; the factory raises UnknownAdapterKind for an unregistered adapter type.
Add a control path to the bootstrap using the registered adapter_type and the backend configuration it requires. Declare the capability under the appropriate zone and domain, and bind it to that control path.
Mark the zone, domain, capability and binding as active when they should be available for execution.
For a newly introduced capability kind, update SRM’s capability definitions and discovery mapping so that SRM recognises it.
Adding an execution adapter does not always require a new discovery implementation. Reuse the existing discovery adapter and update control-path or capability declarations only where the new integration requires them.
Implement a new discovery adapter when introducing another source of topology declarations.
5. Integrate new functionality with SRM and the public API¶
For functionality that SRM does not already handle, add or extend the use case that selects the control path, builds the SDK request and handles the result or error. Update SRM’s execution interface and TF-SDK bridge if the change introduces a new SDK operation.
Connect the use case to the agreed command/event or synchronous-query flow, including state updates and completion handling where required.
If the change introduces or extends a public API, add or update its OEG route, request/response schemas and authorization rules. Connect the API’s application service to the corresponding SRM operation.
Implement the state updates, completion responses or callbacks required by that API. Document the API version and the permissions needed to use it.
6. Test and document the supported path¶
Add tests under tests/contract/ for configuration, capability declarations, request/result translation and error handling. Cover every implemented operation, unsupported inputs and rejection of another control path before backend I/O.
Verify that constructing the adapter, looking up its capabilities and discovering its bindings require no backend access.
Extend the shared test_control_path_guard.py setup for any new configuration or operation that its existing cases do not cover.
Run the repository checks with locked dependencies:
python -m pip install --require-hashes -r ci/requirements.lock
python ci/verify.py lint
python ci/verify.py build
python ci/verify.py tests
Run build before tests: the test runner installs and tests the built wheel. See the CI configuration for the additional checks run by the pipeline.
Add or update tests for the SRM/OEG paths affected by the change. Use an isolated real backend to verify the operation’s results and effects, and clean up any test resources. Extend the platform E2E tests to cover the new functionality.
Document configuration keys, supported operations, limitations and verification results. Record the component and backend versions used, and distinguish implemented functionality from verified backend combinations in the support matrix.
Check it worked¶
- Discovery exposes the intended capabilities and actions, and SRM calls the correct adapter method on the selected backend.
- Requests, results and errors are mapped correctly across the SDK/SRM boundary.
- Public API responses and state transitions match the defined contract and observed backend behaviour.
- Requests for another control path are rejected before backend execution.
- Resources created for verification are cleaned up.
All options¶
Use the SDK contract models, released adapters and contract tests for implementation examples.
The SRM execution bridge shows how SRM calls the SDK. The OEG source shows public routes, application services and authorization mapping.