Beispiel #1
0
    async def download(self, path: BitbucketPath,  # type: ignore
                       range: Tuple[int, int]=None, **kwargs) -> streams.ResponseStreamReader:
        """Get the stream to the specified file on Bitbucket

        In BB API 2.0, the ``repo/username/repo_slug/src/node/path`` endpoint is used for download.

        Please note that same endpoint has several different usages / behaviors depending on the
        type of the path and the query params.

        1) File download: type is file, no query param``format=meta``
        2) File metadata: type is file, with ``format=meta`` as query param
        3) Folder contents: type is folder, no query param``format=meta``
        4) Folder metadata: type is folder, with ``format=meta`` as query param

        API Doc: https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Busername%7D/%7Brepo_slug%7D/src/%7Bnode%7D/%7Bpath%7D

        :param path: the BitbucketPath object of the file to be downloaded
        :param range: the range header
        """
        metadata = await self.metadata(path)
        logger.debug('requested-range:: {}'.format(range))
        resp = await self.make_request(
            'GET',
            self._build_v2_repo_url('src', path.commit_sha, *path.path_tuple()),
            range=range,
            expects=(200, ),
            throws=exceptions.DownloadError,
        )
        logger.debug('download-headers:: {}'.format([(x, resp.headers[x]) for x in resp.headers]))
        return streams.ResponseStreamReader(resp, size=metadata.size)
Beispiel #2
0
    async def download(self, path: BitbucketPath, **kwargs):  # type: ignore
        '''Get the stream to the specified file on bitbucket
        :param str path: The path to the file on bitbucket
        '''
        metadata = await self.metadata(path)

        resp = await self.make_request(
            'GET',
            self._build_v1_repo_url('raw', path.commit_sha,
                                    *path.path_tuple()),
            expects=(200, ),
            throws=exceptions.DownloadError,
        )

        return streams.ResponseStreamReader(resp, size=metadata.size)
Beispiel #3
0
    async def _fetch_path_metadata(self, path: BitbucketPath) -> dict:
        """Get the metadata for folder and file itself.

        Bitbucket API 2.0 provides an easy way to fetch metadata for files and folders by simply
        appending ``?format=meta`` to the path endpoint.

        Quirk 1: This new feature no longer returns several WB required attributes out of the box:
        ``node`` and ``path`` for folder, ``revision``, ``timestamp`` and ``created_utc`` for file.

        1) The ``path`` for folders no longer has an ending slash.
        2) The ``node`` for folders and ``revision`` for files are gone.  They have always been the
           first 12 chars of the commit hash in both 1.0 and 2.0.
        3) ``timestamp`` and ``created_utc`` for files are gone and must be obtained using the file
           history endpoint indicated by ``links.history.href``. See ``_metadata_file()`` and
           ``_fetch_commit_history_by_url()`` for details.

        Quirk 2:

        This PATH endpoint ``/2.0/repositories/{username}/{repo_slug}/src/{node}/{path}`` returns
        HTTP 404 if the ``node`` segment is a branch of which the name contains a slash.  This is
        a either a limitation or a bug on several BB API 2.0 endpoints.  It has nothing to do with
        encoding.  More specifically, neither encoding / with %2F nor enclosing ``node`` with curly
        braces %7B%7D works.  Here is the closest reference to the issue we can find as of May 2019:
        https://bitbucket.org/site/master/issues/9969/get-commit-revision-api-does-not-accept.  The
        fix is simple, just make an extra request to fetch the commit sha of the branch.  See
        ``_fetch_branch_commit_sha()`` for details.  In addition, this will happen on all branches,
        no matter if the name contains a slash or not.

        API Doc: https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Busername%7D/%7Brepo_slug%7D/src/%7Bnode%7D/%7Bpath%7D

        :param path: the file or folder of which the metadata is requested
        :return: the file metadata dict
        """
        query_params = {
            'format': 'meta',
            'fields': 'commit.hash,commit.date,path,size,links.history.href'
        }
        if not path.commit_sha:
            path.set_commit_sha(await self._fetch_branch_commit_sha(path.branch_name))
        path_meta_url = self._build_v2_repo_url('src', path.ref, *path.path_tuple())
        resp = await self.make_request(
            'GET',
            '{}/?{}'.format(path_meta_url, urlencode(query_params)),
            expects=(200,),
            throws=exceptions.ProviderError,
        )
        return await resp.json()
Beispiel #4
0
    async def download(self, path: BitbucketPath,  # type: ignore
                       range: Tuple[int, int]=None, **kwargs) -> streams.ResponseStreamReader:
        """Get the stream to the specified file on Bitbucket

        :param path: The path to the file on Bitbucket
        :param range: the range header
        """
        metadata = await self.metadata(path)

        logger.debug('requested-range:: {}'.format(range))
        resp = await self.make_request(
            'GET',
            self._build_v1_repo_url('raw', path.commit_sha, *path.path_tuple()),
            range=range,
            expects=(200, ),
            throws=exceptions.DownloadError,
        )
        logger.debug('download-headers:: {}'.format([(x, resp.headers[x]) for x in resp.headers]))

        return streams.ResponseStreamReader(resp, size=metadata.size)
Beispiel #5
0
    async def _fetch_dir_listing(self, folder: BitbucketPath) -> dict:
        """Get listing of contents within a BitbucketPath folder object.

        https://confluence.atlassian.com/bitbucket/src-resources-296095214.html#srcResources-GETalistofreposource

        Note::

            Using this endpoint for a file will return the file contents.

        :param BitbucketPath folder: the folder whose contents should be listed
        :rtype dict:
        :returns: a directory listing of the contents of the folder
        """
        assert folder.is_dir  # don't use this method on files

        resp = await self.make_request(
            'GET',
            self._build_v1_repo_url('src', folder.ref, *folder.path_tuple()) + '/',
            expects=(200, ),
            throws=exceptions.ProviderError,
        )
        return await resp.json()
Beispiel #6
0
    async def _fetch_dir_listing(self, folder: BitbucketPath) -> list:
        """Get a list of the folder's full contents (upto the max limit setting if there is one).

        Bitbucket API 2.0 refactored the response structure for listing folder contents.

        1) The response is paginated.  If ``resp_dict`` contains the key ``next``, the contents are
           partial.  The caller must use the URL provided by ``dict['next']`` to fetch the next page
           after this method returns.

        2) The response no longer provides the metadata about the folder itself.  In order to obtain
           the ``node`` and ``path`` attributes, please use  ``_fetch_path_metadata()`` instead.

        API Doc: https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Busername%7D/%7Brepo_slug%7D/src/%7Bnode%7D/%7Bpath%7D

        :param folder: the folder of which the contents should be listed
        :returns: a list of the folder's full contents
        """
        query_params = {
            'pagelen': self.RESP_PAGE_LEN,
            'fields': 'values.path,values.size,values.type,next',
        }
        if not folder.commit_sha:
            folder.set_commit_sha(await self._fetch_branch_commit_sha(folder.branch_name))
        next_url = '{}/?{}'.format(self._build_v2_repo_url('src', folder.ref, *folder.path_tuple()),
                                   urlencode(query_params))
        dir_list = []  # type: ignore
        while next_url:
            resp = await self.make_request(
                'GET',
                next_url,
                expects=(200,),
                throws=exceptions.ProviderError,
            )
            content = await resp.json()
            next_url = content.get('next', None)
            dir_list.extend(content['values'])
        return dir_list