diff --git a/rhodecode/api/tests/test_get_repos.py b/rhodecode/api/tests/test_get_repos.py --- a/rhodecode/api/tests/test_get_repos.py +++ b/rhodecode/api/tests/test_get_repos.py @@ -22,7 +22,8 @@ import pytest from rhodecode.model.repo import RepoModel -from rhodecode.api.tests.utils import build_data, api_call, assert_ok, jsonify +from rhodecode.api.tests.utils import ( + build_data, api_call, assert_ok, assert_error, jsonify) from rhodecode.model.db import User @@ -40,6 +41,76 @@ class TestGetRepos(object): expected = ret assert_ok(id_, expected, given=response.body) + def test_api_get_repos_only_toplevel(self, user_util): + repo_group = user_util.create_repo_group(auto_cleanup=True) + user_util.create_repo(parent=repo_group) + + id_, params = build_data(self.apikey, 'get_repos', traverse=0) + response = api_call(self.app, params) + + result = [] + for repo in RepoModel().get_repos_for_root(root=None): + result.append(repo.get_api_data(include_secrets=True)) + expected = jsonify(result) + + assert_ok(id_, expected, given=response.body) + + def test_api_get_repos_with_wrong_root(self): + id_, params = build_data(self.apikey, 'get_repos', root='abracadabra') + response = api_call(self.app, params) + + expected = 'Root repository group `abracadabra` does not exist' + assert_error(id_, expected, given=response.body) + + def test_api_get_repos_with_root(self, user_util): + repo_group = user_util.create_repo_group(auto_cleanup=True) + repo_group_name = repo_group.group_name + + user_util.create_repo(parent=repo_group) + user_util.create_repo(parent=repo_group) + + # nested, should not show up + user_util._test_name = '{}/'.format(repo_group_name) + sub_repo_group = user_util.create_repo_group(auto_cleanup=True) + user_util.create_repo(parent=sub_repo_group) + + id_, params = build_data(self.apikey, 'get_repos', + root=repo_group_name, traverse=0) + response = api_call(self.app, params) + + result = [] + for repo in RepoModel().get_repos_for_root(repo_group): + result.append(repo.get_api_data(include_secrets=True)) + + assert len(result) == 2 + expected = jsonify(result) + assert_ok(id_, expected, given=response.body) + + def test_api_get_repos_with_root_and_traverse(self, user_util): + repo_group = user_util.create_repo_group(auto_cleanup=True) + repo_group_name = repo_group.group_name + + user_util.create_repo(parent=repo_group) + user_util.create_repo(parent=repo_group) + + # nested, should not show up + user_util._test_name = '{}/'.format(repo_group_name) + sub_repo_group = user_util.create_repo_group(auto_cleanup=True) + user_util.create_repo(parent=sub_repo_group) + + id_, params = build_data(self.apikey, 'get_repos', + root=repo_group_name, traverse=1) + response = api_call(self.app, params) + + result = [] + for repo in RepoModel().get_repos_for_root( + repo_group_name, traverse=True): + result.append(repo.get_api_data(include_secrets=True)) + + assert len(result) == 3 + expected = jsonify(result) + assert_ok(id_, expected, given=response.body) + def test_api_get_repos_non_admin(self): id_, params = build_data(self.apikey_regular, 'get_repos') response = api_call(self.app, params) diff --git a/rhodecode/api/views/repo_api.py b/rhodecode/api/views/repo_api.py --- a/rhodecode/api/views/repo_api.py +++ b/rhodecode/api/views/repo_api.py @@ -36,7 +36,7 @@ from rhodecode.lib.ext_json import json from rhodecode.model.changeset_status import ChangesetStatusModel from rhodecode.model.comment import ChangesetCommentsModel from rhodecode.model.db import ( - Session, ChangesetStatus, RepositoryField, Repository) + Session, ChangesetStatus, RepositoryField, Repository, RepoGroup) from rhodecode.model.repo import RepoModel from rhodecode.model.scm import ScmModel, RepoList from rhodecode.model.settings import SettingsModel, VcsSettingsModel @@ -217,7 +217,7 @@ def get_repo(request, apiuser, repoid, c @jsonrpc_method() -def get_repos(request, apiuser): +def get_repos(request, apiuser, root=Optional(None), traverse=Optional(True)): """ Lists all existing repositories. @@ -226,6 +226,14 @@ def get_repos(request, apiuser): :param apiuser: This is filled automatically from the |authtoken|. :type apiuser: AuthUser + :param root: specify root repository group to fetch repositories. + filters the returned repositories to be members of given root group. + :type root: Optional(None) + :param traverse: traverse given root into subrepositories. With this flag + set to False, it will only return top-level repositories from `root`. + if root is empty it will return just top-level repositories. + :type traverse: Optional(True) + Example output: @@ -257,8 +265,28 @@ def get_repos(request, apiuser): _perms = ('repository.read', 'repository.write', 'repository.admin',) extras = {'user': apiuser} - repo_list = RepoList( - RepoModel().get_all(), perm_set=_perms, extra_kwargs=extras) + root = Optional.extract(root) + traverse = Optional.extract(traverse, binary=True) + + if root: + # verify parent existance, if it's empty return an error + parent = RepoGroup.get_by_group_name(root) + if not parent: + raise JSONRPCError( + 'Root repository group `{}` does not exist'.format(root)) + + if traverse: + repos = RepoModel().get_repos_for_root(root=root, traverse=traverse) + else: + repos = RepoModel().get_repos_for_root(root=parent) + else: + if traverse: + repos = RepoModel().get_all() + else: + # return just top-level + repos = RepoModel().get_repos_for_root(root=None) + + repo_list = RepoList(repos, perm_set=_perms, extra_kwargs=extras) return [repo.get_api_data(include_secrets=include_secrets) for repo in repo_list] diff --git a/rhodecode/model/repo.py b/rhodecode/model/repo.py --- a/rhodecode/model/repo.py +++ b/rhodecode/model/repo.py @@ -143,6 +143,19 @@ class RepoModel(BaseModel): return None + def get_repos_for_root(self, root, traverse=False): + if traverse: + like_expression = u'{}%'.format(safe_unicode(root)) + repos = Repository.query().filter( + Repository.repo_name.like(like_expression)).all() + else: + if root and not isinstance(root, RepoGroup): + raise ValueError( + 'Root must be an instance ' + 'of RepoGroup, got:{} instead'.format(type(root))) + repos = Repository.query().filter(Repository.group == root).all() + return repos + def get_url(self, repo): return h.url('summary_home', repo_name=safe_str(repo.repo_name), qualified=True)