Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Box SDK get_shared_link_download_url throwing error #141

Closed
kthurimella opened this issue Jun 22, 2016 · 18 comments
Closed

Box SDK get_shared_link_download_url throwing error #141

kthurimella opened this issue Jun 22, 2016 · 18 comments

Comments

@kthurimella
Copy link

We are currently seeing this error:

BoxAPIException:
Message: Bad Request
Status: 400
Code: bad_request
Request id: 472788704576acf6cd4ec5
Headers: {'Content-Length': '227', 'Content-Encoding': 'gzip', 'Age': '0', 'Vary': 'Accept-Encoding', 'Server': 'ATS', 'Connection': 'keep-alive', 'Cache-Control': 'no-cache, no-store', 'Date': 'Wed, 22 Jun 2016 17:48:28 GMT', 'Content-Type': 'application/json'}
URL: https://api.box.com/2.0/files/64281490105
Method: PUT

Our flow is as such:

unshared_at = datetime.now() + timedelta(days=1)
file = self.client.file(file_id=file_id)
return file.get_shared_link_download_url(access=u'open', unshared_at=unshared_at)

This only started happening this morning. We're wondering what changed.

@jmoldow
Copy link
Contributor

jmoldow commented Jun 22, 2016

Can you use the LoggingNetwork to see what request is being sent?

@kthurimella
Copy link
Author

Here's the context info:
Context info: {u'errors': [{u'reason': u'invalid_parameter', u'message': u"Invalid value '1466729934'.", u'name': u'unshared_at'}]}

@kthurimella
Copy link
Author

Any info @jmoldow

@jmoldow
Copy link
Contributor

jmoldow commented Jun 23, 2016

What version of Python are you using, and what version of boxsdk are you using?

I tried this out and it worked fine for me:

In [13]: from datetime import datetime, timedelta

In [14]: unshared_at = datetime.now() + timedelta(days=1)

In [15]: unshared_at
Out[15]: datetime.datetime(2016, 6, 24, 11, 35, 26, 458587)

In [16]: download_url = file.get_shared_link_download_url(access=u'open', unshared_at=unshared_at)
PUT https://api.box.com/2.0/files/67796814441 {'data': '{"shared_link": {"unshared_at": "2016-06-24T11:35:26.458587", '
         '"access": "open"}}',
 'headers': {'Authorization': 'Bearer CpKc2yJXo9TVnqa802bvf4R7zuASMPgG',
             'User-Agent': 'box-python-sdk-1.5.3'},
 'params': None}

I'm using the DevelopmentClient in order to get logging for the request and response.

Notice that the unshared_at field is being sent as 2016-06-24T11:35:26.458587. Whereas in your error context info, it says that your client is trying to send a plain integer, 1466729934. I don't know why it would be doing that.

What happens if you print unshared_at and print unshared_at.isoformat()?

@kthurimella
Copy link
Author

kthurimella commented Jun 28, 2016

I'm using Python 2.7 and Box SDK 1.5.3 (upgraded from 1.5.1)

I'm not sure either. It's been bizarre debugging this issue as well. When I do

print unshared_at
print type(unshared_at)
print unshared_at.isoformat()

2016-06-29 22:11:01.162668
type 'datetime.datetime'
2016-06-29T22:11:01.162668

I'm still unsure of why it's being converting to a unix time stamp.

The download works when I remove the unshared_at portion of the method.

@jmoldow
Copy link
Contributor

jmoldow commented Jun 28, 2016

What kinds of debugging have you tried so far? Have you tried modifying the SDK?

For example, can you edit https://github.com/box/box-python-sdk/blob/master/boxsdk/object/item.py#L220 so that it looks like this:

    def create_shared_link(
            self,
            access=None,
            etag=None,
            unshared_at=None,
            allow_download=None,
            allow_preview=None,
            password=None,
):
        print unshared_at
        print type(unshared_at)
        if unshared_at is not None:
            data['shared_link']['unshared_at'] = unshared_at.isoformat()
        print data

        if allow_download is not None or allow_preview is not None:
            data['shared_link']['permissions'] = permissions = {}
            if allow_download is not None:
                permissions['can_download'] = allow_download
            if allow_preview is not None:
                permissions['can_preview'] = allow_preview

        if password is not None:
            data['shared_link']['password'] = password

        print data
        return self.update_info(data, etag=etag)

