[SalesForce] Login with Python SOAP client to Metadata API

I'm using Python zeep to use SOAP to interact with Salesforce's Metadata API.

I'm attempting to start with the simplest thing possible, just log in and read a metadata object.

from zeep import Client as zClient

client = zClient('sf_config/sf_metadata_40.xml')

// readMetadata method exists, prints <zeep.client.OperationProxy object at ...>
print client.service.readMetadata

// errors because login method does not exist
print client.service.login

I know the zeep client reads the XML because it correctly knows that readMetadata exists, but I'm confused about how I am supposed to log in with my Salesforce username and password. I expected a login method to exist.

Additionally, I tried adding username and password to the construction of the client object:

from zeep.wsse.username import UsernameToken

client = zClient('sf_config/sf_metadata_40.xml',
                 wsse=UsernameToken('bub@bob.com', 'scary_secretPassW*rd'))

but it was unsuccessful, in that, executing the readMetadata method caused this error:
Fault: UNKNOWN_EXCEPTION: Destination URL not reset. The URL returned from login must be set in the SforceService

(The Java examples in Salesforce docs use a connection object that I'm not really sure what the Python zeep equivalent would be, so I don't have an example to go off of.)

UPDATE:
That error brings this to mind, from the docs:

Retrieve the metadataServerUrl from the LoginResult returned by your SOAP API login() call.

But I'm not sure what the login() call is, since such a method does not exist on the client.service.

Best Answer

You will need to use the corresponding Partner API login() method to establish a session and get the serverURL where it is valid.

From the ServerUrl you will either need to extract the correct domain or switch it from the Partner API to the Metadata API path.

E.g. Change from

https://server-api.salesforce.com/services/Soap/u/40.0/orgId

To

https://server-api.salesforce.com/services/Soap/m/40.0/orgId

Or even better, use the LoginResult.metadataServerUrl to get the correct endpoint for the session.

Related Topic