From 8d0c9ee1c1a89aff35bd0ab19d45d93675246f60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Mon, 22 Aug 2016 13:05:44 +0200 Subject: [PATCH] Fix test and RelatedUserStoyr API nested endpoint --- taiga/base/api/mixins.py | 29 ++++++++++++++++++++++++---- taiga/base/utils/db.py | 6 +++--- taiga/permissions/services.py | 1 - taiga/projects/epics/api.py | 8 ++++---- taiga/projects/epics/serializers.py | 1 + taiga/projects/epics/services.py | 30 ++++++++++++++++++++++++++++- taiga/projects/epics/validators.py | 2 +- tests/integration/test_epics.py | 19 ++++++++---------- 8 files changed, 71 insertions(+), 25 deletions(-) diff --git a/taiga/base/api/mixins.py b/taiga/base/api/mixins.py index c38b5cb7..b01d7cf2 100644 --- a/taiga/base/api/mixins.py +++ b/taiga/base/api/mixins.py @@ -211,14 +211,14 @@ class UpdateModelMixin: Set any attributes on the object that are implicit in the request. """ # pk and/or slug attributes are implicit in the URL. - lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field - lookup = self.kwargs.get(lookup_url_kwarg, None) + ##lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field + ##lookup = self.kwargs.get(lookup_url_kwarg, None) pk = self.kwargs.get(self.pk_url_kwarg, None) slug = self.kwargs.get(self.slug_url_kwarg, None) slug_field = slug and self.slug_field or None - if lookup: - setattr(obj, self.lookup_field, lookup) + ##if lookup: + ## setattr(obj, self.lookup_field, lookup) if pk: setattr(obj, 'pk', pk) @@ -253,6 +253,27 @@ class DestroyModelMixin: return response.NoContent() +class NestedViewSetMixin(object): + def get_queryset(self): + return self._filter_queryset_by_parents_lookups(super().get_queryset()) + + def _filter_queryset_by_parents_lookups(self, queryset): + parents_query_dict = self._get_parents_query_dict() + if parents_query_dict: + return queryset.filter(**parents_query_dict) + else: + return queryset + + def _get_parents_query_dict(self): + result = {} + for kwarg_name in self.kwargs: + query_value = self.kwargs.get(kwarg_name) + result[kwarg_name] = query_value + return result + + +## TODO: Move blocked mixind out of the base module because is related to project + class BlockeableModelMixin: def is_blocked(self, obj): raise NotImplementedError("is_blocked must be overridden") diff --git a/taiga/base/utils/db.py b/taiga/base/utils/db.py index a6c21d6b..eb610fff 100644 --- a/taiga/base/utils/db.py +++ b/taiga/base/utils/db.py @@ -132,13 +132,13 @@ def update_attr_in_bulk_for_ids(values, attr, model): """ values = [str((id, order)) for id, order in values.items()] sql = """ - UPDATE {tbl} - SET {attr}=update_values.column2 + UPDATE "{tbl}" + SET "{attr}"=update_values.column2 FROM ( VALUES {values} ) AS update_values - WHERE {tbl}.id=update_values.column1; + WHERE "{tbl}"."id"=update_values.column1; """.format(tbl=model._meta.db_table, values=', '.join(values), attr=attr) diff --git a/taiga/permissions/services.py b/taiga/permissions/services.py index 6d89c168..50d8d72d 100644 --- a/taiga/permissions/services.py +++ b/taiga/permissions/services.py @@ -78,7 +78,6 @@ def user_has_perm(user, perm, obj=None, cache="user"): in cache """ project = _get_object_project(obj) - if not project: return False diff --git a/taiga/projects/epics/api.py b/taiga/projects/epics/api.py index cc1b1c5d..5888865d 100644 --- a/taiga/projects/epics/api.py +++ b/taiga/projects/epics/api.py @@ -232,7 +232,7 @@ class EpicRelatedUserStoryViewSet(NestedViewSetMixin, BlockedByProjectMixin, Mod validator_class = validators.EpicRelatedUserStoryValidator model = models.RelatedUserStory permission_classes = (permissions.EpicRelatedUserStoryPermission,) - lookup_field = "user_story_id" + lookup_field = "user_story" """ Updating the order attribute can affect the ordering of another userstories in the epic @@ -259,7 +259,7 @@ class EpicRelatedUserStoryViewSet(NestedViewSetMixin, BlockedByProjectMixin, Mod extra_orders = json.loads(self.request.META.get("HTTP_SET_ORDERS", "{}")) data = [{"us_id": obj.id, "order": getattr(obj, "order")}] for id, order in extra_orders.items(): - data.append({"epic_id": int(id), "order": order}) + data.append({"us_id": int(id), "order": order}) return services.update_epic_related_userstories_order_in_bulk(data, epic=obj.epic) @@ -268,8 +268,8 @@ class EpicRelatedUserStoryViewSet(NestedViewSetMixin, BlockedByProjectMixin, Mod if not created: # Let's reorder the related stuff after edit the element orders_updated = self._reorder_if_needed(obj, - self._old_epics_order_key, - self._epics_order_key(obj)) + self._old_order_key, + self._order_key(obj)) self.headers["Taiga-Info-Order-Updated"] = json.dumps(orders_updated) super().post_save(obj, created) diff --git a/taiga/projects/epics/serializers.py b/taiga/projects/epics/serializers.py index b02238cd..339272de 100644 --- a/taiga/projects/epics/serializers.py +++ b/taiga/projects/epics/serializers.py @@ -81,5 +81,6 @@ class EpicNeighborsSerializer(NeighborsSerializerMixin, EpicSerializer): class EpicRelatedUserStorySerializer(serializers.LightSerializer): + epic = Field(attr="epic_id") user_story = Field(attr="user_story_id") order = Field() diff --git a/taiga/projects/epics/services.py b/taiga/projects/epics/services.py index 145e2339..f953a574 100644 --- a/taiga/projects/epics/services.py +++ b/taiga/projects/epics/services.py @@ -26,9 +26,9 @@ from django.db import connection from django.utils.translation import ugettext as _ from taiga.base.utils import db, text -from taiga.projects.services import apply_order_updates from taiga.projects.epics.apps import connect_epics_signals from taiga.projects.epics.apps import disconnect_epics_signals +from taiga.projects.services import apply_order_updates from taiga.projects.userstories.apps import connect_userstories_signals from taiga.projects.userstories.apps import disconnect_userstories_signals from taiga.projects.userstories.services import get_userstories_from_bulk @@ -127,6 +127,34 @@ def create_related_userstories_in_bulk(bulk_data, epic, **additional_fields): return userstories +def update_epic_related_userstories_order_in_bulk(bulk_data: list, epic: object): + """ + Updates the order of the related userstories of an specific epic. + `bulk_data` should be a list of dicts with the following format: + `epic` is the epic with related stories. + + [{'us_id': , 'order': }, ...] + """ + related_user_stories = epic.relateduserstory_set.all() + rus_orders = {rus.id: rus.order for rus in related_user_stories} + + rus_conversion = {rus.user_story_id: rus.id for rus in related_user_stories} + new_rus_orders = {rus_conversion[e["us_id"]]: e["order"] for e in bulk_data + if e["us_id"] in rus_conversion} + + apply_order_updates(rus_orders, new_rus_orders) + + if rus_orders: + related_user_story_ids = rus_orders.keys() + events.emit_event_for_ids(ids=related_user_story_ids, + content_type="epics.relateduserstory", + projectid=epic.project_id) + + db.update_attr_in_bulk_for_ids(rus_orders, "order", models.RelatedUserStory) + + return rus_orders + + ##################################################### # CSV ##################################################### diff --git a/taiga/projects/epics/validators.py b/taiga/projects/epics/validators.py index 88214aa6..175f1c3c 100644 --- a/taiga/projects/epics/validators.py +++ b/taiga/projects/epics/validators.py @@ -63,4 +63,4 @@ class CrateRelatedUserStoriesBulkValidator(ProjectExistsValidator, EpicExistsVal class EpicRelatedUserStoryValidator(validators.ModelValidator): class Meta: model = models.RelatedUserStory - read_only_fields = ('id', 'epic', 'user_story') + read_only_fields = ('id',) diff --git a/tests/integration/test_epics.py b/tests/integration/test_epics.py index 5d4657df..0ca87d09 100644 --- a/tests/integration/test_epics.py +++ b/tests/integration/test_epics.py @@ -119,13 +119,13 @@ def test_set_related_userstory(client): url = reverse('epics-related-userstories-list', args=[epic.pk]) data = { - "user_story": us.id + "user_story": us.id, + "epic": epic.pk } client.login(user) response = client.json.post(url, json.dumps(data)) - print(response.data) - assert response.status_code == 200 - assert response.data['user_stories_counts'] == {'opened': 1, 'closed': 0} + + assert response.status_code == 201 def test_set_related_userstory_existing(client): @@ -136,18 +136,15 @@ def test_set_related_userstory_existing(client): f.MembershipFactory.create(project=epic.project, user=user, is_admin=True) f.MembershipFactory.create(project=us.project, user=user, is_admin=True) - url = reverse('epics-related-userstories-list', args=[epic.pk]) - + url = reverse('epics-related-userstories-detail', args=[epic.pk, us.pk]) data = { - "user_story": us.id, "order": 77 } client.login(user) - response = client.json.post(url, json.dumps(data)) + response = client.json.patch(url, json.dumps(data)) assert response.status_code == 200 - assert response.data['user_stories_counts'] == {'opened': 1, 'closed': 0} - related_us = models.RelatedUserStory.objects.get(id=related_us.id) + related_us.refresh_from_db() assert related_us.order == 77 @@ -158,7 +155,7 @@ def test_unset_related_userstory(client): related_us = f.RelatedUserStory.create(epic=epic, user_story=us, order=55) f.MembershipFactory.create(project=epic.project, user=user, is_admin=True) - url = reverse('epics-related-userstories-detail', args=[epic.pk, us.pk]) + url = reverse('epics-related-userstories-detail', args=[epic.pk, us.id]) client.login(user) response = client.delete(url)