diff --git a/pagure/cli/admin.py b/pagure/cli/admin.py index f6f5f2c..52da23d 100644 --- a/pagure/cli/admin.py +++ b/pagure/cli/admin.py @@ -30,6 +30,15 @@ from pagure import (SESSION, APP, generate_user_key_files) # noqa: E402 _log = logging.getLogger(__name__) +WATCH = { + '-1': 'reset the watch status to default', + '0': 'unwatch, don\'t notify the user of anything', + '1': 'watch issues and PRs', + '2': 'watch commits', + '3': 'watch issues, PRs and commits', +} + + def _parser_refresh_gitolite(subparser): """ Set up the CLI argument parser for the refresh-gitolite action. """ local_parser = subparser.add_parser( @@ -129,6 +138,34 @@ def _parser_admin_token(subparser): _parser_admin_token_create(subsubparser) +def _parser_get_watch(subparser): + """ Set up the CLI argument parser for the get-watch action. """ + # Update watch status + local_parser = subparser.add_parser( + 'get-watch', help="Get someone's watch status on a project") + local_parser.add_argument( + 'project', help="Project (as namespace/project if there " + "is a namespace) -- Fork not supported") + local_parser.add_argument( + 'user', help="User to get the watch status of") + local_parser.set_defaults(func=do_get_watch_status) + + +def _parser_update_watch(subparser): + """ Set up the CLI argument parser for the update-watch action. """ + # Update watch status + local_parser = subparser.add_parser( + 'update-watch', help="Update someone's watch status on a project") + local_parser.add_argument( + 'project', help="Project to update (as namespace/project if there " + "is a namespace) -- Fork not supported") + local_parser.add_argument( + 'user', help="User to update the watch status of") + local_parser.add_argument( + '-s', '--status', help="Watch status to update to") + local_parser.set_defaults(func=do_update_watch_status) + + def parse_arguments(): """ Set-up the argument parsing. """ parser = argparse.ArgumentParser( @@ -152,6 +189,12 @@ def parse_arguments(): # Admin token actions _parser_admin_token(subparser) + # get-watch + _parser_get_watch(subparser) + + # update-watch + _parser_update_watch(subparser) + return parser.parse_args() @@ -167,6 +210,23 @@ def _get_input(text): return raw_input(text) +def _get_project(arg_project, user=None): + ''' From the project specified to the CLI, extract the actual project. + ''' + namespace = None + if '/' in arg_project: + if arg_project.count('/') > 1: + raise pagure.exceptions.PagureException( + 'Invalid project name, has more than one "/": %s' % + arg_project) + namespace, name = arg_project.split('/') + else: + name = arg_project + + return pagure.lib._get_project( + SESSION, namespace=namespace, name=name, user=user) + + def do_generate_acl(args): """ Regenerate the gitolite ACL file. @@ -182,17 +242,7 @@ def do_generate_acl(args): title = None project = None if args.project: - namespace = None - if '/' in args.project: - if args.project.count('/') > 1: - raise pagure.exceptions.PagureException( - 'Invalid project name, has more than one "/": %s' % - args.project) - namespace, name = args.project.split('/') - else: - name = args.project - project = pagure.lib._get_project( - SESSION, namespace=namespace, name=name, user=args.user) + project = _get_project(args.project, user=args.user) title = project.fullname if args.all_: title = 'all' @@ -358,6 +408,84 @@ def do_create_admin_token(args): print(pagure.lib.add_token_to_user(SESSION, None, acls, args.user)) +def do_get_watch_status(args): + """ Get the watch status of an user on a project. + + :arg args: the argparse object returned by ``parse_arguments()``. + + """ + _log.debug('user: %s', args.user) + _log.debug('project: %s', args.project) + # Validate user + pagure.lib.get_user(SESSION, args.user) + + # Get the project + project = _get_project(args.project) + + if project is None: + raise pagure.exceptions.PagureException( + 'No project found with: %s' % args.project) + + level = pagure.lib.get_watch_level_on_repo( + session=SESSION, + user=args.user, + repo=project.name, + repouser=None, + namespace=project.namespace) or [] + + # Specify that issues == 'issues & PRs' + if 'issues' in level: + level.append('pull-requests') + + print('On %s user: %s is watching the following items: %s' % ( + project.fullname, args.user, ', '.join(level) or None)) + + +def do_update_watch_status(args): + """ Update the watch status of an user on a project. + + :arg args: the argparse object returned by ``parse_arguments()``. + + """ + + _log.debug('user: %s', args.user) + _log.debug('status: %s', args.status) + _log.debug('project: %s', args.project) + + # Validate user + pagure.lib.get_user(SESSION, args.user) + + # Ask the status if none were given + if args.status is None: + print('The watch status can be one of the following: ') + for lvl in WATCH: + print('%s: %s' % (lcl, WATCH[lvl])) + args.status = _get_input('Status:') + + # Validate the status + if args.status not in WATCH: + raise pagure.exceptions.PagureException( + 'Invalid status provided: %s not in %s' % ( + args.status, ', '.join(sorted(WATCH.keys())))) + + # Get the project + project = _get_project(args.project) + + if project is None: + raise pagure.exceptions.PagureException( + 'No project found with: %s' % args.project) + + print('Updating watch status of %s to %s (%s) on %s' % ( + args.user, args.status, WATCH[args.status], args.project)) + + pagure.lib.update_watch_status( + session=SESSION, + project=project, + user=args.user, + watch=args.status) + SESSION.commit() + + def main(): """ Start of the application. """ diff --git a/tests/test_pagure_admin.py b/tests/test_pagure_admin.py index 638744f..2f32a54 100644 --- a/tests/test_pagure_admin.py +++ b/tests/test_pagure_admin.py @@ -53,6 +53,8 @@ def _get_ouput(cmd): class PagureAdminHelptests(tests.Modeltests): """ Tests for pagure-admin --help """ + maxDiff = None + def test_parse_arguments(self): """ Test the parse_arguments function of pagure-admin, empty. """ if 'BUILD_ID' in os.environ: @@ -62,10 +64,10 @@ class PagureAdminHelptests(tests.Modeltests): output = _get_ouput(cmd) self.assertEqual(output[0], '') self.assertEqual(output[1], '''usage: admin.py [-h] [--debug] - {refresh-gitolite,refresh-ssh,clear-hook-token,admin-token} + {refresh-gitolite,refresh-ssh,clear-hook-token,admin-token,get-watch,update-watch} ... admin.py: error: too few arguments -''') +''') # noqa def test_parse_arguments_help(self): """ Test the parse_arguments function of pagure-admin. """ @@ -73,7 +75,7 @@ admin.py: error: too few arguments self.assertEqual( _get_ouput(cmd)[0], '''usage: admin.py [-h] [--debug] - {refresh-gitolite,refresh-ssh,clear-hook-token,admin-token} + {refresh-gitolite,refresh-ssh,clear-hook-token,admin-token,get-watch,update-watch} ... The admin CLI for this pagure instance @@ -83,13 +85,15 @@ optional arguments: --debug Increase the verbosity of the information displayed actions: - {refresh-gitolite,refresh-ssh,clear-hook-token,admin-token} + {refresh-gitolite,refresh-ssh,clear-hook-token,admin-token,get-watch,update-watch} refresh-gitolite Re-generate the gitolite config file refresh-ssh Re-write to disk every user's ssh key stored in the database clear-hook-token Generate a new hook token for every project in this instance admin-token Manages the admin tokens for this instance + get-watch Get someone's watch status on a project + update-watch Update someone's watch status on a project ''') def test_parser_refresh_gitolite_help(self): @@ -219,6 +223,40 @@ optional arguments: admin.py admin-token: error: invalid choice: 'foo' (choose from 'list', 'info', 'expire', 'create') ''') # noqa + def test_parser_get_watch(self): + """ Test the _parser_get_watch function of pagure-admin. """ + cmd = ['python', PAGURE_ADMIN, 'get-watch', '--help'] + self.assertEqual( + _get_ouput(cmd)[0], + '''usage: admin.py get-watch [-h] project user + +positional arguments: + project Project (as namespace/project if there is a namespace) -- Fork + not supported + user User to get the watch status of + +optional arguments: + -h, --help show this help message and exit +''') + + def test_parser_update_watch(self): + """ Test the _parser_update_watch function of pagure-admin. """ + cmd = ['python', PAGURE_ADMIN, 'update-watch', '--help'] + self.assertEqual( + _get_ouput(cmd)[0], + '''usage: admin.py update-watch [-h] [-s STATUS] project user + +positional arguments: + project Project to update (as namespace/project if there is a + namespace) -- Fork not supported + user User to update the watch status of + +optional arguments: + -h, --help show this help message and exit + -s STATUS, --status STATUS + Watch status to update to +''') + class PagureAdminAdminTokenEmptytests(tests.Modeltests): """ Tests for pagure-admin admin-token when there is nothing in the DB @@ -563,5 +601,286 @@ class PagureAdminAdminTokentests(tests.Modeltests): self.assertEqual(output, 'No admin tokens found\n') +class PagureAdminGetWatchTests(tests.Modeltests): + """ Tests for pagure-admin get-watch """ + + def setUp(self): + """ Set up the environnment, ran before every tests. """ + super(PagureAdminGetWatchTests, self).setUp() + + self.configfile = os.path.join(self.path, 'config') + self.dbpath = "sqlite:///%s/pagure_dev.sqlite" % self.path + with open(self.configfile, 'w') as stream: + stream.write('DB_URL="%s"\n' % self.dbpath) + + os.environ['PAGURE_CONFIG'] = self.configfile + + createdb = os.path.abspath( + os.path.join(tests.HERE, '..', 'createdb.py')) + cmd = ['python', createdb] + _get_ouput(cmd) + + self.session = pagure.lib.model.create_tables( + self.dbpath, acls=pagure.APP.config.get('ACLS', {})) + + # Create the user pingou + item = pagure.lib.model.User( + user='pingou', + fullname='PY C', + password='foo', + default_email='bar@pingou.com', + ) + self.session.add(item) + item = pagure.lib.model.UserEmail( + user_id=1, + email='bar@pingou.com') + self.session.add(item) + + # Create the user foo + item = pagure.lib.model.User( + user='foo', + fullname='foo B.', + password='foob', + default_email='foo@pingou.com', + ) + self.session.add(item) + + # Create two projects for the user pingou + item = pagure.lib.model.Project( + user_id=1, # pingou + name='test', + description='namespaced test project', + hook_token='aaabbbeee', + namespace='somenamespace', + ) + self.session.add(item) + + item = pagure.lib.model.Project( + user_id=1, # pingou + name='test', + description='Test project', + hook_token='aaabbbccc', + namespace=None, + ) + self.session.add(item) + + self.session.commit() + + # Make the imported pagure use the correct db session + pagure.cli.admin.SESSION = self.session + + def tearDown(self): + """ Tear down the environnment after running the tests. """ + super(PagureAdminGetWatchTests, self).tearDown() + del(os.environ['PAGURE_CONFIG']) + + def test_get_watch_get_project_unknown_project(self): + """ Test the get-watch function of pagure-admin with an unknown + project. + """ + + cmd = ['python', PAGURE_ADMIN, 'get-watch', 'foobar', 'pingou'] + output = _get_ouput(cmd)[0] + self.assertEqual('No project found with: foobar\n', output) + + def test_get_watch_get_project_invalid_project(self): + """ Test the get-watch function of pagure-admin with an invalid + project. + """ + + cmd = ['python', PAGURE_ADMIN, 'get-watch', 'fo/o/bar', 'pingou'] + output = _get_ouput(cmd)[0] + self.assertEqual( + 'Invalid project name, has more than one "/": fo/o/bar\n', + output) + + def test_get_watch_get_project_invalid_user(self): + """ Test the get-watch function of pagure-admin on a invalid user. + """ + + cmd = ['python', PAGURE_ADMIN, 'get-watch', 'test', 'beebop'] + output = _get_ouput(cmd)[0] + self.assertEqual('No user "beebop" found\n', output) + + def test_get_watch_get_project(self): + """ Test the get-watch function of pagure-admin on a regular project. + """ + + cmd = ['python', PAGURE_ADMIN, 'get-watch', 'test', 'pingou'] + output = _get_ouput(cmd)[0] + self.assertEqual( + 'On test user: pingou is watching the following items: ' + 'issues, pull-requests\n', output) + + def test_get_watch_get_project_not_watching(self): + """ Test the get-watch function of pagure-admin on a regular project. + """ + + cmd = ['python', PAGURE_ADMIN, 'get-watch', 'test', 'foo'] + output = _get_ouput(cmd)[0] + self.assertEqual( + 'On test user: foo is watching the following items: None\n', + output) + + def test_get_watch_get_project_namespaced(self): + """ Test the get-watch function of pagure-admin on a namespaced project. + """ + + cmd = [ + 'python', PAGURE_ADMIN, 'get-watch', + 'somenamespace/test', 'pingou'] + output = _get_ouput(cmd)[0] + self.assertEqual( + 'On somenamespace/test user: pingou is watching the following ' + 'items: issues, pull-requests\n', output) + + def test_get_watch_get_project_namespaced_not_watching(self): + """ Test the get-watch function of pagure-admin on a namespaced project. + """ + + cmd = [ + 'python', PAGURE_ADMIN, 'get-watch', + 'somenamespace/test', 'foo'] + output = _get_ouput(cmd)[0] + self.assertEqual( + 'On somenamespace/test user: foo is watching the following ' + 'items: None\n', output) + + +class PagureAdminUpdateWatchTests(tests.Modeltests): + """ Tests for pagure-admin update-watch """ + + def setUp(self): + """ Set up the environnment, ran before every tests. """ + super(PagureAdminUpdateWatchTests, self).setUp() + + self.configfile = os.path.join(self.path, 'config') + self.dbpath = "sqlite:///%s/pagure_dev.sqlite" % self.path + with open(self.configfile, 'w') as stream: + stream.write('DB_URL="%s"\n' % self.dbpath) + + os.environ['PAGURE_CONFIG'] = self.configfile + + createdb = os.path.abspath( + os.path.join(tests.HERE, '..', 'createdb.py')) + cmd = ['python', createdb] + _get_ouput(cmd) + + self.session = pagure.lib.model.create_tables( + self.dbpath, acls=pagure.APP.config.get('ACLS', {})) + + # Create the user pingou + item = pagure.lib.model.User( + user='pingou', + fullname='PY C', + password='foo', + default_email='bar@pingou.com', + ) + self.session.add(item) + item = pagure.lib.model.UserEmail( + user_id=1, + email='bar@pingou.com') + self.session.add(item) + + # Create the user foo + item = pagure.lib.model.User( + user='foo', + fullname='foo B.', + password='foob', + default_email='foo@pingou.com', + ) + self.session.add(item) + + # Create two projects for the user pingou + item = pagure.lib.model.Project( + user_id=1, # pingou + name='test', + description='namespaced test project', + hook_token='aaabbbeee', + namespace='somenamespace', + ) + self.session.add(item) + + item = pagure.lib.model.Project( + user_id=1, # pingou + name='test', + description='Test project', + hook_token='aaabbbccc', + namespace=None, + ) + self.session.add(item) + + self.session.commit() + + # Make the imported pagure use the correct db session + pagure.cli.admin.SESSION = self.session + + def tearDown(self): + """ Tear down the environnment after running the tests. """ + super(PagureAdminUpdateWatchTests, self).tearDown() + del(os.environ['PAGURE_CONFIG']) + + def test_get_watch_update_project_unknown_project(self): + """ Test the update-watch function of pagure-admin on an unknown + project. + """ + + cmd = ['python', PAGURE_ADMIN, 'update-watch', 'foob', 'pingou', '-s=1'] + output = _get_ouput(cmd)[0] + self.assertEqual('No project found with: foob\n', output) + + def test_get_watch_update_project_invalid_project(self): + """ Test the update-watch function of pagure-admin on an invalid + project. + """ + + cmd = ['python', PAGURE_ADMIN, 'update-watch', 'fo/o/b', 'pingou', '-s=1'] + output = _get_ouput(cmd)[0] + self.assertEqual( + 'Invalid project name, has more than one "/": fo/o/b\n', + output) + + def test_get_watch_update_project_invalid_user(self): + """ Test the update-watch function of pagure-admin on an invalid user. + """ + + cmd = ['python', PAGURE_ADMIN, 'update-watch', 'test', 'foob', '-s=1'] + output = _get_ouput(cmd)[0] + self.assertEqual('No user "foob" found\n', output) + + def test_get_watch_update_project_invalid_status(self): + """ Test the update-watch function of pagure-admin with an invalid + status. + """ + + cmd = ['python', PAGURE_ADMIN, 'update-watch', 'test', 'pingou', '-s=10'] + output = _get_ouput(cmd)[0] + self.assertEqual( + 'Invalid status provided: 10 not in -1, 0, 1, 2, 3\n', output) + + def test_get_watch_update_project_no_effect(self): + """ Test the update-watch function of pagure-admin with a regular + project - nothing changed. + """ + + cmd = ['python', PAGURE_ADMIN, 'get-watch', 'test', 'pingou'] + output = _get_ouput(cmd)[0] + self.assertEqual( + 'On test user: pingou is watching the following items: ' + 'issues, pull-requests\n', output) + + cmd = ['python', PAGURE_ADMIN, 'update-watch', 'test', 'pingou', '-s=1'] + output = _get_ouput(cmd)[0] + self.assertEqual( + 'Updating watch status of pingou to 1 (watch issues and PRs) ' + 'on test\n', output) + + cmd = ['python', PAGURE_ADMIN, 'get-watch', 'test', 'pingou'] + output = _get_ouput(cmd)[0] + self.assertEqual( + 'On test user: pingou is watching the following items: ' + 'issues, pull-requests\n', output) + + if __name__ == '__main__': unittest.main(verbosity=2)