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
This commit is contained in:
Adam Coldrick 2016-10-26 22:31:50 +00:00 committed by Adam Coldrick
parent 4aed46c189
commit e0b093ab89
3 changed files with 72 additions and 27 deletions

View File

@ -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()

View File

@ -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]

View File

@ -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