def test_with_new_child(new_child, extend_default_translator):
    item_type = 'foo'

    class Foo(object):
        pass

    mapping = {item_type: Foo}

    kwargs = {}
    if new_child is not None:
        kwargs['new_child'] = new_child

    translator = Translator(
        mapping, extend_default_translator=extend_default_translator, **kwargs)
    assert item_type in translator
    assert translator.maps[0] == {}
    with pytest.raises(KeyError):
        del translator[item_type]

    class Bar(Foo):
        pass

    translator.register(item_type, Bar)
    assert translator.get(item_type) is Bar
    assert mapping == {item_type: Foo}
Beispiel #2
0
    def _init_user_and_group_instances(session, response_object, user, group):
        """
        Initialize User & Group instances based on the passed in parameters. This allows the GroupMembership
        instance to maintain a 'has-a' relationship with a corresponding User and Group instance.

        :param session:
            The Box session used to make requests.
        :type session:
            :class:`BoxSession`
        :param response_object:
            The Box API response representing the object.
        :type response_object:
            :class:`BoxResponse`
        :param user:
            The User object, if any, to associate with this group_membership instance.
        :type user:
            :class:`User`
        :param group:
            The Group object, if any, to associate with this group_membership instance.
        :type group:
            :class:`Group`
        """
        if not response_object:
            return user, group

        user_info = response_object.get('user')
        group_info = response_object.get('group')

        user = user or Translator().translate(user_info['type'])(
            session, user_info['id'], user_info)
        group = group or Translator().translate(group_info['type'])(
            session, group_info['id'], group_info)

        return user, group
def test_with_extend_default_translator(default_translator, extend_default_translator, new_child):
    item_type = 'foo'

    class Foo(object):
        pass

    kwargs = {}
    if extend_default_translator is not None:
        kwargs['extend_default_translator'] = extend_default_translator
    translator = Translator({item_type: Foo}, new_child=new_child, **kwargs)
    assert set(translator.items()).issuperset(default_translator.items())
def test_with_extend_default_translator(default_translator,
                                        extend_default_translator, new_child):
    item_type = 'foo'

    class Foo(object):
        pass

    kwargs = {}
    if extend_default_translator is not None:
        kwargs['extend_default_translator'] = extend_default_translator
    translator = Translator({item_type: Foo}, new_child=new_child, **kwargs)
    assert set(translator.items()).issuperset(default_translator.items())
    def get_items(self, limit, offset=0, fields=None):
        """Get the items in a folder.

        :param limit:
            The maximum number of items to return.
        :type limit:
            `int`
        :param offset:
            The index at which to start returning items.
        :type offset:
            `int`
        :param fields:
            List of fields to request.
        :type fields:
            `Iterable` of `unicode`
        :returns:
            A list of items in the folder.
        :rtype:
            `list` of :class:`Item`
        """
        url = self.get_url('items')
        params = {
            'limit': limit,
            'offset': offset,
        }
        if fields:
            params['fields'] = ','.join(fields)
        box_response = self._session.get(url, params=params)
        response = box_response.json()
        return [
            Translator().translate(item['type'])(self._session, item['id'],
                                                 item)
            for item in response['entries']
        ]
    def _paging_wrapper(self, url, starting_index, limit, factory=None):
        """
        Helper function that turns any paging API into a generator that transparently implements the paging for
        the caller.

        A caller wanting to implement their own paging can do so by managing the starting_index & limit params,
        and never iterating over more than 'limit' items per call. For example:

            first_ten = list(itertools.islice(_paging_wrapper(..., 0, 10, ...), 10))
            second_ten = list(itertools.islice(_paging_wrapper(..., 10, 10, ...), 10))
            third_ten = list(itertools.islice(_paging_wrapper(..., 20, 10, ...), 10))
            ...
        When one of the lists has less than 10 items... the end has been reached.

        Caveat: any hidden items (see the Box Developer API for more details) will render the above
        inaccurate. Hidden results will lead the above get_slice() code to trigger API calls at non-expected places.

        :param starting_index:
            The index at which to begin.
        :type starting_index:
            `int`
        :param limit:
            The maximum number of items to return in a page.
        :type limit:
            `int`
        :param factory:
            A callable factory method which creates the object instances. Signature should match the __init__
            signature of BaseObject. If no factory is given then the Translator factory is used.
        :type factory:
            `callable` or None
        :returns:
            A generator of 3-tuples. Each tuple contains:
            1) An instance returned by the given factory callable.
            2) The number of objects in the current page.
            3) Index the current instance in the current page.
        :rtype:
            `generator` of `tuple` of (varies, `int`, `int`)
        """
        current_index = starting_index

        while True:
            params = {'limit': limit, 'offset': current_index}
            box_response = self._session.get(url, params=params)
            response = box_response.json()

            current_page_size = len(response['entries'])
            for index_in_current_page, item in enumerate(response['entries']):
                instance_factory = factory
                if not instance_factory:
                    instance_factory = Translator().translate(item['type'])
                instance = instance_factory(self._session, item['id'], item)
                yield instance, current_page_size, index_in_current_page

            current_index += limit
            if current_index >= response['total_count']:
                break
