Skip to content

Adding AccessToken changes header structure #1465

Open
@Kirina

Description

@Kirina

Adding the AccessToken to the header adds the AccessToken as a nested field in the sessionID field. I was very confused as posting raw xml files the the server did give the correct response but when i switched to zeep I suddenly got messages that my header was misformed.

When I show the header structure I get the correctly formed header:

Empty header structure: {
    'SessionID': None,
    'AccessToken': None,
    'CompanyCode': None,
    'CompanyId': None,
    '_attr_1': None
}

When I then add the AccessToken, it is added to the SessionID:

Created header: {
    'SessionID': {
        'AccessToken': 'dummy_access_token_placeholder'
    },
    'AccessToken': None,
    'CompanyCode': None,
    'CompanyId': None,
    '_attr_1': None
}

I also tried to add both the SessionID and AccessToken to see if that fixed it, but then they just both get added to the SessionID:

Created header: {
    'SessionID': {
        'SessionID': None,
        'AccessToken': 'dummy_access_token_placeholder'
    },
    'AccessToken': None,
    'CompanyCode': None,
    'CompanyId': None,
    '_attr_1': None
}

This is the code that I used to create this issue:

from zeep import Client
from zeep.plugins import HistoryPlugin

# zeep==4.3.1

def process_xml_with_zeep(wsdl_url: str, access_token: str):
    client = Client(wsdl_url, plugins=[HistoryPlugin()])

    header_element = client.get_element("ns0:Header")
    print(f"Header element signature: {header_element.signature}")
    print(f"Header element type: {header_element.type}")

    # Empty header gives the correct structure
    empty_header = header_element()
    print(f"Empty header structure: {empty_header}")

    # But the AccessToken somehow gets added to the sessionId as a nested list
    header_data = {"AccessToken": access_token}
    soap_header = header_element(header_data)
    print(f"Created header: {soap_header}")

cluster_url = "https://api.accounting.twinfield.com"
url = f"{cluster_url}/webservices/processxml.asmx?wsdl"
access_token = "dummy_access_token_placeholder"
process_xml_with_zeep(
    wsdl_url=url,
    access_token=access_token,
)

My current solution is to use a manual header construction and avoid zeep for this step and that does work:

def process_xml_with_zeep_manual(access_token: str):
    header = etree.Element(
        "{http://www.twinfield.com/}Header",
        nsmap={None: "http://www.twinfield.com/"},
    )

    # Add AccessToken as direct child
    access_token_elem = etree.SubElement(header, "AccessToken")
    access_token_elem.text = access_token
    print(f"Manual header XML: {etree.tostring(header, pretty_print=True).decode()}")

cluster_url = "https://api.accounting.twinfield.com"
url = f"{cluster_url}/webservices/processxml.asmx?wsdl"
access_token = "dummy_access_token_placeholder"
process_xml_with_zeep_manual(access_token=access_token)

This gives:

Manual header XML: <Header xmlns="http://www.twinfield.com/">
  <AccessToken>dummy_access_token_placeholder</AccessToken>
</Header>

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions