atlassian-python-api
atlassian-python-api copied to clipboard
[Bitbucket Cloud] Improper payload encoding when POSTing with 'x-www-form-urlencoded' content-type
What I'm trying to do
The bitbucket cloud api offers an endpoint to commit a text file using the Content-Type: x-www-form-urlencoded
header. Using curl
the call looks something like:
curl -u username:password https://api.bitbucket.org/2.0/repositories/myorg/myrepo/src \
--data-urlencode '/file.txt=File content.' \
--data-urlencode 'author=Me McMyself <[email protected]>' \
--data-urlencode 'message=A commit message.' \
--data-urlencode 'branch=somebranch'
What I've tried
from atlassian.bitbucket import Cloud
session = Cloud(username=username, password=password, cloud=True)
repo = session.workspaces.get(myorg).repositories.get(myrepo)
data = {
"/file.txt": "File content."
"author": "Me McMyself <[email protected]>",
"message": "A commit message.",
"branch": "somebranch",
}
repo.post(
"src",
headers={"Content-Type": "application/x-www-form-urlencoded"},
data=data
)
Expected behavior
The outgoing request should have a payload that looks like this:
/file.txt=File+content.&author=Me+McMyself+%3Cmy%40email.com%3E&message=A+commit+message.&branch=somebranch
Actual behavior
Instead, it looks like this:
'"{\"/file.txt\": \"File content.\", \"message\": \"A commit message.\", \"author\": \"Me McMyself <[email protected]>\", \"branch\": \"somebranch\"}"'
...which isn't correctly interpreted by bitbucket because they are expecting a urlencoded payload as above.
Explanation
AtlassianRestAPI
's request
method has the following lines
if files is None:
data = None if not data else dumps(data)
json_dump = None if not json else dumps(json)
which run before the request is passed along to the requests
module...
Workaround
After trying all kinds of variations using this library, I ended up making a call with requests
directly:
requests.request(
"POST",
repo.url + "/src",
data=data,
auth=(session.username, session.password),
)
Thanks, let's think about possibility wrap up non json payload
Can you try to set params=data
instead of data=data
? This should add the parameters instead of a JSON payload.
@Spacetown that won't work because the information has to be passed as data, not as a URL params. The reason @nilueps use "/" at the beginning of his example is because atlassian state this in its API:
There could be a field name clash if a client were to upload a file named "message", as this filename clashes with the meta data property for the commit message. To avoid this and to upload files whose names clash with the meta data properties, use a leading slash for the files, e.g. curl --data-urlencode "/message=file contents".
I did find a slightly better workaround for this issue, instead of using requests
:
data = {
"/file.txt": "File content."
"author": "Me McMyself <[email protected]>",
"message": "A commit message.",
"branch": "somebranch",
}
repo.post(
"src",
headers={"Content-Type": "application/x-www-form-urlencoded"},
data=data,
files=[]
)
As @nilueps stated, the problematic lines are https://github.com/atlassian-api/atlassian-python-api/blob/master/atlassian/rest_client.py#L222-L224, so with my workaround files
won't be set to None
.
@gonchik I guess there's no reason to transform the data to JSON when the Content-Type
states otherwise. I am not sure why transforming it at all if there's a json
parameter available. Maybe it should automatically define the Content-Type
depending on what the call contains e,g json
will automatically set the Content-Type
to application/json
and data
will be set to application/x-www/form-urlencoded
. Just sharing some ideas, what do you think?