Beispiel #7
0
    def search(self,
               query,
               limit,
               offset,
               ancestor_folders=None,
               file_extensions=None):
        """
        Search Box for items matching the given query.

        :param query:
            The string to search for.
        :type query:
            `unicode`
        :param limit:
            The maximum number of items to return.
        :type limit:
            `int`
        :param offset:
            The search result at which to start the response.
        :type offset:
            `int`
        :param ancestor_folders:
            Folder ids to limit the search to.
        :type ancestor_folders:
            `list` of `unicode`
        :param file_extensions:
            File extensions to limit the search to.
        :type file_extensions:
            `list` of `unicode`
        :return:
            A list of items that match the search query.
        :rtype:
            `list` of :class:`Item`
        """
        url = '{0}/search'.format(API.BASE_API_URL)
        params = {
            'query': query,
            'limit': limit,
            'offset': offset,
        }
        if ancestor_folders:
            params.update({
                'ancestor_folder_ids':
                ','.join([folder.object_id for folder in ancestor_folders])
            })
        if file_extensions:
            params.update({'file_extensions': ','.join(file_extensions)})
        box_response = self._session.get(url, params=params)
        response = box_response.json()
        return [
            Translator().translate(item['type'])(self._session, item['id'],
                                                 item)
            for item in response['entries']
        ]
def test_without_extend_default_translator(new_child):
    item_type = 'foo'

    class Foo(object):
        pass

    mapping = {item_type: Foo}

    translator = Translator(mapping,
                            extend_default_translator=False,
                            new_child=new_child)
    assert translator == mapping
def test_without_new_child(extend_default_translator):
    item_type = 'foo'

    class Foo(object):
        pass

    mapping = {item_type: Foo}

    translator = Translator(mapping, new_child=False, extend_default_translator=extend_default_translator)
    assert item_type in translator
    assert translator.maps[0] is mapping

    class Bar(Foo):
        pass

    translator.register(item_type, Bar)
    assert translator.translate(item_type) is Bar
    assert mapping == {item_type: Bar}

    del translator[item_type]
    assert not mapping
Beispiel #10
0
def default_translator(original_default_translator):
    """The default translator to use during the test.

    We don't want global state to mutate across tests. So before each test
    (because of autouse=True), we make a copy of the default translator, and
    assign this copy to Translator._default_translator. At the end of the test,
    we reset the reference.
    """
    try:
        translator = Translator(dict(copy.deepcopy(original_default_translator)), extend_default_translator=False, new_child=False)
        Translator._default_translator = translator   # pylint:disable=protected-access
        yield translator
    finally:
        Translator._default_translator = original_default_translator  # pylint:disable=protected-access
def test_without_new_child(extend_default_translator):
    item_type = 'foo'

    class Foo(object):
        pass

    mapping = {item_type: Foo}

    translator = Translator(
        mapping,
        new_child=False,
        extend_default_translator=extend_default_translator)
    assert item_type in translator
    assert translator.maps[0] is mapping

    class Bar(Foo):
        pass

    translator.register(item_type, Bar)
    assert translator.get(item_type) is Bar
    assert mapping == {item_type: Bar}

    del translator[item_type]
    assert not mapping
Beispiel #12
0
def test_with_new_child(new_child, extend_default_translator):
    item_type = 'foo'

    class Foo(object):
        pass

    mapping = {item_type: Foo}

    kwargs = {}
    if new_child is not None:
        kwargs['new_child'] = new_child

    translator = Translator(mapping, extend_default_translator=extend_default_translator, **kwargs)
    assert item_type in translator
    assert translator.maps[0] == {}
    with pytest.raises(KeyError):
        del translator[item_type]

    class Bar(Foo):
        pass

    translator.register(item_type, Bar)
    assert translator.translate(item_type) is Bar
    assert mapping == {item_type: Foo}
