Export to GitHub

salesforce-python-toolkit - issue #8

Any plans on supporting the metadata API?


Posted on Feb 7, 2011 by Happy Rabbit

This is a great library. Any plans on supporting the metadata API?

I tried to hack it up myself, but the trick is that the metadata WSDL does not define login/logout methods, so you have to connect with either the partner or enterprise WSDL. I would like to avoid merging the login/logout definitions. Instead, I tried to login with the partner WSDL, then change the WSDL to metadata WSDL, buy overwriting the underlying Client object, but it didn't work.

sf = SforceBaseClient(parterWsdlFile)
result = sf.login(username, password, sectoken) if '://' not in metaWsdlFile: if os.path.isfile(metaWsdlFile): metaWsdl = 'file://' + os.path.abspath(metaWsdlFile) sf._sforce = Client(metaWsdl) sf._sforce.set_options(location = result.metadataServerUrl)

header = sf.generateHeader('SessionHeader') header.sessionId = result['sessionId'] sf.setSessionHeader(header) sf._sessionId = result['sessionId']

Comment #1

Posted on Feb 7, 2011 by Happy Rabbit

I meant to create this issue as type "enhancement", but I see no way to edit an existing issue, or even delete it (to re-create).

Comment #2

Posted on Feb 7, 2011 by Quick Cat

(No comment was entered for this change.)

Comment #3

Posted on Feb 7, 2011 by Quick Cat

Thanks! hmmm... I haven't ever used the Metadata API, but I can tell you that doing anything low-level with this stuff is a royal pain - that's why I wrote it! :) Just looking at this stuff makes my head hurt. Anyway, I found this Java snippet (http://www.salesforce.com/us/developer/docs/api_meta/Content/meta_deploy.htm) that shows that your approach is correct in terms of logging in with the Enterprise client:

public class DeploySample { // binding for the Enterprise WSDL used for login() call

private SoapBindingStub binding;
// binding for the metadata WSDL used for create() and checkStatus() calls  

The fundamental problem is going to be attaching the session key to each subsequent call, I suspect. Depending on how many different calls you need to make, and the quirkiness of each, it may be easier to use Suds directly - some of the complexity comes from making sure absolutely everything in the API is supported, which you won't need. I'm having trouble envisioning how this would work with Suds without hacking up the WSDLs... Out of morbid curiosity, can you try extending the base client, adding a basic stub method like

def delete(self, ids): ''' Deletes one or more objects ''' self._setHeaders('delete') return self._handleResultTyping(self._sforce.service.delete(ids))

def emptyRecycleBin(self, ids):

and let me know what the output is?

Thanks for your interest!

David

Comment #4

Posted on Feb 7, 2011 by Happy Rabbit

When you say, "base client", I am assuming you're referring to "sforce.SforceBaseClient" - this class already has a method, "delete" with exactly the code you quote. I'm I missing what you're asking?

Comment #5

Posted on Feb 7, 2011 by Quick Cat

ah, that was just an example, but basically I'm suggesting trying to subclass SforceBaseClient (in base.py), and override a method to see what happens, something like

class Foo(SforceBaseClient): def delete(self, ids): return self._sforce.service.delete(ids)

so you're basically just patching through the call to Suds, I'm curious what the output will be.

Comment #6

Posted on Feb 7, 2011 by Happy Rabbit

Got it. Here goes:

{{{ Error, caught suds.WebFault: Server raised fault: 'INVALID_SESSION_ID: Invalid Session ID found in SessionHeader: Illegal Session' No handlers could be found for logger "suds.client" Traceback (most recent call last): File "/Users/cwolf/Documents/workspace/xlstest/src/sfmeta.py", line 393, in ? loginToSalesforce(username, password, sectoken) File "/Users/cwolf/Documents/workspace/xlstest/src/sfmeta.py", line 121, in loginToSalesforce sf.delete(['a015000000YrzWj']) File "/Users/cwolf/Documents/workspace/xlstest/src/sfmeta.py", line 101, in delete return self._sforce.service.delete(ids) File "build/bdist.macosx-10.3-fat/egg/suds/client.py", line 539, in call File "build/bdist.macosx-10.3-fat/egg/suds/client.py", line 598, in invoke File "build/bdist.macosx-10.3-fat/egg/suds/client.py", line 633, in send File "build/bdist.macosx-10.3-fat/egg/suds/client.py", line 684, in failed File "build/bdist.macosx-10.3-fat/egg/suds/bindings/binding.py", line 238, in get_fault suds.WebFault: Server raised fault: 'INVALID_SESSION_ID: Invalid Session ID found in SessionHeader: Illegal Session' }}}

Comment #7

Posted on Feb 7, 2011 by Happy Rabbit

BTW, a bit off-topic, but here's what I used your project for: http://trac-hacks.org/wiki/TicketToSalesforcePlugin

I wish google-code used Trac instead of this half-baked issue-tracker here. (Can't edit issues, can't edit comments, Wiki markup not consistently honored)

Comment #8

Posted on Feb 7, 2011 by Quick Cat

ok, before

return self._sforce.service.delete(ids)

add

self._setHeaders()

and let's see what happens.

Comment #9

Posted on Feb 7, 2011 by Quick Cat

Very cool! I used an open-source project to make my open-source project which is used by your open-source project. And yes, Trac would be awesome :-/

Comment #10

Posted on Feb 7, 2011 by Happy Rabbit

Yep. That worked. Then I changed my original hack to:

if '://' not in metaWsdlFile:
  if os.path.isfile(metaWsdlFile):
    metaWsdl = 'file://' + os.path.abspath(metaWsdlFile)
sf._sforce = Client(metaWsdl)
sf._sforce.set_options(location = result.metadataServerUrl)

sf._setHeaders()

..then I called a metadata API ("listmetadata"):

    obj = sf._sforce.factory.create('ListMetadataQuery')
    obj.type = 'CustomObject'
    args = []
    args.append(obj)
    result = sf._sforce.service.listMetadata(args, 20.0)

    print result

And it totally worked! Of course I'm dropping down into suds to make the call, but I see where this is going...

Comment #11

Posted on Feb 7, 2011 by Quick Cat

Awesome! really, it's not much different from what the Toolkit does for most calls, you're just attaching the HTTP headers as needed and dropping down. A lot of what the Toolkit handles is normalizing the objects that get returned, as it's often inconsistent, but as long as you don't run into any weirdness for the calls that you need, great!

Comment #12

Posted on Feb 7, 2011 by Happy Rabbit

If I find the time to properly implement a subclass of SforceBaseClient, "SforceMetadataClient", which implements metadata methods - would you accept the patch? (Assuming my code is not too horrible) I would base it off 0.3.9 since 0.4.0 appears broken.

Comment #13

Posted on Feb 7, 2011 by Quick Cat

To be honest, I think you'll find that 80% of what you'll need to do is dead simple and the other 20% is very, very difficult. I'd be especially hesitant because you'd need to do threading for deploy(). That said, you are of course more than welcome to share what you've come up with, provided it's compatible with Suds 0.3.6-0.3.9, has unit tests in the ballpark of what I have in tests/base.py, and is documented to the extent that I've done so in the EXAMPLES file.

David

Comment #14

Posted on Feb 7, 2011 by Happy Rabbit

Right, in my prior comment, I was referring to suds, not your code, when mentioning 0.3.9 and 0.4.0. There's nothing wrong with having certain standards, and if what I come up with doesn't fit in - no hard feelings. I will be lucky to find the time anyway...

BTW, thanks a lot for your timely help on this matter.

Status: New

Labels:
Type-Enhancement Priority-Medium