Also, can you please try using the DevelopmentClient, so that we can get logs for the API requests and responses? The error message shows a plain integer, but that could be an API mistake. It could be that the SDK is performing 100% correctly. We won't know unless we can see logs of the request.

@kthurimella
Copy link
Author

kthurimella commented Jun 28, 2016

Sorry for not using the development client earlier:


PUT https://api.box.com/2.0/files/64281490105 {'data': '{"shared_link": {"access": "open", "unshared_at": "2016-06-29T22:59:11.977388"}}',
 'headers': {u'Authorization': u'Bearer e5MTLcQLZUKBV1g91KKpbM56YZsmOcgD',
             u'User-Agent': u'box-python-sdk-1.5.3'},
 'params': None}
PUT https://api.box.com/2.0/files/64281490105 {'data': '{"shared_link": {"access": "open", "unshared_at": "2016-06-29T22:59:11.977388"}}',
 'headers': {u'Authorization': u'Bearer e5MTLcQLZUKBV1g91KKpbM56YZsmOcgD',
             u'User-Agent': u'box-python-sdk-1.5.3'},
 'params': None}
/opt/granite/env/local/lib/python2.7/site-packages/requests/packages/urllib3/util/ssl_.py:122: InsecurePlatformWarning: A true SSLContext object is not available. This prevents urllib3 from configuring SSL appropriately and may cause certain SSL connections to fail. You can upgrade to a newer version of Python to solve this. For more information, see https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning.
  InsecurePlatformWarning
400
{'Content-Length': '228', 'Content-Encoding': 'gzip', 'Age': '0', 'Vary': 'Accept-Encoding', 'Server': 'ATS', 'Connection': 'keep-alive', 'Cache-Control': 'no-cache, no-store', 'Date': 'Tue, 28 Jun 2016 23:04:16 GMT', 'Content-Type': 'application/json'}
'{"type":"error","status":400,"code":"bad_request","context_info":{"errors":[{"reason":"invalid_parameter","name":"unshared_at","message":"Invalid value \'1467266351\'."}]},"help_url":"http:\\/\\/developers.box.com\\/docs\\/#errors","message":"Bad Request","request_id":"11242845495773027048ae8"}'

400
{'Content-Length': '228', 'Content-Encoding': 'gzip', 'Age': '0', 'Vary': 'Accept-Encoding', 'Server': 'ATS', 'Connection': 'keep-alive', 'Cache-Control': 'no-cache, no-store', 'Date': 'Tue, 28 Jun 2016 23:04:16 GMT', 'Content-Type': 'application/json'}
'{"type":"error","status":400,"code":"bad_request","context_info":{"errors":[{"reason":"invalid_parameter","name":"unshared_at","message":"Invalid value \'1467266351\'."}]},"help_url":"http:\\/\\/developers.box.com\\/docs\\/#errors","message":"Bad Request","request_id":"11242845495773027048ae8"}'

It looks like the Put request has the correct value for unshared_at. I'll work on editing the item.py file on my end. Pycharm is being weird about changing the read-only access. Will get back to you asap.

@kthurimella
Copy link
Author

2016-06-29 23:13:25.501101
<type 'datetime.datetime'>
{u'shared_link': {u'access': u'open', u'unshared_at': '2016-06-29T23:13:25.501101'}}
{u'shared_link': {u'access': u'open', u'unshared_at': '2016-06-29T23:13:25.501101'}}

This is the print output when I edited item.py

@jmoldow
Copy link
Contributor

jmoldow commented Jun 29, 2016

I can reproduce the problem by using a time that is in the past.

So it seems like an API bug that it responds with the error message Invalid value '1466983468'.. It'd be better if it used the same parameter you sent to the server, and explained that the time is in the past.

As for why you're hitting this... I'm not entirely sure. Since you've been using + timedelta(days=1), that should definitely be in the future. I can't tell how the Box API is interpreting the datetime string. I don't know if it somehow realizes the timezone of my IP address, or if it always assumes Pacific time, or always assumes UTC time. I tried changing my timezone setting on the Box webapp and that didn't seem to change anything, so I don't think it's based on that.

If you do + timedelta(days=2), does that make the problem go away?