Beispiel #13
0
    def add_collaborator(self, collaborator, role, notify=False):
        """Add a collaborator to the folder

        :param collaborator:
            collaborator to add. May be a User, Group, or email address (unicode string)
        :type collaborator:
            :class:`User` or :class:`Group` or `unicode`
        :param role:
            The collaboration role
        :type role:
            :class:`CollaboratorRole`
        :param notify:
            Whether to send a notification email to the collaborator
        :type notify:
            `bool`
        :return:
            The new collaboration
        :rtype:
            :class:`Collaboration`
        """
        collaborator_helper = _Collaborator(collaborator)
        url = API.BASE_API_URL + '/collaborations'
        item = {'id': self._object_id, 'type': 'folder'}
        access_key, access_value = collaborator_helper.access
        accessible_by = {
            access_key: access_value,
            'type': collaborator_helper.type
        }
        data = json.dumps({
            'item': item,
            'accessible_by': accessible_by,
            'role': role,
        })
        params = {'notify': notify}
        box_response = self._session.post(url, expect_json_response=True, data=data, params=params)
        collaboration_response = box_response.json()
        collab_id = collaboration_response['id']
        return Translator().translate(collaboration_response['type'])(
            session=self._session,
            object_id=collab_id,
            response_object=collaboration_response,
        )
Beispiel #14
0
    def search(self,
               query,
               limit=100,
               offset=0,
               ancestor_folders=None,
               file_extensions=None,
               metadata_filters=None,
               result_type=None,
               content_types=None,
               **kwargs):
        """
        Search Box for items matching the given query.

        :param query:
            The string to search for.
        :type query:
            `unicode`
        :param limit:
            The maximum number of items to return.
        :type limit:
            `int`
        :param offset:
            The search result at which to start the response.
        :type offset:
            `int`
        :param ancestor_folders:
            Folder ids to limit the search to.
        :type ancestor_folders:
            `Iterable` of :class:`Folder`
        :param file_extensions:
            File extensions to limit the search to.
        :type file_extensions:
            `iterable` of `unicode`
        :param metadata_filters:
            Filters used for metadata search
        :type metadata_filters:
            :class:`MetadataSearchFilters`
        :param result_type:
            Which type of result you want. Can be file or folder.
        :type result_type:
            `unicode`
        :param content_types:
            Which content types to search. Valid types include name, description, file_content, comments, and tags.
        :type content_types:
            `Iterable` of `unicode`
        :return:
            A list of items that match the search query.
        :rtype:
            `list` of :class:`Item`
        """
        url = self.get_url()
        params = {
            'query': query,
            'limit': limit,
            'offset': offset,
        }
        if ancestor_folders:
            params.update({
                'ancestor_folder_ids':
                ','.join([folder.object_id for folder in ancestor_folders])
            })
        if file_extensions:
            params.update({'file_extensions': ','.join(file_extensions)})
        if metadata_filters:
            params.update(
                {'mdfilters': json.dumps(metadata_filters.as_list())})
        if content_types:
            params.update({'content_types': ','.join(content_types)})
        if result_type:
            params.update({'type': result_type})
        params.update(kwargs)
        box_response = self._session.get(url, params=params)
        response = box_response.json()
        return [
            Translator().translate(item['type'])(self._session, item['id'],
                                                 item)
            for item in response['entries']
        ]
Beispiel #15
0
def translator():
    return Translator()
def test_translator_converts_response_to_correct_type(response_type):
    response, object_class = _response_to_class_mapping[response_type]
    assert type(Translator().get(response.json()['type']) == object_class)
