Skip to content

ADR-002 — HTTP/SOAP transport: requests + manual XML

Status: Accepted

Context

The module must communicate with the Hanel t-Server via SOAP over HTTP. A choice must be made on how to build XML envelopes and how to execute HTTP calls.

Options evaluated

Option Pros Cons
zeep Auto-generation from WSDL, automatic parsing Requires accessible WSDL, heavy dependency, obscures the payload
suds-jurko Historical Python SOAP library Abandoned, incompatible with Python 3.10+
requests + manual XML Full control, no additional dependencies, deterministic payload Manual response parsing
httpx Native async, modern Async not required by the specification; extra dependency

Decision

We adopt requests for HTTP transport and f-string templates for building SOAP envelopes, with xml.etree.ElementTree (stdlib) for response parsing.

Main motivation: requirements §9 explicitly indicate this approach. The Hanel t-Server exposes stable, documented SOAP operations; deriving the contract from a runtime WSDL is neither necessary nor desirable.

Implementation

Envelope construction

Envelopes are f-strings centralised in _xml.py:

ENVELOPE_SEND_APD = """\
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
                  xmlns:main="{ns_main}"
                  xmlns:xsd="{ns_xsd}">
  <soapenv:Header/>
  <soapenv:Body>
    <main:sendAPDReqV01>
      <main:param>
        <xsd:articlePoolDataRecord>
          <xsd:articleNumber>{article_number}</xsd:articleNumber>
          <xsd:articleName>{article_name}</xsd:articleName>
        </xsd:articlePoolDataRecord>
      </main:param>
    </main:sendAPDReqV01>
  </soapenv:Body>
</soapenv:Envelope>"""

Executing the call

transport.py executes:

response = requests.post(
    url=config.endpoint_url,
    data=envelope.encode("utf-8"),
    headers={"Content-Type": "text/xml; charset=utf-8"},
    timeout=config.timeout_seconds,
)

Response parsing

_xml.py uses ElementTree with explicit namespaces:

ns = {
    "main": "http://main.jws.com.hanel.de",
    "xsd": "http://main.jws.com.hanel.de/xsd",
}
root = ET.fromstring(response.text)
return_value = root.find(".//xsd:returnValue", ns).text

Consequences

  • XML payload is predictable and readable in logs
  • No additional dependencies beyond requests
  • Every envelope change is directly visible in the source
  • Parsing must be tested with real XML fixtures (see ADR-007)
  • If the t-Server protocol changes, modifications are localised in _xml.py