Skip to content

Commit 2cd3209

Browse files
committed
feat: Support get download URL (box/box-codegen#617)
1 parent 7bb1fef commit 2cd3209

File tree

12 files changed

+363
-2
lines changed

12 files changed

+363
-2
lines changed

.codegen.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{ "engineHash": "d435f50", "specHash": "544d370", "version": "1.7.0" }
1+
{ "engineHash": "f073ce3", "specHash": "544d370", "version": "1.7.0" }

box_sdk_gen/client.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,7 @@ def make_request(self, fetch_options: FetchOptions) -> FetchResponse:
424424
multipart_data=fetch_options.multipart_data,
425425
content_type=fetch_options.content_type,
426426
response_format=fetch_options.response_format,
427+
follow_redirects=fetch_options.follow_redirects,
427428
)
428429
return fetch(enriched_fetch_options)
429430

box_sdk_gen/managers/downloads.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,82 @@ def __init__(
4545
self.auth = auth
4646
self.network_session = network_session
4747

48+
def get_download_file_url(
49+
self,
50+
file_id: str,
51+
*,
52+
version: Optional[str] = None,
53+
access_token: Optional[str] = None,
54+
range: Optional[str] = None,
55+
boxapi: Optional[str] = None,
56+
extra_headers: Optional[Dict[str, Optional[str]]] = None
57+
) -> str:
58+
"""
59+
Returns the contents of a file in binary format.
60+
:param file_id: The unique identifier that represents a file.
61+
62+
The ID for any file can be determined
63+
by visiting a file in the web application
64+
and copying the ID from the URL. For example,
65+
for the URL `https://*.app.box.com/files/123`
66+
the `file_id` is `123`.
67+
Example: "12345"
68+
:type file_id: str
69+
:param version: The file version to download, defaults to None
70+
:type version: Optional[str], optional
71+
:param access_token: An optional access token that can be used to pre-authenticate this request, which means that a download link can be shared with a browser or a third party service without them needing to know how to handle the authentication.
72+
When using this parameter, please make sure that the access token is sufficiently scoped down to only allow read access to that file and no other files or folders., defaults to None
73+
:type access_token: Optional[str], optional
74+
:param range: The byte range of the content to download.
75+
76+
The format `bytes={start_byte}-{end_byte}` can be used to specify
77+
what section of the file to download., defaults to None
78+
:type range: Optional[str], optional
79+
:param boxapi: The URL, and optional password, for the shared link of this item.
80+
81+
This header can be used to access items that have not been
82+
explicitly shared with a user.
83+
84+
Use the format `shared_link=[link]` or if a password is required then
85+
use `shared_link=[link]&shared_link_password=[password]`.
86+
87+
This header can be used on the file or folder shared, as well as on any files
88+
or folders nested within the item., defaults to None
89+
:type boxapi: Optional[str], optional
90+
:param extra_headers: Extra headers that will be included in the HTTP request., defaults to None
91+
:type extra_headers: Optional[Dict[str, Optional[str]]], optional
92+
"""
93+
if extra_headers is None:
94+
extra_headers = {}
95+
query_params_map: Dict[str, str] = prepare_params(
96+
{'version': to_string(version), 'access_token': to_string(access_token)}
97+
)
98+
headers_map: Dict[str, str] = prepare_params(
99+
{'range': to_string(range), 'boxapi': to_string(boxapi), **extra_headers}
100+
)
101+
response: FetchResponse = fetch(
102+
FetchOptions(
103+
url=''.join(
104+
[
105+
self.network_session.base_urls.base_url,
106+
'/2.0/files/',
107+
to_string(file_id),
108+
'/content',
109+
]
110+
),
111+
method='GET',
112+
params=query_params_map,
113+
headers=headers_map,
114+
response_format=ResponseFormat.NO_CONTENT,
115+
auth=self.auth,
116+
network_session=self.network_session,
117+
follow_redirects=False,
118+
)
119+
)
120+
if response.headers['location'] == None:
121+
raise BoxSDKError(message='No location header in response')
122+
return response.headers['location']
123+
48124
def download_file(
49125
self,
50126
file_id: str,

box_sdk_gen/managers/files.py

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,11 @@ def __init__(self, id: str, **kwargs):
207207
self.id = id
208208

209209

210+
class GetFileThumbnailUrlExtension(str, Enum):
211+
PNG = 'png'
212+
JPG = 'jpg'
213+
214+
210215
class GetFileThumbnailByIdExtension(str, Enum):
211216
PNG = 'png'
212217
JPG = 'jpg'
@@ -595,6 +600,95 @@ def copy_file(
595600
)
596601
return deserialize(response.data, FileFull)
597602

603+
def get_file_thumbnail_url(
604+
self,
605+
file_id: str,
606+
extension: GetFileThumbnailUrlExtension,
607+
*,
608+
min_height: Optional[int] = None,
609+
min_width: Optional[int] = None,
610+
max_height: Optional[int] = None,
611+
max_width: Optional[int] = None,
612+
extra_headers: Optional[Dict[str, Optional[str]]] = None
613+
) -> str:
614+
"""
615+
Retrieves a thumbnail, or smaller image representation, of a file.
616+
617+
Sizes of `32x32`,`64x64`, `128x128`, and `256x256` can be returned in
618+
619+
620+
the `.png` format and sizes of `32x32`, `160x160`, and `320x320`
621+
622+
623+
can be returned in the `.jpg` format.
624+
625+
626+
Thumbnails can be generated for the image and video file formats listed
627+
628+
629+
[found on our community site][1].
630+
631+
632+
[1]: https://community.box.com/t5/Migrating-and-Previewing-Content/File-Types-and-Fonts-Supported-in-Box-Content-Preview/ta-p/327
633+
634+
:param file_id: The unique identifier that represents a file.
635+
636+
The ID for any file can be determined
637+
by visiting a file in the web application
638+
and copying the ID from the URL. For example,
639+
for the URL `https://*.app.box.com/files/123`
640+
the `file_id` is `123`.
641+
Example: "12345"
642+
:type file_id: str
643+
:param extension: The file format for the thumbnail
644+
Example: "png"
645+
:type extension: GetFileThumbnailUrlExtension
646+
:param min_height: The minimum height of the thumbnail, defaults to None
647+
:type min_height: Optional[int], optional
648+
:param min_width: The minimum width of the thumbnail, defaults to None
649+
:type min_width: Optional[int], optional
650+
:param max_height: The maximum height of the thumbnail, defaults to None
651+
:type max_height: Optional[int], optional
652+
:param max_width: The maximum width of the thumbnail, defaults to None
653+
:type max_width: Optional[int], optional
654+
:param extra_headers: Extra headers that will be included in the HTTP request., defaults to None
655+
:type extra_headers: Optional[Dict[str, Optional[str]]], optional
656+
"""
657+
if extra_headers is None:
658+
extra_headers = {}
659+
query_params_map: Dict[str, str] = prepare_params(
660+
{
661+
'min_height': to_string(min_height),
662+
'min_width': to_string(min_width),
663+
'max_height': to_string(max_height),
664+
'max_width': to_string(max_width),
665+
}
666+
)
667+
headers_map: Dict[str, str] = prepare_params({**extra_headers})
668+
response: FetchResponse = fetch(
669+
FetchOptions(
670+
url=''.join(
671+
[
672+
self.network_session.base_urls.base_url,
673+
'/2.0/files/',
674+
to_string(file_id),
675+
'/thumbnail.',
676+
to_string(extension),
677+
]
678+
),
679+
method='GET',
680+
params=query_params_map,
681+
headers=headers_map,
682+
response_format=ResponseFormat.NO_CONTENT,
683+
auth=self.auth,
684+
network_session=self.network_session,
685+
follow_redirects=False,
686+
)
687+
)
688+
if response.headers['location'] == None:
689+
raise BoxSDKError(message='No location header in response')
690+
return response.headers['location']
691+
598692
def get_file_thumbnail_by_id(
599693
self,
600694
file_id: str,

box_sdk_gen/networking/fetch.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ class APIRequest:
4242
headers: Dict[str, str]
4343
params: Dict[str, str]
4444
data: Optional[Union[str, ByteStream, MultipartEncoder]]
45+
allow_redirects: bool = True
4546

4647

4748
@dataclass
@@ -95,6 +96,7 @@ def fetch(options: FetchOptions) -> FetchResponse:
9596
):
9697
if options.response_format == 'binary':
9798
return FetchResponse(
99+
url=network_response.url,
98100
status=network_response.status_code,
99101
headers=dict(response.network_response.headers),
100102
content=ResponseByteStream(
@@ -103,6 +105,7 @@ def fetch(options: FetchOptions) -> FetchResponse:
103105
)
104106
else:
105107
return FetchResponse(
108+
url=network_response.url,
106109
status=network_response.status_code,
107110
headers=dict(response.network_response.headers),
108111
data=(
@@ -149,6 +152,7 @@ def __prepare_request(
149152
headers = __prepare_headers(options, reauthenticate)
150153
params = options.params or {}
151154
data = __prepare_body(options.content_type, options.file_stream or options.data)
155+
allow_redirects = options.follow_redirects
152156

153157
if options.content_type:
154158
if options.content_type == 'multipart/form-data':
@@ -175,6 +179,7 @@ def __prepare_request(
175179
headers=headers,
176180
params=params,
177181
data=data,
182+
allow_redirects=allow_redirects,
178183
)
179184

180185

@@ -226,6 +231,7 @@ def __make_request(request: APIRequest, session: Session) -> APIResponse:
226231
headers=request.headers,
227232
data=request.data,
228233
params=request.params,
234+
allow_redirects=request.allow_redirects,
229235
stream=True,
230236
)
231237
reauthentication_needed = (

box_sdk_gen/networking/fetch_options.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ def __init__(
6464
content_type: str = 'application/json',
6565
response_format: ResponseFormat = ResponseFormat.JSON,
6666
auth: Optional[Authentication] = None,
67-
network_session: Optional[NetworkSession] = None
67+
network_session: Optional[NetworkSession] = None,
68+
follow_redirects: Optional[bool] = True
6869
):
6970
"""
7071
:param url: URL of the request
@@ -89,6 +90,8 @@ def __init__(
8990
:type auth: Optional[Authentication], optional
9091
:param network_session: Network session object, defaults to None
9192
:type network_session: Optional[NetworkSession], optional
93+
:param follow_redirects: A boolean value indicate if the request should follow redirects. Defaults to True. Not supported in Browser environment., defaults to True
94+
:type follow_redirects: Optional[bool], optional
9295
"""
9396
self.url = url
9497
self.method = method
@@ -101,3 +104,4 @@ def __init__(
101104
self.response_format = response_format
102105
self.auth = auth
103106
self.network_session = network_session
107+
self.follow_redirects = follow_redirects

box_sdk_gen/networking/fetch_response.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ def __init__(
1313
status: int,
1414
headers: Dict[str, str],
1515
*,
16+
url: Optional[str] = None,
1617
data: Optional[SerializedData] = None,
1718
content: Optional[ByteStream] = None
1819
):
@@ -21,12 +22,15 @@ def __init__(
2122
:type status: int
2223
:param headers: HTTP headers of the response
2324
:type headers: Dict[str, str]
25+
:param url: URL of the response, defaults to None
26+
:type url: Optional[str], optional
2427
:param data: Response body of the response, defaults to None
2528
:type data: Optional[SerializedData], optional
2629
:param content: Streamed content of the response, defaults to None
2730
:type content: Optional[ByteStream], optional
2831
"""
2932
self.status = status
3033
self.headers = headers
34+
self.url = url
3135
self.data = data
3236
self.content = content

docs/downloads.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,55 @@
11
# DownloadsManager
22

3+
- [Download file URL](#download-file-url)
34
- [Download file](#download-file)
45
- [Download file](#download-file)
56

7+
## Download file URL
8+
9+
Get the download URL without downloading the content.
10+
11+
This operation is performed by calling function `get_download_file_url`.
12+
13+
See the endpoint docs at
14+
[API Reference](https://developer.box.com/reference/get-files-id-content/).
15+
16+
<!-- sample get_files_id_content -->
17+
18+
```python
19+
client.downloads.get_download_file_url(uploaded_file.id)
20+
```
21+
22+
### Arguments
23+
24+
- file_id `str`
25+
- The unique identifier that represents a file. The ID for any file can be determined by visiting a file in the web application and copying the ID from the URL. For example, for the URL `https://*.app.box.com/files/123` the `file_id` is `123`. Example: "12345"
26+
- version `Optional[str]`
27+
- The file version to download
28+
- access_token `Optional[str]`
29+
- An optional access token that can be used to pre-authenticate this request, which means that a download link can be shared with a browser or a third party service without them needing to know how to handle the authentication. When using this parameter, please make sure that the access token is sufficiently scoped down to only allow read access to that file and no other files or folders.
30+
- range `Optional[str]`
31+
- The byte range of the content to download. The format `bytes={start_byte}-{end_byte}` can be used to specify what section of the file to download.
32+
- boxapi `Optional[str]`
33+
- The URL, and optional password, for the shared link of this item. This header can be used to access items that have not been explicitly shared with a user. Use the format `shared_link=[link]` or if a password is required then use `shared_link=[link]&shared_link_password=[password]`. This header can be used on the file or folder shared, as well as on any files or folders nested within the item.
34+
- extra_headers `Optional[Dict[str, Optional[str]]]`
35+
- Extra headers that will be included in the HTTP request.
36+
37+
### Returns
38+
39+
This function returns a value of type `str`.
40+
41+
Returns the requested file if the client has the **follow
42+
redirects** setting enabled to automatically
43+
follow HTTP `3xx` responses as redirects. If not, the request
44+
will return `302` instead.
45+
For details, see
46+
the [download file guide](g://downloads/file#download-url).If the file is not ready to be downloaded yet `Retry-After` header will
47+
be returned indicating the time in seconds after which the file will
48+
be available for the client to download.
49+
50+
This response can occur when the file was uploaded immediately before the
51+
download request.
52+
653
## Download file
754

855
Returns the contents of a file in binary format.

0 commit comments

Comments
 (0)