def run_collab(box_client):
    global migration_folder, collaboration

    migration_folder = client.folder(folder_id=config.base_folder_id).get()

    users = migration_folder.get_items(limit=1000, offset=0)  # get the items from inside
    for user in users:  # go through the folders in migration
        new_name = config.new_folder_prefix + user.name
        collab_folder = user.rename(new_name)  # rename the folder
        f.write('\nFolder {0} renamed'.format(collab_folder.get()['name']))
        print('\nFolder {0} renamed'.format(collab_folder.get()['name']))

        box_users = box_client.users(filter_term=user.name + "@")
        if len(box_users) == 0:
            f.write('\nNo Box users found for ' + user.name)
            print('No Box users found for ' + user.name)
            continue
        if len(box_users) > 1:
            f.write('\nMultiple Box users found for ' + user.name)
            print('Multiple Box users found for ' + user.name)
            continue

        first_user = box_users[0]
        f.write('\nCreating a collaboration with ' + user.name + ' (login: '******')...')
        print('Creating a collaboration with ' + user.name + ' (login: '******')...')

        try:
            collaboration = collab_folder.add_collaborator(first_user.login, CollaborationRole.CO_OWNER)  # add a
            # collaborator using the folder name
        except BoxAPIException as box:
            if box.status == 204:  # success
                f.write('\nCreated a collaboration with ' + user.name + '\n')
                print('Created a collaboration with ' + user.name)
                pass
            elif box.status == 400:  # auto-accept is off
                failed_users.append(user.name)
                f.write('\nFAILED to create the collaboration with ' + user.name + ': ' + box.message + '\n')
                print('FAILED to create the collaboration with ' + user.name + ': ' + box.message)
                collab_folder.rename(user.name)
                continue
                pass
            elif box.status == 409:  # auto-accept is off
                failed_users.append(user.name)
                f.write('\nFAILED to create the collaboration with ' + user.name + ': ' + box.message + '\n')
                print('FAILED to create the collaboration with ' + user.name + ': ' + box.message)
                collab_folder.rename(user.name)
                continue
                pass
            else:
                raise

        try:
            collaboration.update_info(role=CollaborationRole.OWNER)  # make the collaborator the owner
        except BoxAPIException as box:
            if box.status == 204:  # success
                f.write('\nModified the collaboration to OWNER\n')
                print('Modified the collaboration to OWNER')
                pass
            elif box.status == 400:  # auto-accept collaboration is likely off
                failed_users.append(user.name)
                f.write('\nFAILED to modify the collaboration to OWNER for ' + user.name + ': ' + box.message + '\n')
                print('FAILED to modify the collaboration to OWNER for ' + user.name + ': ' + box.message)
                collab_folder.rename(user.name)
                pass
            else:
                raise

        json_response = box_client.make_request(  # get the collaboration id
            'GET',
            box_client.get_url('folders', collab_folder.id, 'collaborations'),
        ).json()

        id_num = [Translator().translate(item['type'])(None, item['id'], item) for item in
                  json_response['entries']]  # translate the id for the machine
        collab = id_num[0]  # grab the ID

        try:
            json_send = box_client.make_request(  # delete the collab_id
                'DELETE',
                box_client.get_url('collaborations', collab.id),
            )
            pprint.pprint(json_send)
        except BoxAPIException as box:
            if box.status == 204:  # success
                pass
            else:
                raise
        print("\n")
 def __init__(cls, name, bases, attrs):
     super(ObjectMeta, cls).__init__(name, bases, attrs)
     item_type = attrs.get('_item_type', None)
     if item_type is not None:
         Translator().register(item_type, cls)
Beispiel #19
0
def translator(default_translator):   # pylint:disable=unused-argument
    return Translator(extend_default_translator=True, new_child=True)
Beispiel #20
0
    def upload_stream(
            self,
            file_stream,
            file_name,
            preflight_check=False,
            preflight_expected_size=0,
            upload_using_accelerator=False,
    ):
        """
        Upload a file to the folder.
        The contents are taken from the given file stream, and it will have the given name.

        :param file_stream:
            The file-like object containing the bytes
        :type file_stream:
            `file`
        :param file_name:
            The name to give the file on Box.
        :type file_name:
            `unicode`
        :param preflight_check:
            If specified, preflight check will be performed before actually uploading the file.
        :type preflight_check:
            `bool`
        :param preflight_expected_size:
            The size of the file to be uploaded in bytes, which is used for preflight check. The default value is '0',
            which means the file size is unknown.
        :type preflight_expected_size:
            `int`
        :param upload_using_accelerator:
            If specified, the upload will try to use Box Accelerator to speed up the uploads for big files.
            It will make an extra API call before the actual upload to get the Accelerator upload url, and then make
            a POST request to that url instead of the default Box upload url. It falls back to normal upload endpoint,
            if cannot get the Accelerator upload url.

            Please notice that this is a premium feature, which might not be available to your app.
        :type upload_using_accelerator:
            `bool`
        :returns:
            The newly uploaded file.
        :rtype:
            :class:`File`
        """
        if preflight_check:
            self.preflight_check(size=preflight_expected_size, name=file_name)

        url = '{0}/files/content'.format(API.UPLOAD_URL)
        if upload_using_accelerator:
            accelerator_upload_url = self._get_accelerator_upload_url_fow_new_uploads()
            if accelerator_upload_url:
                url = accelerator_upload_url

        data = {'attributes': json.dumps({
            'name': file_name,
            'parent': {'id': self._object_id},
        })}
        files = {
            'file': ('unused', file_stream),
        }
        box_response = self._session.post(url, data=data, files=files, expect_json_response=False)
        file_response = box_response.json()['entries'][0]
        file_id = file_response['id']
        return Translator().translate(file_response['type'])(
            session=self._session,
            object_id=file_id,
            response_object=file_response,
        )