Skip to content

Commit 2cfdc80

Browse files
feat: Support chunk upload session plan API (box/box-openapi#616) (#1562)
1 parent 9b570c0 commit 2cfdc80

9 files changed

Lines changed: 321 additions & 13 deletions

‎.codegen.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{ "engineHash": "04310d4", "specHash": "be75fa1", "version": "10.14.0" }
1+
{ "engineHash": "04310d4", "specHash": "88cd5aa", "version": "10.14.0" }

‎box_sdk_gen/managers/chunked_uploads.py‎

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212

1313
from box_sdk_gen.networking.fetch_options import ResponseFormat
1414

15+
from box_sdk_gen.schemas.upload_part_plan import UploadPartPlan
16+
1517
from box_sdk_gen.internal.utils import Buffer
1618

1719
from box_sdk_gen.internal.utils import HashName
@@ -26,6 +28,10 @@
2628

2729
from box_sdk_gen.schemas.upload_parts import UploadParts
2830

31+
from box_sdk_gen.schemas.upload_session_plan_response import UploadSessionPlanResponse
32+
33+
from box_sdk_gen.schemas.upload_session_plan_request import UploadSessionPlanRequest
34+
2935
from box_sdk_gen.schemas.files import Files
3036

3137
from box_sdk_gen.schemas.upload_part import UploadPart
@@ -617,6 +623,112 @@ def get_file_upload_session_parts(
617623
)
618624
return deserialize(response.data, UploadParts)
619625

626+
def create_file_upload_session_plan_by_url(
627+
self,
628+
url: str,
629+
parts: List[UploadPartPlan],
630+
*,
631+
extra_headers: Optional[Dict[str, Optional[str]]] = None
632+
) -> UploadSessionPlanResponse:
633+
"""
634+
Using this method with urls provided in response when creating a new upload session is preferred to use over CreateFileUploadSessionPlan method.
635+
636+
This allows to always upload your content to the closest Box data center and can significantly improve upload speed.
637+
638+
639+
Plan an upload session by checking which parts already exist on the server.
640+
641+
642+
This endpoint allows clients to optimize uploads by skipping parts that
643+
644+
645+
have already been uploaded (cache hits) and only uploading missing parts.
646+
647+
648+
The actual endpoint URL is returned by the [`Create upload session`](e://post-files-upload-sessions)
649+
650+
651+
and [`Get upload session`](e://get-files-upload-sessions-id) endpoints.
652+
653+
:param url: URL of createFileUploadSessionPlan method
654+
:type url: str
655+
:param parts: The list of parts to check for existence.
656+
:type parts: List[UploadPartPlan]
657+
:param extra_headers: Extra headers that will be included in the HTTP request., defaults to None
658+
:type extra_headers: Optional[Dict[str, Optional[str]]], optional
659+
"""
660+
if extra_headers is None:
661+
extra_headers = {}
662+
request_body: Dict = {'parts': parts}
663+
headers_map: Dict[str, str] = prepare_params({**extra_headers})
664+
response: FetchResponse = self.network_session.network_client.fetch(
665+
FetchOptions(
666+
url=url,
667+
method='POST',
668+
headers=headers_map,
669+
data=serialize(request_body),
670+
content_type='application/json',
671+
response_format=ResponseFormat.JSON,
672+
auth=self.auth,
673+
network_session=self.network_session,
674+
)
675+
)
676+
return deserialize(response.data, UploadSessionPlanResponse)
677+
678+
def create_file_upload_session_plan(
679+
self,
680+
upload_session_id: str,
681+
parts: List[UploadPartPlan],
682+
*,
683+
extra_headers: Optional[Dict[str, Optional[str]]] = None
684+
) -> UploadSessionPlanResponse:
685+
"""
686+
Plan an upload session by checking which parts already exist on the server.
687+
688+
This endpoint allows clients to optimize uploads by skipping parts that
689+
690+
691+
have already been uploaded (cache hits) and only uploading missing parts.
692+
693+
694+
The actual endpoint URL is returned by the [`Create upload session`](e://post-files-upload-sessions)
695+
696+
697+
and [`Get upload session`](e://get-files-upload-sessions-id) endpoints.
698+
699+
:param upload_session_id: The ID of the upload session.
700+
Example: "D5E3F7A"
701+
:type upload_session_id: str
702+
:param parts: The list of parts to check for existence.
703+
:type parts: List[UploadPartPlan]
704+
:param extra_headers: Extra headers that will be included in the HTTP request., defaults to None
705+
:type extra_headers: Optional[Dict[str, Optional[str]]], optional
706+
"""
707+
if extra_headers is None:
708+
extra_headers = {}
709+
request_body: Dict = {'parts': parts}
710+
headers_map: Dict[str, str] = prepare_params({**extra_headers})
711+
response: FetchResponse = self.network_session.network_client.fetch(
712+
FetchOptions(
713+
url=''.join(
714+
[
715+
self.network_session.base_urls.upload_url,
716+
'/2.0/files/upload_sessions/',
717+
to_string(upload_session_id),
718+
'/plan',
719+
]
720+
),
721+
method='POST',
722+
headers=headers_map,
723+
data=serialize(request_body),
724+
content_type='application/json',
725+
response_format=ResponseFormat.JSON,
726+
auth=self.auth,
727+
network_session=self.network_session,
728+
)
729+
)
730+
return deserialize(response.data, UploadSessionPlanResponse)
731+
620732
def create_file_upload_session_commit_by_url(
621733
self,
622734
url: str,

‎box_sdk_gen/schemas/__init__.py‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,14 @@
382382

383383
from box_sdk_gen.schemas.uploaded_part import *
384384

385+
from box_sdk_gen.schemas.upload_part_plan import *
386+
387+
from box_sdk_gen.schemas.upload_session_plan_request import *
388+
389+
from box_sdk_gen.schemas.upload_part_plan_hit import *
390+
391+
from box_sdk_gen.schemas.upload_session_plan_response import *
392+
385393
from box_sdk_gen.schemas.upload_session import *
386394

387395
from box_sdk_gen.schemas.upload_url import *
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from typing import Dict
2+
3+
from box_sdk_gen.internal.base_object import BaseObject
4+
5+
from box_sdk_gen.box.errors import BoxSDKError
6+
7+
8+
class UploadPartPlan(BaseObject):
9+
_fields_to_json_mapping: Dict[str, str] = {
10+
'sha_512': 'sha512',
11+
**BaseObject._fields_to_json_mapping,
12+
}
13+
_json_to_fields_mapping: Dict[str, str] = {
14+
'sha512': 'sha_512',
15+
**BaseObject._json_to_fields_mapping,
16+
}
17+
18+
def __init__(self, offset: int, size: int, sha_512: str, **kwargs):
19+
"""
20+
:param offset: The offset of the chunk within the file
21+
in bytes. The lower bound of the position
22+
of the chunk within the file.
23+
:type offset: int
24+
:param size: The size of the chunk in bytes.
25+
:type size: int
26+
:param sha_512: The `SHA-512` hash of the chunk.
27+
:type sha_512: str
28+
"""
29+
super().__init__(**kwargs)
30+
self.offset = offset
31+
self.size = size
32+
self.sha_512 = sha_512
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from typing import Dict
2+
3+
from box_sdk_gen.internal.base_object import BaseObject
4+
5+
from box_sdk_gen.box.errors import BoxSDKError
6+
7+
8+
class UploadPartPlanHit(BaseObject):
9+
_fields_to_json_mapping: Dict[str, str] = {
10+
'sha_512': 'sha512',
11+
**BaseObject._fields_to_json_mapping,
12+
}
13+
_json_to_fields_mapping: Dict[str, str] = {
14+
'sha512': 'sha_512',
15+
**BaseObject._json_to_fields_mapping,
16+
}
17+
18+
def __init__(self, offset: int, size: int, sha_512: str, part_id: str, **kwargs):
19+
"""
20+
:param offset: The offset of the chunk within the file
21+
in bytes. The lower bound of the position
22+
of the chunk within the file.
23+
:type offset: int
24+
:param size: The size of the chunk in bytes.
25+
:type size: int
26+
:param sha_512: The `SHA-512` hash of the chunk.
27+
:type sha_512: str
28+
:param part_id: The unique ID of the chunk.
29+
:type part_id: str
30+
"""
31+
super().__init__(**kwargs)
32+
self.offset = offset
33+
self.size = size
34+
self.sha_512 = sha_512
35+
self.part_id = part_id

‎box_sdk_gen/schemas/upload_session.py‎

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class UploadSessionSessionEndpointsField(BaseObject):
1717
def __init__(
1818
self,
1919
*,
20+
plan: Optional[str] = None,
2021
upload_part: Optional[str] = None,
2122
commit: Optional[str] = None,
2223
abort: Optional[str] = None,
@@ -26,20 +27,24 @@ def __init__(
2627
**kwargs
2728
):
2829
"""
29-
:param upload_part: The URL to upload parts to., defaults to None
30-
:type upload_part: Optional[str], optional
31-
:param commit: The URL used to commit the file., defaults to None
32-
:type commit: Optional[str], optional
33-
:param abort: The URL for used to abort the session., defaults to None
34-
:type abort: Optional[str], optional
35-
:param list_parts: The URL users to list all parts., defaults to None
36-
:type list_parts: Optional[str], optional
37-
:param status: The URL used to get the status of the upload., defaults to None
38-
:type status: Optional[str], optional
39-
:param log_event: The URL used to get the upload log from., defaults to None
40-
:type log_event: Optional[str], optional
30+
:param plan: The URL used to plan the upload session by checking which parts
31+
already exist on the server., defaults to None
32+
:type plan: Optional[str], optional
33+
:param upload_part: The URL to upload parts to., defaults to None
34+
:type upload_part: Optional[str], optional
35+
:param commit: The URL used to commit the file., defaults to None
36+
:type commit: Optional[str], optional
37+
:param abort: The URL for used to abort the session., defaults to None
38+
:type abort: Optional[str], optional
39+
:param list_parts: The URL users to list all parts., defaults to None
40+
:type list_parts: Optional[str], optional
41+
:param status: The URL used to get the status of the upload., defaults to None
42+
:type status: Optional[str], optional
43+
:param log_event: The URL used to get the upload log from., defaults to None
44+
:type log_event: Optional[str], optional
4145
"""
4246
super().__init__(**kwargs)
47+
self.plan = plan
4348
self.upload_part = upload_part
4449
self.commit = commit
4550
self.abort = abort
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from typing import List
2+
3+
from box_sdk_gen.internal.base_object import BaseObject
4+
5+
from box_sdk_gen.schemas.upload_part_plan import UploadPartPlan
6+
7+
from box_sdk_gen.box.errors import BoxSDKError
8+
9+
10+
class UploadSessionPlanRequest(BaseObject):
11+
def __init__(self, parts: List[UploadPartPlan], **kwargs):
12+
"""
13+
:param parts: The list of parts to check for existence.
14+
:type parts: List[UploadPartPlan]
15+
"""
16+
super().__init__(**kwargs)
17+
self.parts = parts
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
from typing import List
2+
3+
from box_sdk_gen.internal.base_object import BaseObject
4+
5+
from box_sdk_gen.schemas.upload_part_plan_hit import UploadPartPlanHit
6+
7+
from box_sdk_gen.schemas.upload_part_plan import UploadPartPlan
8+
9+
from box_sdk_gen.box.errors import BoxSDKError
10+
11+
12+
class UploadSessionPlanResponse(BaseObject):
13+
def __init__(
14+
self,
15+
upload_session_id: str,
16+
hits: List[UploadPartPlanHit],
17+
misses: List[UploadPartPlan],
18+
**kwargs
19+
):
20+
"""
21+
:param upload_session_id: The unique identifier for this upload session.
22+
:type upload_session_id: str
23+
:param hits: Parts that already exist on the server and
24+
do not need to be uploaded again.
25+
:type hits: List[UploadPartPlanHit]
26+
:param misses: Parts that do not exist on the server and
27+
need to be uploaded.
28+
:type misses: List[UploadPartPlan]
29+
"""
30+
super().__init__(**kwargs)
31+
self.upload_session_id = upload_session_id
32+
self.hits = hits
33+
self.misses = misses

‎docs/chunked_uploads.md‎

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ This is a manager for chunked uploads (allowed for files at least 20MB).
1212
- [Remove upload session](#remove-upload-session)
1313
- [List parts by URL](#list-parts-by-url)
1414
- [List parts](#list-parts)
15+
- [Plan upload session by URL](#plan-upload-session-by-url)
16+
- [Plan upload session](#plan-upload-session)
1517
- [Commit upload session by URL](#commit-upload-session-by-url)
1618
- [Commit upload session](#commit-upload-session)
1719
- [Upload big file](#upload-big-file)
@@ -360,6 +362,70 @@ This function returns a value of type `UploadParts`.
360362

361363
Returns a list of parts that have been uploaded.
362364

365+
## Plan upload session by URL
366+
367+
Plan an upload session by checking which parts already exist on the server.
368+
This endpoint allows clients to optimize uploads by skipping parts that
369+
have already been uploaded (cache hits) and only uploading missing parts.
370+
371+
The actual endpoint URL is returned by the [`Create upload session`](e://post-files-upload-sessions)
372+
and [`Get upload session`](e://get-files-upload-sessions-id) endpoints.
373+
374+
This operation is performed by calling function `create_file_upload_session_plan_by_url`.
375+
376+
See the endpoint docs at
377+
[API Reference](https://developer.box.com/reference/post-files-upload-sessions-id-plan/).
378+
379+
_Currently we don't have an example for calling `create_file_upload_session_plan_by_url` in integration tests_
380+
381+
### Arguments
382+
383+
- url `str`
384+
- URL of createFileUploadSessionPlan method
385+
- parts `List[UploadPartPlan]`
386+
- The list of parts to check for existence.
387+
- extra_headers `Optional[Dict[str, Optional[str]]]`
388+
- Extra headers that will be included in the HTTP request.
389+
390+
### Returns
391+
392+
This function returns a value of type `UploadSessionPlanResponse`.
393+
394+
Returns information about which parts already exist (hits)
395+
and which parts need to be uploaded (misses).
396+
397+
## Plan upload session
398+
399+
Plan an upload session by checking which parts already exist on the server.
400+
This endpoint allows clients to optimize uploads by skipping parts that
401+
have already been uploaded (cache hits) and only uploading missing parts.
402+
403+
The actual endpoint URL is returned by the [`Create upload session`](e://post-files-upload-sessions)
404+
and [`Get upload session`](e://get-files-upload-sessions-id) endpoints.
405+
406+
This operation is performed by calling function `create_file_upload_session_plan`.
407+
408+
See the endpoint docs at
409+
[API Reference](https://developer.box.com/reference/post-files-upload-sessions-id-plan/).
410+
411+
_Currently we don't have an example for calling `create_file_upload_session_plan` in integration tests_
412+
413+
### Arguments
414+
415+
- upload_session_id `str`
416+
- The ID of the upload session. Example: "D5E3F7A"
417+
- parts `List[UploadPartPlan]`
418+
- The list of parts to check for existence.
419+
- extra_headers `Optional[Dict[str, Optional[str]]]`
420+
- Extra headers that will be included in the HTTP request.
421+
422+
### Returns
423+
424+
This function returns a value of type `UploadSessionPlanResponse`.
425+
426+
Returns information about which parts already exist (hits)
427+
and which parts need to be uploaded (misses).
428+
363429
## Commit upload session by URL
364430

365431
Close an upload session and create a file from the uploaded chunks.

0 commit comments

Comments
 (0)