From e0b093ab8941a14e5a31616f04461c3d62115c2e Mon Sep 17 00:00:00 2001 From: Adam Coldrick Date: Wed, 26 Oct 2016 22:31:50 +0000 Subject: [PATCH] Add filters to the /v1/tasks/search endpoint This commit adds almost the same set of parameters that the /v1/tasks endpoint supports to the /v1/tasks/search endpoint. This allows one to obtain some search results and then filter them more easily than before, as well as avoiding a bug when trying to naively search for tasks without removing the default criteria in the web ui. Eventually we should probably merge the two endpoints. Change-Id: Ie65d5b8a23c96369050013face90f15d3a39d84b --- storyboard/api/v1/search/sqlalchemy_impl.py | 32 +++++++++++---- storyboard/api/v1/tasks.py | 44 ++++++++++++++++----- storyboard/db/api/tasks.py | 23 ++++++----- 3 files changed, 72 insertions(+), 27 deletions(-) diff --git a/storyboard/api/v1/search/sqlalchemy_impl.py b/storyboard/api/v1/search/sqlalchemy_impl.py index aa5cc94b..2ddee57a 100644 --- a/storyboard/api/v1/search/sqlalchemy_impl.py +++ b/storyboard/api/v1/search/sqlalchemy_impl.py @@ -20,6 +20,7 @@ import sqlalchemy_fulltext.modes as FullTextMode from storyboard.api.v1.search import search_engine from storyboard.db.api import base as api_base from storyboard.db.api import stories as stories_api +from storyboard.db.api import tasks as tasks_api from storyboard.db import models @@ -45,9 +46,10 @@ class SqlAlchemySearchImpl(search_engine.SearchEngine): return api_base.paginate_query(query=query, model=model_cls, limit=limit, - sort_key="id", + sort_key=sort_field, marker=marker_entity, - offset=offset) + offset=offset, + sort_dir=sort_dir) def projects_query(self, q, sort_dir=None, marker=None, offset=None, limit=None): @@ -112,19 +114,35 @@ class SqlAlchemySearchImpl(search_engine.SearchEngine): return query.all() - def tasks_query(self, q, marker=None, offset=None, limit=None, - current_user=None, **kwargs): + def tasks_query(self, q, story_id=None, assignee_id=None, project_id=None, + project_group_id=None, branch_id=None, milestone_id=None, + status=None, offset=None, limit=None, current_user=None, + sort_field='id', sort_dir='asc'): session = api_base.get_session() - query = api_base.model_query(models.Task, session) + + query = tasks_api.task_build_query( + project_group_id=project_group_id, + story_id=story_id, + assignee_id=assignee_id, + project_id=project_id, + branch_id=branch_id, + milestone_id=milestone_id, + status=status, + session=session) query = self._build_fulltext_search(models.Task, query, q) - # Filter out tasks or stories that the current user can't see + # Filter out stories that the current user can't see query = query.outerjoin(models.Story) query = api_base.filter_private_stories(query, current_user) query = self._apply_pagination( - models.Task, query, marker, offset, limit) + models.Task, + query, + offset=offset, + limit=limit, + sort_field=sort_field, + sort_dir=sort_dir) return query.all() diff --git a/storyboard/api/v1/tasks.py b/storyboard/api/v1/tasks.py index 401e832c..f5ad01ed 100644 --- a/storyboard/api/v1/tasks.py +++ b/storyboard/api/v1/tasks.py @@ -444,25 +444,49 @@ class TasksPrimaryController(rest.RestController): @decorators.db_exceptions @secure(checks.guest) - @wsme_pecan.wsexpose([wmodels.Task], wtypes.text, wtypes.text, int, - int, int) - def search(self, q="", marker=None, offset=None, limit=None): - """The search endpoint for tasks. + @wsme_pecan.wsexpose([wmodels.Task], wtypes.text, int, int, int, int, int, + int, [wtypes.text], int, int, wtypes.text, + wtypes.text) + def search(self, q="", story_id=None, assignee_id=None, + project_id=None, project_group_id=None, branch_id=None, + milestone_id=None, status=None, offset=None, limit=None, + sort_field='id', sort_dir='asc'): + """Search and filter the tasks. Example:: curl https://my.example.org/api/v1/tasks/search?q=mary - :param q: The query string. + :param q: Fulltext search query parameter. + :param story_id: Filter tasks by story ID. + :param assignee_id: Filter tasks by who they are assigned to. + :param project_id: Filter the tasks based on project. + :param project_group_id: Filter tasks based on project group. + :param branch_id: Filter tasks based on branch_id. + :param milestone_id: Filter tasks based on milestone. + :param status: Filter tasks by status. + :param offset: The offset to start the results at. + :param limit: The number of tasks to retrieve. + :param sort_field: The name of the field to sort on. + :param sort_dir: Sort direction for results (asc, desc). :return: List of Tasks matching the query. """ user = request.current_user_id - tasks = SEARCH_ENGINE.tasks_query(q=q, - marker=marker, - offset=offset, - limit=limit, - current_user=user) + tasks = SEARCH_ENGINE.tasks_query( + q=q, + story_id=story_id, + assignee_id=assignee_id, + project_id=project_id, + project_group_id=project_group_id, + branch_id=branch_id, + milestone_id=milestone_id, + status=status, + sort_field=sort_field, + sort_dir=sort_dir, + offset=offset, + limit=limit, + current_user=user) return [wmodels.Task.from_db_model(task) for task in tasks] diff --git a/storyboard/db/api/tasks.py b/storyboard/db/api/tasks.py index 030c8f81..af43c472 100644 --- a/storyboard/db/api/tasks.py +++ b/storyboard/db/api/tasks.py @@ -38,8 +38,11 @@ def task_get_all(marker=None, limit=None, sort_field=None, sort_dir=None, sort_dir = 'asc' # Construct the query - query = task_build_query( - project_group_id, current_user=current_user, **kwargs) + query = task_build_query(project_group_id, **kwargs) + + # Filter out tasks or stories that the current user can't see + query = query.outerjoin(models.Story) + query = api_base.filter_private_stories(query, current_user) query = api_base.paginate_query(query=query, model=models.Task, @@ -53,8 +56,12 @@ def task_get_all(marker=None, limit=None, sort_field=None, sort_dir=None, def task_get_count(project_group_id=None, current_user=None, **kwargs): - query = task_build_query( - project_group_id, current_user=current_user, **kwargs) + query = task_build_query(project_group_id, **kwargs) + + # Filter out tasks or stories that the current user can't see + query = query.outerjoin(models.Story) + query = api_base.filter_private_stories(query, current_user) + return query.count() @@ -82,9 +89,9 @@ def task_delete(task_id): api_base.entity_hard_delete(models.Task, task_id) -def task_build_query(project_group_id, current_user=None, **kwargs): +def task_build_query(project_group_id, session=None, **kwargs): # Construct the query - query = api_base.model_query(models.Task) + query = api_base.model_query(models.Task, session=session) if project_group_id: query = query.join(models.Project, @@ -97,10 +104,6 @@ def task_build_query(project_group_id, current_user=None, **kwargs): model=models.Task, **kwargs) - # Filter out tasks or stories that the current user can't see - query = query.outerjoin(models.Story) - query = api_base.filter_private_stories(query, current_user) - return query