Ticket #21 (reopened enhancement)

Opened 6 years ago

Last modified 6 months ago

Allow attributes to be set on method Element [PATCH]

Reported by: wyatt.lee.baldwin@… Owned by: jortel
Priority: major Milestone: 0.4.1
Component: suds (general) Version: 0.2.4
Keywords: Cc: daevaorn@…, erikcederstrand
Blocked By: Blocking:

Description

I have a use case where I need to set an attribute on the Element that is associated with a Method. E.g., I have a Method named OTA_HotelResRQ and I need to set the attribute ResStatus on the OTA_HotelResRQ Element, like this:

<SOAP-ENV:Envelope>
   <SOAP-ENV:Header>
      <...>
   </SOAP-ENV:Header>
   <SOAP-ENV:Body>
      <OTA_HotelResRQ ResStatus="Commit">
         <...Method Args...>
      </OTA_HotelResRQ>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

I developed a simple patch that accomplishes this with a few changes in the client and binding modules. Not being super familiar with the library or SOAP in general, I'm not sure if I used the best approach.

Here's a simplified example of client code that uses the new functionality:

    attrs = dict(ResStatus='Commit')        
    reservation = client.service.OTA_HotelResRQ(*args, attributes=attrs)

This produces XML output similar to that shown above.

Here's the patch (also attached to this ticket):

Index: client.py
===================================================================
--- client.py	(revision 204)
+++ client.py	(working copy)
@@ -284,7 +284,8 @@
         binding = self.wsdl.binding().operation(method.name).binding.input
         binding.faults = self.arg.faults
         soapheaders = kwargs.get('soapheaders', ())
-        msg = binding.get_message(method.name, args, soapheaders)
+        attributes = kwargs.get('attributes', None)
+        msg = binding.get_message(method.name, args, soapheaders, attributes)
         timer.stop()
         metrics.log.debug("message for '%s' created: %s", method.name, timer)
         timer.start()
Index: bindings/binding.py
===================================================================
--- bindings/binding.py	(revision 204)
+++ bindings/binding.py	(working copy)
@@ -14,7 +14,7 @@
 # written by: Jeff Ortel ( jortel@redhat.com )
 
 from suds import *
-from suds.sax import Parser, Element, Namespace
+from suds.sax import Parser, Element, Namespace, Attribute
 from suds.sudsobject import Factory, Object
 from suds.bindings.marshaller import Marshaller
 from suds.bindings.unmarshaller import Unmarshaller
@@ -62,9 +62,12 @@
         self.encoded = True
         return self
 
-    def get_message(self, method_name, args, soapheaders):
+    def get_message(self, method_name, args, soapheaders, attributes=None):
         """get the soap message for the specified method and args"""
         method = self.method(method_name)
+        if attributes:
+            for name, val in attributes.items():
+                method.attributes.append(Attribute(name, val))
         body = self.body(method)
         header = self.header(soapheaders)
         env = self.envelope(body, header)

Attachments

set-attrs-on-method-element.patch (1.7 KB) - added by wyatt.lee.baldwin@… 6 years ago.
Patch that adds ability to set attributes of a Method's associated Element
set-attrs-on-method-element.patch-0.4.patch (1.2 KB) - added by erikcederstrand 4 years ago.
Patch for Suds 0.4

Change History

Changed 6 years ago by wyatt.lee.baldwin@…

Patch that adds ability to set attributes of a Method's associated Element

comment:1 Changed 6 years ago by jortel

  • Status changed from new to closed
  • Resolution set to invalid

