SOAP custom adapters
Spinta can load optional Python modules that plug into the SOAP request pipeline. Use this when a SOAP backend needs values that depend on the full request body (for example a signature over several fields) and cannot be prepared from a single manifest property alone.
How it works
Startup —
config.ymllists absolute paths undersoap_adapter_modules. Each file is loaded withimportlib; Spinta readsget_deferred_prepare_names()andget_body_resolvers()from the module.Deferred prepare names — the manifest
preparecolumn can contain ufunc expressions. Names returned byget_deferred_prepare_names()are treated as deferred: they stay as unevaluatedExprobjects while defaults are merged and URL/query parameters are applied property-by-property (so a resolver never sees a half-built body).Body resolvers — After that pass completes, Spinta resolves each deferred expression by calling the matching resolver from
get_body_resolvers(). The callable receives aSoapQueryBuilderenvwithenv.soap_request_body,env.context, etc., and must return the final value (e.g. a string for a SOAP element).
Optional hook: if the module defines validate_soap_adapter_config(raw_config), it runs immediately after the module is loaded. Use it to require extra keys in config.yml so misconfiguration fails at startup.
config.yml
soap_adapter_modules:
- /absolute/path/to/my_adapter.py
List one or more filesystem paths. Paths must exist; missing files are logged and skipped.
Adapter-specific settings live next to other top-level keys, for example the RC signature helper expects:
rc_signature:
private_key_path: /absolute/path/to/private.pem
(Only adapters that read those keys need them; your own adapter can use different keys and validate them in validate_soap_adapter_config.)
DSA manifest (prepare column)
SOAP resource params map manifest properties to the SOAP body. The prepare column describes how each param gets its default value before the request runs.
Typical prepares (
input(),cdata().input(), …) are resolved as soon as the body is assembled.A prepare that calls a deferred ufunc (name registered via
get_deferred_prepare_names()) stays as anExpruntil the adapter resolver runs.
Example (pattern only; column names depend on your CSV layout):
property |
source |
prepare |
|---|---|---|
signature |
|
|
Here rc_signature must be both:
listed in
get_deferred_prepare_names()→["rc_signature"], andprovided in
get_body_resolvers()→{"rc_signature": callable}.
The resolver runs when other body fields are already on env.soap_request_body, so it can read them and compute the signature (or any derived field).
Adapter module contract
Implement a normal Python module (any filename) with:
Function |
Required |
Purpose |
|---|---|---|
|
Yes |
Return |
|
Yes |
Return |
|
No |
Raise if |
Reference implementation in the Spinta tree: spinta/adapters/rc/signature_adapter.py, wired from a manifest row like rc_signature() in the RC broker example (prepare on the signature param). An end-to-end DSA + SoapQueryBuilder.build() example (including rc.fork config keys) lives in tests/adapters/test_soap_dsa_deferred_rc_signature.py.
Operational notes
Use absolute paths in
soap_adapter_modulesso the process cwd does not matter.Adapter modules run in the same Python environment as Spinta; their imports must be available there.
If your resolver needs configuration, read it from
env.context.get("rc")the same way built-in code readsconfig.yml(nested.get("section", "key", …)on the raw config object).