Here are two possible workarounds:

  1. Since Box only uses the date (not the time) component anyway, you can pass datetime.combine(your_date, time.max) to make sure that it will (hopefully) be accepted by the API. Since I can't tell what rules the API uses, I'm not 100% sure that this will always work.

  2. Use https://pypi.python.org/pypi/pytz. Instead of using datetime.now() + timedelta(days=1), use pytz.UTC.localize(datetime.utcnow()) + timedelta(days=1). Then, when the SDK calls isoformat on that, you'll get something that looks like '2016-06-30T04:46:00.407874+00:00'. Since the UTC-offset is now on the end of the string, the time is unambiguous, and as long as it is definitely in the future, the Box API shouldn't have a problem processing it.

@kthurimella
Copy link
Author

kthurimella commented Jun 29, 2016

Hey Jordan,

Thanks for the lengthy response. I appreciate you putting so much time into helping me investigate this issue. Unfortunately, none of the workarounds ended up working :(. In case you're curious,

  1. When I added a 5 days to timedelta I ran into the same issue. Here are the logs:
PUT https://api.box.com/2.0/files/64281490105 {'data': '{"shared_link": {"access": "open", "unshared_at": "2016-07-04T18:25:00.160057"}}',
 'headers': {u'Authorization': u'Bearer Eo0F9tGnXQg7K9zt8k6W1Rh49h9qDGcJ',
             u'User-Agent': u'box-python-sdk-1.5.3'},
 'params': None}
/opt/granite/env/local/lib/python2.7/site-packages/requests/packages/urllib3/util/ssl_.py:122: InsecurePlatformWarning: A true SSLContext object is not available. This prevents urllib3 from configuring SSL appropriately and may cause certain SSL connections to fail. You can upgrade to a newer version of Python to solve this. For more information, see https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning.
  InsecurePlatformWarning
400
{'Content-Length': '228', 'Content-Encoding': 'gzip', 'Age': '0', 'Vary': 'Accept-Encoding', 'Server': 'ATS', 'Connection': 'keep-alive', 'Cache-Control': 'no-cache, no-store', 'Date': 'Wed, 29 Jun 2016 18:25:00 GMT', 'Content-Type': 'application/json'}
'{"type":"error","status":400,"code":"bad_request","context_info":{"errors":[{"reason":"invalid_parameter","name":"unshared_at","message":"Invalid value \'1467681900\'."}]},"help_url":"http:\\/\\/developers.box.com\\/docs\\/#errors","message":"Bad Request","request_id":"16326248835774127c3b88f"}'
  1. When I used the datetime combine:
PUT https://api.box.com/2.0/files/64281490105 {'data': '{"shared_link": {"access": "open", "unshared_at": "2016-06-30T23:59:59.999999"}}',
 'headers': {u'Authorization': u'Bearer Eo0F9tGnXQg7K9zt8k6W1Rh49h9qDGcJ',
             u'User-Agent': u'box-python-sdk-1.5.3'},
 'params': None}
/opt/granite/env/local/lib/python2.7/site-packages/requests/packages/urllib3/util/ssl_.py:122: InsecurePlatformWarning: A true SSLContext object is not available. This prevents urllib3 from configuring SSL appropriately and may cause certain SSL connections to fail. You can upgrade to a newer version of Python to solve this. For more information, see https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning.
  InsecurePlatformWarning
400
{'Content-Length': '229', 'Content-Encoding': 'gzip', 'Age': '0', 'Vary': 'Accept-Encoding', 'Server': 'ATS', 'Connection': 'keep-alive', 'Cache-Control': 'no-cache, no-store', 'Date': 'Wed, 29 Jun 2016 18:23:53 GMT', 'Content-Type': 'application/json'}
'{"type":"error","status":400,"code":"bad_request","context_info":{"errors":[{"reason":"invalid_parameter","name":"unshared_at","message":"Invalid value \'1467356399\'."}]},"help_url":"http:\\/\\/developers.box.com\\/docs\\/#errors","message":"Bad Request","request_id":"182506547557741239ccf8f"}'
  1. When I used pytz:
PUT https://api.box.com/2.0/files/64281490105 {'data': '{"shared_link": {"access": "open", "unshared_at": "2016-06-30T18:19:15.863631+00:00"}}',
 'headers': {u'Authorization': u'Bearer Eo0F9tGnXQg7K9zt8k6W1Rh49h9qDGcJ',
             u'User-Agent': u'box-python-sdk-1.5.3'},
 'params': None}
