pythonweb-servicesencoderallypyral

PyRal getAttachment


I have a fairly simple use-case but i'm not understanding the error message i'm receiving.

I'm using the requests and pyral modules, pyral (http://pyral.readthedocs.io/en/latest/interface.html#) is really just a wrapper for Rally's Restful api. My goal is to get a file (attachment) from a Rally (a CA product) UserStory and store it to a local file system.

For context, here is my environment setup (authenticate to Rally and create an object). I've obviously removed authentication information.

from pyral import Rally, rallyWorkset

options = [arg for arg in sys.argv[1:] if arg.startswith('--')]
args = [arg for arg in sys.argv[1:] if arg not in options]
server, user, password, apikey, workspace, project = rallyWorkset(options)
rally = Rally(server='rally1.rallydev.com', 
user='**********', password='***********', 
          apikey="**************",
          workspace='**************', project='**************',
          server_ping=False)

After that I get a response object for just one user story (see the query for US845), i do this just to simplify the problem.

r = rally.get('UserStory', fetch = True, projectScopeDown=True, query = 'FormattedID = US845')

and then I use the built-in iterator to get the user story from the RallyRESTResponse object.

us = r.next()

from there it feels like I should be able to easily use the getAttachment() method that accepts a artifact (us) and filename (name of an attachment). I'm able to use getAttachmentNames(us) to return a list of attachment names. The issue arrises when i try something like

attachment_names = rally.getAttachmentNames(us) #get attachments for this UserStory
attachment_file = rally.getAttachment(us, attachment_names[0]) #Try to get the first attachment 

returns an error like this

Traceback (most recent call last):

File "<ipython-input-81-a4a342a59c5a>", line 1, in <module>
attachment_file = rally.getAttachment(us, attachment_names[0])

File "C:\Miniconda3\lib\site-packages\pyral\restapi.py", line 1700, in getAttachment
att.Content = base64.decodebytes(att_content.Content)  # maybe further txfm to Unicode ?

File "C:\Miniconda3\lib\base64.py", line 552, in decodebytes
_input_type_check(s)

File "C:\Miniconda3\lib\base64.py", line 520, in _input_type_check
raise TypeError(msg) from err

TypeError: expected bytes-like object, not str

I receive a similar error if i try to use

test_obj = rally.getAttachments(us)

Which returns an error like this:

Traceback (most recent call last):

File "<ipython-input-82-06a8cd525177>", line 1, in <module>
rally.getAttachments(us)

File "C:\Miniconda3\lib\site-packages\pyral\restapi.py", line 1721, in getAttachments
attachments = [self.getAttachment(artifact, attachment_name) for attachment_name in attachment_names]

File "C:\Miniconda3\lib\site-packages\pyral\restapi.py", line 1721, in <listcomp>
attachments = [self.getAttachment(artifact, attachment_name) for attachment_name in attachment_names]

File "C:\Miniconda3\lib\site-packages\pyral\restapi.py", line 1700, in getAttachment
att.Content = base64.decodebytes(att_content.Content)  # maybe further txfm to Unicode ?

File "C:\Miniconda3\lib\base64.py", line 552, in decodebytes
_input_type_check(s)

File "C:\Miniconda3\lib\base64.py", line 520, in _input_type_check
raise TypeError(msg) from err

TypeError: expected bytes-like object, not str

It seems that i'm fundamentally misunderstanding the parameters that this method requires? Has anyone been able to do this successfully before? For what it's worth i have no issues using the addAttachment() method with a workflow similar to the above. I've tried converting the filename (string) with the bytes() method to utf-8 but that didn't help.

I've also looked at this example in the pyral source, but i receive exactly the same error when trying to execute that.

https://github.com/klehman-rally/pyral/blob/master/examples/get_attachments.py


Solution

  • It looks like the issue in restapi.py script - there is no decodebytes method in base64 library:

    att.Content = base64.decodebytes(att_content.Content)
    

    All available methods are described at: RFC 3548: Base16, Base32, Base64 Data Encodings So, workaround is to replace decodebytes by base64.b64decode in restapi.py. At least, it works me.

    E.g. location at Mac OS X:

    /Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/pyral/restapi.py