From c4a2f011844ec20155a32b930938ee3b7e92419a Mon Sep 17 00:00:00 2001 From: Jana Cupova Date: Jun 08 2021 14:01:42 +0000 Subject: listBuilds accept also package name and user name Fixes: https://pagure.io/koji/issue/1209 --- diff --git a/hub/kojihub.py b/hub/kojihub.py index ac8baee..9e9807d 100644 --- a/hub/kojihub.py +++ b/hub/kojihub.py @@ -11315,79 +11315,78 @@ class RootExports(object): createdBefore=None, createdAfter=None, completeBefore=None, completeAfter=None, type=None, typeInfo=None, queryOpts=None, pattern=None): - """Return a list of builds that match the given parameters + """ + Return a list of builds that match the given parameters Filter parameters - - packageID: only builds of the specified package (numeric id) - - userID: only builds owned by the given user (numeric id) - - taskID: only builds with the given task ID - If taskID is -1, only builds with a non-null task id - - volumeID: only builds stored on the given volume (numeric id) - - source: only builds where the source field matches (glob pattern) - - prefix: only builds whose package name starts with that prefix - - pattern: only builds whose nvr matches the glob pattern - - state: only builds in the given state (numeric value) + :param int|str packageID: only builds of the specified package + :param int|str userID: only builds owned by the given user + :param int taskID: only builds with the given task ID + If taskID is -1, only builds with a non-null task id + :param int volumeID: only builds stored on the given volume + :param str source: only builds where the source field matches (glob pattern) + :param str prefix: only builds whose package name starts with that prefix + :param str pattern: only builds whose nvr matches the glob pattern + :param int stage: only builds in the given state Timestamp filter parameters - these limit the results to builds where the corresponding timestamp is before or after the given time - the time value may be specified as seconds since the epoch or in ISO format ('YYYY-MM-DD HH24:MI:SS') - - filters for creation_time: - - createdBefore - - createdAfter - - filters for completion_time: - - completeBefore - - completeAfter + :param str|timestamp createdBefore: filter for creation_time + :param str|timestamp createdAfter: filter for creation_time + :param str|timestamp completeBefore: filter for completion_time + :param str|timestamp completeAfter: filter for completion_time Build type parameters: - - type: only builds of the given btype (such as maven or image) - - typeInfo: only builds with matching type-specific info (given - as a dictionary). Can only be used in conjunction with the - type parameter. Only limited types are supported. - - For type=maven, the provided group_id, artifact_id, and/or version - fields are matched - - For type=win, the provided platform fields are matched - - Returns a list of maps. Each map contains the following keys: - - - build_id - - version - - release - - epoch - - state - - package_id - - package_name - - name (same as package_name) - - nvr (synthesized for sorting purposes) - - owner_id - - owner_name - - volume_id - - volume_name - - source - - creation_event_id - - creation_time - - creation_ts - - start_time - - start_ts - - completion_time - - completion_ts - - task_id - - extra - - If type == 'maven', each map will also contain the following keys: - - - maven_group_id - - maven_artifact_id - - maven_version - - If type == 'win', each map will also contain the following key: - - - platform - - If no builds match, an empty list is returned. + :param str type: only builds of the given btype (such as maven or image) + :param dict typeInfo: only builds with matching type-specific info (given + as a dictionary). Can only be used in conjunction with the + type parameter. Only limited types are supported. + + For type=maven, the provided group_id, artifact_id, and/or version + fields are matched + + For type=win, the provided platform fields are matched + + :returns: Returns a list of maps. Each map contains the following keys: + + - build_id + - version + - release + - epoch + - state + - package_id + - package_name + - name (same as package_name) + - nvr (synthesized for sorting purposes) + - owner_id + - owner_name + - volume_id + - volume_name + - source + - creation_event_id + - creation_time + - creation_ts + - start_time + - start_ts + - completion_time + - completion_ts + - task_id + - extra + + If type == 'maven', each map will also contain the following keys: + + - maven_group_id + - maven_artifact_id + - maven_version + + If type == 'win', each map will also contain the following key: + + - platform + + If no builds match, an empty list is returned. """ fields = [('build.id', 'build_id'), ('build.version', 'version'), ('build.release', 'release'), @@ -11414,8 +11413,10 @@ class RootExports(object): 'LEFT JOIN users ON build.owner = users.id'] clauses = [] if packageID is not None: + packageID = get_package_id(packageID, strict=True) clauses.append('package.id = %(packageID)i') if userID is not None: + userID = get_user(userID, strict=True)['id'] clauses.append('users.id = %(userID)i') if volumeID is not None: clauses.append('volume.id = %(volumeID)i') diff --git a/tests/test_hub/test_list_builds.py b/tests/test_hub/test_list_builds.py new file mode 100644 index 0000000..3e0cc2d --- /dev/null +++ b/tests/test_hub/test_list_builds.py @@ -0,0 +1,86 @@ +import unittest + +import mock + +import koji +import kojihub + +QP = kojihub.QueryProcessor + + +class TestListBuilds(unittest.TestCase): + def getQuery(self, *args, **kwargs): + query = QP(*args, **kwargs) + query.execute = mock.MagicMock() + query.executeOne = self.query_executeOne + query.iterate = mock.MagicMock() + self.queries.append(query) + return query + + def setUp(self): + self.maxDiff = None + self.exports = kojihub.RootExports() + self.query_executeOne = mock.MagicMock() + self.QueryProcessor = mock.patch('kojihub.QueryProcessor', + side_effect=self.getQuery).start() + self.queries = [] + + self.context = mock.patch('kojihub.context').start() + self.cursor = mock.MagicMock() + self.build_list = [{'build_id': 9, + 'epoch': 0, + 'name': 'test-package', + 'nvr': 'test-package-11-12', + 'owner_id': 1, + 'owner_name': 'kojiadmin', + 'package_id': 11, + 'package_name': 'test-package', + 'release': '12', + 'state': 3, + 'task_id': 879, + 'version': '11', + 'volume_id': 0, + 'volume_name': 'DEFAULT'}] + + def test_wrong_package(self): + package = 'test-package' + kojihub.get_package_id.return_value = package + with self.assertRaises(koji.GenericError) as cm: + self.exports.listBuilds(packageID=package) + self.assertEqual('No such entry in table package: %s' % package, str(cm.exception)) + + @mock.patch('kojihub.get_package_id') + def test_package_string(self, get_package_id): + package = 'test-package' + package_id = 1 + get_package_id.return_value = package_id + self.query_executeOne.return_value = None + self.exports.listBuilds(packageID=package) + self.assertEqual(len(self.queries), 1) + args, kwargs = self.QueryProcessor.call_args + qp = QP(**kwargs) + self.assertEquals(qp.tables, ['build']) + self.assertEquals(qp.columns, ['build.id', 'build.completion_time', + 'EXTRACT(EPOCH FROM build.completion_time)', + 'events.id', 'events.time', + 'EXTRACT(EPOCH FROM events.time)', 'build.epoch', + 'build.extra', 'package.name', + "package.name || '-' || build.version || '-' || " + "build.release", 'users.id', 'users.name', 'package.id', + 'package.name', 'build.release', 'build.source', + 'build.start_time', 'EXTRACT(EPOCH FROM build.start_time)', + 'build.state', 'build.task_id', 'build.version', + 'volume.id', 'volume.name']) + self.assertEqual(qp.clauses, ['package.id = %(packageID)i']) + self.assertEquals(qp.joins, ['LEFT JOIN events ON build.create_event = events.id', + 'LEFT JOIN package ON build.pkg_id = package.id', + 'LEFT JOIN volume ON build.volume_id = volume.id', + 'LEFT JOIN users ON build.owner = users.id']) + + @mock.patch('kojihub.get_user') + def test_wrong_user(self, get_user): + user = 'test-user' + get_user.side_effect = koji.GenericError('No such user: %s' % user) + with self.assertRaises(koji.GenericError) as cm: + self.exports.listBuilds(userID=user) + self.assertEqual('No such user: %s' % user, str(cm.exception))