/opt/granite/env/local/lib/python2.7/site-packages/requests/packages/urllib3/util/ssl_.py:122: InsecurePlatformWarning: A true SSLContext object is not available. This prevents urllib3 from configuring SSL appropriately and may cause certain SSL connections to fail. You can upgrade to a newer version of Python to solve this. For more information, see https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning.
  InsecurePlatformWarning
400
{'Content-Length': '229', 'Content-Encoding': 'gzip', 'Age': '1', 'Vary': 'Accept-Encoding', 'Server': 'ATS', 'Connection': 'keep-alive', 'Cache-Control': 'no-cache, no-store', 'Date': 'Wed, 29 Jun 2016 18:19:16 GMT', 'Content-Type': 'application/json'}
'{"type":"error","status":400,"code":"bad_request","context_info":{"errors":[{"reason":"invalid_parameter","name":"unshared_at","message":"Invalid value \'1467310755\'."}]},"help_url":"http:\\/\\/developers.box.com\\/docs\\/#errors","message":"Bad Request","request_id":"139691758457741123ef041"}'

The invalid unix time value is strange because when I convert from Epoch time, it matches when I want the link expired. It's super strange.

I'm still unsure of what it is that's causing this issue. I don't want to take up too much more of your time but I'll either modify it to remove the unshared_at portion or remove the download option in the first place. Thanks for all your help!

@jmoldow
Copy link
Contributor

jmoldow commented Jun 29, 2016

It doesn't look, to me, like you or the SDK are doing anything wrong. When I take that epoch value in the error message, and I use it, it works fine.

Does it work for different files? Or from a different Box account?

I'll see about reporting a bug to our API team.

@kthurimella
Copy link
Author

kthurimella commented Jun 29, 2016

I've tested it for quite a few of the files we have uploaded onto our account and it doesn't seem to work. I haven't tried testing it on another account but I'll give that a go (although our app needs to use this specific account).

Let me check the details of my account to see if anything has changed.

Thanks again for all the help.

@jmoldow
Copy link
Contributor

jmoldow commented Jun 29, 2016

What kind of account do you have?

Is it a free account, enterprise account, developer account, app user, or something else?

Can I see client.user().get()?

@kthurimella
Copy link
Author

<Box User - me (Scout Integration)>

That's the above printed out. The account I'm using is a part of the Uber enterprise, so I believe it's an enterprise.

@jmoldow
Copy link
Contributor

jmoldow commented Jun 29, 2016

Sorry, I meant the HTTP response dict (you can see that in the logs for the DevelopmentClient, or with client.user().get()._response_object.

You can censor anything that looks like it might be private (such as the user and enterprise ids).

@kthurimella
Copy link
Author

Ah sorry sorry:

I've censored information by replacing it with a <>


{"type":"user","id":"<>","name":"<>","login":"<>","created_at":"2016-04-25T13:22:10-07:00","modified_at":"2016-06-29T13:21:19-07:00","language":"en","timezone":"America\/Los_Angeles","space_amount":1.0e+15,"space_used":2287015743,"max_upload_size":16106127360,"status":"active","job_title":"","phone":"","address":"","avatar_url":"https:\/\/<>\/api\/avatar\/large\/<>"}
{u'status': u'active', u'max_upload_size': 16106127360, u'name': u'<>', u'language': u'en', u'created_at': u'2016-04-25T13:22:10-07:00', u'address': u'', u'modified_at': u'2016-06-29T13:21:19-07:00', u'phone': u'', u'avatar_url': u'<>', u'space_used': 2287015743, u'space_amount': 1000000000000000.0, u'timezone': u'America/Los_Angeles', u'login': u'<>', u'type': u'user', u'id': u'<>', u'job_title': u''}

@jmoldow
Copy link
Contributor

jmoldow commented Jun 29, 2016

Can you also do client.user().get(fields=['enterprise']).enterprise, just to confirm that the account is in an enterprise. Don't need to copy the output, just confirm for me that it isn't None.

@kthurimella
Copy link
Author

Confirmed that it isn't none. returns the type, id and name under "enterprise"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants