1904 lines
75 KiB
Python
1904 lines
75 KiB
Python
# pylint: disable=too-many-lines
|
|
import json
|
|
from datetime import datetime
|
|
from typing import TYPE_CHECKING, Optional, Iterable, Union, Any, IO
|
|
from warnings import warn
|
|
|
|
from ..auth.oauth2 import TokenResponse
|
|
from ..session.session import Session, AuthorizedSession
|
|
from ..object.cloneable import Cloneable
|
|
from ..util.api_call_decorator import api_call
|
|
from ..object.search import Search
|
|
from ..object.events import Events
|
|
from ..object.collaboration_allowlist import CollaborationAllowlist
|
|
from ..object.trash import Trash
|
|
from ..pagination.limit_offset_based_object_collection import LimitOffsetBasedObjectCollection
|
|
from ..pagination.marker_based_object_collection import MarkerBasedObjectCollection
|
|
from ..util.datetime_formatter import normalize_date_to_rfc3339_format
|
|
from ..util.shared_link import get_shared_link_header
|
|
from ..util.deprecation_decorator import deprecated
|
|
|
|
if TYPE_CHECKING:
|
|
from boxsdk import OAuth2
|
|
from boxsdk.util.translator import Translator
|
|
from boxsdk.object.folder import Folder
|
|
from boxsdk.object.file import File
|
|
from boxsdk.object.file_request import FileRequest
|
|
from boxsdk.object.file_version import FileVersion
|
|
from boxsdk.object.upload_session import UploadSession
|
|
from boxsdk.object.comment import Comment
|
|
from boxsdk.object.legal_hold import LegalHold
|
|
from boxsdk.object.legal_hold_policy_assignment import LegalHoldPolicyAssignment
|
|
from boxsdk.object.legal_hold_policy import LegalHoldPolicy
|
|
from boxsdk.object.collaboration_allowlist_exempt_target import CollaborationAllowlistExemptTarget
|
|
from boxsdk.object.collaboration_allowlist_entry import CollaborationAllowlistEntry
|
|
from boxsdk.object.collaboration import Collaboration
|
|
from boxsdk.object.group import Group
|
|
from boxsdk.object.email_alias import EmailAlias
|
|
from boxsdk.object.invite import Invite
|
|
from boxsdk.object.user import User
|
|
from boxsdk.object.sign_request import SignRequest
|
|
from boxsdk.object.folder_lock import FolderLock
|
|
from boxsdk.object.metadata_template import MetadataTemplate, MetadataField
|
|
from boxsdk.object.metadata_cascade_policy import MetadataCascadePolicy
|
|
from boxsdk.object.device_pinner import DevicePinner
|
|
from boxsdk.auth.oauth2 import TokenScope
|
|
from boxsdk.session.box_response import BoxResponse
|
|
from boxsdk.object.item import Item
|
|
from boxsdk.object.web_link import WebLink
|
|
from boxsdk.object.retention_policy_assignment import RetentionPolicyAssignment
|
|
from boxsdk.object.file_version_retention import FileVersionRetention
|
|
from boxsdk.object.retention_policy import RetentionPolicy
|
|
from boxsdk.object.task_assignment import TaskAssignment
|
|
from boxsdk.object.task import Task
|
|
from boxsdk.object.terms_of_service_user_status import TermsOfServiceUserStatus
|
|
from boxsdk.object.terms_of_service import TermsOfService, TermsOfServiceType, TermsOfServiceStatus
|
|
from boxsdk.object.storage_policy_assignment import StoragePolicyAssignment
|
|
from boxsdk.object.storage_policy import StoragePolicy
|
|
from boxsdk.object.webhook import Webhook
|
|
from boxsdk.object.group_membership import GroupMembership
|
|
from boxsdk.object.enterprise import Enterprise
|
|
from boxsdk.object.collection import Collection
|
|
from boxsdk.object.sign_template import SignTemplate
|
|
from boxsdk.pagination.box_object_collection import BoxObjectCollection
|
|
|
|
|
|
class Client(Cloneable):
|
|
unauthorized_session_class = Session
|
|
authorized_session_class = AuthorizedSession
|
|
|
|
def __init__(self, oauth: 'OAuth2', session: Session = None):
|
|
"""
|
|
:param oauth:
|
|
OAuth2 object used by the session to authorize requests.
|
|
:param session:
|
|
The session object to use. If None is provided then an instance of :class:`AuthorizedSession` will be used.
|
|
"""
|
|
super().__init__()
|
|
warn('Package \'boxsdk\' is going to be deprecated soon. Please use \'box-sdk-gen\' instead.', DeprecationWarning)
|
|
self._oauth = oauth
|
|
if session is not None:
|
|
self._session = session
|
|
else:
|
|
session = session or self.unauthorized_session_class()
|
|
self._session = self.authorized_session_class(self._oauth, **session.get_constructor_kwargs())
|
|
|
|
@property
|
|
def auth(self) -> 'OAuth2':
|
|
"""
|
|
Get the :class:`OAuth2` instance the client is using for auth to Box.
|
|
"""
|
|
return self._oauth
|
|
|
|
@property
|
|
def session(self) -> Session:
|
|
"""
|
|
Get the :class:`BoxSession` instance the client is using.
|
|
"""
|
|
return self._session
|
|
|
|
@property
|
|
def translator(self) -> 'Translator':
|
|
"""The translator used for translating Box API JSON responses into `BaseAPIJSONObject` smart objects.
|
|
"""
|
|
return self._session.translator
|
|
|
|
def folder(self, folder_id: str) -> 'Folder':
|
|
"""
|
|
Initialize a :class:`Folder` object, whose box id is folder_id.
|
|
|
|
:param folder_id:
|
|
The box id of the :class:`Folder` object. Can use '0' to get the root folder on Box.
|
|
:return:
|
|
A :class:`Folder` object with the given folder id.
|
|
"""
|
|
return self.translator.get('folder')(session=self._session, object_id=folder_id)
|
|
|
|
def root_folder(self) -> 'Folder':
|
|
"""
|
|
Returns a user's root folder object.
|
|
"""
|
|
return self.folder('0')
|
|
|
|
def file(self, file_id: str) -> 'File':
|
|
"""
|
|
Initialize a :class:`File` object, whose box id is file_id.
|
|
|
|
:param file_id:
|
|
The box id of the :class:`File` object.
|
|
:return:
|
|
A :class:`File` object with the given file id.
|
|
"""
|
|
return self.translator.get('file')(session=self._session, object_id=file_id)
|
|
|
|
def file_request(self, request_id: str) -> 'FileRequest':
|
|
"""
|
|
Initialize a :class:`FileRequest` object, whose box id is request_id.
|
|
|
|
:param request_id:
|
|
The box id of the :class:`FileRequest` object.
|
|
:return:
|
|
A :class:`FileRequest` object with the given file request id.
|
|
"""
|
|
return self.translator.get('file_request')(session=self._session, object_id=request_id)
|
|
|
|
def file_version(self, version_id: str) -> 'FileVersion':
|
|
"""
|
|
Initialize a :class:`FileVersion` object, whose box id is version_id.
|
|
|
|
:param version_id:
|
|
The box id of the :class:`FileVersion` object.
|
|
:return:
|
|
A :class:`FileVersion` object with the given file version id.
|
|
"""
|
|
return self.translator.get('file_version')(session=self._session, object_id=version_id)
|
|
|
|
def upload_session(self, session_id: str) -> 'UploadSession':
|
|
"""
|
|
Initialize a :class:`UploadSession` object, whose box id is session_id.
|
|
|
|
:param session_id:
|
|
The box id of the :class:`UploadSession` object.
|
|
:return:
|
|
A :class:`UploadSession` object with the given session id.
|
|
"""
|
|
return self.translator.get('upload_session')(session=self._session, object_id=session_id)
|
|
|
|
def comment(self, comment_id: str) -> 'Comment':
|
|
"""
|
|
Initialize a :class:`Comment` object, whose Box ID is comment_id.
|
|
|
|
:param comment_id:
|
|
The Box ID of the :class:`Comment` object.
|
|
:return:
|
|
A :class:`Comment` object with the given comment ID.
|
|
"""
|
|
return self.translator.get('comment')(session=self._session, object_id=comment_id)
|
|
|
|
def user(self, user_id: str = 'me') -> 'User':
|
|
"""
|
|
Initialize a :class:`User` object, whose box id is user_id.
|
|
|
|
:param user_id:
|
|
The user id of the :class:`User` object. Can use 'me' to get the User for the current/authenticated user.
|
|
:return:
|
|
A :class:`User` object with the given id.
|
|
"""
|
|
return self.translator.get('user')(session=self._session, object_id=user_id)
|
|
|
|
def invite(self, invite_id: str) -> 'Invite':
|
|
"""
|
|
Initialize a :class:`Invite` object, whose box id is invite_id.
|
|
|
|
:param invite_id:
|
|
The invite ID of the :class:`Invite` object.
|
|
:return:
|
|
A :class:`Invite` object with the given entry ID.
|
|
"""
|
|
return self.translator.get('invite')(session=self._session, object_id=invite_id)
|
|
|
|
def email_alias(self, alias_id: str) -> 'EmailAlias':
|
|
"""
|
|
Initialize a :class: `EmailAlias` object, whose box id is alias_id.
|
|
|
|
:param alias_id:
|
|
The aliad id of the :class:`EmailAlias` object.
|
|
:return:
|
|
A :class:`EmailAlias` object with the given entry ID.
|
|
"""
|
|
return self.translator.get('email_alias')(session=self._session, object_id=alias_id)
|
|
|
|
def group(self, group_id: str) -> 'Group':
|
|
"""
|
|
Initialize a :class:`Group` object, whose box id is group_id.
|
|
|
|
:param group_id:
|
|
The box id of the :class:`Group` object.
|
|
:return:
|
|
A :class:`Group` object with the given group id.
|
|
"""
|
|
return self.translator.get('group')(session=self._session, object_id=group_id)
|
|
|
|
def collaboration(self, collab_id: str) -> 'Collaboration':
|
|
"""
|
|
Initialize a :class:`Collaboration` object, whose box id is collab_id.
|
|
|
|
:param collab_id:
|
|
The box id of the :class:`Collaboration` object.
|
|
:return:
|
|
A :class:`Collaboration` object with the given group id.
|
|
"""
|
|
return self.translator.get('collaboration')(session=self._session, object_id=collab_id)
|
|
|
|
def collaboration_allowlist(self):
|
|
"""
|
|
Initilializes a :class:`CollaborationAllowlist` object.
|
|
|
|
:return:
|
|
A :class:`CollaborationAllowlist` object.
|
|
"""
|
|
return CollaborationAllowlist(self._session)
|
|
|
|
def collaboration_allowlist_entry(self, entry_id: str) -> 'CollaborationAllowlistEntry':
|
|
"""
|
|
Initialize a :class:`CollaborationAllowlistEntry` object, whose box id is entry_id.
|
|
|
|
:param entry_id:
|
|
The box id of the :class:`CollaborationAllowlistEntry` object.
|
|
:return:
|
|
A :class:`CollaborationAllowlistEntry` object with the given entry id.
|
|
"""
|
|
return self.translator.get('collaboration_whitelist_entry')(session=self._session, object_id=entry_id)
|
|
|
|
def collaboration_allowlist_exempt_target(self, exemption_id: str) -> 'CollaborationAllowlistExemptTarget':
|
|
"""
|
|
Initialize a :class:`CollaborationAllowlistExemptTarget` object, whose box id is target_id.
|
|
|
|
:param exemption_id:
|
|
The box id of the :class:`CollaborationAllowlistExemptTarget` object.
|
|
:return:
|
|
A :class:`CollaborationAllowlistExemptTarget` object with the given target id.
|
|
"""
|
|
return self.translator.get('collaboration_whitelist_exempt_target')(
|
|
session=self._session,
|
|
object_id=exemption_id
|
|
)
|
|
|
|
def trash(self) -> Trash:
|
|
"""
|
|
Initialize a :class:`Trash` object.
|
|
|
|
:return:
|
|
A :class:`Trash` object.
|
|
"""
|
|
return Trash(self._session)
|
|
|
|
def legal_hold_policy(self, policy_id: str) -> 'LegalHoldPolicy':
|
|
"""
|
|
Initialize a :class:`LegalHoldPolicy` object, whose box id is policy_id.
|
|
|
|
:param policy_id:
|
|
The box ID of the :class:`LegalHoldPolicy` object.
|
|
:return:
|
|
A :class:`LegalHoldPolicy` object with the given entry ID.
|
|
"""
|
|
return self.translator.get('legal_hold_policy')(session=self._session, object_id=policy_id)
|
|
|
|
def legal_hold_policy_assignment(self, policy_assignment_id: str) -> 'LegalHoldPolicyAssignment':
|
|
"""
|
|
Initialize a :class:`LegalHoldPolicyAssignment` object, whose box id is policy_assignment_id.
|
|
|
|
:param policy_assignment_id:
|
|
The assignment ID of the :class:`LegalHoldPolicyAssignment` object.
|
|
:return:
|
|
A :class:`LegalHoldPolicyAssignment` object with the given entry ID.
|
|
"""
|
|
return self.translator.get('legal_hold_policy_assignment')(session=self._session, object_id=policy_assignment_id)
|
|
|
|
def legal_hold(self, hold_id: str) -> 'LegalHold':
|
|
"""
|
|
Initialize a :class:`LegalHold` object, whose box id is policy_id.
|
|
|
|
:param hold_id:
|
|
The legal hold ID of the :class:`LegalHold` object.
|
|
:return:
|
|
A :class:`LegalHold` object with the given entry ID.
|
|
"""
|
|
return self.translator.get('legal_hold')(session=self._session, object_id=hold_id)
|
|
|
|
@api_call
|
|
def create_legal_hold_policy(
|
|
self,
|
|
policy_name: str,
|
|
description: Optional[str] = None,
|
|
filter_started_at: Union[datetime, str] = None,
|
|
filter_ended_at: Union[datetime, str] = None,
|
|
is_ongoing: Optional[bool] = None
|
|
) -> 'LegalHoldPolicy':
|
|
"""
|
|
Create a legal hold policy.
|
|
|
|
:param policy_name:
|
|
The legal hold policy's display name.
|
|
:param description:
|
|
The description of the legal hold policy.
|
|
:param filter_started_at:
|
|
The start date filter for legal hold policy. Takes a datetime string supported by the dateutil library
|
|
or a datetime.datetime object. If no timezone info provided, local timezone will be applied.
|
|
:param filter_ended_at:
|
|
The end date filter for legal hold policy. Takes a datetime string supported by the dateutil library
|
|
or a datetime.datetime object. If no timezone info provided, local timezone will be applied.
|
|
:param is_ongoing:
|
|
After initialization, Assignments under this Policy will continue applying to
|
|
files based on events, indefinitely.
|
|
:returns:
|
|
A legal hold policy object
|
|
"""
|
|
url = self.get_url('legal_hold_policies')
|
|
policy_attributes = {'policy_name': policy_name}
|
|
if description is not None:
|
|
policy_attributes['description'] = description
|
|
if filter_started_at is not None:
|
|
policy_attributes['filter_started_at'] = normalize_date_to_rfc3339_format(filter_started_at)
|
|
if filter_ended_at is not None:
|
|
policy_attributes['filter_ended_at'] = normalize_date_to_rfc3339_format(filter_ended_at)
|
|
if is_ongoing is not None:
|
|
policy_attributes['is_ongoing'] = is_ongoing
|
|
box_response = self._session.post(url, data=json.dumps(policy_attributes))
|
|
response = box_response.json()
|
|
return self.translator.translate(
|
|
session=self._session,
|
|
response_object=response,
|
|
)
|
|
|
|
@api_call
|
|
def get_legal_hold_policies(
|
|
self,
|
|
policy_name: Optional[str] = None,
|
|
limit: Optional[int] = None,
|
|
marker: Optional[str] = None,
|
|
fields: Iterable[str] = None
|
|
) -> 'BoxObjectCollection':
|
|
"""
|
|
Get the entries in the legal hold policy using limit-offset paging.
|
|
|
|
:param policy_name:
|
|
The name of the legal hold policy case insensitive to search for
|
|
:param limit:
|
|
The maximum number of entries to return per page. If not specified, then will use the server-side default.
|
|
:param marker:
|
|
The paging marker to start paging from.
|
|
:param fields:
|
|
List of fields to request.
|
|
:returns:
|
|
An iterator of the entries in the legal hold policy
|
|
"""
|
|
additional_params = {}
|
|
if policy_name is not None:
|
|
additional_params['policy_name'] = policy_name
|
|
return MarkerBasedObjectCollection(
|
|
session=self._session,
|
|
url=self.get_url('legal_hold_policies'),
|
|
additional_params=additional_params,
|
|
limit=limit,
|
|
marker=marker,
|
|
fields=fields,
|
|
return_full_pages=False,
|
|
)
|
|
|
|
def collection(self, collection_id: str) -> 'Collection':
|
|
"""
|
|
Initialize a :class:`Collection` object, whose box ID is collection_id.
|
|
|
|
:param collection_id:
|
|
The box id of the :class:`Collection` object.
|
|
:return:
|
|
A :class:`Collection` object with the given collection ID.
|
|
"""
|
|
return self.translator.get('collection')(session=self._session, object_id=collection_id)
|
|
|
|
@api_call
|
|
def collections(
|
|
self,
|
|
limit: Optional[int] = None,
|
|
offset: int = 0,
|
|
fields: Iterable[str] = None
|
|
) -> 'BoxObjectCollection':
|
|
"""
|
|
Get a list of collections for the current user.
|
|
|
|
:param limit:
|
|
The maximum number of users to return. If not specified, the Box API will determine an appropriate limit.
|
|
:param offset:
|
|
The user index at which to start the response.
|
|
:param fields:
|
|
List of fields to request.
|
|
"""
|
|
return LimitOffsetBasedObjectCollection(
|
|
self.session,
|
|
self._session.get_url('collections'),
|
|
limit=limit,
|
|
fields=fields,
|
|
offset=offset,
|
|
return_full_pages=False,
|
|
)
|
|
|
|
def enterprise(self, enterprise_id: str) -> 'Enterprise':
|
|
"""
|
|
Initialize a :class:`Enterprise` object, whose box ID is enterprise_id.
|
|
|
|
:param enterprise_id:
|
|
The box id of the :class:`Enterprise` object.
|
|
:return:
|
|
A :class:`Enterprise` object with the given enterprise ID.
|
|
"""
|
|
return self.translator.get('enterprise')(session=self._session, object_id=enterprise_id)
|
|
|
|
@api_call
|
|
def get_current_enterprise(self) -> 'Enterprise':
|
|
"""
|
|
Get the enterprise of the current user.
|
|
|
|
:returns:
|
|
The authenticated user's enterprise
|
|
"""
|
|
user = self.user().get(fields=['enterprise'])
|
|
enterprise_object = user['enterprise']
|
|
return self.translator.translate(
|
|
session=self._session,
|
|
response_object=enterprise_object,
|
|
)
|
|
|
|
@api_call
|
|
def users(
|
|
self,
|
|
limit: Optional[int] = None,
|
|
offset: int = 0,
|
|
filter_term: Optional[str] = None,
|
|
user_type: Optional[str] = None,
|
|
fields: Iterable[str] = None,
|
|
use_marker: bool = False,
|
|
marker: Optional[str] = None
|
|
) -> Iterable['User']:
|
|
"""
|
|
Get a list of all users for the Enterprise along with their user_id, public_name, and login.
|
|
|
|
:param limit:
|
|
The maximum number of users to return. If not specified, the Box API will determine an appropriate limit.
|
|
:param offset:
|
|
The user index at which to start the response.
|
|
:param filter_term:
|
|
Filters the results to only users starting with the filter_term in either the name or the login.
|
|
:param user_type:
|
|
Filters the results to only users of the given type: 'managed', 'external', or 'all'.
|
|
:param fields:
|
|
List of fields to request on the :class:`User` objects.
|
|
:param use_marker:
|
|
Whether to use marker-based paging instead of offset-based paging, defaults to False.
|
|
:param marker:
|
|
The paging marker to start returning items from when using marker-based paging.
|
|
:return:
|
|
The list of all users in the enterprise.
|
|
"""
|
|
url = self.get_url('users')
|
|
additional_params = {}
|
|
if filter_term:
|
|
additional_params['filter_term'] = filter_term
|
|
if user_type:
|
|
additional_params['user_type'] = user_type
|
|
|
|
if use_marker:
|
|
additional_params['usemarker'] = True
|
|
return MarkerBasedObjectCollection(
|
|
url=url,
|
|
session=self._session,
|
|
limit=limit,
|
|
marker=marker,
|
|
fields=fields,
|
|
additional_params=additional_params,
|
|
return_full_pages=False,
|
|
)
|
|
return LimitOffsetBasedObjectCollection(
|
|
url=url,
|
|
session=self._session,
|
|
additional_params=additional_params,
|
|
limit=limit,
|
|
offset=offset,
|
|
fields=fields,
|
|
return_full_pages=False,
|
|
)
|
|
|
|
@api_call
|
|
def search(self) -> Search:
|
|
"""
|
|
Get a Search object that can be used for searching Box content.
|
|
|
|
:return:
|
|
The Search object
|
|
"""
|
|
return Search(self._session)
|
|
|
|
def events(self) -> Events:
|
|
"""
|
|
Get an events object that can get the latest events from Box or set up a long polling event subscription.
|
|
"""
|
|
return Events(self._session)
|
|
|
|
def group_membership(self, group_membership_id: str) -> 'GroupMembership':
|
|
"""
|
|
Initialize a :class:`GroupMembership` object, whose box id is group_membership_id.
|
|
|
|
:param group_membership_id:
|
|
The box id of the :class:`GroupMembership` object.
|
|
:return:
|
|
A :class:`GroupMembership` object with the given membership id.
|
|
"""
|
|
return self.translator.get('group_membership')(
|
|
session=self._session,
|
|
object_id=group_membership_id,
|
|
)
|
|
|
|
@api_call
|
|
def get_groups(
|
|
self,
|
|
name: Optional[str] = None,
|
|
limit: Optional[int] = None,
|
|
offset: Optional[int] = None,
|
|
fields: Iterable[str] = None
|
|
) -> Iterable['Group']:
|
|
"""
|
|
Get a list of all groups for the current user.
|
|
|
|
:param name:
|
|
Filter on the name of the groups to return.
|
|
:param limit:
|
|
The maximum number of groups to return. If not specified, the Box API will determine an appropriate limit.
|
|
:param offset:
|
|
The group index at which to start the response.
|
|
:param fields:
|
|
List of fields to request on the :class:`Group` objects.
|
|
:return:
|
|
The collection of all groups.
|
|
"""
|
|
url = self.get_url('groups')
|
|
additional_params = {}
|
|
if name:
|
|
additional_params['filter_term'] = name
|
|
return LimitOffsetBasedObjectCollection(
|
|
url=url,
|
|
session=self._session,
|
|
additional_params=additional_params,
|
|
limit=limit,
|
|
offset=offset,
|
|
fields=fields,
|
|
return_full_pages=False,
|
|
)
|
|
|
|
def webhook(self, webhook_id: str) -> 'Webhook':
|
|
"""
|
|
Initialize a :class:`Webhook` object, whose box id is webhook_id.
|
|
|
|
:param webhook_id:
|
|
The box ID of the :class: `Webhook` object.
|
|
:return:
|
|
A :class:`Webhook` object with the given entry ID.
|
|
"""
|
|
return self.translator.get('webhook')(session=self._session, object_id=webhook_id)
|
|
|
|
@api_call
|
|
def create_webhook(self, target: Union['File', 'Folder'], triggers: Union[list, str], address: str) -> 'Webhook':
|
|
"""
|
|
Create a webhook on the given file.
|
|
|
|
:param target:
|
|
Either a :class:`File` or :class:`Folder` to assign a webhook to.
|
|
:param triggers:
|
|
Event types that trigger notifications for the target.
|
|
:param address:
|
|
The url to send the notification to.
|
|
:return:
|
|
A :class:`Webhook` object with the given entry ID.
|
|
"""
|
|
url = self.get_url('webhooks')
|
|
webhook_attributes = {
|
|
'target': {
|
|
'type': target.object_type,
|
|
'id': target.object_id,
|
|
},
|
|
'triggers': triggers,
|
|
'address': address,
|
|
}
|
|
box_response = self._session.post(url, data=json.dumps(webhook_attributes))
|
|
response = box_response.json()
|
|
return self.translator.translate(
|
|
session=self._session,
|
|
response_object=response,
|
|
)
|
|
|
|
@api_call
|
|
def get_webhooks(
|
|
self,
|
|
limit: Optional[int] = None,
|
|
marker: Optional[str] = None,
|
|
fields: Iterable[str] = None
|
|
) -> 'BoxObjectCollection':
|
|
"""
|
|
Get all webhooks in an enterprise.
|
|
|
|
:param limit:
|
|
The maximum number of entries to return.
|
|
:param marker:
|
|
The position marker at which to begin the response.
|
|
:param fields:
|
|
List of fields to request on the file or folder which the `RecentItem` references.
|
|
:returns:
|
|
An iterator of the entries in the webhook
|
|
"""
|
|
return MarkerBasedObjectCollection(
|
|
session=self._session,
|
|
url=self.get_url('webhooks'),
|
|
limit=limit,
|
|
marker=marker,
|
|
fields=fields,
|
|
)
|
|
|
|
@api_call
|
|
def create_group(
|
|
self,
|
|
name: str,
|
|
provenance: Optional[str] = None,
|
|
external_sync_identifier: Optional[str] = None,
|
|
description: Optional[str] = None,
|
|
invitability_level: str = None,
|
|
member_viewability_level: str = None,
|
|
fields: Iterable[str] = None,
|
|
) -> 'Group':
|
|
"""
|
|
Create a group with the given name.
|
|
|
|
:param name:
|
|
The name of the group.
|
|
:param provenance:
|
|
Used to track the external source where the group is coming from.
|
|
:param external_sync_identifier:
|
|
Used as a group identifier for groups coming from an external source.
|
|
:param description:
|
|
Description of the group.
|
|
:param invitability_level:
|
|
Specifies who can invite this group to folders.
|
|
:param member_viewability_level:
|
|
Specifies who can view the members of this group.
|
|
:param fields:
|
|
List of fields to request on the :class:`Group` objects.
|
|
:return:
|
|
The newly created Group.
|
|
:raises:
|
|
:class:`BoxAPIException` if current user doesn't have permissions to create a group.
|
|
"""
|
|
url = self.get_url('groups')
|
|
additional_params = {}
|
|
body_attributes = {
|
|
'name': name,
|
|
}
|
|
if provenance is not None:
|
|
body_attributes['provenance'] = provenance
|
|
if external_sync_identifier is not None:
|
|
body_attributes['external_sync_identifier'] = external_sync_identifier
|
|
if description is not None:
|
|
body_attributes['description'] = description
|
|
if invitability_level is not None:
|
|
body_attributes['invitability_level'] = invitability_level
|
|
if member_viewability_level is not None:
|
|
body_attributes['member_viewability_level'] = member_viewability_level
|
|
if fields is not None:
|
|
additional_params['fields'] = ','.join(fields)
|
|
box_response = self._session.post(url, data=json.dumps(body_attributes), params=additional_params)
|
|
response = box_response.json()
|
|
return self.translator.translate(
|
|
session=self._session,
|
|
response_object=response,
|
|
)
|
|
|
|
def storage_policy(self, policy_id: str) -> 'StoragePolicy':
|
|
"""
|
|
Initialize a :class:`StoragePolicy` object, whose box id is policy_id.
|
|
|
|
:param policy_id:
|
|
The box ID of the :class:`StoragePolicy` object.
|
|
:return:
|
|
A :class:`StoragePolicy` object with the given entry ID.
|
|
"""
|
|
return self.translator.get('storage_policy')(session=self._session, object_id=policy_id)
|
|
|
|
def storage_policy_assignment(self, assignment_id: str) -> 'StoragePolicyAssignment':
|
|
"""
|
|
Initialize a :class:`StoragePolicyAssignment` object, whose box id is assignment_id.
|
|
|
|
:param assignment_id:
|
|
The box ID of the :class:`StoragePolicyAssignment` object.
|
|
:return:
|
|
A :class:`StoragePolicyAssignment` object with the given entry ID.
|
|
"""
|
|
return self.translator.get('storage_policy_assignment')(session=self._session, object_id=assignment_id)
|
|
|
|
def get_storage_policies(
|
|
self,
|
|
limit: Optional[int] = None,
|
|
marker: Optional[str] = None,
|
|
fields: Iterable[str] = None
|
|
) -> 'BoxObjectCollection':
|
|
"""
|
|
Get the entries in the storage policy using marker-based paging.
|
|
|
|
:param limit:
|
|
The maximum number of items to return.
|
|
:param marker:
|
|
The paging marker to start returning items from when using marker-based paging.
|
|
:param fields:
|
|
List of fields to request.
|
|
:returns:
|
|
Returns the storage policies available for the current enterprise.
|
|
"""
|
|
return MarkerBasedObjectCollection(
|
|
session=self._session,
|
|
url=self.get_url('storage_policies'),
|
|
limit=limit,
|
|
marker=marker,
|
|
fields=fields,
|
|
return_full_pages=False,
|
|
)
|
|
|
|
def terms_of_service(self, tos_id: str) -> 'TermsOfService':
|
|
"""
|
|
Initialize a :class:`TermsOfService` object, whose box id is tos_id.
|
|
|
|
:param tos_id:
|
|
The box id of the :class:`TermsOfService` object.
|
|
:return:
|
|
A :class:`TermsOfService` object with the given terms of service id.
|
|
"""
|
|
return self.translator.get('terms_of_service')(session=self._session, object_id=tos_id)
|
|
|
|
def terms_of_service_user_status(self, tos_user_status_id: str) -> 'TermsOfServiceUserStatus':
|
|
"""
|
|
Initialize a :class:`TermsOfServiceUserStatus` object, whose box id is tos_user_status_id.
|
|
|
|
:param tos_user_status_id:
|
|
The box id of the :class:`TermsOfServiceUserStatus` object.
|
|
:return:
|
|
A :class:`TermsOfServiceUserStatus` object with the given terms of service user status id.
|
|
"""
|
|
return self.translator.get('terms_of_service_user_status')(session=self._session, object_id=tos_user_status_id)
|
|
|
|
def get_terms_of_services(
|
|
self,
|
|
tos_type: 'TermsOfServiceType' = None,
|
|
limit: Optional[int] = None,
|
|
fields: Iterable[str] = None
|
|
) -> 'BoxObjectCollection':
|
|
"""
|
|
Get the entries in the terms of service using limit-offset paging.
|
|
:param tos_type:
|
|
Can be set to `managed` or `external` for the type of terms of service.
|
|
:param limit:
|
|
The maximum number of items to return. If limit is set to None, then the default
|
|
limit (returned by Box in the response) is used.
|
|
:param fields:
|
|
List of fields to request
|
|
:returns:
|
|
An iterator of the entries in the terms of service
|
|
"""
|
|
additional_params = {}
|
|
if tos_type is not None:
|
|
additional_params['tos_type'] = tos_type
|
|
return MarkerBasedObjectCollection(
|
|
session=self._session,
|
|
url=self._session.get_url('terms_of_services'),
|
|
additional_params=additional_params,
|
|
limit=limit,
|
|
marker=None,
|
|
fields=fields,
|
|
return_full_pages=False,
|
|
)
|
|
|
|
def task(self, task_id: str) -> 'Task':
|
|
"""
|
|
Initialize a :class:`Task` object, whose box id is task_id.
|
|
|
|
:param task_id:
|
|
The box ID of the :class:`Task` object.
|
|
:return:
|
|
A :class:`Task` object with the given entry ID.
|
|
"""
|
|
return self.translator.get('task')(session=self._session, object_id=task_id)
|
|
|
|
def task_assignment(self, assignment_id: str) -> 'TaskAssignment':
|
|
"""
|
|
Initialize a :class:`TaskAssignment` object, whose box id is assignment_id.
|
|
|
|
:param assignment_id:
|
|
The box ID of the :class:`TaskAssignment` object.
|
|
:return:
|
|
A :class:`TaskAssignment` object with the given entry ID.
|
|
"""
|
|
return self.translator.get('task_assignment')(session=self._session, object_id=assignment_id)
|
|
|
|
def retention_policy(self, retention_id: str) -> 'RetentionPolicy':
|
|
"""
|
|
Initialize a :class:`RetentionPolicy` object, whose box id is retention_id.
|
|
|
|
:param retention_id:
|
|
The box ID of the :class:`RetentionPolicy` object.
|
|
:return:
|
|
A :class:`RetentionPolicy` object with the given entry ID.
|
|
"""
|
|
return self.translator.get('retention_policy')(session=self._session, object_id=retention_id)
|
|
|
|
def file_version_retention(self, retention_id: str) -> 'FileVersionRetention':
|
|
"""
|
|
Initialize a :class:`FileVersionRetention` object, whose box id is retention_id.
|
|
|
|
:param retention_id:
|
|
The box ID of the :class:`FileVersionRetention` object.
|
|
:return:
|
|
A :class:`FileVersionRetention` object with the given retention ID.
|
|
"""
|
|
return self.translator.get('file_version_retention')(session=self._session, object_id=retention_id)
|
|
|
|
def retention_policy_assignment(self, assignment_id: str) -> 'RetentionPolicyAssignment':
|
|
"""
|
|
Initialize a :class:`RetentionPolicyAssignment` object, whose box id is assignment_id.
|
|
|
|
:param assignment_id:
|
|
The box ID of the :class:`RetentionPolicyAssignment` object.
|
|
:return:
|
|
A :class:`RetentionPolicyAssignment` object with the given assignment ID.
|
|
"""
|
|
return self.translator.get('retention_policy_assignment')(session=self._session, object_id=assignment_id)
|
|
|
|
@api_call
|
|
def create_retention_policy(
|
|
self,
|
|
policy_name: str,
|
|
disposition_action: str,
|
|
retention_length: Union[int, float],
|
|
can_owner_extend_retention: Optional[bool] = None,
|
|
are_owners_notified: Optional[bool] = None,
|
|
custom_notification_recipients: Iterable['User'] = None,
|
|
retention_type: Optional[str] = None,
|
|
description: Optional[str] = None,
|
|
) -> 'RetentionPolicy':
|
|
"""
|
|
Create a retention policy for the given enterprise.
|
|
|
|
:param policy_name:
|
|
The name of the retention policy.
|
|
:param disposition_action:
|
|
For `finite` policy can be set to `permanently delete` or `remove retention`.
|
|
For `indefinite` policy this must be set to `remove_retention`
|
|
:param retention_length:
|
|
The amount of time in days to apply the retention policy to the selected content.
|
|
The retention_length should be set to float('inf') for indefinite policies.
|
|
:param can_owner_extend_retention:
|
|
The owner of a file will be allowed to extend the retention if set to true.
|
|
:param are_owners_notified:
|
|
The owner or co-owner will get notified when a file is nearing expiration.
|
|
:param custom_notification_recipients:
|
|
A custom list of user mini objects that should be notified when a file is nearing expiration.
|
|
:param retention_type:
|
|
Specifies the retention type. It can be one of the values:
|
|
- `modifiable`: You can modify the retention policy. For example, you can add or remove folders,
|
|
shorten or lengthen the policy duration, or delete the assignment.
|
|
- `non_modifiable`: You can modify the retention policy only in a limited way: add a folder,
|
|
lengthen the duration, retire the policy, change the disposition action or notification settings.
|
|
You cannot perform other actions, such as deleting the assignment or shortening the policy duration.
|
|
:param description:
|
|
The additional text description of the retention policy.
|
|
:return:
|
|
The newly created Retention Policy
|
|
"""
|
|
url = self.get_url('retention_policies')
|
|
retention_attributes = {
|
|
'policy_name': policy_name,
|
|
'disposition_action': disposition_action,
|
|
}
|
|
if retention_length == float('inf'):
|
|
retention_attributes['policy_type'] = 'indefinite'
|
|
else:
|
|
retention_attributes['policy_type'] = 'finite'
|
|
retention_attributes['retention_length'] = retention_length
|
|
if can_owner_extend_retention is not None:
|
|
retention_attributes['can_owner_extend_retention'] = can_owner_extend_retention
|
|
if are_owners_notified is not None:
|
|
retention_attributes['are_owners_notified'] = are_owners_notified
|
|
if custom_notification_recipients is not None:
|
|
user_list = [{'type': user.object_type, 'id': user.object_id} for user in custom_notification_recipients]
|
|
retention_attributes['custom_notification_recipients'] = user_list
|
|
if retention_type is not None:
|
|
retention_attributes['retention_type'] = retention_type
|
|
if description is not None:
|
|
retention_attributes['description'] = description
|
|
box_response = self._session.post(url, data=json.dumps(retention_attributes))
|
|
response = box_response.json()
|
|
return self.translator.translate(
|
|
session=self._session,
|
|
response_object=response
|
|
)
|
|
|
|
@api_call
|
|
def get_retention_policies(
|
|
self,
|
|
policy_name: Optional[str] = None,
|
|
policy_type: Optional[str] = None,
|
|
user: Optional['User'] = None,
|
|
limit: Optional[int] = None,
|
|
marker: Optional[str] = None,
|
|
fields: Iterable[str] = None,
|
|
) -> 'BoxObjectCollection':
|
|
"""
|
|
Get the entries in the retention policy using marker-based paging.
|
|
|
|
:param policy_name:
|
|
The name of the retention policy.
|
|
:param policy_type:
|
|
Set to either `finite` or `indefinite`
|
|
:param user:
|
|
A user to filter the retention policies.
|
|
:param limit:
|
|
The maximum number of entries to return per page. If not specified, then will use the server-side default.
|
|
:param marker:
|
|
The paging marker to start paging from
|
|
:param fields:
|
|
List of fields to request
|
|
:returns:
|
|
An iterator of the entries in the retention policy
|
|
"""
|
|
additional_params = {}
|
|
if policy_name is not None:
|
|
additional_params['policy_name'] = policy_name
|
|
if policy_type is not None:
|
|
additional_params['policy_type'] = policy_type
|
|
if user is not None:
|
|
additional_params['created_by_user_id'] = user.object_id
|
|
return MarkerBasedObjectCollection(
|
|
session=self._session,
|
|
url=self._session.get_url('retention_policies'),
|
|
additional_params=additional_params,
|
|
limit=limit,
|
|
marker=marker,
|
|
fields=fields,
|
|
return_full_pages=False,
|
|
)
|
|
|
|
def create_terms_of_service(
|
|
self,
|
|
status: 'TermsOfServiceStatus',
|
|
tos_type: 'TermsOfServiceType',
|
|
text: str
|
|
) -> 'TermsOfService':
|
|
"""
|
|
Create a terms of service.
|
|
|
|
:param status:
|
|
The status of the terms of service.
|
|
:param tos_type:
|
|
The type of the terms of service. Can be set to `managed` or `external`.
|
|
:param text:
|
|
The message of the terms of service.
|
|
:returns:
|
|
A newly created :class:`TermsOfService` object
|
|
"""
|
|
url = self.get_url('terms_of_services')
|
|
body = {
|
|
'status': status,
|
|
'tos_type': tos_type,
|
|
'text': text
|
|
}
|
|
box_response = self._session.post(url, data=json.dumps(body))
|
|
response = box_response.json()
|
|
return self.translator.translate(
|
|
session=self._session,
|
|
response_object=response,
|
|
)
|
|
|
|
@deprecated("Use RetentionPolicyAssignment.get_files_under_retention "
|
|
"or RetentionPolicyAssignment.get_file_versions_under_retention instead")
|
|
@api_call
|
|
def get_file_version_retentions(
|
|
self,
|
|
target_file: Optional['File'] = None,
|
|
file_version: Optional['FileVersion'] = None,
|
|
policy: Optional['RetentionPolicy'] = None,
|
|
disposition_action: Optional[str] = None,
|
|
disposition_before: Optional[str] = None,
|
|
disposition_after: Optional[str] = None,
|
|
limit: Optional[int] = None,
|
|
marker: Optional[str] = None,
|
|
fields: Iterable[str] = None,
|
|
) -> 'BoxObjectCollection':
|
|
"""
|
|
Get the entries in the file version retention.
|
|
|
|
:param target_file:
|
|
The file to filter the file version.
|
|
:param file_version:
|
|
A file version to filter the file version retentions by.
|
|
:param policy:
|
|
A policy to filter the file version retentions by.
|
|
:param disposition_action:
|
|
Can be set to `permanently_delete` or `remove_retention`.
|
|
:param disposition_before:
|
|
A date time filter for disposition action.
|
|
:param disposition_after:
|
|
A date time filter for disposition action.
|
|
:param limit:
|
|
The maximum number of entries to return per page. If not specified, then will use the server-side default.
|
|
:param marker:
|
|
The paging marker to start paging from
|
|
:param fields:
|
|
List of fields to request
|
|
:returns:
|
|
An iterator of the entries in the file version retention.
|
|
"""
|
|
additional_params = {}
|
|
if target_file is not None:
|
|
additional_params['file_id'] = target_file.object_id
|
|
if file_version is not None:
|
|
additional_params['file_version_id'] = file_version.object_id
|
|
if policy is not None:
|
|
additional_params['policy_id'] = policy.object_id
|
|
if disposition_action is not None:
|
|
additional_params['disposition_action'] = disposition_action
|
|
if disposition_before is not None:
|
|
additional_params['disposition_before'] = disposition_before
|
|
if disposition_after is not None:
|
|
additional_params['disposition_after'] = disposition_after
|
|
return MarkerBasedObjectCollection(
|
|
session=self._session,
|
|
url=self._session.get_url('file_version_retentions'),
|
|
additional_params=additional_params,
|
|
limit=limit,
|
|
marker=marker,
|
|
fields=fields,
|
|
return_full_pages=False,
|
|
)
|
|
|
|
def web_link(self, web_link_id: str) -> 'WebLink':
|
|
"""
|
|
Initialize a :class: `WebLink` object, whose box id is web_link_id.
|
|
:param web_link_id:
|
|
The box ID of the :class:`WebLink` object.
|
|
:return:
|
|
A :class:`WebLink` object with the given entry ID.
|
|
"""
|
|
return self.translator.get('web_link')(session=self._session, object_id=web_link_id)
|
|
|
|
@api_call
|
|
def get_recent_items(
|
|
self,
|
|
limit: Optional[int] = None,
|
|
marker: Optional[str] = None,
|
|
fields: Iterable[str] = None,
|
|
**collection_kwargs: Any
|
|
) -> MarkerBasedObjectCollection:
|
|
"""
|
|
Get the user's recently accessed items.
|
|
|
|
:param limit:
|
|
The maximum number of items to return. If limit is set to None, then the default
|
|
limit (returned by Box in the response) is used. See https://developer.box.com/en/reference/get-recent-items/
|
|
for default.
|
|
:param marker:
|
|
The index at which to start returning items.
|
|
:param fields:
|
|
List of fields to request on the file or folder which the `RecentItem` references.
|
|
:param collection_kwargs:
|
|
Keyword arguments passed to `MarkerBasedObjectCollection`.
|
|
:returns:
|
|
An iterator on the user's recent items
|
|
"""
|
|
return MarkerBasedObjectCollection(
|
|
self.session,
|
|
self.get_url('recent_items'),
|
|
limit=limit,
|
|
fields=fields,
|
|
marker=marker,
|
|
**collection_kwargs
|
|
)
|
|
|
|
@api_call
|
|
def get_shared_item(self, shared_link: str, password: str = None) -> 'Item':
|
|
"""
|
|
Get information about a Box shared link. https://developer.box.com/en/reference/get-shared-items/
|
|
|
|
:param shared_link:
|
|
The shared link.
|
|
:param password:
|
|
The password for the shared link.
|
|
:return:
|
|
The item referred to by the shared link.
|
|
:raises:
|
|
:class:`BoxAPIException` if current user doesn't have permissions to view the shared link.
|
|
"""
|
|
response = self.make_request(
|
|
'GET',
|
|
self.get_url('shared_items'),
|
|
headers=get_shared_link_header(shared_link, password),
|
|
).json()
|
|
return self.translator.translate(
|
|
session=self._session.with_shared_link(shared_link, password),
|
|
response_object=response,
|
|
)
|
|
|
|
@api_call
|
|
def make_request(self, method: str, url: str, **kwargs: Any) -> 'BoxResponse':
|
|
"""
|
|
Make an authenticated request to the Box API.
|
|
|
|
:param method:
|
|
The HTTP verb to use for the request.
|
|
:param url:
|
|
The URL for the request.
|
|
:return:
|
|
The network response for the given request.
|
|
:raises:
|
|
:class:`BoxAPIException`
|
|
"""
|
|
return self._session.request(method, url, **kwargs)
|
|
|
|
@api_call
|
|
def create_user(self, name: str, login: Optional[str] = None, **user_attributes: Any) -> 'User':
|
|
"""
|
|
Create a new user. Can only be used if the current user is an enterprise admin, or the current authorization
|
|
scope is a Box developer edition instance.
|
|
|
|
:param name:
|
|
The user's display name.
|
|
:param login:
|
|
The user's email address. Required for an enterprise user, but None for an app user.
|
|
:param user_attributes:
|
|
Additional attributes for the user. See the documentation at
|
|
https://developer.box.com/en/reference/post-users/
|
|
:return
|
|
Newly created user
|
|
"""
|
|
url = self.get_url('users')
|
|
user_attributes['name'] = name
|
|
if login is not None:
|
|
user_attributes['login'] = login
|
|
else:
|
|
user_attributes['is_platform_access_only'] = True
|
|
box_response = self._session.post(url, data=json.dumps(user_attributes))
|
|
response = box_response.json()
|
|
return self.translator.translate(
|
|
session=self._session,
|
|
response_object=response,
|
|
)
|
|
|
|
@api_call
|
|
def get_pending_collaborations(
|
|
self,
|
|
limit: Optional[int] = None,
|
|
offset: Optional[int] = None,
|
|
fields: Iterable[str] = None
|
|
) -> 'BoxObjectCollection':
|
|
"""
|
|
Get the entries in the pending collaborations using limit-offset paging.
|
|
|
|
:param limit:
|
|
The maximum number of entries to return per page. If not specified, then will use the server-side default.
|
|
:param offset:
|
|
The offset of the item at which to begin the response.
|
|
:param fields:
|
|
List of fields to request.
|
|
:returns:
|
|
An iterator of the entries in the pending collaborations
|
|
"""
|
|
return LimitOffsetBasedObjectCollection(
|
|
session=self._session,
|
|
url=self.get_url('collaborations'),
|
|
additional_params={'status': 'pending'},
|
|
limit=limit,
|
|
offset=offset,
|
|
fields=fields,
|
|
return_full_pages=False,
|
|
)
|
|
|
|
@api_call
|
|
def downscope_token(
|
|
self,
|
|
scopes: Iterable['TokenScope'],
|
|
item: 'Item' = None,
|
|
additional_data: dict = None,
|
|
shared_link: str = None
|
|
) -> TokenResponse:
|
|
"""
|
|
Generate a downscoped token for the provided file or folder with the provided scopes.
|
|
There is no way of refreshing this token. You will need to add you own logic to
|
|
generate new token if this one expires.
|
|
|
|
:param scopes:
|
|
The scope(s) to apply to the resulting token.
|
|
:param item:
|
|
(Optional) The file or folder to get a downscoped token for. If None and shared_link None, the resulting
|
|
token will not be scoped down to just a single item.
|
|
:param additional_data:
|
|
(Optional) Key value pairs which can be used to add/update the default data values in the request.
|
|
:param shared_link:
|
|
(Optional) The shared link to get a downscoped token for. If None and item None, the resulting token
|
|
will not be scoped down to just a single item.
|
|
:return:
|
|
The response for the downscope token request.
|
|
"""
|
|
url = f'{self._session.api_config.OAUTH2_API_URL}/token'
|
|
access_token = self.auth.access_token or self.auth.refresh(None)
|
|
data = {
|
|
'subject_token': access_token,
|
|
'subject_token_type': 'urn:ietf:params:oauth:token-type:access_token',
|
|
'scope': ' '.join(scopes),
|
|
'grant_type': 'urn:ietf:params:oauth:grant-type:token-exchange',
|
|
}
|
|
|
|
if item:
|
|
data['resource'] = item.get_url()
|
|
if shared_link:
|
|
data['box_shared_link'] = shared_link
|
|
if additional_data:
|
|
data.update(additional_data)
|
|
|
|
box_response = self._session.post(url, data=data)
|
|
|
|
return TokenResponse(box_response.json())
|
|
|
|
def clone(self, session: 'Session' = None) -> 'Client':
|
|
"""Base class override."""
|
|
return self.__class__(oauth=self._oauth, session=(session or self._session))
|
|
|
|
def get_url(self, endpoint: str, *args: Any) -> str:
|
|
"""
|
|
Return the URL for the given Box API endpoint.
|
|
|
|
:param endpoint:
|
|
The name of the endpoint.
|
|
:param args:
|
|
Additional parts of the endpoint URL.
|
|
"""
|
|
# pylint:disable=no-self-use
|
|
return self._session.get_url(endpoint, *args)
|
|
|
|
def device_pinner(self, device_pin_id: str) -> 'DevicePinner':
|
|
"""
|
|
Initialize a :class:`DevicePinner` object, whose box id is device_pin_id.
|
|
|
|
:param device_pin_id:
|
|
The assignment ID of the :class:`DevicePin` object.
|
|
:return:
|
|
A :class:`DevicePinner` object with the given entry ID.
|
|
"""
|
|
return self.translator.get('device_pinner')(session=self._session, object_id=device_pin_id)
|
|
|
|
def device_pinners(
|
|
self,
|
|
enterprise: Optional['Enterprise'] = None,
|
|
direction: Optional[str] = None,
|
|
limit: Optional[int] = None,
|
|
marker: Optional[str] = None,
|
|
fields: Iterable[str] = None
|
|
) -> 'BoxObjectCollection':
|
|
"""
|
|
Returns all of the device pins for the given enterprise.
|
|
|
|
:param enterprise:
|
|
The enterprise to retrieve device pinners for, defaulting to the current enterprise.
|
|
:param direction:
|
|
The sorting direction. Set to `ASC` or `DESC`
|
|
:param limit:
|
|
The maximum number of entries to return per page. If not specified, then will use the server-side default.
|
|
:param marker:
|
|
The paging marker to start paging from.
|
|
:param fields:
|
|
List of fields to request.
|
|
:returns:
|
|
An iterator of the entries in the device pins.
|
|
"""
|
|
enterprise_id = enterprise.object_id if enterprise is not None else self.get_current_enterprise().id
|
|
additional_params = {}
|
|
if direction is not None:
|
|
additional_params['direction'] = direction
|
|
return MarkerBasedObjectCollection(
|
|
session=self._session,
|
|
url=self.get_url('enterprises', enterprise_id, 'device_pinners'),
|
|
additional_params=additional_params,
|
|
limit=limit,
|
|
marker=marker,
|
|
fields=fields,
|
|
return_full_pages=False,
|
|
)
|
|
|
|
def metadata_cascade_policy(self, policy_id: str) -> 'MetadataCascadePolicy':
|
|
"""
|
|
Initializes a :class:`MetadataCascadePolicy` object with the given policy ID.
|
|
|
|
:param policy_id:
|
|
The ID of the cascade policy object
|
|
:returns:
|
|
The cascade policy object
|
|
"""
|
|
return self.translator.get('metadata_cascade_policy')(
|
|
session=self._session,
|
|
object_id=policy_id,
|
|
)
|
|
|
|
def metadata_template(self, scope: str, template_key: str) -> 'MetadataTemplate':
|
|
"""
|
|
Initialize a :class:`MetadataTemplate` object with the given scope and template key.
|
|
|
|
:param scope:
|
|
The scope of the metadata template, e.g. 'enterprise' or 'global'
|
|
:param template_key:
|
|
The key of the metadata template
|
|
:returns:
|
|
The metadata template object
|
|
"""
|
|
return self.translator.get('metadata_template')(
|
|
session=self._session,
|
|
object_id=None,
|
|
response_object={
|
|
'type': 'metadata_template',
|
|
'scope': scope,
|
|
'templateKey': template_key,
|
|
},
|
|
)
|
|
|
|
def metadata_template_by_id(self, template_id: str) -> 'MetadataTemplate':
|
|
"""
|
|
Retrieves a metadata template by ID
|
|
|
|
:param template_id:
|
|
The ID of the template object
|
|
:returns:
|
|
The metadata template with data populated from the API
|
|
"""
|
|
return self.translator.get('metadata_template')(
|
|
session=self._session,
|
|
object_id=template_id,
|
|
)
|
|
|
|
@api_call
|
|
def get_metadata_templates(
|
|
self,
|
|
scope: str = 'enterprise',
|
|
limit: Optional[int] = None,
|
|
marker: Optional[str] = None,
|
|
fields: Iterable[str] = None
|
|
) -> 'BoxObjectCollection':
|
|
"""
|
|
Get all metadata templates for a given scope. By default, retrieves all metadata templates for the current
|
|
enterprise.
|
|
|
|
:param scope:
|
|
The scope to retrieve templates for
|
|
:param limit:
|
|
The maximum number of entries to return per page.
|
|
:param marker:
|
|
The paging marker to start paging from.
|
|
:param fields:
|
|
List of fields to request.
|
|
:returns:
|
|
The collection of metadata templates for the given scope
|
|
"""
|
|
return MarkerBasedObjectCollection(
|
|
url=self._session.get_url('metadata_templates', scope),
|
|
session=self._session,
|
|
limit=limit,
|
|
marker=marker,
|
|
fields=fields,
|
|
return_full_pages=False,
|
|
)
|
|
|
|
@api_call
|
|
def create_metadata_template(
|
|
self,
|
|
display_name: str,
|
|
fields: Iterable['MetadataField'],
|
|
template_key: str = None,
|
|
hidden: bool = False,
|
|
scope: str = 'enterprise',
|
|
copy_instance_on_item_copy: bool = False
|
|
) -> 'MetadataTemplate':
|
|
"""
|
|
Create a new metadata template. By default, only the display name and fields are required; the template key
|
|
will be automatically generated based on the display name and the template will be created in the enterprise
|
|
scope.
|
|
|
|
:param display_name:
|
|
The human-readable name of the template
|
|
:param fields:
|
|
The metadata fields for the template.
|
|
:param template_key:
|
|
An optional key for the template. If one is not provided, it will be derived from the display name.
|
|
:param hidden:
|
|
Whether the template should be hidden in the UI
|
|
:param scope:
|
|
The scope the template should be created in
|
|
:param copy_instance_on_item_copy:
|
|
Whether or not to include the metadata when a file or folder is copied.
|
|
"""
|
|
url = self._session.get_url('metadata_templates', 'schema')
|
|
body = {
|
|
'scope': scope,
|
|
'displayName': display_name,
|
|
'hidden': hidden,
|
|
'fields': [field.json() for field in fields],
|
|
'copyInstanceOnItemCopy': copy_instance_on_item_copy
|
|
}
|
|
|
|
if template_key is not None:
|
|
body['templateKey'] = template_key
|
|
|
|
response = self._session.post(url, data=json.dumps(body)).json()
|
|
return self.translator.translate(
|
|
session=self._session,
|
|
response_object=response,
|
|
)
|
|
|
|
@api_call
|
|
def __create_zip(self, name: str, items: Iterable) -> dict:
|
|
"""
|
|
Creates a zip file containing multiple files and/or folders for later download.
|
|
|
|
:param name:
|
|
The name of the zip file to be created.
|
|
:param items:
|
|
List of files and/or folders to be contained in the zip file.
|
|
:returns:
|
|
A dictionary representing a created zip
|
|
"""
|
|
# pylint: disable=protected-access
|
|
url = self._session.get_url('zip_downloads')
|
|
zip_file_items = []
|
|
for item in items:
|
|
zip_file_items.append({'type': item._item_type, 'id': item.object_id})
|
|
data = {
|
|
'download_file_name': name,
|
|
'items': zip_file_items
|
|
}
|
|
return self._session.post(url, data=json.dumps(data), skip_retry_codes={202}).json()
|
|
|
|
@api_call
|
|
def download_zip(self, name: str, items: Iterable, writeable_stream: IO) -> dict:
|
|
"""
|
|
Downloads a zip file containing multiple files and/or folders.
|
|
|
|
:param name:
|
|
The name of the zip file to be created.
|
|
:param items:
|
|
List of files or folders to be part of the created zip.
|
|
:param writeable_stream:
|
|
Stream to pipe the readable stream of the zip file.
|
|
:returns:
|
|
A status response object
|
|
"""
|
|
created_zip = self.__create_zip(name, items)
|
|
response = self._session.get(created_zip['download_url'], expect_json_response=False, stream=True)
|
|
for chunk in response.network_response.response_as_stream.stream(decode_content=True):
|
|
writeable_stream.write(chunk)
|
|
status = self._session.get(created_zip['status_url']).json()
|
|
status.update(created_zip)
|
|
return self.translator.translate(
|
|
session=self._session,
|
|
response_object=status,
|
|
)
|
|
|
|
def folder_lock(self, folder_lock_id: str) -> 'FolderLock':
|
|
"""
|
|
Initialize a :class:`FolderLock` object, whose box id is folder_lock_id.
|
|
|
|
:param folder_lock_id:
|
|
The ID of the :class:`FolderLock` object.
|
|
:return:
|
|
A :class:`FolderLock` object with the given entry ID.
|
|
"""
|
|
return self.translator.get('folder_lock')(session=self._session, object_id=folder_lock_id)
|
|
|
|
def sign_request(self, sign_request_id: str) -> 'SignRequest':
|
|
"""
|
|
Initialize a :class:`SignRequest` object, whose box id is sign_request_id.
|
|
|
|
:param sign_request_id:
|
|
The box id of the :class:`SignRequest` object.
|
|
:return:
|
|
A :class:`SignRequest` object with the given file id.
|
|
"""
|
|
return self.translator.get('sign_request')(session=self._session, object_id=sign_request_id)
|
|
|
|
# pylint: disable=too-many-branches
|
|
def __create_sign_request(
|
|
self,
|
|
signers: Iterable,
|
|
files: Optional[Iterable] = None,
|
|
parent_folder_id: Optional[str] = None,
|
|
prefill_tags: Optional[Iterable] = None,
|
|
are_reminders_enabled: Optional[bool] = None,
|
|
are_text_signatures_enabled: Optional[bool] = None,
|
|
days_valid: Optional[str] = None,
|
|
email_message: Optional[Iterable] = None,
|
|
email_subject: Optional[str] = None,
|
|
external_id: Optional[str] = None,
|
|
is_document_preparation_needed: Optional[bool] = None,
|
|
redirect_url: Optional[str] = None,
|
|
declined_redirect_url: Optional[str] = None,
|
|
template_id: Optional[str] = None) -> 'SignRequest':
|
|
url = self._session.get_url('sign_requests')
|
|
|
|
body = {
|
|
'signers': signers,
|
|
}
|
|
|
|
if files:
|
|
body['source_files'] = files
|
|
if parent_folder_id:
|
|
body['parent_folder'] = {
|
|
'id': parent_folder_id,
|
|
'type': 'folder'
|
|
}
|
|
if prefill_tags:
|
|
body['prefill_tags'] = prefill_tags
|
|
if are_reminders_enabled:
|
|
body['are_reminders_enabled'] = are_reminders_enabled
|
|
if are_text_signatures_enabled:
|
|
body['are_text_signatures_enabled'] = are_text_signatures_enabled
|
|
if days_valid:
|
|
body['days_valid'] = days_valid
|
|
if email_message:
|
|
body['email_message'] = email_message
|
|
if email_subject:
|
|
body['email_subject'] = email_subject
|
|
if external_id:
|
|
body['external_id'] = external_id
|
|
if is_document_preparation_needed:
|
|
body['is_document_preparation_needed'] = is_document_preparation_needed
|
|
if redirect_url:
|
|
body['redirect_url'] = redirect_url
|
|
if declined_redirect_url:
|
|
body['declined_redirect_url'] = declined_redirect_url
|
|
if template_id:
|
|
body['template_id'] = template_id
|
|
|
|
box_response = self._session.post(url, data=json.dumps(body))
|
|
response = box_response.json()
|
|
return self.translator.translate(
|
|
session=self._session,
|
|
response_object=response,
|
|
)
|
|
|
|
@api_call
|
|
@deprecated('Use create_sign_request_v2 instead')
|
|
def create_sign_request(
|
|
self,
|
|
files: Iterable,
|
|
signers: Iterable,
|
|
parent_folder_id: str,
|
|
prefill_tags: Optional[Iterable] = None,
|
|
are_reminders_enabled: Optional[bool] = None,
|
|
are_text_signatures_enabled: Optional[bool] = None,
|
|
days_valid: Optional[str] = None,
|
|
email_message: Optional[Iterable] = None,
|
|
email_subject: Optional[str] = None,
|
|
external_id: Optional[str] = None,
|
|
is_document_preparation_needed: Optional[bool] = None,
|
|
redirect_url: Optional[str] = None,
|
|
declined_redirect_url: Optional[str] = None,
|
|
template_id: Optional[str] = None,
|
|
) -> 'SignRequest':
|
|
"""
|
|
Used to create a new sign request.
|
|
|
|
:param files:
|
|
List of files to create a signing document from.
|
|
:param signers:
|
|
List of signers for the sign request. 35 is the max number of signers permitted.
|
|
:param parent_folder_id:
|
|
The id of the destination folder to place sign request specific data in.
|
|
:param prefill_tags:
|
|
When a document contains sign related tags in the content,
|
|
you can prefill them using this prefill_tags by referencing the 'id' of the tag as the external_id field of the prefill tag.
|
|
:param are_reminders_enabled:
|
|
Reminds signers to sign a document on day 3, 8, 13 and 18. Reminders are only sent to outstanding signers.
|
|
:param are_text_signatures_enabled:
|
|
Disables the usage of signatures generated by typing (text).
|
|
:param days_valid:
|
|
Number of days after which this request will automatically expire if not completed.
|
|
:param email_message:
|
|
Message to include in sign request email. The field is cleaned through sanitization of specific characters.
|
|
However, some html tags are allowed. Links included in the message are also converted to hyperlinks in the email.
|
|
The message may contain the following html tags including a, abbr, acronym, b, blockquote, code, em, i, ul, li, ol, and strong.
|
|
Be aware that when the text to html ratio is too high, the email may end up in spam filters. Custom styles on these tags are not allowed.
|
|
If this field is not passed, a default message will be used.
|
|
:param email_subject:
|
|
Subject of sign request email. This is cleaned by sign request. If this field is not passed, a default subject will be used.
|
|
:param external_id:
|
|
This can be used to reference an ID in an external system that the sign request is related to.
|
|
:param is_document_preparation_needed:
|
|
Indicates if the sender should receive a prepare_url in the response to complete document preparation via UI.
|
|
:param redirect_url:
|
|
The URL that a signer will be redirected to after signing a document.
|
|
If no declined redirect URL is specified, this URL will be used for decline actions as well.
|
|
:param declined_redirect_url:
|
|
The URL that a signer will be redirected to after declining to sign a document.
|
|
:param template_id:
|
|
The ID of the sign template to use for the sign request.
|
|
:returns:
|
|
A dictionary representing a created SignRequest
|
|
"""
|
|
return self.__create_sign_request(
|
|
signers, files, parent_folder_id, prefill_tags, are_reminders_enabled, are_text_signatures_enabled, days_valid, email_message,
|
|
email_subject, external_id, is_document_preparation_needed, redirect_url, declined_redirect_url, template_id)
|
|
|
|
@api_call
|
|
def create_sign_request_v2(
|
|
self,
|
|
signers: Iterable,
|
|
files: Optional[Iterable] = None,
|
|
parent_folder_id: Optional[str] = None,
|
|
prefill_tags: Optional[Iterable] = None,
|
|
are_reminders_enabled: Optional[bool] = None,
|
|
are_text_signatures_enabled: Optional[bool] = None,
|
|
days_valid: Optional[str] = None,
|
|
email_message: Optional[Iterable] = None,
|
|
email_subject: Optional[str] = None,
|
|
external_id: Optional[str] = None,
|
|
is_document_preparation_needed: Optional[bool] = None,
|
|
redirect_url: Optional[str] = None,
|
|
declined_redirect_url: Optional[str] = None,
|
|
template_id: Optional[str] = None,
|
|
) -> 'SignRequest':
|
|
"""
|
|
Used to create a new sign request.
|
|
|
|
:param signers:
|
|
List of signers for the sign request. 35 is the max number of signers permitted.
|
|
:param files:
|
|
List of files to create a signing document from.
|
|
:param parent_folder_id:
|
|
The id of the destination folder to place sign request specific data in.
|
|
:param prefill_tags:
|
|
When a document contains sign related tags in the content,
|
|
you can prefill them using this prefill_tags by referencing the 'id' of the tag as the external_id field of the prefill tag.
|
|
:param are_reminders_enabled:
|
|
Reminds signers to sign a document on day 3, 8, 13 and 18. Reminders are only sent to outstanding signers.
|
|
:param are_text_signatures_enabled:
|
|
Disables the usage of signatures generated by typing (text).
|
|
:param days_valid:
|
|
Number of days after which this request will automatically expire if not completed.
|
|
:param email_message:
|
|
Message to include in sign request email. The field is cleaned through sanitization of specific characters.
|
|
However, some html tags are allowed. Links included in the message are also converted to hyperlinks in the email.
|
|
The message may contain the following html tags including a, abbr, acronym, b, blockquote, code, em, i, ul, li, ol, and strong.
|
|
Be aware that when the text to html ratio is too high, the email may end up in spam filters. Custom styles on these tags are not allowed.
|
|
If this field is not passed, a default message will be used.
|
|
:param email_subject:
|
|
Subject of sign request email. This is cleaned by sign request. If this field is not passed, a default subject will be used.
|
|
:param external_id:
|
|
This can be used to reference an ID in an external system that the sign request is related to.
|
|
:param is_document_preparation_needed:
|
|
Indicates if the sender should receive a prepare_url in the response to complete document preparation via UI.
|
|
:param redirect_url:
|
|
The URL that a signer will be redirected to after signing a document.
|
|
If no declined redirect URL is specified, this URL will be used for decline actions as well.
|
|
:param declined_redirect_url:
|
|
The URL that a signer will be redirected to after declining to sign a document.
|
|
:param template_id:
|
|
The ID of the sign template to use for the sign request.
|
|
:returns:
|
|
A dictionary representing a created SignRequest
|
|
"""
|
|
return self.__create_sign_request(
|
|
signers, files, parent_folder_id, prefill_tags, are_reminders_enabled, are_text_signatures_enabled, days_valid, email_message,
|
|
email_subject, external_id, is_document_preparation_needed, redirect_url, declined_redirect_url, template_id)
|
|
|
|
@api_call
|
|
def get_sign_requests(
|
|
self,
|
|
limit: Optional[int] = None,
|
|
marker: Optional[str] = None,
|
|
fields: Iterable[str] = None
|
|
) -> 'BoxObjectCollection':
|
|
"""
|
|
Returns all the sign requests.
|
|
|
|
:param limit:
|
|
The maximum number of entries to return per page. If not specified, then will use the server-side default.
|
|
:param marker:
|
|
The paging marker to start paging from.
|
|
:param fields:
|
|
List of fields to request.
|
|
:returns:
|
|
An iterator of the entries in the device pins.
|
|
"""
|
|
return MarkerBasedObjectCollection(
|
|
session=self._session,
|
|
url=self.get_url('sign_requests'),
|
|
limit=limit,
|
|
marker=marker,
|
|
fields=fields,
|
|
return_full_pages=False,
|
|
)
|
|
|
|
def sign_template(self, sign_template_id: str) -> 'SignTemplate':
|
|
"""
|
|
Initialize a :class:`SignTemplate` object, whose box id is sign_template_id.
|
|
|
|
:param sign_template_id:
|
|
The box id of the :class:`SignTemplate` object.
|
|
:return:
|
|
A :class:`SignTemplate` object with the given sign_template_id.
|
|
"""
|
|
return self.translator.get('sign_template')(session=self._session, object_id=sign_template_id)
|
|
|
|
@api_call
|
|
def get_sign_templates(
|
|
self,
|
|
limit: Optional[int] = None,
|
|
marker: Optional[str] = None,
|
|
) -> 'BoxObjectCollection':
|
|
"""
|
|
Returns all sign templates
|
|
|
|
:param limit:
|
|
The maximum number of entries to return per page. If not specified, then will use the server-side default.
|
|
:param marker:
|
|
The paging marker to start paging from.
|
|
:returns:
|
|
Sign templates
|
|
"""
|
|
return MarkerBasedObjectCollection(
|
|
session=self._session,
|
|
url=self.get_url("sign_templates"),
|
|
limit=limit,
|
|
marker=marker,
|
|
return_full_pages=False,
|
|
)
|
|
|
|
@api_call
|
|
def get_sign_template(
|
|
self,
|
|
sign_template_id: str,
|
|
) -> Any:
|
|
"""
|
|
Returns a sign template
|
|
|
|
:param sign_template_id:
|
|
ID of Sign template to fetch
|
|
:returns:
|
|
Sign template
|
|
"""
|
|
response = self._session.get(f"{self._session.get_url('sign_templates')}/{sign_template_id}")
|
|
return self.translator.translate(
|
|
session=self._session,
|
|
response_object=response.json(),
|
|
)
|
|
|
|
@api_call
|
|
def send_ai_question(
|
|
self,
|
|
items: Iterable,
|
|
prompt: str,
|
|
mode: Optional[str] = None,
|
|
ai_agent: Optional[dict] = None
|
|
) -> Any:
|
|
"""
|
|
Sends an AI request to supported LLMs and returns an answer specifically focused on the user's
|
|
question given the provided context.
|
|
|
|
:param items:
|
|
The items to be processed by the LLM, often files.
|
|
:param prompt:
|
|
The prompt provided by the client to be answered by the LLM.
|
|
The prompt's length is limited to 10000 characters.
|
|
:param mode:
|
|
The mode specifies if this request is for a single or multiple items.
|
|
If you select single_item_qa the items array can have one element only.
|
|
Selecting multiple_item_qa allows you to provide up to 25 items.
|
|
|
|
Value is one of `multiple_item_qa`, `single_item_qa`
|
|
:param ai_agent:
|
|
The AI agent used to handle queries.
|
|
:returns:
|
|
A response including the answer from the LLM.
|
|
"""
|
|
url = self._session.get_url('ai/ask')
|
|
if mode is None:
|
|
mode = ('single_item_qa' if len(items) == 1 else 'multiple_item_qa')
|
|
body = {
|
|
'items': items,
|
|
'prompt': prompt,
|
|
'mode': mode
|
|
}
|
|
|
|
if ai_agent is not None:
|
|
body['ai_agent'] = ai_agent
|
|
|
|
box_response = self._session.post(url, data=json.dumps(body))
|
|
response = box_response.json()
|
|
return self.translator.translate(
|
|
session=self._session,
|
|
response_object=response,
|
|
)
|
|
|
|
@api_call
|
|
def send_ai_text_gen(
|
|
self,
|
|
dialogue_history: Iterable,
|
|
items: Iterable,
|
|
prompt: str,
|
|
ai_agent: Optional[dict] = None
|
|
):
|
|
"""
|
|
Sends an AI request to supported LLMs and returns an answer specifically focused on the creation of new text.
|
|
|
|
:param dialogue_history:
|
|
The history of prompts and answers previously passed to the LLM.
|
|
This provides additional context to the LLM in generating the response.
|
|
:param items:
|
|
The items to be processed by the LLM, often files. The array can include exactly one element.
|
|
:param prompt:
|
|
The prompt provided by the client to be answered by the LLM.
|
|
The prompt's length is limited to 10000 characters.
|
|
:param ai_agent:
|
|
The AI agent used for generating text.
|
|
:returns:
|
|
A response including the generated text from the LLM.
|
|
"""
|
|
url = self._session.get_url('ai/text_gen')
|
|
body = {
|
|
'dialogue_history': dialogue_history,
|
|
'items': items,
|
|
'prompt': prompt
|
|
}
|
|
|
|
if ai_agent is not None:
|
|
body['ai_agent'] = ai_agent
|
|
|
|
box_response = self._session.post(url, data=json.dumps(body))
|
|
response = box_response.json()
|
|
return self.translator.translate(
|
|
session=self._session,
|
|
response_object=response,
|
|
)
|
|
|
|
@api_call
|
|
def get_ai_agent_default_config(
|
|
self,
|
|
mode: str,
|
|
language: Optional[str] = None,
|
|
model: Optional[str] = None,
|
|
):
|
|
"""
|
|
Get the AI agent default configuration.
|
|
|
|
:param mode:
|
|
The mode to filter the agent config to return.
|
|
:param language:
|
|
The ISO language code to return the agent config for.
|
|
If the language is not supported the default agent configuration is returned.
|
|
:param model:
|
|
The model to return the default agent config for.
|
|
:returns:
|
|
A default agent configuration.
|
|
This can be one of the following two objects:
|
|
AI agent for questions and AI agent for text generation.
|
|
The response depends on the agent configuration requested in this endpoint.
|
|
"""
|
|
url = self._session.get_url('ai_agent_default')
|
|
params = {'mode': mode}
|
|
if language is not None:
|
|
params['language'] = language
|
|
if model is not None:
|
|
params['model'] = model
|
|
|
|
box_response = self._session.get(url, params=params)
|
|
return self.translator.translate(
|
|
session=self._session,
|
|
response_object=box_response.json(),
|
|
)
|