diff --git a/swh/lister/bitbucket/__init__.py b/swh/lister/bitbucket/__init__.py --- a/swh/lister/bitbucket/__init__.py +++ b/swh/lister/bitbucket/__init__.py @@ -4,11 +4,10 @@ def register(): - from .lister import BitBucketLister - from .models import BitBucketModel + from .lister import BitbucketLister return { - "models": [BitBucketModel], - "lister": BitBucketLister, + "models": [], + "lister": BitbucketLister, "task_modules": ["%s.tasks" % __name__], } diff --git a/swh/lister/bitbucket/lister.py b/swh/lister/bitbucket/lister.py --- a/swh/lister/bitbucket/lister.py +++ b/swh/lister/bitbucket/lister.py @@ -3,83 +3,199 @@ # License: GNU General Public License version 3, or any later version # See top-level LICENSE file for more information -from datetime import datetime, timezone +from dataclasses import asdict, dataclass +from datetime import datetime import logging -from typing import Any, Dict, List, Optional +import time +from typing import Any, Dict, Iterator, List, Optional from urllib import parse import iso8601 -from requests import Response +import requests -from swh.lister.bitbucket.models import BitBucketModel -from swh.lister.core.indexing_lister import IndexingHttpLister +from swh.scheduler.interface import SchedulerInterface +from swh.scheduler.model import ListedOrigin + +from .. import USER_AGENT +from ..pattern import CredentialsType, Lister logger = logging.getLogger(__name__) -class BitBucketLister(IndexingHttpLister): - PATH_TEMPLATE = "/repositories?after=%s" - MODEL = BitBucketModel +@dataclass +class BitbucketListerState: + """State of my lister""" + + last_repo_cdate: Optional[datetime] = None + """Date and time of the last repository listed on an incremental pass""" + + +class BitbucketLister(Lister[BitbucketListerState, List[Dict[str, Any]]]): + """List origins from Bitbucket. + + """ + LISTER_NAME = "bitbucket" - DEFAULT_URL = "https://api.bitbucket.org/2.0" - instance = "bitbucket" - default_min_bound = datetime.fromtimestamp(0, timezone.utc) # type: Any + INSTANCE = "bitbucket" - def __init__( - self, url: str = None, override_config=None, per_page: int = 100 - ) -> None: - super().__init__(url=url, override_config=override_config) - per_page = self.config.get("per_page", per_page) - - self.PATH_TEMPLATE = "%s&pagelen=%s" % (self.PATH_TEMPLATE, per_page) - - def get_model_from_repo(self, repo: Dict) -> Dict[str, Any]: - return { - "uid": repo["uuid"], - "indexable": iso8601.parse_date(repo["created_on"]), - "name": repo["name"], - "full_name": repo["full_name"], - "html_url": repo["links"]["html"]["href"], - "origin_url": repo["links"]["clone"][0]["href"], - "origin_type": repo["scm"], - } + API_URL = "https://api.bitbucket.org/2.0/repositories" + PAGE_SIZE = 100 - def get_next_target_from_response(self, response: Response) -> Optional[datetime]: - """This will read the 'next' link from the api response if any - and return it as a datetime. + BACKOFF_FACTOR = 10 # max anonymous 60 per hour, authenticated 1000 per hour + MAX_RETRIES = 5 - Args: - response (Response): requests' response from api call + def __init__( + self, + scheduler: SchedulerInterface, + per_page: int = 100, + credentials: CredentialsType = None, + ): + super().__init__( + scheduler=scheduler, + credentials=credentials, + url=self.API_URL, + instance=self.INSTANCE, + ) + + self.url_params = { + "pagelen": per_page, + "fields": ( + "next,values.links.clone.href,values.scm,values.updated_on," + "values.created_on" + ), + } - Returns: - next date as a datetime + self.session = requests.Session() + self.session.headers.update( + {"Accept": "application/json", "User-Agent": USER_AGENT} + ) + + self.backoff = self.BACKOFF_FACTOR + self.request_count = 0 + + def state_from_dict(self, d: Dict[str, Any]) -> BitbucketListerState: + last_repo_cdate = d.get("last_repo_cdate") + if last_repo_cdate is not None: + d["last_repo_cdate"] = iso8601.parse_date(last_repo_cdate) + return BitbucketListerState(**d) + + def state_to_dict(self, state: BitbucketListerState) -> Dict[str, Any]: + d = asdict(state) + last_repo_cdate = d.get("last_repo_cdate") + if last_repo_cdate is not None: + d["last_repo_cdate"] = last_repo_cdate.isoformat() + return d + + def set_session_credentials(self): + """Updates session authentication headers with given credentials + + Support only one credential of type basic auth""" + + if len(self.credentials) > 0: + cred = self.credentials[0] + if "username" in cred: + self.session.auth = (cred["username"], cred["password"]) + + def get_pages(self) -> Iterator[List[Dict[str, Any]]]: + + self.set_session_credentials() + + last_repo_cdate: Optional[str] = "1970-01-01" + if self.state is not None and self.state.last_repo_cdate is not None: + last_repo_cdate = self.state.last_repo_cdate.isoformat() + + while last_repo_cdate is not None: + self.url_params["after"] = last_repo_cdate + logger.debug("Fetching URL %s with params %s", self.url, self.url_params) + + response = self.session.get(self.url, params=self.url_params) + + # handle rate-limiting + if response.status_code == 429: + if self.request_count >= self.MAX_RETRIES: + logger.info( + "Max number of attempts hit (%s), giving up", + self.request_count, + ) + break + + logger.info("Rate limit was hit, sleeping %ss", self.backoff) + time.sleep(self.backoff) + + self.backoff *= self.BACKOFF_FACTOR + self.request_count += 1 + continue + + # log other HTTP errors + if response.status_code != 200: + logger.warning( + "Got unexpected status_code %s: %s", + response.status_code, + response.content, + ) + break + + # response is OK, reset rate-limiting state + self.request_count = 0 + self.backoff = self.BACKOFF_FACTOR + + body = response.json() + + next_page_url = body.get("next") + if next_page_url is not None: + next_page_url = parse.urlparse(next_page_url) + if not next_page_url.query: + logger.warning("Failed to parse url %s", next_page_url) + break + last_repo_cdate = parse.parse_qs(next_page_url.query)["after"][0] + else: + last_repo_cdate = None + + yield body["values"] + + def get_origins_from_page( + self, page: List[Dict[str, Any]] + ) -> Iterator[ListedOrigin]: + """Convert a page of Bitbucket repositories into a list of ListedOrigins. """ - body = response.json() - next_ = body.get("next") - if next_ is not None: - next_ = parse.urlparse(next_) - return iso8601.parse_date(parse.parse_qs(next_.query)["after"][0]) - return None - - def transport_response_simplified(self, response: Response) -> List[Dict[str, Any]]: - repos = response.json()["values"] - return [self.get_model_from_repo(repo) for repo in repos] - - def request_uri(self, identifier: datetime) -> str: # type: ignore - identifier_str = parse.quote(identifier.isoformat()) - return super().request_uri(identifier_str or "1970-01-01") - - def is_within_bounds( - self, inner: int, lower: Optional[int] = None, upper: Optional[int] = None - ) -> bool: - # values are expected to be datetimes - if lower is None and upper is None: - ret = True - elif lower is None: - ret = inner <= upper # type: ignore - elif upper is None: - ret = inner >= lower - else: - ret = lower <= inner <= upper - return ret + assert self.lister_obj.id is not None + + for repo in page: + last_update = iso8601.parse_date(repo["updated_on"]) + origin_url = repo["links"]["clone"][0]["href"] + origin_type = repo["scm"] + + yield ListedOrigin( + lister_id=self.lister_obj.id, + url=origin_url, + visit_type=origin_type, + last_update=last_update, + ) + + def commit_page(self, page: List[Dict[str, Any]]): + """Update the currently stored state using the latest listed page""" + + last_repo = page[-1] + last_repo_cdate = iso8601.parse_date(last_repo["created_on"]) + + if ( + self.state.last_repo_cdate is None + or last_repo_cdate > self.state.last_repo_cdate + ): + self.state.last_repo_cdate = last_repo_cdate + + def finalize(self): + + scheduler_state = self.get_state_from_scheduler() + + if self.state.last_repo_cdate is None: + return + + # Update the lister state in the backend only if the last seen id of + # the current run is higher than that stored in the database. + if ( + scheduler_state.last_repo_cdate is None + or self.state.last_repo_cdate > scheduler_state.last_repo_cdate + ): + self.updated = True diff --git a/swh/lister/bitbucket/tasks.py b/swh/lister/bitbucket/tasks.py --- a/swh/lister/bitbucket/tasks.py +++ b/swh/lister/bitbucket/tasks.py @@ -3,35 +3,40 @@ # See top-level LICENSE file for more information import random +from typing import Optional from celery import group, shared_task -from .lister import BitBucketLister +from .lister import BitbucketLister GROUP_SPLIT = 10000 @shared_task(name=__name__ + ".IncrementalBitBucketLister") -def list_bitbucket_incremental(**lister_args): - """Incremental update of the BitBucket forge""" - lister = BitBucketLister(**lister_args) - return lister.run(min_bound=lister.db_last_index(), max_bound=None) +def list_bitbucket_incremental( + per_page: int = 1000, username: Optional[str] = None, password: Optional[str] = None +): + """Incremental update of the Bitbucket forge""" + lister = BitbucketLister.from_configfile(per_page=per_page) + if username is not None and password is not None: + lister.credentials = [{"username": username, "password": password}] + return lister.run().dict() @shared_task(name=__name__ + ".RangeBitBucketLister") def _range_bitbucket_lister(start, end, **lister_args): - lister = BitBucketLister(**lister_args) + lister = BitbucketLister(**lister_args) return lister.run(min_bound=start, max_bound=end) @shared_task(name=__name__ + ".FullBitBucketRelister", bind=True) def list_bitbucket_full(self, split=None, **lister_args): - """Full update of the BitBucket forge + """Full update of the Bitbucket forge It's not to be called for an initial listing. """ - lister = BitBucketLister(**lister_args) + lister = BitbucketLister(**lister_args) ranges = lister.db_partition_indices(split or GROUP_SPLIT) if not ranges: self.log.info("Nothing to list") @@ -46,7 +51,6 @@ promise.save() # so that we can restore the GroupResult in tests except (NotImplementedError, AttributeError): self.log.info("Unable to call save_group with current result backend.") - # FIXME: what to do in terms of return here? return promise.id diff --git a/swh/lister/bitbucket/tests/data/bb_api_repositories_page1.json b/swh/lister/bitbucket/tests/data/bb_api_repositories_page1.json new file mode 100644 --- /dev/null +++ b/swh/lister/bitbucket/tests/data/bb_api_repositories_page1.json @@ -0,0 +1,795 @@ +{ + "pagelen": 10, + "values": [{ + "scm": "hg", + "website": "", + "has_wiki": true, + "name": "app-template", + "links": { + "watchers": { + "href": "https://api.bitbucket.org/2.0/repositories/bebac/app-template/watchers" + }, + "branches": { + "href": "https://api.bitbucket.org/2.0/repositories/bebac/app-template/refs/branches" + }, + "tags": { + "href": "https://api.bitbucket.org/2.0/repositories/bebac/app-template/refs/tags" + }, + "commits": { + "href": "https://api.bitbucket.org/2.0/repositories/bebac/app-template/commits" + }, + "clone": [{ + "href": "https://bitbucket.org/bebac/app-template", + "name": "https" + }, + { + "href": "ssh://hg@bitbucket.org/bebac/app-template", + "name": "ssh" + } + ], + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/bebac/app-template" + }, + "html": { + "href": "https://bitbucket.org/bebac/app-template" + }, + "avatar": { + "href": "https://bitbucket.org/bebac/app-template/avatar/32/" + }, + "hooks": { + "href": "https://api.bitbucket.org/2.0/repositories/bebac/app-template/hooks" + }, + "forks": { + "href": "https://api.bitbucket.org/2.0/repositories/bebac/app-template/forks" + }, + "downloads": { + "href": "https://api.bitbucket.org/2.0/repositories/bebac/app-template/downloads" + }, + "pullrequests": { + "href": "https://api.bitbucket.org/2.0/repositories/bebac/app-template/pullrequests" + } + }, + "fork_policy": "allow_forks", + "uuid": "{0cf80a6e-e91f-4a4c-a61b-8c8ff51ca3ec}", + "language": "c++", + "created_on": "2008-07-12T07:44:01.476818+00:00", + "full_name": "bebac/app-template", + "has_issues": true, + "owner": { + "username": "bebac", + "display_name": "Benny Bach", + "type": "user", + "uuid": "{d1a83a2a-be1b-4034-8c1d-386a6690cddb}", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/users/bebac" + }, + "html": { + "href": "https://bitbucket.org/bebac/" + }, + "avatar": { + "href": "https://bitbucket.org/account/bebac/avatar/32/" + } + } + }, + "updated_on": "2011-10-05T15:36:19.409008+00:00", + "size": 71548, + "type": "repository", + "slug": "app-template", + "is_private": false, + "description": "Basic files and directory structure for a C++ project. Intended as a starting point for a new project. Includes a basic cross platform core library." + }, + { + "scm": "git", + "website": "", + "has_wiki": true, + "name": "mercurialeclipse", + "links": { + "watchers": { + "href": "https://api.bitbucket.org/2.0/repositories/bastiand/mercurialeclipse/watchers" + }, + "branches": { + "href": "https://api.bitbucket.org/2.0/repositories/bastiand/mercurialeclipse/refs/branches" + }, + "tags": { + "href": "https://api.bitbucket.org/2.0/repositories/bastiand/mercurialeclipse/refs/tags" + }, + "commits": { + "href": "https://api.bitbucket.org/2.0/repositories/bastiand/mercurialeclipse/commits" + }, + "clone": [{ + "href": "https://bitbucket.org/bastiand/mercurialeclipse", + "name": "https" + }, + { + "href": "ssh://hg@bitbucket.org/bastiand/mercurialeclipse", + "name": "ssh" + } + ], + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/bastiand/mercurialeclipse" + }, + "html": { + "href": "https://bitbucket.org/bastiand/mercurialeclipse" + }, + "avatar": { + "href": "https://bitbucket.org/bastiand/mercurialeclipse/avatar/32/" + }, + "hooks": { + "href": "https://api.bitbucket.org/2.0/repositories/bastiand/mercurialeclipse/hooks" + }, + "forks": { + "href": "https://api.bitbucket.org/2.0/repositories/bastiand/mercurialeclipse/forks" + }, + "downloads": { + "href": "https://api.bitbucket.org/2.0/repositories/bastiand/mercurialeclipse/downloads" + }, + "pullrequests": { + "href": "https://api.bitbucket.org/2.0/repositories/bastiand/mercurialeclipse/pullrequests" + } + }, + "fork_policy": "allow_forks", + "uuid": "{f7a08670-bdd1-4465-aa97-7a5ce8d1a25b}", + "language": "", + "created_on": "2008-07-12T09:37:06.254721+00:00", + "full_name": "bastiand/mercurialeclipse", + "has_issues": false, + "owner": { + "username": "bastiand", + "display_name": "Bastian Doetsch", + "type": "user", + "uuid": "{3742cd48-adad-4205-ab0d-04fc992a1728}", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/users/bastiand" + }, + "html": { + "href": "https://bitbucket.org/bastiand/" + }, + "avatar": { + "href": "https://bitbucket.org/account/bastiand/avatar/32/" + } + } + }, + "updated_on": "2011-09-17T02:36:59.062596+00:00", + "size": 6445145, + "type": "repository", + "slug": "mercurialeclipse", + "is_private": false, + "description": "my own repo for MercurialEclipse." + }, + { + "scm": "hg", + "website": "", + "has_wiki": true, + "name": "sandboxpublic", + "links": { + "watchers": { + "href": "https://api.bitbucket.org/2.0/repositories/aleax/sandboxpublic/watchers" + }, + "branches": { + "href": "https://api.bitbucket.org/2.0/repositories/aleax/sandboxpublic/refs/branches" + }, + "tags": { + "href": "https://api.bitbucket.org/2.0/repositories/aleax/sandboxpublic/refs/tags" + }, + "commits": { + "href": "https://api.bitbucket.org/2.0/repositories/aleax/sandboxpublic/commits" + }, + "clone": [{ + "href": "https://bitbucket.org/aleax/sandboxpublic", + "name": "https" + }, + { + "href": "ssh://hg@bitbucket.org/aleax/sandboxpublic", + "name": "ssh" + } + ], + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/aleax/sandboxpublic" + }, + "html": { + "href": "https://bitbucket.org/aleax/sandboxpublic" + }, + "avatar": { + "href": "https://bitbucket.org/aleax/sandboxpublic/avatar/32/" + }, + "hooks": { + "href": "https://api.bitbucket.org/2.0/repositories/aleax/sandboxpublic/hooks" + }, + "forks": { + "href": "https://api.bitbucket.org/2.0/repositories/aleax/sandboxpublic/forks" + }, + "downloads": { + "href": "https://api.bitbucket.org/2.0/repositories/aleax/sandboxpublic/downloads" + }, + "pullrequests": { + "href": "https://api.bitbucket.org/2.0/repositories/aleax/sandboxpublic/pullrequests" + } + }, + "fork_policy": "allow_forks", + "uuid": "{452c716c-a1ce-42bc-a95b-d38da49cbb37}", + "language": "", + "created_on": "2008-07-14T01:59:23.568048+00:00", + "full_name": "aleax/sandboxpublic", + "has_issues": true, + "owner": { + "username": "aleax", + "display_name": "Alex Martelli", + "type": "user", + "uuid": "{1155d94d-fb48-43fe-a431-ec07c900b636}", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/users/aleax" + }, + "html": { + "href": "https://bitbucket.org/aleax/" + }, + "avatar": { + "href": "https://bitbucket.org/account/aleax/avatar/32/" + } + } + }, + "updated_on": "2012-06-22T21:55:28.753727+00:00", + "size": 3120, + "type": "repository", + "slug": "sandboxpublic", + "is_private": false, + "description": "to help debug ACLs for private vs public bitbucket repos" + }, + { + "scm": "hg", + "website": "", + "has_wiki": true, + "name": "otrsfix-ng", + "links": { + "watchers": { + "href": "https://api.bitbucket.org/2.0/repositories/adiakin/otrsfix-ng/watchers" + }, + "branches": { + "href": "https://api.bitbucket.org/2.0/repositories/adiakin/otrsfix-ng/refs/branches" + }, + "tags": { + "href": "https://api.bitbucket.org/2.0/repositories/adiakin/otrsfix-ng/refs/tags" + }, + "commits": { + "href": "https://api.bitbucket.org/2.0/repositories/adiakin/otrsfix-ng/commits" + }, + "clone": [{ + "href": "https://bitbucket.org/adiakin/otrsfix-ng", + "name": "https" + }, + { + "href": "ssh://hg@bitbucket.org/adiakin/otrsfix-ng", + "name": "ssh" + } + ], + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/adiakin/otrsfix-ng" + }, + "html": { + "href": "https://bitbucket.org/adiakin/otrsfix-ng" + }, + "avatar": { + "href": "https://bitbucket.org/adiakin/otrsfix-ng/avatar/32/" + }, + "hooks": { + "href": "https://api.bitbucket.org/2.0/repositories/adiakin/otrsfix-ng/hooks" + }, + "forks": { + "href": "https://api.bitbucket.org/2.0/repositories/adiakin/otrsfix-ng/forks" + }, + "downloads": { + "href": "https://api.bitbucket.org/2.0/repositories/adiakin/otrsfix-ng/downloads" + }, + "pullrequests": { + "href": "https://api.bitbucket.org/2.0/repositories/adiakin/otrsfix-ng/pullrequests" + } + }, + "fork_policy": "allow_forks", + "uuid": "{05b1b9dc-a7b6-46d6-ae1b-e66a17aa4f55}", + "language": "", + "created_on": "2008-07-15T06:14:39.306314+00:00", + "full_name": "adiakin/otrsfix-ng", + "has_issues": true, + "owner": { + "username": "adiakin", + "display_name": "adiakin", + "type": "user", + "uuid": "{414012b5-1ac9-4096-9f46-8893cfa3cda5}", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/users/adiakin" + }, + "html": { + "href": "https://bitbucket.org/adiakin/" + }, + "avatar": { + "href": "https://bitbucket.org/account/adiakin/avatar/32/" + } + } + }, + "updated_on": "2016-06-02T18:56:34.868302+00:00", + "size": 211631, + "type": "repository", + "slug": "otrsfix-ng", + "is_private": false, + "description": "OTRS greasemonkey extension" + }, + { + "scm": "hg", + "website": "", + "has_wiki": true, + "name": "pida-pypaned", + "links": { + "watchers": { + "href": "https://api.bitbucket.org/2.0/repositories/aafshar/pida-pypaned/watchers" + }, + "branches": { + "href": "https://api.bitbucket.org/2.0/repositories/aafshar/pida-pypaned/refs/branches" + }, + "tags": { + "href": "https://api.bitbucket.org/2.0/repositories/aafshar/pida-pypaned/refs/tags" + }, + "commits": { + "href": "https://api.bitbucket.org/2.0/repositories/aafshar/pida-pypaned/commits" + }, + "clone": [{ + "href": "https://bitbucket.org/aafshar/pida-pypaned", + "name": "https" + }, + { + "href": "ssh://hg@bitbucket.org/aafshar/pida-pypaned", + "name": "ssh" + } + ], + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/aafshar/pida-pypaned" + }, + "html": { + "href": "https://bitbucket.org/aafshar/pida-pypaned" + }, + "avatar": { + "href": "https://bitbucket.org/aafshar/pida-pypaned/avatar/32/" + }, + "hooks": { + "href": "https://api.bitbucket.org/2.0/repositories/aafshar/pida-pypaned/hooks" + }, + "forks": { + "href": "https://api.bitbucket.org/2.0/repositories/aafshar/pida-pypaned/forks" + }, + "downloads": { + "href": "https://api.bitbucket.org/2.0/repositories/aafshar/pida-pypaned/downloads" + }, + "pullrequests": { + "href": "https://api.bitbucket.org/2.0/repositories/aafshar/pida-pypaned/pullrequests" + } + }, + "fork_policy": "allow_forks", + "uuid": "{94cb830a-1784-4e51-9791-8f5cc93990a9}", + "language": "", + "created_on": "2008-07-16T22:47:38.682491+00:00", + "full_name": "aafshar/pida-pypaned", + "has_issues": true, + "owner": { + "username": "aafshar", + "display_name": "Ali Afshar", + "type": "user", + "uuid": "{bcb87110-6a92-41fc-b95c-680feeea5512}", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/users/aafshar" + }, + "html": { + "href": "https://bitbucket.org/aafshar/" + }, + "avatar": { + "href": "https://bitbucket.org/account/aafshar/avatar/32/" + } + } + }, + "updated_on": "2012-06-22T21:55:42.451431+00:00", + "size": 4680652, + "type": "repository", + "slug": "pida-pypaned", + "is_private": false, + "description": "" + }, + { + "scm": "hg", + "website": "", + "has_wiki": true, + "name": "TLOMM-testing", + "links": { + "watchers": { + "href": "https://api.bitbucket.org/2.0/repositories/tgrimley/tlomm-testing/watchers" + }, + "branches": { + "href": "https://api.bitbucket.org/2.0/repositories/tgrimley/tlomm-testing/refs/branches" + }, + "tags": { + "href": "https://api.bitbucket.org/2.0/repositories/tgrimley/tlomm-testing/refs/tags" + }, + "commits": { + "href": "https://api.bitbucket.org/2.0/repositories/tgrimley/tlomm-testing/commits" + }, + "clone": [{ + "href": "https://bitbucket.org/tgrimley/tlomm-testing", + "name": "https" + }, + { + "href": "ssh://hg@bitbucket.org/tgrimley/tlomm-testing", + "name": "ssh" + } + ], + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/tgrimley/tlomm-testing" + }, + "html": { + "href": "https://bitbucket.org/tgrimley/tlomm-testing" + }, + "avatar": { + "href": "https://bitbucket.org/tgrimley/tlomm-testing/avatar/32/" + }, + "hooks": { + "href": "https://api.bitbucket.org/2.0/repositories/tgrimley/tlomm-testing/hooks" + }, + "forks": { + "href": "https://api.bitbucket.org/2.0/repositories/tgrimley/tlomm-testing/forks" + }, + "downloads": { + "href": "https://api.bitbucket.org/2.0/repositories/tgrimley/tlomm-testing/downloads" + }, + "pullrequests": { + "href": "https://api.bitbucket.org/2.0/repositories/tgrimley/tlomm-testing/pullrequests" + } + }, + "fork_policy": "allow_forks", + "uuid": "{95283ca1-f77e-40d6-b3ed-5bfae6ed2d15}", + "language": "", + "created_on": "2008-07-18T21:05:17.750587+00:00", + "full_name": "tgrimley/tlomm-testing", + "has_issues": true, + "owner": { + "username": "tgrimley", + "display_name": "Thomas Grimley", + "type": "user", + "uuid": "{c958a08f-4669-4c77-81ec-4e2faa8ebf35}", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/users/tgrimley" + }, + "html": { + "href": "https://bitbucket.org/tgrimley/" + }, + "avatar": { + "href": "https://bitbucket.org/account/tgrimley/avatar/32/" + } + } + }, + "updated_on": "2012-06-22T21:55:46.627825+00:00", + "size": 3128, + "type": "repository", + "slug": "tlomm-testing", + "is_private": false, + "description": "File related to testing functionality of TLOMM->TLOTTS transition" + }, + { + "scm": "hg", + "website": "", + "has_wiki": true, + "name": "test", + "links": { + "watchers": { + "href": "https://api.bitbucket.org/2.0/repositories/tingle/test/watchers" + }, + "branches": { + "href": "https://api.bitbucket.org/2.0/repositories/tingle/test/refs/branches" + }, + "tags": { + "href": "https://api.bitbucket.org/2.0/repositories/tingle/test/refs/tags" + }, + "commits": { + "href": "https://api.bitbucket.org/2.0/repositories/tingle/test/commits" + }, + "clone": [{ + "href": "https://bitbucket.org/tingle/test", + "name": "https" + }, + { + "href": "ssh://hg@bitbucket.org/tingle/test", + "name": "ssh" + } + ], + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/tingle/test" + }, + "html": { + "href": "https://bitbucket.org/tingle/test" + }, + "avatar": { + "href": "https://bitbucket.org/tingle/test/avatar/32/" + }, + "hooks": { + "href": "https://api.bitbucket.org/2.0/repositories/tingle/test/hooks" + }, + "forks": { + "href": "https://api.bitbucket.org/2.0/repositories/tingle/test/forks" + }, + "downloads": { + "href": "https://api.bitbucket.org/2.0/repositories/tingle/test/downloads" + }, + "pullrequests": { + "href": "https://api.bitbucket.org/2.0/repositories/tingle/test/pullrequests" + } + }, + "fork_policy": "allow_forks", + "uuid": "{457953ec-fe87-41b9-b659-94756fb40ece}", + "language": "", + "created_on": "2008-07-18T22:24:31.984981+00:00", + "full_name": "tingle/test", + "has_issues": true, + "owner": { + "username": "tingle", + "display_name": "tingle", + "type": "user", + "uuid": "{dddce42b-bd19-417b-90ff-72cdbfb6eb7d}", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/users/tingle" + }, + "html": { + "href": "https://bitbucket.org/tingle/" + }, + "avatar": { + "href": "https://bitbucket.org/account/tingle/avatar/32/" + } + } + }, + "updated_on": "2012-06-22T21:55:49.860564+00:00", + "size": 3090, + "type": "repository", + "slug": "test", + "is_private": false, + "description": "" + }, + { + "scm": "hg", + "website": "http://shaze.myopenid.com/", + "has_wiki": true, + "name": "Repository", + "links": { + "watchers": { + "href": "https://api.bitbucket.org/2.0/repositories/Shaze/repository/watchers" + }, + "branches": { + "href": "https://api.bitbucket.org/2.0/repositories/Shaze/repository/refs/branches" + }, + "tags": { + "href": "https://api.bitbucket.org/2.0/repositories/Shaze/repository/refs/tags" + }, + "commits": { + "href": "https://api.bitbucket.org/2.0/repositories/Shaze/repository/commits" + }, + "clone": [{ + "href": "https://bitbucket.org/Shaze/repository", + "name": "https" + }, + { + "href": "ssh://hg@bitbucket.org/Shaze/repository", + "name": "ssh" + } + ], + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/Shaze/repository" + }, + "html": { + "href": "https://bitbucket.org/Shaze/repository" + }, + "avatar": { + "href": "https://bitbucket.org/Shaze/repository/avatar/32/" + }, + "hooks": { + "href": "https://api.bitbucket.org/2.0/repositories/Shaze/repository/hooks" + }, + "forks": { + "href": "https://api.bitbucket.org/2.0/repositories/Shaze/repository/forks" + }, + "downloads": { + "href": "https://api.bitbucket.org/2.0/repositories/Shaze/repository/downloads" + }, + "pullrequests": { + "href": "https://api.bitbucket.org/2.0/repositories/Shaze/repository/pullrequests" + } + }, + "fork_policy": "allow_forks", + "uuid": "{3c0b8076-caef-465a-8d08-a184459f659b}", + "language": "", + "created_on": "2008-07-18T22:39:51.380134+00:00", + "full_name": "Shaze/repository", + "has_issues": true, + "owner": { + "username": "Shaze", + "display_name": "Shaze", + "type": "user", + "uuid": "{f57817e9-bfe4-4c65-84dd-662152430323}", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/users/Shaze" + }, + "html": { + "href": "https://bitbucket.org/Shaze/" + }, + "avatar": { + "href": "https://bitbucket.org/account/Shaze/avatar/32/" + } + } + }, + "updated_on": "2012-06-22T21:55:51.570502+00:00", + "size": 3052, + "type": "repository", + "slug": "repository", + "is_private": false, + "description": "Mine, all mine!" + }, + { + "scm": "hg", + "website": "http://bitbucket.org/copiesofcopies/identifox/", + "has_wiki": true, + "name": "identifox", + "links": { + "watchers": { + "href": "https://api.bitbucket.org/2.0/repositories/uncryptic/identifox/watchers" + }, + "branches": { + "href": "https://api.bitbucket.org/2.0/repositories/uncryptic/identifox/refs/branches" + }, + "tags": { + "href": "https://api.bitbucket.org/2.0/repositories/uncryptic/identifox/refs/tags" + }, + "commits": { + "href": "https://api.bitbucket.org/2.0/repositories/uncryptic/identifox/commits" + }, + "clone": [{ + "href": "https://bitbucket.org/uncryptic/identifox", + "name": "https" + }, + { + "href": "ssh://hg@bitbucket.org/uncryptic/identifox", + "name": "ssh" + } + ], + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/uncryptic/identifox" + }, + "html": { + "href": "https://bitbucket.org/uncryptic/identifox" + }, + "avatar": { + "href": "https://bitbucket.org/uncryptic/identifox/avatar/32/" + }, + "hooks": { + "href": "https://api.bitbucket.org/2.0/repositories/uncryptic/identifox/hooks" + }, + "forks": { + "href": "https://api.bitbucket.org/2.0/repositories/uncryptic/identifox/forks" + }, + "downloads": { + "href": "https://api.bitbucket.org/2.0/repositories/uncryptic/identifox/downloads" + }, + "pullrequests": { + "href": "https://api.bitbucket.org/2.0/repositories/uncryptic/identifox/pullrequests" + } + }, + "fork_policy": "allow_forks", + "uuid": "{78a1a080-a77e-4d0d-823a-b107484477a8}", + "language": "", + "created_on": "2008-07-19T00:33:14.065946+00:00", + "full_name": "uncryptic/identifox", + "has_issues": true, + "owner": { + "username": "uncryptic", + "display_name": "Uncryptic Communications", + "type": "user", + "uuid": "{db87bb9a-9980-4840-bd4a-61f7748a56b4}", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/users/uncryptic" + }, + "html": { + "href": "https://bitbucket.org/uncryptic/" + }, + "avatar": { + "href": "https://bitbucket.org/account/uncryptic/avatar/32/" + } + } + }, + "updated_on": "2008-07-19T00:33:14+00:00", + "size": 1918, + "type": "repository", + "slug": "identifox", + "is_private": false, + "description": "TwitterFox, modified to work with Identi.ca, including cosmetic and subtle code changes. For the most part, the code is nearly identical to the TwitterFox base: http://www.naan.net/trac/wiki/TwitterFox" + }, + { + "scm": "hg", + "website": "http://rforce.rubyforge.org", + "has_wiki": false, + "name": "rforce", + "links": { + "watchers": { + "href": "https://api.bitbucket.org/2.0/repositories/undees/rforce/watchers" + }, + "branches": { + "href": "https://api.bitbucket.org/2.0/repositories/undees/rforce/refs/branches" + }, + "tags": { + "href": "https://api.bitbucket.org/2.0/repositories/undees/rforce/refs/tags" + }, + "commits": { + "href": "https://api.bitbucket.org/2.0/repositories/undees/rforce/commits" + }, + "clone": [{ + "href": "https://bitbucket.org/undees/rforce", + "name": "https" + }, + { + "href": "ssh://hg@bitbucket.org/undees/rforce", + "name": "ssh" + } + ], + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/undees/rforce" + }, + "html": { + "href": "https://bitbucket.org/undees/rforce" + }, + "avatar": { + "href": "https://bitbucket.org/undees/rforce/avatar/32/" + }, + "hooks": { + "href": "https://api.bitbucket.org/2.0/repositories/undees/rforce/hooks" + }, + "forks": { + "href": "https://api.bitbucket.org/2.0/repositories/undees/rforce/forks" + }, + "downloads": { + "href": "https://api.bitbucket.org/2.0/repositories/undees/rforce/downloads" + }, + "pullrequests": { + "href": "https://api.bitbucket.org/2.0/repositories/undees/rforce/pullrequests" + } + }, + "fork_policy": "allow_forks", + "uuid": "{ec2ffee7-bfcd-4e95-83c8-22ac31e69fa3}", + "language": "", + "created_on": "2008-07-19T06:16:43.044743+00:00", + "full_name": "undees/rforce", + "has_issues": false, + "owner": { + "username": "undees", + "display_name": "Ian Dees", + "type": "user", + "uuid": "{6ff66a34-6412-4f28-bf57-707a2a5c6d7b}", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/users/undees" + }, + "html": { + "href": "https://bitbucket.org/undees/" + }, + "avatar": { + "href": "https://bitbucket.org/account/undees/avatar/32/" + } + } + }, + "updated_on": "2015-02-09T00:48:15.408680+00:00", + "size": 338402, + "type": "repository", + "slug": "rforce", + "is_private": false, + "description": "A simple, usable binding to the SalesForce API." + } + ], + "next": "https://api.bitbucket.org/2.0/repositories?after=2008-07-19T19%3A53%3A07.031845%2B00%3A00" +} \ No newline at end of file diff --git a/swh/lister/bitbucket/tests/data/bb_api_repositories_page2.json b/swh/lister/bitbucket/tests/data/bb_api_repositories_page2.json new file mode 100644 --- /dev/null +++ b/swh/lister/bitbucket/tests/data/bb_api_repositories_page2.json @@ -0,0 +1,1216 @@ +{ + "pagelen": 10, + "values": [{ + "scm": "git", + "website": "", + "has_wiki": false, + "uuid": "{3f630668-75f1-4903-ae5e-8ea37437e09e}", + "links": { + "watchers": { + "href": "https://api.bitbucket.org/2.0/repositories/opensymphony/xwork/watchers" + }, + "branches": { + "href": "https://api.bitbucket.org/2.0/repositories/opensymphony/xwork/refs/branches" + }, + "tags": { + "href": "https://api.bitbucket.org/2.0/repositories/opensymphony/xwork/refs/tags" + }, + "commits": { + "href": "https://api.bitbucket.org/2.0/repositories/opensymphony/xwork/commits" + }, + "clone": [{ + "href": "https://bitbucket.org/opensymphony/xwork.git", + "name": "https" + }, + { + "href": "git@bitbucket.org:opensymphony/xwork.git", + "name": "ssh" + } + ], + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/opensymphony/xwork" + }, + "source": { + "href": "https://api.bitbucket.org/2.0/repositories/opensymphony/xwork/src" + }, + "html": { + "href": "https://bitbucket.org/opensymphony/xwork" + }, + "avatar": { + "href": "https://bytebucket.org/ravatar/%7B3f630668-75f1-4903-ae5e-8ea37437e09e%7D?ts=java" + }, + "hooks": { + "href": "https://api.bitbucket.org/2.0/repositories/opensymphony/xwork/hooks" + }, + "forks": { + "href": "https://api.bitbucket.org/2.0/repositories/opensymphony/xwork/forks" + }, + "downloads": { + "href": "https://api.bitbucket.org/2.0/repositories/opensymphony/xwork/downloads" + }, + "pullrequests": { + "href": "https://api.bitbucket.org/2.0/repositories/opensymphony/xwork/pullrequests" + } + }, + "fork_policy": "allow_forks", + "full_name": "opensymphony/xwork", + "name": "xwork", + "project": { + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/workspaces/opensymphony/projects/PROJ" + }, + "html": { + "href": "https://bitbucket.org/opensymphony/workspace/projects/PROJ" + }, + "avatar": { + "href": "https://bitbucket.org/account/user/opensymphony/projects/PROJ/avatar/32?ts=1543460518" + } + }, + "type": "project", + "name": "Untitled project", + "key": "PROJ", + "uuid": "{57fac509-0df2-47ce-ad8e-27be013523fa}" + }, + "language": "java", + "created_on": "2011-06-06T03:40:09.505792+00:00", + "mainbranch": { + "type": "branch", + "name": "master" + }, + "workspace": { + "slug": "opensymphony", + "type": "workspace", + "name": "opensymphony", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/workspaces/opensymphony" + }, + "html": { + "href": "https://bitbucket.org/opensymphony/" + }, + "avatar": { + "href": "https://bitbucket.org/workspaces/opensymphony/avatar/?ts=1543460518" + } + }, + "uuid": "{cedfd0d1-899f-49de-acf7-a2fa8e924b6f}" + }, + "has_issues": false, + "owner": { + "display_name": "opensymphony", + "uuid": "{cedfd0d1-899f-49de-acf7-a2fa8e924b6f}", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/users/%7Bcedfd0d1-899f-49de-acf7-a2fa8e924b6f%7D" + }, + "html": { + "href": "https://bitbucket.org/%7Bcedfd0d1-899f-49de-acf7-a2fa8e924b6f%7D/" + }, + "avatar": { + "href": "https://bitbucket.org/account/opensymphony/avatar/" + } + }, + "nickname": "opensymphony", + "type": "user", + "account_id": null + }, + "updated_on": "2014-11-16T23:19:16.674082+00:00", + "size": 22877949, + "type": "repository", + "slug": "xwork", + "is_private": false, + "description": "" + }, + { + "scm": "git", + "website": "", + "has_wiki": false, + "uuid": "{ee2b6927-24b9-4b8d-a22a-e68d24e49dec}", + "links": { + "watchers": { + "href": "https://api.bitbucket.org/2.0/repositories/opensymphony/webwork/watchers" + }, + "branches": { + "href": "https://api.bitbucket.org/2.0/repositories/opensymphony/webwork/refs/branches" + }, + "tags": { + "href": "https://api.bitbucket.org/2.0/repositories/opensymphony/webwork/refs/tags" + }, + "commits": { + "href": "https://api.bitbucket.org/2.0/repositories/opensymphony/webwork/commits" + }, + "clone": [{ + "href": "https://bitbucket.org/opensymphony/webwork.git", + "name": "https" + }, + { + "href": "git@bitbucket.org:opensymphony/webwork.git", + "name": "ssh" + } + ], + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/opensymphony/webwork" + }, + "source": { + "href": "https://api.bitbucket.org/2.0/repositories/opensymphony/webwork/src" + }, + "html": { + "href": "https://bitbucket.org/opensymphony/webwork" + }, + "avatar": { + "href": "https://bytebucket.org/ravatar/%7Bee2b6927-24b9-4b8d-a22a-e68d24e49dec%7D?ts=java" + }, + "hooks": { + "href": "https://api.bitbucket.org/2.0/repositories/opensymphony/webwork/hooks" + }, + "forks": { + "href": "https://api.bitbucket.org/2.0/repositories/opensymphony/webwork/forks" + }, + "downloads": { + "href": "https://api.bitbucket.org/2.0/repositories/opensymphony/webwork/downloads" + }, + "pullrequests": { + "href": "https://api.bitbucket.org/2.0/repositories/opensymphony/webwork/pullrequests" + } + }, + "fork_policy": "allow_forks", + "full_name": "opensymphony/webwork", + "name": "webwork", + "project": { + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/workspaces/opensymphony/projects/PROJ" + }, + "html": { + "href": "https://bitbucket.org/opensymphony/workspace/projects/PROJ" + }, + "avatar": { + "href": "https://bitbucket.org/account/user/opensymphony/projects/PROJ/avatar/32?ts=1543460518" + } + }, + "type": "project", + "name": "Untitled project", + "key": "PROJ", + "uuid": "{57fac509-0df2-47ce-ad8e-27be013523fa}" + }, + "language": "java", + "created_on": "2011-06-07T02:25:57.515877+00:00", + "mainbranch": { + "type": "branch", + "name": "master" + }, + "workspace": { + "slug": "opensymphony", + "type": "workspace", + "name": "opensymphony", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/workspaces/opensymphony" + }, + "html": { + "href": "https://bitbucket.org/opensymphony/" + }, + "avatar": { + "href": "https://bitbucket.org/workspaces/opensymphony/avatar/?ts=1543460518" + } + }, + "uuid": "{cedfd0d1-899f-49de-acf7-a2fa8e924b6f}" + }, + "has_issues": false, + "owner": { + "display_name": "opensymphony", + "uuid": "{cedfd0d1-899f-49de-acf7-a2fa8e924b6f}", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/users/%7Bcedfd0d1-899f-49de-acf7-a2fa8e924b6f%7D" + }, + "html": { + "href": "https://bitbucket.org/%7Bcedfd0d1-899f-49de-acf7-a2fa8e924b6f%7D/" + }, + "avatar": { + "href": "https://bitbucket.org/account/opensymphony/avatar/" + } + }, + "nickname": "opensymphony", + "type": "user", + "account_id": null + }, + "updated_on": "2013-08-16T05:17:12.385393+00:00", + "size": 106935051, + "type": "repository", + "slug": "webwork", + "is_private": false, + "description": "" + }, + { + "scm": "git", + "website": "", + "has_wiki": false, + "uuid": "{1d497238-48c8-4016-8c0d-15c8a12fa9ed}", + "links": { + "watchers": { + "href": "https://api.bitbucket.org/2.0/repositories/opensymphony/propertyset/watchers" + }, + "branches": { + "href": "https://api.bitbucket.org/2.0/repositories/opensymphony/propertyset/refs/branches" + }, + "tags": { + "href": "https://api.bitbucket.org/2.0/repositories/opensymphony/propertyset/refs/tags" + }, + "commits": { + "href": "https://api.bitbucket.org/2.0/repositories/opensymphony/propertyset/commits" + }, + "clone": [{ + "href": "https://bitbucket.org/opensymphony/propertyset.git", + "name": "https" + }, + { + "href": "git@bitbucket.org:opensymphony/propertyset.git", + "name": "ssh" + } + ], + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/opensymphony/propertyset" + }, + "source": { + "href": "https://api.bitbucket.org/2.0/repositories/opensymphony/propertyset/src" + }, + "html": { + "href": "https://bitbucket.org/opensymphony/propertyset" + }, + "avatar": { + "href": "https://bytebucket.org/ravatar/%7B1d497238-48c8-4016-8c0d-15c8a12fa9ed%7D?ts=java" + }, + "hooks": { + "href": "https://api.bitbucket.org/2.0/repositories/opensymphony/propertyset/hooks" + }, + "forks": { + "href": "https://api.bitbucket.org/2.0/repositories/opensymphony/propertyset/forks" + }, + "downloads": { + "href": "https://api.bitbucket.org/2.0/repositories/opensymphony/propertyset/downloads" + }, + "pullrequests": { + "href": "https://api.bitbucket.org/2.0/repositories/opensymphony/propertyset/pullrequests" + } + }, + "fork_policy": "allow_forks", + "full_name": "opensymphony/propertyset", + "name": "propertyset", + "project": { + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/workspaces/opensymphony/projects/PROJ" + }, + "html": { + "href": "https://bitbucket.org/opensymphony/workspace/projects/PROJ" + }, + "avatar": { + "href": "https://bitbucket.org/account/user/opensymphony/projects/PROJ/avatar/32?ts=1543460518" + } + }, + "type": "project", + "name": "Untitled project", + "key": "PROJ", + "uuid": "{57fac509-0df2-47ce-ad8e-27be013523fa}" + }, + "language": "java", + "created_on": "2011-06-07T04:13:28.097554+00:00", + "mainbranch": { + "type": "branch", + "name": "master" + }, + "workspace": { + "slug": "opensymphony", + "type": "workspace", + "name": "opensymphony", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/workspaces/opensymphony" + }, + "html": { + "href": "https://bitbucket.org/opensymphony/" + }, + "avatar": { + "href": "https://bitbucket.org/workspaces/opensymphony/avatar/?ts=1543460518" + } + }, + "uuid": "{cedfd0d1-899f-49de-acf7-a2fa8e924b6f}" + }, + "has_issues": false, + "owner": { + "display_name": "opensymphony", + "uuid": "{cedfd0d1-899f-49de-acf7-a2fa8e924b6f}", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/users/%7Bcedfd0d1-899f-49de-acf7-a2fa8e924b6f%7D" + }, + "html": { + "href": "https://bitbucket.org/%7Bcedfd0d1-899f-49de-acf7-a2fa8e924b6f%7D/" + }, + "avatar": { + "href": "https://bitbucket.org/account/opensymphony/avatar/" + } + }, + "nickname": "opensymphony", + "type": "user", + "account_id": null + }, + "updated_on": "2017-02-05T20:25:16.398281+00:00", + "size": 25747672, + "type": "repository", + "slug": "propertyset", + "is_private": false, + "description": "" + }, + { + "scm": "git", + "website": "", + "has_wiki": false, + "uuid": "{5aa7a0c5-73ae-4d2c-9c09-276866c16059}", + "links": { + "watchers": { + "href": "https://api.bitbucket.org/2.0/repositories/opensymphony/quartz/watchers" + }, + "branches": { + "href": "https://api.bitbucket.org/2.0/repositories/opensymphony/quartz/refs/branches" + }, + "tags": { + "href": "https://api.bitbucket.org/2.0/repositories/opensymphony/quartz/refs/tags" + }, + "commits": { + "href": "https://api.bitbucket.org/2.0/repositories/opensymphony/quartz/commits" + }, + "clone": [{ + "href": "https://bitbucket.org/opensymphony/quartz.git", + "name": "https" + }, + { + "href": "git@bitbucket.org:opensymphony/quartz.git", + "name": "ssh" + } + ], + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/opensymphony/quartz" + }, + "source": { + "href": "https://api.bitbucket.org/2.0/repositories/opensymphony/quartz/src" + }, + "html": { + "href": "https://bitbucket.org/opensymphony/quartz" + }, + "avatar": { + "href": "https://bytebucket.org/ravatar/%7B5aa7a0c5-73ae-4d2c-9c09-276866c16059%7D?ts=java" + }, + "hooks": { + "href": "https://api.bitbucket.org/2.0/repositories/opensymphony/quartz/hooks" + }, + "forks": { + "href": "https://api.bitbucket.org/2.0/repositories/opensymphony/quartz/forks" + }, + "downloads": { + "href": "https://api.bitbucket.org/2.0/repositories/opensymphony/quartz/downloads" + }, + "pullrequests": { + "href": "https://api.bitbucket.org/2.0/repositories/opensymphony/quartz/pullrequests" + } + }, + "fork_policy": "allow_forks", + "full_name": "opensymphony/quartz", + "name": "quartz", + "project": { + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/workspaces/opensymphony/projects/PROJ" + }, + "html": { + "href": "https://bitbucket.org/opensymphony/workspace/projects/PROJ" + }, + "avatar": { + "href": "https://bitbucket.org/account/user/opensymphony/projects/PROJ/avatar/32?ts=1543460518" + } + }, + "type": "project", + "name": "Untitled project", + "key": "PROJ", + "uuid": "{57fac509-0df2-47ce-ad8e-27be013523fa}" + }, + "language": "java", + "created_on": "2011-06-07T04:15:47.909191+00:00", + "mainbranch": { + "type": "branch", + "name": "master" + }, + "workspace": { + "slug": "opensymphony", + "type": "workspace", + "name": "opensymphony", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/workspaces/opensymphony" + }, + "html": { + "href": "https://bitbucket.org/opensymphony/" + }, + "avatar": { + "href": "https://bitbucket.org/workspaces/opensymphony/avatar/?ts=1543460518" + } + }, + "uuid": "{cedfd0d1-899f-49de-acf7-a2fa8e924b6f}" + }, + "has_issues": false, + "owner": { + "display_name": "opensymphony", + "uuid": "{cedfd0d1-899f-49de-acf7-a2fa8e924b6f}", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/users/%7Bcedfd0d1-899f-49de-acf7-a2fa8e924b6f%7D" + }, + "html": { + "href": "https://bitbucket.org/%7Bcedfd0d1-899f-49de-acf7-a2fa8e924b6f%7D/" + }, + "avatar": { + "href": "https://bitbucket.org/account/opensymphony/avatar/" + } + }, + "nickname": "opensymphony", + "type": "user", + "account_id": null + }, + "updated_on": "2012-07-06T23:05:13.437602+00:00", + "size": 12845056, + "type": "repository", + "slug": "quartz", + "is_private": false, + "description": "" + }, + { + "scm": "git", + "website": "", + "has_wiki": false, + "uuid": "{7e35bde1-67e7-4d8f-ae29-05dd10424072}", + "links": { + "watchers": { + "href": "https://api.bitbucket.org/2.0/repositories/jwalton/opup/watchers" + }, + "branches": { + "href": "https://api.bitbucket.org/2.0/repositories/jwalton/opup/refs/branches" + }, + "tags": { + "href": "https://api.bitbucket.org/2.0/repositories/jwalton/opup/refs/tags" + }, + "commits": { + "href": "https://api.bitbucket.org/2.0/repositories/jwalton/opup/commits" + }, + "clone": [{ + "href": "https://bitbucket.org/jwalton/opup.git", + "name": "https" + }, + { + "href": "git@bitbucket.org:jwalton/opup.git", + "name": "ssh" + } + ], + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/jwalton/opup" + }, + "source": { + "href": "https://api.bitbucket.org/2.0/repositories/jwalton/opup/src" + }, + "html": { + "href": "https://bitbucket.org/jwalton/opup" + }, + "avatar": { + "href": "https://bytebucket.org/ravatar/%7B7e35bde1-67e7-4d8f-ae29-05dd10424072%7D?ts=java" + }, + "hooks": { + "href": "https://api.bitbucket.org/2.0/repositories/jwalton/opup/hooks" + }, + "forks": { + "href": "https://api.bitbucket.org/2.0/repositories/jwalton/opup/forks" + }, + "downloads": { + "href": "https://api.bitbucket.org/2.0/repositories/jwalton/opup/downloads" + }, + "pullrequests": { + "href": "https://api.bitbucket.org/2.0/repositories/jwalton/opup/pullrequests" + } + }, + "fork_policy": "allow_forks", + "full_name": "jwalton/opup", + "name": "opup", + "project": { + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/workspaces/jwalton/projects/PROJ" + }, + "html": { + "href": "https://bitbucket.org/jwalton/workspace/projects/PROJ" + }, + "avatar": { + "href": "https://bitbucket.org/account/user/jwalton/projects/PROJ/avatar/32?ts=1543459298" + } + }, + "type": "project", + "name": "Untitled project", + "key": "PROJ", + "uuid": "{6eed76e0-e831-48d0-83ac-cbbd8ee04173}" + }, + "language": "java", + "created_on": "2011-06-16T09:16:27.957216+00:00", + "mainbranch": { + "type": "branch", + "name": "master" + }, + "workspace": { + "slug": "jwalton", + "type": "workspace", + "name": "Joseph Walton", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/workspaces/jwalton" + }, + "html": { + "href": "https://bitbucket.org/jwalton/" + }, + "avatar": { + "href": "https://bitbucket.org/workspaces/jwalton/avatar/?ts=1543459298" + } + }, + "uuid": "{c040300f-f69e-4a65-87a6-5a8f3ef1bbf1}" + }, + "has_issues": false, + "owner": { + "display_name": "Joseph Walton", + "uuid": "{c040300f-f69e-4a65-87a6-5a8f3ef1bbf1}", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/users/%7Bc040300f-f69e-4a65-87a6-5a8f3ef1bbf1%7D" + }, + "html": { + "href": "https://bitbucket.org/%7Bc040300f-f69e-4a65-87a6-5a8f3ef1bbf1%7D/" + }, + "avatar": { + "href": "https://secure.gravatar.com/avatar/cbed2f56195ed90bdebd4feec31ac054?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FJW-1.png" + } + }, + "nickname": "jwalton", + "type": "user", + "account_id": "557058:8679ff30-e82d-47b5-90f0-94127260782a" + }, + "updated_on": "2018-02-06T04:36:52.369420+00:00", + "size": 1529686, + "type": "repository", + "slug": "opup", + "is_private": false, + "description": "" + }, + { + "scm": "git", + "website": "", + "has_wiki": false, + "uuid": "{9a355a32-dad9-4efd-9828-ccce80dd3109}", + "links": { + "watchers": { + "href": "https://api.bitbucket.org/2.0/repositories/jwalton/git-scripts/watchers" + }, + "branches": { + "href": "https://api.bitbucket.org/2.0/repositories/jwalton/git-scripts/refs/branches" + }, + "tags": { + "href": "https://api.bitbucket.org/2.0/repositories/jwalton/git-scripts/refs/tags" + }, + "commits": { + "href": "https://api.bitbucket.org/2.0/repositories/jwalton/git-scripts/commits" + }, + "clone": [{ + "href": "https://bitbucket.org/jwalton/git-scripts.git", + "name": "https" + }, + { + "href": "git@bitbucket.org:jwalton/git-scripts.git", + "name": "ssh" + } + ], + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/jwalton/git-scripts" + }, + "source": { + "href": "https://api.bitbucket.org/2.0/repositories/jwalton/git-scripts/src" + }, + "html": { + "href": "https://bitbucket.org/jwalton/git-scripts" + }, + "avatar": { + "href": "https://bytebucket.org/ravatar/%7B9a355a32-dad9-4efd-9828-ccce80dd3109%7D?ts=default" + }, + "hooks": { + "href": "https://api.bitbucket.org/2.0/repositories/jwalton/git-scripts/hooks" + }, + "forks": { + "href": "https://api.bitbucket.org/2.0/repositories/jwalton/git-scripts/forks" + }, + "downloads": { + "href": "https://api.bitbucket.org/2.0/repositories/jwalton/git-scripts/downloads" + }, + "pullrequests": { + "href": "https://api.bitbucket.org/2.0/repositories/jwalton/git-scripts/pullrequests" + } + }, + "fork_policy": "allow_forks", + "full_name": "jwalton/git-scripts", + "name": "git-scripts", + "project": { + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/workspaces/jwalton/projects/PROJ" + }, + "html": { + "href": "https://bitbucket.org/jwalton/workspace/projects/PROJ" + }, + "avatar": { + "href": "https://bitbucket.org/account/user/jwalton/projects/PROJ/avatar/32?ts=1543459298" + } + }, + "type": "project", + "name": "Untitled project", + "key": "PROJ", + "uuid": "{6eed76e0-e831-48d0-83ac-cbbd8ee04173}" + }, + "language": "shell", + "created_on": "2011-07-08T08:59:53.298617+00:00", + "mainbranch": { + "type": "branch", + "name": "master" + }, + "workspace": { + "slug": "jwalton", + "type": "workspace", + "name": "Joseph Walton", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/workspaces/jwalton" + }, + "html": { + "href": "https://bitbucket.org/jwalton/" + }, + "avatar": { + "href": "https://bitbucket.org/workspaces/jwalton/avatar/?ts=1543459298" + } + }, + "uuid": "{c040300f-f69e-4a65-87a6-5a8f3ef1bbf1}" + }, + "has_issues": false, + "owner": { + "display_name": "Joseph Walton", + "uuid": "{c040300f-f69e-4a65-87a6-5a8f3ef1bbf1}", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/users/%7Bc040300f-f69e-4a65-87a6-5a8f3ef1bbf1%7D" + }, + "html": { + "href": "https://bitbucket.org/%7Bc040300f-f69e-4a65-87a6-5a8f3ef1bbf1%7D/" + }, + "avatar": { + "href": "https://secure.gravatar.com/avatar/cbed2f56195ed90bdebd4feec31ac054?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FJW-1.png" + } + }, + "nickname": "jwalton", + "type": "user", + "account_id": "557058:8679ff30-e82d-47b5-90f0-94127260782a" + }, + "updated_on": "2017-03-19T16:09:30.336053+00:00", + "size": 394778, + "type": "repository", + "slug": "git-scripts", + "is_private": false, + "description": "" + }, + { + "scm": "git", + "website": "", + "has_wiki": false, + "uuid": "{08725752-2ec2-41f8-ba6f-a7e4a9a55d07}", + "links": { + "watchers": { + "href": "https://api.bitbucket.org/2.0/repositories/evzijst/git-tests/watchers" + }, + "branches": { + "href": "https://api.bitbucket.org/2.0/repositories/evzijst/git-tests/refs/branches" + }, + "tags": { + "href": "https://api.bitbucket.org/2.0/repositories/evzijst/git-tests/refs/tags" + }, + "commits": { + "href": "https://api.bitbucket.org/2.0/repositories/evzijst/git-tests/commits" + }, + "clone": [{ + "href": "https://bitbucket.org/evzijst/git-tests.git", + "name": "https" + }, + { + "href": "git@bitbucket.org:evzijst/git-tests.git", + "name": "ssh" + } + ], + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/evzijst/git-tests" + }, + "source": { + "href": "https://api.bitbucket.org/2.0/repositories/evzijst/git-tests/src" + }, + "html": { + "href": "https://bitbucket.org/evzijst/git-tests" + }, + "avatar": { + "href": "https://bytebucket.org/ravatar/%7B08725752-2ec2-41f8-ba6f-a7e4a9a55d07%7D?ts=default" + }, + "hooks": { + "href": "https://api.bitbucket.org/2.0/repositories/evzijst/git-tests/hooks" + }, + "forks": { + "href": "https://api.bitbucket.org/2.0/repositories/evzijst/git-tests/forks" + }, + "downloads": { + "href": "https://api.bitbucket.org/2.0/repositories/evzijst/git-tests/downloads" + }, + "pullrequests": { + "href": "https://api.bitbucket.org/2.0/repositories/evzijst/git-tests/pullrequests" + } + }, + "fork_policy": "allow_forks", + "full_name": "evzijst/git-tests", + "name": "git-tests", + "project": { + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/workspaces/evzijst/projects/PROJ" + }, + "html": { + "href": "https://bitbucket.org/evzijst/workspace/projects/PROJ" + }, + "avatar": { + "href": "https://bitbucket.org/account/user/evzijst/projects/PROJ/avatar/32?ts=1543458574" + } + }, + "type": "project", + "name": "Untitled project", + "key": "PROJ", + "uuid": "{920e7a86-80d7-4310-828e-2ef2a486ba92}" + }, + "language": "", + "created_on": "2011-08-10T00:42:35.509559+00:00", + "mainbranch": { + "type": "branch", + "name": "master" + }, + "workspace": { + "slug": "evzijst", + "type": "workspace", + "name": "Erik van Zijst", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/workspaces/evzijst" + }, + "html": { + "href": "https://bitbucket.org/evzijst/" + }, + "avatar": { + "href": "https://bitbucket.org/workspaces/evzijst/avatar/?ts=1543458574" + } + }, + "uuid": "{a288a0ab-e13b-43f0-a689-c4ef0a249875}" + }, + "has_issues": false, + "owner": { + "display_name": "Erik van Zijst", + "uuid": "{a288a0ab-e13b-43f0-a689-c4ef0a249875}", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/users/%7Ba288a0ab-e13b-43f0-a689-c4ef0a249875%7D" + }, + "html": { + "href": "https://bitbucket.org/%7Ba288a0ab-e13b-43f0-a689-c4ef0a249875%7D/" + }, + "avatar": { + "href": "https://secure.gravatar.com/avatar/f6bcbb4e3f665e74455bd8c0b4b3afba?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FEZ-4.png" + } + }, + "nickname": "evzijst", + "type": "user", + "account_id": "557058:c0b72ad0-1cb5-4018-9cdc-0cde8492c443" + }, + "updated_on": "2015-10-15T17:35:06.978690+00:00", + "size": 324796, + "type": "repository", + "slug": "git-tests", + "is_private": false, + "description": "Git repo used for testing with pull requests and difficult merge scenarios." + }, + { + "scm": "git", + "website": "", + "has_wiki": false, + "uuid": "{3cfaf49c-d6e8-4d09-80cc-a4ec9feebace}", + "links": { + "watchers": { + "href": "https://api.bitbucket.org/2.0/repositories/brodie/libgit2/watchers" + }, + "branches": { + "href": "https://api.bitbucket.org/2.0/repositories/brodie/libgit2/refs/branches" + }, + "tags": { + "href": "https://api.bitbucket.org/2.0/repositories/brodie/libgit2/refs/tags" + }, + "commits": { + "href": "https://api.bitbucket.org/2.0/repositories/brodie/libgit2/commits" + }, + "clone": [{ + "href": "https://bitbucket.org/brodie/libgit2.git", + "name": "https" + }, + { + "href": "git@bitbucket.org:brodie/libgit2.git", + "name": "ssh" + } + ], + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/brodie/libgit2" + }, + "source": { + "href": "https://api.bitbucket.org/2.0/repositories/brodie/libgit2/src" + }, + "html": { + "href": "https://bitbucket.org/brodie/libgit2" + }, + "avatar": { + "href": "https://bytebucket.org/ravatar/%7B3cfaf49c-d6e8-4d09-80cc-a4ec9feebace%7D?ts=c" + }, + "hooks": { + "href": "https://api.bitbucket.org/2.0/repositories/brodie/libgit2/hooks" + }, + "forks": { + "href": "https://api.bitbucket.org/2.0/repositories/brodie/libgit2/forks" + }, + "downloads": { + "href": "https://api.bitbucket.org/2.0/repositories/brodie/libgit2/downloads" + }, + "pullrequests": { + "href": "https://api.bitbucket.org/2.0/repositories/brodie/libgit2/pullrequests" + } + }, + "fork_policy": "allow_forks", + "full_name": "brodie/libgit2", + "name": "libgit2", + "project": { + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/workspaces/brodie/projects/PROJ" + }, + "html": { + "href": "https://bitbucket.org/brodie/workspace/projects/PROJ" + }, + "avatar": { + "href": "https://bitbucket.org/account/user/brodie/projects/PROJ/avatar/32?ts=1543457344" + } + }, + "type": "project", + "name": "Untitled project", + "key": "PROJ", + "uuid": "{635ae7e0-a73c-4de5-884b-29fcd7f2b7b9}" + }, + "language": "c", + "created_on": "2011-08-10T03:48:05.820933+00:00", + "mainbranch": { + "type": "branch", + "name": "master" + }, + "workspace": { + "slug": "brodie", + "type": "workspace", + "name": "Brodie Rao", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/workspaces/brodie" + }, + "html": { + "href": "https://bitbucket.org/brodie/" + }, + "avatar": { + "href": "https://bitbucket.org/workspaces/brodie/avatar/?ts=1543457344" + } + }, + "uuid": "{9484702e-c663-4afd-aefb-c93a8cd31c28}" + }, + "has_issues": false, + "owner": { + "display_name": "Brodie Rao", + "uuid": "{9484702e-c663-4afd-aefb-c93a8cd31c28}", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/users/%7B9484702e-c663-4afd-aefb-c93a8cd31c28%7D" + }, + "html": { + "href": "https://bitbucket.org/%7B9484702e-c663-4afd-aefb-c93a8cd31c28%7D/" + }, + "avatar": { + "href": "https://avatar-management--avatars.us-west-2.prod.public.atl-paas.net/557058:3aae1e05-702a-41e5-81c8-f36f29afb6ca/613070db-28b0-421f-8dba-ae8a87e2a5c7/128" + } + }, + "nickname": "brodie", + "type": "user", + "account_id": "557058:3aae1e05-702a-41e5-81c8-f36f29afb6ca" + }, + "updated_on": "2013-07-17T23:08:05.997544+00:00", + "size": 4520824, + "type": "repository", + "slug": "libgit2", + "is_private": false, + "description": "" + }, + { + "scm": "git", + "website": "", + "has_wiki": false, + "uuid": "{e2c4ca61-b444-4af5-892a-da8afa64671d}", + "links": { + "watchers": { + "href": "https://api.bitbucket.org/2.0/repositories/evzijst/git/watchers" + }, + "branches": { + "href": "https://api.bitbucket.org/2.0/repositories/evzijst/git/refs/branches" + }, + "tags": { + "href": "https://api.bitbucket.org/2.0/repositories/evzijst/git/refs/tags" + }, + "commits": { + "href": "https://api.bitbucket.org/2.0/repositories/evzijst/git/commits" + }, + "clone": [{ + "href": "https://bitbucket.org/evzijst/git.git", + "name": "https" + }, + { + "href": "git@bitbucket.org:evzijst/git.git", + "name": "ssh" + } + ], + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/evzijst/git" + }, + "source": { + "href": "https://api.bitbucket.org/2.0/repositories/evzijst/git/src" + }, + "html": { + "href": "https://bitbucket.org/evzijst/git" + }, + "avatar": { + "href": "https://bytebucket.org/ravatar/%7Be2c4ca61-b444-4af5-892a-da8afa64671d%7D?ts=default" + }, + "hooks": { + "href": "https://api.bitbucket.org/2.0/repositories/evzijst/git/hooks" + }, + "forks": { + "href": "https://api.bitbucket.org/2.0/repositories/evzijst/git/forks" + }, + "downloads": { + "href": "https://api.bitbucket.org/2.0/repositories/evzijst/git/downloads" + }, + "pullrequests": { + "href": "https://api.bitbucket.org/2.0/repositories/evzijst/git/pullrequests" + } + }, + "fork_policy": "allow_forks", + "full_name": "evzijst/git", + "name": "git", + "project": { + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/workspaces/evzijst/projects/PROJ" + }, + "html": { + "href": "https://bitbucket.org/evzijst/workspace/projects/PROJ" + }, + "avatar": { + "href": "https://bitbucket.org/account/user/evzijst/projects/PROJ/avatar/32?ts=1543458574" + } + }, + "type": "project", + "name": "Untitled project", + "key": "PROJ", + "uuid": "{920e7a86-80d7-4310-828e-2ef2a486ba92}" + }, + "language": "", + "created_on": "2011-08-15T05:19:11.022316+00:00", + "mainbranch": { + "type": "branch", + "name": "master" + }, + "workspace": { + "slug": "evzijst", + "type": "workspace", + "name": "Erik van Zijst", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/workspaces/evzijst" + }, + "html": { + "href": "https://bitbucket.org/evzijst/" + }, + "avatar": { + "href": "https://bitbucket.org/workspaces/evzijst/avatar/?ts=1543458574" + } + }, + "uuid": "{a288a0ab-e13b-43f0-a689-c4ef0a249875}" + }, + "has_issues": false, + "owner": { + "display_name": "Erik van Zijst", + "uuid": "{a288a0ab-e13b-43f0-a689-c4ef0a249875}", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/users/%7Ba288a0ab-e13b-43f0-a689-c4ef0a249875%7D" + }, + "html": { + "href": "https://bitbucket.org/%7Ba288a0ab-e13b-43f0-a689-c4ef0a249875%7D/" + }, + "avatar": { + "href": "https://secure.gravatar.com/avatar/f6bcbb4e3f665e74455bd8c0b4b3afba?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FEZ-4.png" + } + }, + "nickname": "evzijst", + "type": "user", + "account_id": "557058:c0b72ad0-1cb5-4018-9cdc-0cde8492c443" + }, + "updated_on": "2013-10-10T23:43:15.183665+00:00", + "size": 36467762, + "type": "repository", + "slug": "git", + "is_private": false, + "description": "" + }, + { + "scm": "git", + "website": "", + "has_wiki": true, + "uuid": "{e1ff0acd-5fe0-4722-8d6f-0d9e7bb69436}", + "links": { + "watchers": { + "href": "https://api.bitbucket.org/2.0/repositories/atlassian_tutorial/streams-jira-delete-issue-plugin/watchers" + }, + "branches": { + "href": "https://api.bitbucket.org/2.0/repositories/atlassian_tutorial/streams-jira-delete-issue-plugin/refs/branches" + }, + "tags": { + "href": "https://api.bitbucket.org/2.0/repositories/atlassian_tutorial/streams-jira-delete-issue-plugin/refs/tags" + }, + "commits": { + "href": "https://api.bitbucket.org/2.0/repositories/atlassian_tutorial/streams-jira-delete-issue-plugin/commits" + }, + "clone": [{ + "href": "https://bitbucket.org/atlassian_tutorial/streams-jira-delete-issue-plugin.git", + "name": "https" + }, + { + "href": "git@bitbucket.org:atlassian_tutorial/streams-jira-delete-issue-plugin.git", + "name": "ssh" + } + ], + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/atlassian_tutorial/streams-jira-delete-issue-plugin" + }, + "source": { + "href": "https://api.bitbucket.org/2.0/repositories/atlassian_tutorial/streams-jira-delete-issue-plugin/src" + }, + "html": { + "href": "https://bitbucket.org/atlassian_tutorial/streams-jira-delete-issue-plugin" + }, + "avatar": { + "href": "https://bytebucket.org/ravatar/%7Be1ff0acd-5fe0-4722-8d6f-0d9e7bb69436%7D?ts=java" + }, + "hooks": { + "href": "https://api.bitbucket.org/2.0/repositories/atlassian_tutorial/streams-jira-delete-issue-plugin/hooks" + }, + "forks": { + "href": "https://api.bitbucket.org/2.0/repositories/atlassian_tutorial/streams-jira-delete-issue-plugin/forks" + }, + "downloads": { + "href": "https://api.bitbucket.org/2.0/repositories/atlassian_tutorial/streams-jira-delete-issue-plugin/downloads" + }, + "issues": { + "href": "https://api.bitbucket.org/2.0/repositories/atlassian_tutorial/streams-jira-delete-issue-plugin/issues" + }, + "pullrequests": { + "href": "https://api.bitbucket.org/2.0/repositories/atlassian_tutorial/streams-jira-delete-issue-plugin/pullrequests" + } + }, + "fork_policy": "allow_forks", + "full_name": "atlassian_tutorial/streams-jira-delete-issue-plugin", + "name": "streams-jira-delete-issue-plugin", + "project": { + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/workspaces/atlassian_tutorial/projects/PROJ" + }, + "html": { + "href": "https://bitbucket.org/atlassian_tutorial/workspace/projects/PROJ" + }, + "avatar": { + "href": "https://bitbucket.org/account/user/atlassian_tutorial/projects/PROJ/avatar/32?ts=1568941536" + } + }, + "type": "project", + "name": "Tutorials", + "key": "PROJ", + "uuid": "{1605e801-929d-4a5a-8653-835b5778c315}" + }, + "language": "java", + "created_on": "2011-08-18T00:17:00.862842+00:00", + "mainbranch": { + "type": "branch", + "name": "master" + }, + "workspace": { + "slug": "atlassian_tutorial", + "type": "workspace", + "name": "Atlassian Tutorials", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/workspaces/atlassian_tutorial" + }, + "html": { + "href": "https://bitbucket.org/atlassian_tutorial/" + }, + "avatar": { + "href": "https://bitbucket.org/workspaces/atlassian_tutorial/avatar/?ts=1543462696" + } + }, + "uuid": "{38d7ea9c-d213-4366-bd21-3417324c520f}" + }, + "has_issues": true, + "owner": { + "username": "atlassian_tutorial", + "display_name": "Atlassian Tutorials", + "type": "team", + "uuid": "{38d7ea9c-d213-4366-bd21-3417324c520f}", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/teams/%7B38d7ea9c-d213-4366-bd21-3417324c520f%7D" + }, + "html": { + "href": "https://bitbucket.org/%7B38d7ea9c-d213-4366-bd21-3417324c520f%7D/" + }, + "avatar": { + "href": "https://bitbucket.org/account/atlassian_tutorial/avatar/" + } + } + }, + "updated_on": "2013-06-12T22:42:52.654728+00:00", + "size": 37629, + "type": "repository", + "slug": "streams-jira-delete-issue-plugin", + "is_private": false, + "description": "" + } + ] +} \ No newline at end of file diff --git a/swh/lister/bitbucket/tests/data/https_api.bitbucket.org/2.0_repositories,after=1970-01-01T00:00:00+00:00,pagelen=100 b/swh/lister/bitbucket/tests/data/https_api.bitbucket.org/2.0_repositories,after=1970-01-01T00:00:00+00:00,pagelen=100 deleted file mode 100644 --- a/swh/lister/bitbucket/tests/data/https_api.bitbucket.org/2.0_repositories,after=1970-01-01T00:00:00+00:00,pagelen=100 +++ /dev/null @@ -1,806 +0,0 @@ -{ - "pagelen": 10, - "values": [ - { - "scm": "hg", - "website": "", - "has_wiki": true, - "name": "app-template", - "links": { - "watchers": { - "href": "https://api.bitbucket.org/2.0/repositories/bebac/app-template/watchers" - }, - "branches": { - "href": "https://api.bitbucket.org/2.0/repositories/bebac/app-template/refs/branches" - }, - "tags": { - "href": "https://api.bitbucket.org/2.0/repositories/bebac/app-template/refs/tags" - }, - "commits": { - "href": "https://api.bitbucket.org/2.0/repositories/bebac/app-template/commits" - }, - "clone": [ - { - "href": "https://bitbucket.org/bebac/app-template", - "name": "https" - }, - { - "href": "ssh://hg@bitbucket.org/bebac/app-template", - "name": "ssh" - } - ], - "self": { - "href": "https://api.bitbucket.org/2.0/repositories/bebac/app-template" - }, - "html": { - "href": "https://bitbucket.org/bebac/app-template" - }, - "avatar": { - "href": "https://bitbucket.org/bebac/app-template/avatar/32/" - }, - "hooks": { - "href": "https://api.bitbucket.org/2.0/repositories/bebac/app-template/hooks" - }, - "forks": { - "href": "https://api.bitbucket.org/2.0/repositories/bebac/app-template/forks" - }, - "downloads": { - "href": "https://api.bitbucket.org/2.0/repositories/bebac/app-template/downloads" - }, - "pullrequests": { - "href": "https://api.bitbucket.org/2.0/repositories/bebac/app-template/pullrequests" - } - }, - "fork_policy": "allow_forks", - "uuid": "{0cf80a6e-e91f-4a4c-a61b-8c8ff51ca3ec}", - "language": "c++", - "created_on": "2008-07-12T07:44:01.476818+00:00", - "full_name": "bebac/app-template", - "has_issues": true, - "owner": { - "username": "bebac", - "display_name": "Benny Bach", - "type": "user", - "uuid": "{d1a83a2a-be1b-4034-8c1d-386a6690cddb}", - "links": { - "self": { - "href": "https://api.bitbucket.org/2.0/users/bebac" - }, - "html": { - "href": "https://bitbucket.org/bebac/" - }, - "avatar": { - "href": "https://bitbucket.org/account/bebac/avatar/32/" - } - } - }, - "updated_on": "2011-10-05T15:36:19.409008+00:00", - "size": 71548, - "type": "repository", - "slug": "app-template", - "is_private": false, - "description": "Basic files and directory structure for a C++ project. Intended as a starting point for a new project. Includes a basic cross platform core library." - }, - { - "scm": "git", - "website": "", - "has_wiki": true, - "name": "mercurialeclipse", - "links": { - "watchers": { - "href": "https://api.bitbucket.org/2.0/repositories/bastiand/mercurialeclipse/watchers" - }, - "branches": { - "href": "https://api.bitbucket.org/2.0/repositories/bastiand/mercurialeclipse/refs/branches" - }, - "tags": { - "href": "https://api.bitbucket.org/2.0/repositories/bastiand/mercurialeclipse/refs/tags" - }, - "commits": { - "href": "https://api.bitbucket.org/2.0/repositories/bastiand/mercurialeclipse/commits" - }, - "clone": [ - { - "href": "https://bitbucket.org/bastiand/mercurialeclipse", - "name": "https" - }, - { - "href": "ssh://hg@bitbucket.org/bastiand/mercurialeclipse", - "name": "ssh" - } - ], - "self": { - "href": "https://api.bitbucket.org/2.0/repositories/bastiand/mercurialeclipse" - }, - "html": { - "href": "https://bitbucket.org/bastiand/mercurialeclipse" - }, - "avatar": { - "href": "https://bitbucket.org/bastiand/mercurialeclipse/avatar/32/" - }, - "hooks": { - "href": "https://api.bitbucket.org/2.0/repositories/bastiand/mercurialeclipse/hooks" - }, - "forks": { - "href": "https://api.bitbucket.org/2.0/repositories/bastiand/mercurialeclipse/forks" - }, - "downloads": { - "href": "https://api.bitbucket.org/2.0/repositories/bastiand/mercurialeclipse/downloads" - }, - "pullrequests": { - "href": "https://api.bitbucket.org/2.0/repositories/bastiand/mercurialeclipse/pullrequests" - } - }, - "fork_policy": "allow_forks", - "uuid": "{f7a08670-bdd1-4465-aa97-7a5ce8d1a25b}", - "language": "", - "created_on": "2008-07-12T09:37:06.254721+00:00", - "full_name": "bastiand/mercurialeclipse", - "has_issues": false, - "owner": { - "username": "bastiand", - "display_name": "Bastian Doetsch", - "type": "user", - "uuid": "{3742cd48-adad-4205-ab0d-04fc992a1728}", - "links": { - "self": { - "href": "https://api.bitbucket.org/2.0/users/bastiand" - }, - "html": { - "href": "https://bitbucket.org/bastiand/" - }, - "avatar": { - "href": "https://bitbucket.org/account/bastiand/avatar/32/" - } - } - }, - "updated_on": "2011-09-17T02:36:59.062596+00:00", - "size": 6445145, - "type": "repository", - "slug": "mercurialeclipse", - "is_private": false, - "description": "my own repo for MercurialEclipse." - }, - { - "scm": "hg", - "website": "", - "has_wiki": true, - "name": "sandboxpublic", - "links": { - "watchers": { - "href": "https://api.bitbucket.org/2.0/repositories/aleax/sandboxpublic/watchers" - }, - "branches": { - "href": "https://api.bitbucket.org/2.0/repositories/aleax/sandboxpublic/refs/branches" - }, - "tags": { - "href": "https://api.bitbucket.org/2.0/repositories/aleax/sandboxpublic/refs/tags" - }, - "commits": { - "href": "https://api.bitbucket.org/2.0/repositories/aleax/sandboxpublic/commits" - }, - "clone": [ - { - "href": "https://bitbucket.org/aleax/sandboxpublic", - "name": "https" - }, - { - "href": "ssh://hg@bitbucket.org/aleax/sandboxpublic", - "name": "ssh" - } - ], - "self": { - "href": "https://api.bitbucket.org/2.0/repositories/aleax/sandboxpublic" - }, - "html": { - "href": "https://bitbucket.org/aleax/sandboxpublic" - }, - "avatar": { - "href": "https://bitbucket.org/aleax/sandboxpublic/avatar/32/" - }, - "hooks": { - "href": "https://api.bitbucket.org/2.0/repositories/aleax/sandboxpublic/hooks" - }, - "forks": { - "href": "https://api.bitbucket.org/2.0/repositories/aleax/sandboxpublic/forks" - }, - "downloads": { - "href": "https://api.bitbucket.org/2.0/repositories/aleax/sandboxpublic/downloads" - }, - "pullrequests": { - "href": "https://api.bitbucket.org/2.0/repositories/aleax/sandboxpublic/pullrequests" - } - }, - "fork_policy": "allow_forks", - "uuid": "{452c716c-a1ce-42bc-a95b-d38da49cbb37}", - "language": "", - "created_on": "2008-07-14T01:59:23.568048+00:00", - "full_name": "aleax/sandboxpublic", - "has_issues": true, - "owner": { - "username": "aleax", - "display_name": "Alex Martelli", - "type": "user", - "uuid": "{1155d94d-fb48-43fe-a431-ec07c900b636}", - "links": { - "self": { - "href": "https://api.bitbucket.org/2.0/users/aleax" - }, - "html": { - "href": "https://bitbucket.org/aleax/" - }, - "avatar": { - "href": "https://bitbucket.org/account/aleax/avatar/32/" - } - } - }, - "updated_on": "2012-06-22T21:55:28.753727+00:00", - "size": 3120, - "type": "repository", - "slug": "sandboxpublic", - "is_private": false, - "description": "to help debug ACLs for private vs public bitbucket repos" - }, - { - "scm": "hg", - "website": "", - "has_wiki": true, - "name": "otrsfix-ng", - "links": { - "watchers": { - "href": "https://api.bitbucket.org/2.0/repositories/adiakin/otrsfix-ng/watchers" - }, - "branches": { - "href": "https://api.bitbucket.org/2.0/repositories/adiakin/otrsfix-ng/refs/branches" - }, - "tags": { - "href": "https://api.bitbucket.org/2.0/repositories/adiakin/otrsfix-ng/refs/tags" - }, - "commits": { - "href": "https://api.bitbucket.org/2.0/repositories/adiakin/otrsfix-ng/commits" - }, - "clone": [ - { - "href": "https://bitbucket.org/adiakin/otrsfix-ng", - "name": "https" - }, - { - "href": "ssh://hg@bitbucket.org/adiakin/otrsfix-ng", - "name": "ssh" - } - ], - "self": { - "href": "https://api.bitbucket.org/2.0/repositories/adiakin/otrsfix-ng" - }, - "html": { - "href": "https://bitbucket.org/adiakin/otrsfix-ng" - }, - "avatar": { - "href": "https://bitbucket.org/adiakin/otrsfix-ng/avatar/32/" - }, - "hooks": { - "href": "https://api.bitbucket.org/2.0/repositories/adiakin/otrsfix-ng/hooks" - }, - "forks": { - "href": "https://api.bitbucket.org/2.0/repositories/adiakin/otrsfix-ng/forks" - }, - "downloads": { - "href": "https://api.bitbucket.org/2.0/repositories/adiakin/otrsfix-ng/downloads" - }, - "pullrequests": { - "href": "https://api.bitbucket.org/2.0/repositories/adiakin/otrsfix-ng/pullrequests" - } - }, - "fork_policy": "allow_forks", - "uuid": "{05b1b9dc-a7b6-46d6-ae1b-e66a17aa4f55}", - "language": "", - "created_on": "2008-07-15T06:14:39.306314+00:00", - "full_name": "adiakin/otrsfix-ng", - "has_issues": true, - "owner": { - "username": "adiakin", - "display_name": "adiakin", - "type": "user", - "uuid": "{414012b5-1ac9-4096-9f46-8893cfa3cda5}", - "links": { - "self": { - "href": "https://api.bitbucket.org/2.0/users/adiakin" - }, - "html": { - "href": "https://bitbucket.org/adiakin/" - }, - "avatar": { - "href": "https://bitbucket.org/account/adiakin/avatar/32/" - } - } - }, - "updated_on": "2016-06-02T18:56:34.868302+00:00", - "size": 211631, - "type": "repository", - "slug": "otrsfix-ng", - "is_private": false, - "description": "OTRS greasemonkey extension" - }, - { - "scm": "hg", - "website": "", - "has_wiki": true, - "name": "pida-pypaned", - "links": { - "watchers": { - "href": "https://api.bitbucket.org/2.0/repositories/aafshar/pida-pypaned/watchers" - }, - "branches": { - "href": "https://api.bitbucket.org/2.0/repositories/aafshar/pida-pypaned/refs/branches" - }, - "tags": { - "href": "https://api.bitbucket.org/2.0/repositories/aafshar/pida-pypaned/refs/tags" - }, - "commits": { - "href": "https://api.bitbucket.org/2.0/repositories/aafshar/pida-pypaned/commits" - }, - "clone": [ - { - "href": "https://bitbucket.org/aafshar/pida-pypaned", - "name": "https" - }, - { - "href": "ssh://hg@bitbucket.org/aafshar/pida-pypaned", - "name": "ssh" - } - ], - "self": { - "href": "https://api.bitbucket.org/2.0/repositories/aafshar/pida-pypaned" - }, - "html": { - "href": "https://bitbucket.org/aafshar/pida-pypaned" - }, - "avatar": { - "href": "https://bitbucket.org/aafshar/pida-pypaned/avatar/32/" - }, - "hooks": { - "href": "https://api.bitbucket.org/2.0/repositories/aafshar/pida-pypaned/hooks" - }, - "forks": { - "href": "https://api.bitbucket.org/2.0/repositories/aafshar/pida-pypaned/forks" - }, - "downloads": { - "href": "https://api.bitbucket.org/2.0/repositories/aafshar/pida-pypaned/downloads" - }, - "pullrequests": { - "href": "https://api.bitbucket.org/2.0/repositories/aafshar/pida-pypaned/pullrequests" - } - }, - "fork_policy": "allow_forks", - "uuid": "{94cb830a-1784-4e51-9791-8f5cc93990a9}", - "language": "", - "created_on": "2008-07-16T22:47:38.682491+00:00", - "full_name": "aafshar/pida-pypaned", - "has_issues": true, - "owner": { - "username": "aafshar", - "display_name": "Ali Afshar", - "type": "user", - "uuid": "{bcb87110-6a92-41fc-b95c-680feeea5512}", - "links": { - "self": { - "href": "https://api.bitbucket.org/2.0/users/aafshar" - }, - "html": { - "href": "https://bitbucket.org/aafshar/" - }, - "avatar": { - "href": "https://bitbucket.org/account/aafshar/avatar/32/" - } - } - }, - "updated_on": "2012-06-22T21:55:42.451431+00:00", - "size": 4680652, - "type": "repository", - "slug": "pida-pypaned", - "is_private": false, - "description": "" - }, - { - "scm": "hg", - "website": "", - "has_wiki": true, - "name": "TLOMM-testing", - "links": { - "watchers": { - "href": "https://api.bitbucket.org/2.0/repositories/tgrimley/tlomm-testing/watchers" - }, - "branches": { - "href": "https://api.bitbucket.org/2.0/repositories/tgrimley/tlomm-testing/refs/branches" - }, - "tags": { - "href": "https://api.bitbucket.org/2.0/repositories/tgrimley/tlomm-testing/refs/tags" - }, - "commits": { - "href": "https://api.bitbucket.org/2.0/repositories/tgrimley/tlomm-testing/commits" - }, - "clone": [ - { - "href": "https://bitbucket.org/tgrimley/tlomm-testing", - "name": "https" - }, - { - "href": "ssh://hg@bitbucket.org/tgrimley/tlomm-testing", - "name": "ssh" - } - ], - "self": { - "href": "https://api.bitbucket.org/2.0/repositories/tgrimley/tlomm-testing" - }, - "html": { - "href": "https://bitbucket.org/tgrimley/tlomm-testing" - }, - "avatar": { - "href": "https://bitbucket.org/tgrimley/tlomm-testing/avatar/32/" - }, - "hooks": { - "href": "https://api.bitbucket.org/2.0/repositories/tgrimley/tlomm-testing/hooks" - }, - "forks": { - "href": "https://api.bitbucket.org/2.0/repositories/tgrimley/tlomm-testing/forks" - }, - "downloads": { - "href": "https://api.bitbucket.org/2.0/repositories/tgrimley/tlomm-testing/downloads" - }, - "pullrequests": { - "href": "https://api.bitbucket.org/2.0/repositories/tgrimley/tlomm-testing/pullrequests" - } - }, - "fork_policy": "allow_forks", - "uuid": "{95283ca1-f77e-40d6-b3ed-5bfae6ed2d15}", - "language": "", - "created_on": "2008-07-18T21:05:17.750587+00:00", - "full_name": "tgrimley/tlomm-testing", - "has_issues": true, - "owner": { - "username": "tgrimley", - "display_name": "Thomas Grimley", - "type": "user", - "uuid": "{c958a08f-4669-4c77-81ec-4e2faa8ebf35}", - "links": { - "self": { - "href": "https://api.bitbucket.org/2.0/users/tgrimley" - }, - "html": { - "href": "https://bitbucket.org/tgrimley/" - }, - "avatar": { - "href": "https://bitbucket.org/account/tgrimley/avatar/32/" - } - } - }, - "updated_on": "2012-06-22T21:55:46.627825+00:00", - "size": 3128, - "type": "repository", - "slug": "tlomm-testing", - "is_private": false, - "description": "File related to testing functionality of TLOMM->TLOTTS transition" - }, - { - "scm": "hg", - "website": "", - "has_wiki": true, - "name": "test", - "links": { - "watchers": { - "href": "https://api.bitbucket.org/2.0/repositories/tingle/test/watchers" - }, - "branches": { - "href": "https://api.bitbucket.org/2.0/repositories/tingle/test/refs/branches" - }, - "tags": { - "href": "https://api.bitbucket.org/2.0/repositories/tingle/test/refs/tags" - }, - "commits": { - "href": "https://api.bitbucket.org/2.0/repositories/tingle/test/commits" - }, - "clone": [ - { - "href": "https://bitbucket.org/tingle/test", - "name": "https" - }, - { - "href": "ssh://hg@bitbucket.org/tingle/test", - "name": "ssh" - } - ], - "self": { - "href": "https://api.bitbucket.org/2.0/repositories/tingle/test" - }, - "html": { - "href": "https://bitbucket.org/tingle/test" - }, - "avatar": { - "href": "https://bitbucket.org/tingle/test/avatar/32/" - }, - "hooks": { - "href": "https://api.bitbucket.org/2.0/repositories/tingle/test/hooks" - }, - "forks": { - "href": "https://api.bitbucket.org/2.0/repositories/tingle/test/forks" - }, - "downloads": { - "href": "https://api.bitbucket.org/2.0/repositories/tingle/test/downloads" - }, - "pullrequests": { - "href": "https://api.bitbucket.org/2.0/repositories/tingle/test/pullrequests" - } - }, - "fork_policy": "allow_forks", - "uuid": "{457953ec-fe87-41b9-b659-94756fb40ece}", - "language": "", - "created_on": "2008-07-18T22:24:31.984981+00:00", - "full_name": "tingle/test", - "has_issues": true, - "owner": { - "username": "tingle", - "display_name": "tingle", - "type": "user", - "uuid": "{dddce42b-bd19-417b-90ff-72cdbfb6eb7d}", - "links": { - "self": { - "href": "https://api.bitbucket.org/2.0/users/tingle" - }, - "html": { - "href": "https://bitbucket.org/tingle/" - }, - "avatar": { - "href": "https://bitbucket.org/account/tingle/avatar/32/" - } - } - }, - "updated_on": "2012-06-22T21:55:49.860564+00:00", - "size": 3090, - "type": "repository", - "slug": "test", - "is_private": false, - "description": "" - }, - { - "scm": "hg", - "website": "http://shaze.myopenid.com/", - "has_wiki": true, - "name": "Repository", - "links": { - "watchers": { - "href": "https://api.bitbucket.org/2.0/repositories/Shaze/repository/watchers" - }, - "branches": { - "href": "https://api.bitbucket.org/2.0/repositories/Shaze/repository/refs/branches" - }, - "tags": { - "href": "https://api.bitbucket.org/2.0/repositories/Shaze/repository/refs/tags" - }, - "commits": { - "href": "https://api.bitbucket.org/2.0/repositories/Shaze/repository/commits" - }, - "clone": [ - { - "href": "https://bitbucket.org/Shaze/repository", - "name": "https" - }, - { - "href": "ssh://hg@bitbucket.org/Shaze/repository", - "name": "ssh" - } - ], - "self": { - "href": "https://api.bitbucket.org/2.0/repositories/Shaze/repository" - }, - "html": { - "href": "https://bitbucket.org/Shaze/repository" - }, - "avatar": { - "href": "https://bitbucket.org/Shaze/repository/avatar/32/" - }, - "hooks": { - "href": "https://api.bitbucket.org/2.0/repositories/Shaze/repository/hooks" - }, - "forks": { - "href": "https://api.bitbucket.org/2.0/repositories/Shaze/repository/forks" - }, - "downloads": { - "href": "https://api.bitbucket.org/2.0/repositories/Shaze/repository/downloads" - }, - "pullrequests": { - "href": "https://api.bitbucket.org/2.0/repositories/Shaze/repository/pullrequests" - } - }, - "fork_policy": "allow_forks", - "uuid": "{3c0b8076-caef-465a-8d08-a184459f659b}", - "language": "", - "created_on": "2008-07-18T22:39:51.380134+00:00", - "full_name": "Shaze/repository", - "has_issues": true, - "owner": { - "username": "Shaze", - "display_name": "Shaze", - "type": "user", - "uuid": "{f57817e9-bfe4-4c65-84dd-662152430323}", - "links": { - "self": { - "href": "https://api.bitbucket.org/2.0/users/Shaze" - }, - "html": { - "href": "https://bitbucket.org/Shaze/" - }, - "avatar": { - "href": "https://bitbucket.org/account/Shaze/avatar/32/" - } - } - }, - "updated_on": "2012-06-22T21:55:51.570502+00:00", - "size": 3052, - "type": "repository", - "slug": "repository", - "is_private": false, - "description": "Mine, all mine!" - }, - { - "scm": "hg", - "website": "http://bitbucket.org/copiesofcopies/identifox/", - "has_wiki": true, - "name": "identifox", - "links": { - "watchers": { - "href": "https://api.bitbucket.org/2.0/repositories/uncryptic/identifox/watchers" - }, - "branches": { - "href": "https://api.bitbucket.org/2.0/repositories/uncryptic/identifox/refs/branches" - }, - "tags": { - "href": "https://api.bitbucket.org/2.0/repositories/uncryptic/identifox/refs/tags" - }, - "commits": { - "href": "https://api.bitbucket.org/2.0/repositories/uncryptic/identifox/commits" - }, - "clone": [ - { - "href": "https://bitbucket.org/uncryptic/identifox", - "name": "https" - }, - { - "href": "ssh://hg@bitbucket.org/uncryptic/identifox", - "name": "ssh" - } - ], - "self": { - "href": "https://api.bitbucket.org/2.0/repositories/uncryptic/identifox" - }, - "html": { - "href": "https://bitbucket.org/uncryptic/identifox" - }, - "avatar": { - "href": "https://bitbucket.org/uncryptic/identifox/avatar/32/" - }, - "hooks": { - "href": "https://api.bitbucket.org/2.0/repositories/uncryptic/identifox/hooks" - }, - "forks": { - "href": "https://api.bitbucket.org/2.0/repositories/uncryptic/identifox/forks" - }, - "downloads": { - "href": "https://api.bitbucket.org/2.0/repositories/uncryptic/identifox/downloads" - }, - "pullrequests": { - "href": "https://api.bitbucket.org/2.0/repositories/uncryptic/identifox/pullrequests" - } - }, - "fork_policy": "allow_forks", - "uuid": "{78a1a080-a77e-4d0d-823a-b107484477a8}", - "language": "", - "created_on": "2008-07-19T00:33:14.065946+00:00", - "full_name": "uncryptic/identifox", - "has_issues": true, - "owner": { - "username": "uncryptic", - "display_name": "Uncryptic Communications", - "type": "user", - "uuid": "{db87bb9a-9980-4840-bd4a-61f7748a56b4}", - "links": { - "self": { - "href": "https://api.bitbucket.org/2.0/users/uncryptic" - }, - "html": { - "href": "https://bitbucket.org/uncryptic/" - }, - "avatar": { - "href": "https://bitbucket.org/account/uncryptic/avatar/32/" - } - } - }, - "updated_on": "2008-07-19T00:33:14+00:00", - "size": 1918, - "type": "repository", - "slug": "identifox", - "is_private": false, - "description": "TwitterFox, modified to work with Identi.ca, including cosmetic and subtle code changes. For the most part, the code is nearly identical to the TwitterFox base: http://www.naan.net/trac/wiki/TwitterFox" - }, - { - "scm": "hg", - "website": "http://rforce.rubyforge.org", - "has_wiki": false, - "name": "rforce", - "links": { - "watchers": { - "href": "https://api.bitbucket.org/2.0/repositories/undees/rforce/watchers" - }, - "branches": { - "href": "https://api.bitbucket.org/2.0/repositories/undees/rforce/refs/branches" - }, - "tags": { - "href": "https://api.bitbucket.org/2.0/repositories/undees/rforce/refs/tags" - }, - "commits": { - "href": "https://api.bitbucket.org/2.0/repositories/undees/rforce/commits" - }, - "clone": [ - { - "href": "https://bitbucket.org/undees/rforce", - "name": "https" - }, - { - "href": "ssh://hg@bitbucket.org/undees/rforce", - "name": "ssh" - } - ], - "self": { - "href": "https://api.bitbucket.org/2.0/repositories/undees/rforce" - }, - "html": { - "href": "https://bitbucket.org/undees/rforce" - }, - "avatar": { - "href": "https://bitbucket.org/undees/rforce/avatar/32/" - }, - "hooks": { - "href": "https://api.bitbucket.org/2.0/repositories/undees/rforce/hooks" - }, - "forks": { - "href": "https://api.bitbucket.org/2.0/repositories/undees/rforce/forks" - }, - "downloads": { - "href": "https://api.bitbucket.org/2.0/repositories/undees/rforce/downloads" - }, - "pullrequests": { - "href": "https://api.bitbucket.org/2.0/repositories/undees/rforce/pullrequests" - } - }, - "fork_policy": "allow_forks", - "uuid": "{ec2ffee7-bfcd-4e95-83c8-22ac31e69fa3}", - "language": "", - "created_on": "2008-07-19T06:16:43.044743+00:00", - "full_name": "undees/rforce", - "has_issues": false, - "owner": { - "username": "undees", - "display_name": "Ian Dees", - "type": "user", - "uuid": "{6ff66a34-6412-4f28-bf57-707a2a5c6d7b}", - "links": { - "self": { - "href": "https://api.bitbucket.org/2.0/users/undees" - }, - "html": { - "href": "https://bitbucket.org/undees/" - }, - "avatar": { - "href": "https://bitbucket.org/account/undees/avatar/32/" - } - } - }, - "updated_on": "2015-02-09T00:48:15.408680+00:00", - "size": 338402, - "type": "repository", - "slug": "rforce", - "is_private": false, - "description": "A simple, usable binding to the SalesForce API." - } - ], - "next": "https://api.bitbucket.org/2.0/repositories?after=2008-07-19T19%3A53%3A07.031845%2B00%3A00" -} diff --git a/swh/lister/bitbucket/tests/data/https_api.bitbucket.org/empty_response.json b/swh/lister/bitbucket/tests/data/https_api.bitbucket.org/empty_response.json deleted file mode 100644 --- a/swh/lister/bitbucket/tests/data/https_api.bitbucket.org/empty_response.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "pagelen": 10, - "values": [] -} diff --git a/swh/lister/bitbucket/tests/data/https_api.bitbucket.org/response.json b/swh/lister/bitbucket/tests/data/https_api.bitbucket.org/response.json deleted file mode 120000 --- a/swh/lister/bitbucket/tests/data/https_api.bitbucket.org/response.json +++ /dev/null @@ -1 +0,0 @@ -2.0_repositories,after=1970-01-01T00:00:00+00:00,pagelen=100 \ No newline at end of file diff --git a/swh/lister/bitbucket/tests/test_lister.py b/swh/lister/bitbucket/tests/test_lister.py --- a/swh/lister/bitbucket/tests/test_lister.py +++ b/swh/lister/bitbucket/tests/test_lister.py @@ -1,117 +1,32 @@ -# Copyright (C) 2017-2020 The Software Heritage developers +# Copyright (C) 2017-2021 The Software Heritage developers # See the AUTHORS file at the top-level directory of this distribution # License: GNU General Public License version 3, or any later version # See top-level LICENSE file for more information -from datetime import timedelta -import re -import unittest -from urllib.parse import unquote +import json +import os -import iso8601 -import requests_mock +from swh.lister.bitbucket.lister import BitbucketLister -from swh.lister.bitbucket.lister import BitBucketLister -from swh.lister.core.tests.test_lister import HttpListerTester +def bitbucket_response_callback(request, context): + data_dir = os.path.join(os.path.dirname(__file__), "data") + data_file_path = os.path.join(data_dir, "bb_api_repositories_page1.json") + if "1970-01-01" not in request.query: + data_file_path = os.path.join(data_dir, "bb_api_repositories_page2.json") + with open(data_file_path, "r") as data_file: + return json.load(data_file) -def _convert_type(req_index): - """Convert the req_index to its right type according to the model's - "indexable" column. - """ - return iso8601.parse_date(unquote(req_index)) +def test_lister_bitbucket(swh_scheduler, requests_mock): + """Simple Bitbucket listing with two pages containing 10 origins""" + requests_mock.get(BitbucketLister.API_URL, json=bitbucket_response_callback) -class BitBucketListerTester(HttpListerTester, unittest.TestCase): - Lister = BitBucketLister - test_re = re.compile(r"/repositories\?after=([^?&]+)") - lister_subdir = "bitbucket" - good_api_response_file = "data/https_api.bitbucket.org/response.json" - bad_api_response_file = "data/https_api.bitbucket.org/empty_response.json" - first_index = _convert_type("2008-07-12T07:44:01.476818+00:00") - last_index = _convert_type("2008-07-19T06:16:43.044743+00:00") - entries_per_page = 10 - convert_type = _convert_type + lister = BitbucketLister(scheduler=swh_scheduler, per_page=10) - def request_index(self, request): - """(Override) This is needed to emulate the listing bootstrap - when no min_bound is provided to run - """ - m = self.test_re.search(request.path_url) - idx = _convert_type(m.group(1)) - if idx == self.Lister.default_min_bound: - idx = self.first_index - return idx + stats = lister.run() - @requests_mock.Mocker() - def test_fetch_none_nodb(self, http_mocker): - """Overridden because index is not an integer nor a string - - """ - http_mocker.get(self.test_re, text=self.mock_response) - fl = self.get_fl() - - self.disable_scheduler(fl) - self.disable_db(fl) - - # stores no results - fl.run( - min_bound=self.first_index - timedelta(days=3), max_bound=self.first_index - ) - - def test_is_within_bounds(self): - fl = self.get_fl() - self.assertTrue( - fl.is_within_bounds( - iso8601.parse_date("2008-07-15"), self.first_index, self.last_index - ) - ) - self.assertFalse( - fl.is_within_bounds( - iso8601.parse_date("2008-07-20"), self.first_index, self.last_index - ) - ) - self.assertFalse( - fl.is_within_bounds( - iso8601.parse_date("2008-07-11"), self.first_index, self.last_index - ) - ) - - -def test_lister_bitbucket(lister_bitbucket, requests_mock_datadir): - """Simple bitbucket listing should create scheduled tasks (git, hg) - - """ - lister_bitbucket.run() - - r = lister_bitbucket.scheduler.search_tasks(task_type="load-hg") - assert len(r) == 9 - - for row in r: - args = row["arguments"]["args"] - kwargs = row["arguments"]["kwargs"] - - assert len(args) == 0 - assert len(kwargs) == 1 - url = kwargs["url"] - - assert url.startswith("https://bitbucket.org") - - assert row["policy"] == "recurring" - assert row["priority"] is None - - r = lister_bitbucket.scheduler.search_tasks(task_type="load-git") - assert len(r) == 1 - - for row in r: - args = row["arguments"]["args"] - kwargs = row["arguments"]["kwargs"] - assert len(args) == 0 - assert len(kwargs) == 1 - url = kwargs["url"] - - assert url.startswith("https://bitbucket.org") - - assert row["policy"] == "recurring" - assert row["priority"] is None + assert stats.pages == 2 + assert stats.origins == 20 + assert len(swh_scheduler.get_listed_origins(lister.lister_obj.id).origins) == 20 diff --git a/swh/lister/bitbucket/tests/test_tasks.py b/swh/lister/bitbucket/tests/test_tasks.py --- a/swh/lister/bitbucket/tests/test_tasks.py +++ b/swh/lister/bitbucket/tests/test_tasks.py @@ -1,12 +1,11 @@ -# Copyright (C) 2019-2020 The Software Heritage developers +# Copyright (C) 2019-2021 The Software Heritage developers # See the AUTHORS file at the top-level directory of this distribution # License: GNU General Public License version 3, or any later version # See top-level LICENSE file for more information -from time import sleep from unittest.mock import patch -from celery.result import GroupResult +from swh.lister.pattern import ListerStats def test_ping(swh_scheduler_celery_app, swh_scheduler_celery_worker): @@ -17,12 +16,12 @@ assert res.result == "OK" -@patch("swh.lister.bitbucket.tasks.BitBucketLister") +@patch("swh.lister.bitbucket.tasks.BitbucketLister") def test_incremental(lister, swh_scheduler_celery_app, swh_scheduler_celery_worker): # setup the mocked BitbucketLister lister.return_value = lister - lister.db_last_index.return_value = 42 - lister.run.return_value = None + lister.from_configfile.return_value = lister + lister.run.return_value = ListerStats(pages=5, origins=5000) res = swh_scheduler_celery_app.send_task( "swh.lister.bitbucket.tasks.IncrementalBitBucketLister" @@ -31,65 +30,4 @@ res.wait() assert res.successful() - lister.assert_called_once_with() - lister.db_last_index.assert_called_once_with() - lister.run.assert_called_once_with(min_bound=42, max_bound=None) - - -@patch("swh.lister.bitbucket.tasks.BitBucketLister") -def test_range(lister, swh_scheduler_celery_app, swh_scheduler_celery_worker): - # setup the mocked BitbucketLister - lister.return_value = lister - lister.run.return_value = None - - res = swh_scheduler_celery_app.send_task( - "swh.lister.bitbucket.tasks.RangeBitBucketLister", kwargs=dict(start=12, end=42) - ) - assert res - res.wait() - assert res.successful() - - lister.assert_called_once_with() - lister.db_last_index.assert_not_called() - lister.run.assert_called_once_with(min_bound=12, max_bound=42) - - -@patch("swh.lister.bitbucket.tasks.BitBucketLister") -def test_relister(lister, swh_scheduler_celery_app, swh_scheduler_celery_worker): - # setup the mocked BitbucketLister - lister.return_value = lister - lister.run.return_value = None - lister.db_partition_indices.return_value = [(i, i + 9) for i in range(0, 50, 10)] - - res = swh_scheduler_celery_app.send_task( - "swh.lister.bitbucket.tasks.FullBitBucketRelister" - ) - assert res - - res.wait() - assert res.successful() - - # retrieve the GroupResult for this task and wait for all the subtasks - # to complete - promise_id = res.result - assert promise_id - promise = GroupResult.restore(promise_id, app=swh_scheduler_celery_app) - for i in range(5): - if promise.ready(): - break - sleep(1) - - lister.assert_called_with() - - # one by the FullBitbucketRelister task - # + 5 for the RangeBitbucketLister subtasks - assert lister.call_count == 6 - - lister.db_last_index.assert_not_called() - lister.db_partition_indices.assert_called_once_with(10000) - - # lister.run should have been called once per partition interval - for i in range(5): - assert ( - dict(min_bound=10 * i, max_bound=10 * i + 9), - ) in lister.run.call_args_list + lister.run.assert_called_once()