This is a hack (which you're obviously welcome to do in your copy). Suds already supports setting attributes on objects as:

myobject._ResStatus="Commit"

Again, you're just not seeing this because Suds is not properly handling the inherited Attributes in you wsdl. I'm currently looking at this. Please be patient.

comment:2 Changed 6 years ago by wyatt.lee.baldwin@…

The problem I'm having is not with setting attributes in general, but setting attributes on a method's element. I don't see how to set attributes on a method's element when calling the method. I'll look again, since I may have missed how to do that.

comment:3 Changed 6 years ago by jortel

  • Status changed from closed to reopened
  • Resolution invalid deleted

Ah, after looking closer, the wrapper element (or document root element) for the message part does have attributes.

My apologies Wyatt if my response was harsh - I didn't look closely enough at the wsdl :-/.

This looks like document/literal unwrapped since the document root element name doesn't match the operation name. And, the soap message you're trying to generate has a root element of <OTA_HotelResRQ/> instead of the operation name <CreateReservations/?>. So, maybe the correct approach might be to add detection/support the unwrapped style which would make all the methods take a single parameter representing the document root. In this case it would be something like:

 >
 > client = Client(url)
 > print client
 service (ChannelConnectService)
	prefixes:
		ns0 = "http://www.opentravel.org/OTA/2003/05"
		ns1 = "http://htng.org/1.1/Header/"
	methods (10):
                 CreateReservations(ns0:OTA_HotelResRQ, )
                 ...
 > doc = client.factory.create('ns0:OTA_HotelResRQ')
 > doc._ResStatus = "Commit"
 > ... # complete the document
 > client.service.CreateReservations(doc)
 >

Which would allow for setting the attributes as needed (and specified by the schema) in a "suds" way. Also, it would generate the correct soap message. Currently suds assumes wrapped for document/literal and would set the child of the <soap:body/> as the <operation-name/> containing the contents of the wrapper element.

I need to give some more thought to this.

comment:4 Changed 5 years ago by jortel

  • Status changed from reopened to closed
  • Resolution set to fixed
  • Milestone set to 0.3.4

Wyatt - I'm pretty sure this was provided for in an earlier release. In any case 0.3.4 provides for custom soap headers and passing parameters via named arguments. If 0.3.4 still has deficiencies, please reopen the ticket.

comment:5 Changed 5 years ago by daevaorn

  • Status changed from closed to reopened
  • Component changed from unmarshalling (XML/SOAP input) to suds (general)
  • Resolution fixed deleted
  • Cc daevaorn@… added

I have the similar issue while working with EWS. There is no way to specify method attributes right now& The proposed by Wyatt patch solves the problem.

comment:6 Changed 5 years ago by daevaorn

Just added such capabilities in my bitbucket fork: http://bitbucket.org/daevaorn/suds-ews/

comment:7 Changed 5 years ago by jortel

  • Milestone changed from 0.3.4 to 0.3.8

comment:8 follow-up: ↓ 9 Changed 4 years ago by erikcederstrand

I just spent too much time struggling with this issue until I stumbled upon this patch. Like daevaorn, I'm trying to use EWS.

I haven't found any workarounds for this problem.

The patch seems pretty straight forward. Any chance of committing this soon?

Changed 4 years ago by erikcederstrand

Patch for Suds 0.4

comment:9 in reply to: ↑ 8 Changed 4 years ago by erikcederstrand

The original patch doesn't apply anymore. I created a new minimal patch which allows adding attributes as arguments to the service call:

result = client.service.MyService?(arg1=x, arg2=y, _attr1=z, _attr2=w)

comment:10 Changed 4 years ago by erikcederstrand

  • Cc erikcederstrand added

comment:11 Changed 3 years ago by beaugunderson

Yup, I'm hitting this too on EWS.

I also noticed that it seems like attributes are supported in some way but are placed on the wrong elements. If I create a FindItem? request via the factory there's a _Traversal attribute, but when I send it via service.FindItem? that attribute ends up on the element below the FindItem? element!

comment:12 Changed 3 years ago by jortel

  • Milestone changed from 0.3.8 to 0.4.1

Evaluating patch.

comment:13 Changed 3 years ago by michaelgruenewald

I can confirm that the 0.4 patch works without flaws on EWS 2007 and also doesn't seem to produce any other problems.

comment:14 follow-up: ↓ 15 Changed 3 years ago by gecco

Me too I can confirm that this patch solved my problem BUT with a small addition: An attribute is only added if a value is passed in kwargs. Currently, if no value is passed, the attribute is created with 'None' as content. (Which is of course not acceptable for OPTIONAL arguments)

comment:15 in reply to: ↑ 14 Changed 6 months ago by bfjuju

Replying to gecco:

Me too I can confirm that this patch solved my problem BUT with a small addition: An attribute is only added if a value is passed in kwargs. Currently, if no value is passed, the attribute is created with 'None' as content. (Which is of course not acceptable for OPTIONAL arguments)

I have same problem like you when using suds with EWS. I apply the patch added by erikcederstrand but I had same problem on attribute like you. I change suds/bindings/document.py

Original after patching l. 66

            if pd[1].isattr():
                root.attributes.append(Attribute(pd[0], value))
                continue

Code modified

            if pd[1].isattr():
                if value:
                    root.attributes.append(Attribute(pd[0], value))
                continue
Note: See TracTickets for help on using tickets.