diff --git a/taiga/searches/api.py b/taiga/searches/api.py index 37f9c7f3..0c252b5a 100644 --- a/taiga/searches/api.py +++ b/taiga/searches/api.py @@ -40,6 +40,10 @@ class SearchViewSet(viewsets.ViewSet): result = {} with futures.ThreadPoolExecutor(max_workers=4) as executor: futures_list = [] + if user_has_perm(request.user, "view_epics", project): + epics_future = executor.submit(self._search_epics, project, text) + epics_future.result_key = "epics" + futures_list.append(epics_future) if user_has_perm(request.user, "view_us", project): uss_future = executor.submit(self._search_user_stories, project, text) uss_future.result_key = "userstories" @@ -73,6 +77,11 @@ class SearchViewSet(viewsets.ViewSet): project_model = apps.get_model("projects", "Project") return get_object_or_404(project_model, pk=project_id) + def _search_epics(self, project, text): + queryset = services.search_epics(project, text) + serializer = serializers.EpicSearchResultsSerializer(queryset, many=True) + return serializer.data + def _search_user_stories(self, project, text): queryset = services.search_user_stories(project, text) serializer = serializers.UserStorySearchResultsSerializer(queryset, many=True) diff --git a/taiga/searches/serializers.py b/taiga/searches/serializers.py index e96e1131..7adc34b2 100644 --- a/taiga/searches/serializers.py +++ b/taiga/searches/serializers.py @@ -20,15 +20,7 @@ from taiga.base.api import serializers from taiga.base.fields import Field, MethodField -class IssueSearchResultsSerializer(serializers.LightSerializer): - id = Field() - ref = Field() - subject = Field() - status = Field(attr="status_id") - assigned_to = Field(attr="assigned_to_id") - - -class TaskSearchResultsSerializer(serializers.LightSerializer): +class EpicSearchResultsSerializer(serializers.LightSerializer): id = Field() ref = Field() subject = Field() @@ -58,6 +50,22 @@ class UserStorySearchResultsSerializer(serializers.LightSerializer): return obj.total_points_attr +class TaskSearchResultsSerializer(serializers.LightSerializer): + id = Field() + ref = Field() + subject = Field() + status = Field(attr="status_id") + assigned_to = Field(attr="assigned_to_id") + + +class IssueSearchResultsSerializer(serializers.LightSerializer): + id = Field() + ref = Field() + subject = Field() + status = Field(attr="status_id") + assigned_to = Field(attr="assigned_to_id") + + class WikiPageSearchResultsSerializer(serializers.LightSerializer): id = Field() slug = Field() diff --git a/taiga/searches/services.py b/taiga/searches/services.py index afc6a7e5..adda60bb 100644 --- a/taiga/searches/services.py +++ b/taiga/searches/services.py @@ -24,6 +24,13 @@ from taiga.projects.userstories.utils import attach_total_points MAX_RESULTS = getattr(settings, "SEARCHES_MAX_RESULTS", 150) +def search_epics(project, text): + model = apps.get_model("epics", "Epic") + queryset = model.objects.filter(project_id=project.pk) + table = "epics_epic" + return _search_items(queryset, table, text) + + def search_user_stories(project, text): model = apps.get_model("userstories", "UserStory") queryset = model.objects.filter(project_id=project.pk) @@ -32,16 +39,16 @@ def search_user_stories(project, text): def search_tasks(project, text): - model = apps.get_model("userstories", "UserStory") + model = apps.get_model("tasks", "Task") queryset = model.objects.filter(project_id=project.pk) - table = "userstories_userstory" + table = "tasks_task" return _search_items(queryset, table, text) def search_issues(project, text): - model = apps.get_model("userstories", "UserStory") + model = apps.get_model("issues", "Issue") queryset = model.objects.filter(project_id=project.pk) - table = "userstories_userstory" + table = "issues_issue" return _search_items(queryset, table, text) diff --git a/tests/integration/test_searches.py b/tests/integration/test_searches.py index 1ccd5233..bb5e681d 100644 --- a/tests/integration/test_searches.py +++ b/tests/integration/test_searches.py @@ -53,6 +53,12 @@ def searches_initial_data(): role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS))) + m.epic11 = f.EpicFactory(project=m.project1, subject="Back to the future") + m.epic12 = f.EpicFactory(project=m.project1, tags=["Back", "future"]) + m.epic13 = f.EpicFactory(project=m.project1) + m.epic14 = f.EpicFactory(project=m.project1, description="Backend to the future") + m.epic21 = f.EpicFactory(project=m.project2, subject="Back to the future") + m.us11 = f.UserStoryFactory(project=m.project1, subject="Back to the future") m.us12 = f.UserStoryFactory(project=m.project1, description="Back to the future") m.us13 = f.UserStoryFactory(project=m.project1, tags=["Backend", "future"]) @@ -87,7 +93,8 @@ def test_search_all_objects_in_my_project(client, searches_initial_data): response = client.get(reverse("search-list"), {"project": data.project1.id}) assert response.status_code == 200 - assert response.data["count"] == 16 + assert response.data["count"] == 20 + assert len(response.data["epics"]) == 4 assert len(response.data["userstories"]) == 4 assert len(response.data["tasks"]) == 4 assert len(response.data["issues"]) == 4 @@ -111,20 +118,48 @@ def test_search_text_query_in_my_project(client, searches_initial_data): response = client.get(reverse("search-list"), {"project": data.project1.id, "text": "future"}) assert response.status_code == 200 - assert response.data["count"] == 9 + assert response.data["count"] == 12 + assert len(response.data["epics"]) == 3 + assert response.data["epics"][0]["id"] == searches_initial_data.epic11.id + assert response.data["epics"][1]["id"] == searches_initial_data.epic12.id + assert response.data["epics"][2]["id"] == searches_initial_data.epic14.id assert len(response.data["userstories"]) == 3 + assert response.data["userstories"][0]["id"] == searches_initial_data.us11.id + assert response.data["userstories"][1]["id"] == searches_initial_data.us13.id + assert response.data["userstories"][2]["id"] == searches_initial_data.us12.id assert len(response.data["tasks"]) == 3 + assert response.data["tasks"][0]["id"] == searches_initial_data.task11.id + assert response.data["tasks"][1]["id"] == searches_initial_data.task12.id + assert response.data["tasks"][2]["id"] == searches_initial_data.task14.id assert len(response.data["issues"]) == 3 + assert response.data["issues"][0]["id"] == searches_initial_data.issue14.id + assert response.data["issues"][1]["id"] == searches_initial_data.issue12.id + assert response.data["issues"][2]["id"] == searches_initial_data.issue11.id assert len(response.data["wikipages"]) == 0 response = client.get(reverse("search-list"), {"project": data.project1.id, "text": "back"}) assert response.status_code == 200 - assert response.data["count"] == 11 + assert response.data["count"] == 14 + assert len(response.data["epics"]) == 3 + assert response.data["epics"][0]["id"] == searches_initial_data.epic11.id + assert response.data["epics"][1]["id"] == searches_initial_data.epic12.id + assert response.data["epics"][2]["id"] == searches_initial_data.epic14.id assert len(response.data["userstories"]) == 3 + assert response.data["userstories"][0]["id"] == searches_initial_data.us11.id + assert response.data["userstories"][1]["id"] == searches_initial_data.us13.id + assert response.data["userstories"][2]["id"] == searches_initial_data.us12.id assert len(response.data["tasks"]) == 3 + assert response.data["tasks"][0]["id"] == searches_initial_data.task11.id + assert response.data["tasks"][1]["id"] == searches_initial_data.task12.id + assert response.data["tasks"][2]["id"] == searches_initial_data.task14.id assert len(response.data["issues"]) == 3 + assert response.data["issues"][0]["id"] == searches_initial_data.issue14.id + assert response.data["issues"][1]["id"] == searches_initial_data.issue12.id + assert response.data["issues"][2]["id"] == searches_initial_data.issue11.id # Back is a backend substring assert len(response.data["wikipages"]) == 2 + assert response.data["wikipages"][0]["id"] == searches_initial_data.wikipage14.id + assert response.data["wikipages"][1]["id"] == searches_initial_data.wikipage13.id def test_search_text_query_with_an_invalid_project_id(client, searches_initial_data):