Skip to content

Commit bb4597e

Browse files
feat: Support upload with preflight check (box/box-codegen#676) (#515)
1 parent ef15629 commit bb4597e

File tree

6 files changed

+223
-19
lines changed

6 files changed

+223
-19
lines changed

.codegen.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{ "engineHash": "3d68c3f", "specHash": "c303afc", "version": "1.12.0" }
1+
{ "engineHash": "0f745bf", "specHash": "c303afc", "version": "1.12.0" }

box_sdk_gen/managers/avatars.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,9 @@ def create_user_avatar(
124124
multipart_data=[
125125
MultipartItem(
126126
part_name='pic',
127-
file_stream=request_body.get('pic'),
128-
file_name=request_body.get('pic_file_name'),
129-
content_type=request_body.get('pic_content_type'),
127+
file_stream=pic,
128+
file_name=pic_file_name,
129+
content_type=pic_content_type,
130130
)
131131
],
132132
content_type='multipart/form-data',

box_sdk_gen/managers/uploads.py

Lines changed: 138 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,52 @@ def __init__(
117117
self.content_modified_at = content_modified_at
118118

119119

120+
class UploadWithPreflightCheckAttributesParentField(BaseObject):
121+
def __init__(self, id: str, **kwargs):
122+
"""
123+
:param id: The id of the parent folder. Use
124+
`0` for the user's root folder.
125+
:type id: str
126+
"""
127+
super().__init__(**kwargs)
128+
self.id = id
129+
130+
131+
class UploadWithPreflightCheckAttributes(BaseObject):
132+
def __init__(
133+
self,
134+
name: str,
135+
parent: UploadWithPreflightCheckAttributesParentField,
136+
size: int,
137+
*,
138+
content_created_at: Optional[DateTime] = None,
139+
content_modified_at: Optional[DateTime] = None,
140+
**kwargs
141+
):
142+
"""
143+
:param name: The name of the file
144+
:type name: str
145+
:param parent: The parent folder to upload the file to
146+
:type parent: UploadWithPreflightCheckAttributesParentField
147+
:param size: The size of the file in bytes
148+
:type size: int
149+
:param content_created_at: Defines the time the file was originally created at.
150+
151+
If not set, the upload time will be used., defaults to None
152+
:type content_created_at: Optional[DateTime], optional
153+
:param content_modified_at: Defines the time the file was last modified at.
154+
155+
If not set, the upload time will be used., defaults to None
156+
:type content_modified_at: Optional[DateTime], optional
157+
"""
158+
super().__init__(**kwargs)
159+
self.name = name
160+
self.parent = parent
161+
self.size = size
162+
self.content_created_at = content_created_at
163+
self.content_modified_at = content_modified_at
164+
165+
120166
class UploadsManager:
121167
def __init__(
122168
self,
@@ -246,15 +292,12 @@ def upload_file_version(
246292
params=query_params_map,
247293
headers=headers_map,
248294
multipart_data=[
249-
MultipartItem(
250-
part_name='attributes',
251-
data=serialize(request_body.get('attributes')),
252-
),
295+
MultipartItem(part_name='attributes', data=serialize(attributes)),
253296
MultipartItem(
254297
part_name='file',
255-
file_stream=request_body.get('file'),
256-
file_name=request_body.get('file_file_name'),
257-
content_type=request_body.get('file_content_type'),
298+
file_stream=file,
299+
file_name=file_file_name,
300+
content_type=file_content_type,
258301
),
259302
],
260303
content_type='multipart/form-data',
@@ -394,15 +437,98 @@ def upload_file(
394437
params=query_params_map,
395438
headers=headers_map,
396439
multipart_data=[
440+
MultipartItem(part_name='attributes', data=serialize(attributes)),
397441
MultipartItem(
398-
part_name='attributes',
399-
data=serialize(request_body.get('attributes')),
442+
part_name='file',
443+
file_stream=file,
444+
file_name=file_file_name,
445+
content_type=file_content_type,
400446
),
447+
],
448+
content_type='multipart/form-data',
449+
response_format=ResponseFormat.JSON,
450+
auth=self.auth,
451+
network_session=self.network_session,
452+
)
453+
)
454+
return deserialize(response.data, Files)
455+
456+
def upload_with_preflight_check(
457+
self,
458+
attributes: UploadWithPreflightCheckAttributes,
459+
file: ByteStream,
460+
*,
461+
file_file_name: Optional[str] = None,
462+
file_content_type: Optional[str] = None,
463+
fields: Optional[List[str]] = None,
464+
content_md_5: Optional[str] = None,
465+
extra_headers: Optional[Dict[str, Optional[str]]] = None
466+
) -> Files:
467+
"""
468+
Upload a file with a preflight check
469+
:param file: The content of the file to upload to Box.
470+
471+
<Message warning>
472+
473+
The `attributes` part of the body must come **before** the
474+
`file` part. Requests that do not follow this format when
475+
uploading the file will receive a HTTP `400` error with a
476+
`metadata_after_file_contents` error code.
477+
478+
</Message>
479+
:type file: ByteStream
480+
:param fields: A comma-separated list of attributes to include in the
481+
response. This can be used to request fields that are
482+
not normally returned in a standard response.
483+
484+
Be aware that specifying this parameter will have the
485+
effect that none of the standard fields are returned in
486+
the response unless explicitly specified, instead only
487+
fields for the mini representation are returned, additional
488+
to the fields requested., defaults to None
489+
:type fields: Optional[List[str]], optional
490+
:param content_md_5: An optional header containing the SHA1 hash of the file to
491+
ensure that the file was not corrupted in transit., defaults to None
492+
:type content_md_5: Optional[str], optional
493+
:param extra_headers: Extra headers that will be included in the HTTP request., defaults to None
494+
:type extra_headers: Optional[Dict[str, Optional[str]]], optional
495+
"""
496+
if extra_headers is None:
497+
extra_headers = {}
498+
request_body: Dict = {
499+
'attributes': attributes,
500+
'file': file,
501+
'file_file_name': file_file_name,
502+
'file_content_type': file_content_type,
503+
}
504+
query_params_map: Dict[str, str] = prepare_params({'fields': to_string(fields)})
505+
headers_map: Dict[str, str] = prepare_params(
506+
{'content-md5': to_string(content_md_5), **extra_headers}
507+
)
508+
preflight_upload_url: UploadUrl = self.preflight_file_upload_check(
509+
name=attributes.name,
510+
size=attributes.size,
511+
parent=PreflightFileUploadCheckParent(id=attributes.parent.id),
512+
extra_headers=extra_headers,
513+
)
514+
if (
515+
preflight_upload_url.upload_url == None
516+
or not 'http' in preflight_upload_url.upload_url
517+
):
518+
raise BoxSDKError(message='Unable to get preflight upload URL')
519+
response: FetchResponse = self.network_session.network_client.fetch(
520+
FetchOptions(
521+
url=preflight_upload_url.upload_url,
522+
method='POST',
523+
params=query_params_map,
524+
headers=headers_map,
525+
multipart_data=[
526+
MultipartItem(part_name='attributes', data=serialize(attributes)),
401527
MultipartItem(
402528
part_name='file',
403-
file_stream=request_body.get('file'),
404-
file_name=request_body.get('file_file_name'),
405-
content_type=request_body.get('file_content_type'),
529+
file_stream=file,
530+
file_name=file_file_name,
531+
content_type=file_content_type,
406532
),
407533
],
408534
content_type='multipart/form-data',

box_sdk_gen/managers/zip_downloads.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -311,9 +311,7 @@ def download_zip(
311311
extra_headers = {}
312312
request_body: Dict = {'items': items, 'download_file_name': download_file_name}
313313
zip_download_session: ZipDownload = self.create_zip_download(
314-
request_body.get('items'),
315-
download_file_name=request_body.get('download_file_name'),
316-
extra_headers=extra_headers,
314+
items, download_file_name=download_file_name, extra_headers=extra_headers
317315
)
318316
return self.get_zip_download_content(
319317
zip_download_session.download_url, extra_headers=extra_headers

docs/uploads.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
- [Upload file version](#upload-file-version)
44
- [Preflight check before upload](#preflight-check-before-upload)
55
- [Upload file](#upload-file)
6+
- [Upload a file with a preflight check](#upload-a-file-with-a-preflight-check)
67

78
## Upload file version
89

@@ -140,3 +141,41 @@ client.uploads.upload_file(
140141
This function returns a value of type `Files`.
141142

142143
Returns the new file object in a list.
144+
145+
## Upload a file with a preflight check
146+
147+
Upload a file with a preflight check
148+
149+
This operation is performed by calling function `upload_with_preflight_check`.
150+
151+
```python
152+
client.uploads.upload_with_preflight_check(
153+
UploadWithPreflightCheckAttributes(
154+
name=new_file_name,
155+
size=-1,
156+
parent=UploadWithPreflightCheckAttributesParentField(id="0"),
157+
),
158+
file_content_stream,
159+
)
160+
```
161+
162+
### Arguments
163+
164+
- attributes `UploadWithPreflightCheckAttributes`
165+
-
166+
- file `ByteStream`
167+
- The content of the file to upload to Box. <Message warning> The `attributes` part of the body must come **before** the `file` part. Requests that do not follow this format when uploading the file will receive a HTTP `400` error with a `metadata_after_file_contents` error code. </Message>
168+
- file_file_name `Optional[str]`
169+
-
170+
- file_content_type `Optional[str]`
171+
-
172+
- fields `Optional[List[str]]`
173+
- A comma-separated list of attributes to include in the response. This can be used to request fields that are not normally returned in a standard response. Be aware that specifying this parameter will have the effect that none of the standard fields are returned in the response unless explicitly specified, instead only fields for the mini representation are returned, additional to the fields requested.
174+
- content_md_5 `Optional[str]`
175+
- An optional header containing the SHA1 hash of the file to ensure that the file was not corrupted in transit.
176+
- extra_headers `Optional[Dict[str, Optional[str]]]`
177+
- Extra headers that will be included in the HTTP request.
178+
179+
### Returns
180+
181+
This function returns a value of type `Files`.

test/uploads.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import pytest
2+
13
from box_sdk_gen.internal.utils import ByteStream
24

35
from box_sdk_gen.schemas.files import Files
@@ -10,6 +12,10 @@
1012

1113
from box_sdk_gen.managers.uploads import UploadFileVersionAttributes
1214

15+
from box_sdk_gen.managers.uploads import UploadWithPreflightCheckAttributes
16+
17+
from box_sdk_gen.managers.uploads import UploadWithPreflightCheckAttributesParentField
18+
1319
from box_sdk_gen.schemas.upload_url import UploadUrl
1420

1521
from box_sdk_gen.managers.uploads import PreflightFileUploadCheckParent
@@ -48,6 +54,41 @@ def testUploadFileAndFileVersion():
4854
client.files.delete_file_by_id(new_file_version.id)
4955

5056

57+
def testUploadFileWithPreflightCheck():
58+
new_file_name: str = get_uuid()
59+
file_content_stream: ByteStream = generate_byte_stream(1024 * 1024)
60+
with pytest.raises(Exception):
61+
client.uploads.upload_with_preflight_check(
62+
UploadWithPreflightCheckAttributes(
63+
name=new_file_name,
64+
size=-1,
65+
parent=UploadWithPreflightCheckAttributesParentField(id='0'),
66+
),
67+
file_content_stream,
68+
)
69+
upload_files_with_preflight: Files = client.uploads.upload_with_preflight_check(
70+
UploadWithPreflightCheckAttributes(
71+
name=new_file_name,
72+
size=1024 * 1024,
73+
parent=UploadWithPreflightCheckAttributesParentField(id='0'),
74+
),
75+
file_content_stream,
76+
)
77+
file: FileFull = upload_files_with_preflight.entries[0]
78+
assert file.name == new_file_name
79+
assert file.size == 1024 * 1024
80+
with pytest.raises(Exception):
81+
client.uploads.upload_with_preflight_check(
82+
UploadWithPreflightCheckAttributes(
83+
name=new_file_name,
84+
size=1024 * 1024,
85+
parent=UploadWithPreflightCheckAttributesParentField(id='0'),
86+
),
87+
file_content_stream,
88+
)
89+
client.files.delete_file_by_id(file.id)
90+
91+
5192
def testPreflightCheck():
5293
new_file_name: str = get_uuid()
5394
preflight_check_result: UploadUrl = client.uploads.preflight_file_upload_check(

0 commit comments

Comments
 (0)