diff --git a/README.rst b/README.rst index 7b297f73..c668c9b4 100644 --- a/README.rst +++ b/README.rst @@ -492,29 +492,27 @@ parameters:: Filtering module builds ----------------------- -The module-builds can be filtered by a variety of GET parameters. These -parameters are: +The module builds can be filtered by a variety of GET parameters. Some of these +parameters include: -- ``name`` - Shows builds of modules with a particular name (e.g. - ``name=testmodule``) -- ``koji_tag`` - Shows builds tagged with a particular Koji tag (e.g. - ``koji_tag=module-984ed60dd37b9361``) -- ``owner`` - Shows builds submitted by a particular user (e.g. - ``owner=mprahl``) -- ``state`` - Shows builds in a particular state (can be the state name or - the state ID) (e.g. ``state=done``) -- ``submitted_before`` - Shows builds that were submitted before a particular - Zulu ISO 8601 timestamp (e.g. ``submitted_before=2016-08-23T09:40:07Z``) -- ``submitted_after`` - Shows builds that were submitted after a particular - Zulu ISO 8601 timestamp (e.g. ``submitted_after=2016-08-22T09:40:07Z``) -- ``modified_before`` - Shows builds that were modified before a particular - Zulu ISO 8601 timestamp (e.g. ``modified_before=2016-08-23T09:40:07Z``) -- ``modified_after`` - Shows builds that were modified after a particular - Zulu ISO 8601 timestamp (e.g. ``modified_after=2016-08-22T09:40:07Z``) -- ``completed_before`` - Shows builds that were completed before a particular - Zulu ISO 8601 timestamp (e.g. ``completed_before=2016-08-22T09:40:07Z``) -- ``completed_after`` - Shows builds that were completed after a particular - Zulu ISO 8601 timestamp (e.g. ``completed_after=2016-08-23T09:40:07Z``) +- ``batch`` +- ``cg_build_koji_tag`` +- ``completed_after`` (Zulu ISO 8601 format e.g. ``completed_after=2016-08-23T09:40:07Z``) +- ``completed_before`` (Zulu ISO 8601 format e.g. ``completed_before=2016-08-22T09:40:07Z``) +- ``koji_tag`` +- ``modified_after`` (Zulu ISO 8601 format e.g. ``modified_after=2016-08-22T09:40:07Z``) +- ``modified_before`` (Zulu ISO 8601 format e.g. ``modified_before=2016-08-23T09:40:07Z``) +- ``name`` +- ``new_repo_task_id`` +- ``owner`` +- ``rebuild_strategy`` +- ``scmurl`` +- ``state`` (can be the state name or the state ID e.g. ``state=done``) +- ``state_reason`` +- ``stream`` +- ``submitted_after`` (Zulu ISO 8601 format e.g. ``submitted_after=2016-08-22T09:40:07Z``) +- ``submitted_before`` (Zulu ISO 8601 format e.g. ``submitted_before=2016-08-23T09:40:07Z``) +- ``version`` An example of querying the "module-builds" resource with the "state", and the "submitted_before" parameters:: @@ -658,6 +656,28 @@ parameters:: } + +Filtering component builds +-------------------------- + +The component builds can be filtered by a variety of GET parameters. Some of these +parameters include: + +- ``batch`` +- ``build_time_only`` (boolean e.g. "true" or "false") +- ``format`` +- ``module_id`` or ``module_build`` +- ``nvr`` +- ``package`` +- ``ref`` +- ``scmurl`` +- ``state`` +- ``state_reason`` +- ``tagged`` (boolean e.g. "true" or "false") +- ``tagged_in_final`` (boolean e.g. "true" or "false") +- ``task_id`` + + Listing about ------------- diff --git a/module_build_service/utils.py b/module_build_service/utils.py index 06cfb026..327f4683 100644 --- a/module_build_service/utils.py +++ b/module_build_service/utils.py @@ -39,6 +39,7 @@ import yaml from flask import request, url_for, Response from datetime import datetime +from sqlalchemy.sql.sqltypes import Boolean as sqlalchemy_boolean from module_build_service import log, models from module_build_service.errors import (ValidationError, UnprocessableEntity, @@ -436,6 +437,15 @@ def _add_order_by_clause(flask_request, query, column_source): return query.order_by(column) +def str_to_bool(value): + """ + Parses a string to determine its boolean value + :param value: a string + :return: a boolean + """ + return value.lower() in ["true", "1"] + + def filter_component_builds(flask_request): """ Returns a flask_sqlalchemy.Pagination object based on the request parameters @@ -443,8 +453,15 @@ def filter_component_builds(flask_request): :return: flask_sqlalchemy.Pagination """ search_query = dict() - state = flask_request.args.get('state', None) + for key in request.args.keys(): + # Only filter on valid database columns + if key in models.ComponentBuild.__table__.columns.keys(): + if isinstance(models.ComponentBuild.__table__.columns[key].type, sqlalchemy_boolean): + search_query[key] = str_to_bool(flask_request.args[key]) + else: + search_query[key] = flask_request.args[key] + state = flask_request.args.get('state', None) if state: if state.isdigit(): search_query['state'] = state @@ -454,11 +471,9 @@ def filter_component_builds(flask_request): else: raise ValidationError('An invalid state was supplied') - # Lookup module_build from task_id, ref, format, nvr or tagged attribute - # of a component build. - for key in ['task_id', 'ref', 'nvr', 'format', 'tagged']: - if flask_request.args.get(key, None): - search_query[key] = flask_request.args[key] + # Allow the user to specify the module build ID with a more intuitive key name + if 'module_build' in flask_request.args: + search_query['module_id'] = flask_request.args['module_build'] query = models.ComponentBuild.query @@ -479,8 +494,14 @@ def filter_module_builds(flask_request): :return: flask_sqlalchemy.Pagination """ search_query = dict() - state = flask_request.args.get('state', None) + special_columns = ['time_submitted', 'time_modified', 'time_completed', 'state'] + for key in request.args.keys(): + # Only filter on valid database columns but skip columns that are treated specially or + # ignored + if key not in special_columns and key in models.ModuleBuild.__table__.columns.keys(): + search_query[key] = flask_request.args[key] + state = flask_request.args.get('state', None) if state: if state.isdigit(): search_query['state'] = state @@ -490,10 +511,6 @@ def filter_module_builds(flask_request): else: raise ValidationError('An invalid state was supplied') - for key in ['name', 'owner', 'koji_tag']: - if flask_request.args.get(key, None): - search_query[key] = flask_request.args[key] - query = models.ModuleBuild.query if search_query: diff --git a/tests/test_views/test_views.py b/tests/test_views/test_views.py index e60c7dae..6e63ab09 100644 --- a/tests/test_views/test_views.py +++ b/tests/test_views/test_views.py @@ -365,10 +365,9 @@ class TestViews(unittest.TestCase): self.assertEquals(data['meta']['total'], 0) def test_query_component_builds_filter_tagged(self): - rv = self.client.get('/module-build-service/1/component-builds/' - '?tagged=this-filter-query-should-return-zero-items') + rv = self.client.get('/module-build-service/1/component-builds/?tagged=true') data = json.loads(rv.data) - self.assertEquals(data['meta']['total'], 0) + self.assertEquals(data['meta']['total'], 40) def test_query_component_builds_filter_nvr(self): rv = self.client.get( @@ -446,6 +445,17 @@ class TestViews(unittest.TestCase): data = json.loads(rv.data) self.assertEquals(data['meta']['total'], 4) + def test_query_builds_filter_nsv(self): + rv = self.client.get( + '/module-build-service/1/module-builds/?name=postgressql&stream=1&version=2') + data = json.loads(rv.data) + # TODO: The nsv should really be unique in the test data + for item in data['items']: + self.assertEqual(item['name'], 'postgressql') + self.assertEqual(item['stream'], '1') + self.assertEqual(item['version'], '2') + self.assertEquals(data['meta']['total'], 10) + def test_query_builds_filter_invalid_date(self): rv = self.client.get( '/module-build-service/1/module-builds/?modified_after=2016-09-03T12:25:00-05:00')