In working with a SOAP api, the wsdl spec describes the api key passed in the header in a complex namespaced structure as well as additional non-namespaced XML that relates to a paging mechanism for accessing bulk results successively:
Specification:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns="https://webservice_address_here">
<soapenv:Header>
<ns:apiKey>
<api_key>***</api_key>
</ns:apiKey>
<pager>
<page>1</page>
<per_page>100</per_page>
</pager>
</soapenv:Header>
</soapenv:Envelope>
API Documentation:
Pagination; this method returns paginated results. To specify pages or results per page, use the pager header:
<soapenv:Header> <ns:pager> <page>1</page> <per_page>100</per_page> </ns:pager> </soapenv:Header> Max per page is 100
Pagination information is returned in a pager header:
<soapenv:Header> <ns:pager> <page>1</page> <per_page>100</per_page> <next_page>2</next_page> <page_items>100</page_items> <total_items>2829</total_items> <total_pages>29</total_pages> </ns:pager> </soapenv:Header>
The answer, How to set soap headers in zeep when header has multiple elements, describes a similar scenario, without the namespace "ns" but with "acm." I have not been successful in using this method.
This works, allowing access to the api but without the pager making it mostly useless for any methods that return more than 100 results:
from zeep import Client, xsd
# Generate the header structure
header = xsd.Element(
'{wsdl}AuthenticateRequest',
xsd.ComplexType([xsd.Element("{wsdl}api_key", xsd.String())])
)
# Insert values into header placeholders
self._header_value = header(api_key=self.api_key)
This does not work:
from zeep import Client, xsd
# Generate the header structure
header = xsd.Element(
'Header',
xsd.ComplexType([
xsd.Element(
'{wsdl}AuthenticateRequest',
xsd.ComplexType([
xsd.Element('{wsdl}api_key', xsd.String()),
])
),
xsd.Element(
'pager',
xsd.ComplexType([
xsd.Element('page', xsd.String()),
xsd.Element('per_page', xsd.String()),
])
),
])
)
# ERROR HERE: Insert values into header placeholders
self._header_value = header(api_key=self.api_key, pager={'page':1,'per_page':100})
Error: TypeError: ComplexType() got an unexpected keyword argument 'api_key'. Signature: AuthenticateRequest: {api_key: xsd:string}, pager: {page: xsd:string, per_page: xsd:string}
This also does not work:
header = xsd.Element(
'{wsdl}AuthenticateRequest',
xsd.ComplexType([xsd.Element("{wsdl}api_key", xsd.String())]),
xsd.Element(
'pager',
xsd.ComplexType([
xsd.Element('page', xsd.String()),
xsd.Element('per_page', xsd.String()),
])
)
)
# ERROR HERE: Insert values into header placeholders
self._header_value = header(api_key=self.api_key, pager={"page":1,"per_page":100})
'pager' is not defined in the wsdl but the server expects that it could be there.
TypeError: ComplexType() got an unexpected keyword argument 'pager'. Signature:
api_key: xsd:string
Recent Failed (2022) Attempt Using @markoan answer:
def get_pager(self, page: int = 1, per_page: int = 100):
""" Create header that contains the page and records per page. """
pager_header = xsd.Element(
'pager',
xsd.ComplexType([
xsd.Element(
'page', xsd.Integer()
),
xsd.Element(
'per_page', xsd.Integer()
)
])
)
return pager_header(page=page, per_page=per_page)
def call(self, endpoint: str, *args, **kwargs):
"""Allows calling of any client service defined in the WSDL."""
# get the endpoint
endpoint = getattr(self.client.service, endpoint)
# get SOAP authentication header which includes the API key embedded from CFG
headers = [self.get_header()]
# add the pager complex element to headers if required in kwargs
if page := kwargs.get("page"):
per_page = kwargs.get("per_page") or 100
headers.append(self.get_pager(page, per_page))
# call the endpoint with provided unnamed and named parameters if any
result = endpoint(*args, **kwargs, _soapheaders=headers)
# serialize and return result
return self.serialize(result)
What is the simplest way using Zeep to set the namespace api_key and non-namespaced complex pager element?