Adding bulk_create_related_userstories endpoint to epics API

remotes/origin/issue/4795/notification_even_they_are_disabled
Alejandro Alonso 2016-08-02 12:49:33 +02:00 committed by David Barragán Merino
parent dd3b098d4e
commit 46f6fa71e6
7 changed files with 114 additions and 6 deletions

View File

@ -83,6 +83,7 @@ def save_in_bulk(instances, callback=None, precall=None, **save_options):
:params callback: Callback to call after each save. :params callback: Callback to call after each save.
:params save_options: Additional options to use when saving each instance. :params save_options: Additional options to use when saving each instance.
""" """
ret = []
if callback is None: if callback is None:
callback = functions.noop callback = functions.noop
@ -98,6 +99,7 @@ def save_in_bulk(instances, callback=None, precall=None, **save_options):
instance.save(**save_options) instance.save(**save_options)
callback(instance, created=created) callback(instance, created=created)
return ret
@transaction.atomic @transaction.atomic
def update_in_bulk(instances, list_of_new_values, callback=None, precall=None): def update_in_bulk(instances, list_of_new_values, callback=None, precall=None):

View File

@ -22,7 +22,7 @@ from django.utils.translation import ugettext as _
from taiga.base.api.utils import get_object_or_404 from taiga.base.api.utils import get_object_or_404
from taiga.base import filters, response from taiga.base import filters, response
from taiga.base import exceptions as exc from taiga.base import exceptions as exc
from taiga.base.decorators import list_route from taiga.base.decorators import list_route, detail_route
from taiga.base.api import ModelCrudViewSet, ModelListViewSet from taiga.base.api import ModelCrudViewSet, ModelListViewSet
from taiga.base.api.mixins import BlockedByProjectMixin from taiga.base.api.mixins import BlockedByProjectMixin
@ -210,6 +210,29 @@ class EpicViewSet(OCCResourceMixin, VotedResourceMixin, HistoryResourceMixin,
def bulk_update_epics_order(self, request, **kwargs): def bulk_update_epics_order(self, request, **kwargs):
return self._bulk_update_order("epics_order", request, **kwargs) return self._bulk_update_order("epics_order", request, **kwargs)
@detail_route(methods=["POST"])
def bulk_create_related_userstories(self, request, **kwargs):
validator = validators.CrateRelatedUserStoriesBulkValidator(data=request.DATA)
if validator.is_valid():
data = validator.data
obj = self.get_object()
project = obj.project
self.check_permissions(request, 'bulk_create_userstories', project)
if project.blocked_code is not None:
raise exc.Blocked(_("Blocked element"))
services.create_related_userstories_in_bulk(
data["userstories"],
obj,
project=project,
owner=request.user
)
obj = self.get_queryset().get(id=obj.id)
epic_serialized = self.get_serializer_class()(obj)
return response.Ok(epic_serialized.data)
return response.BadRequest(validator.errors)
class EpicVotersViewSet(VotersViewSetMixin, ModelListViewSet): class EpicVotersViewSet(VotersViewSetMixin, ModelListViewSet):
permission_classes = (permissions.EpicVotersPermission,) permission_classes = (permissions.EpicVotersPermission,)

View File

@ -16,8 +16,8 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from taiga.base.api.permissions import TaigaResourcePermission, AllowAny, IsAuthenticated, IsSuperUser from taiga.base.api.permissions import TaigaResourcePermission, AllowAny, IsAuthenticated
from taiga.permissions.permissions import HasProjectPerm, IsProjectAdmin from taiga.base.api.permissions import IsSuperUser, HasProjectPerm, IsProjectAdmin
from taiga.permissions.permissions import CommentAndOrUpdatePerm from taiga.permissions.permissions import CommentAndOrUpdatePerm
@ -35,6 +35,7 @@ class EpicPermission(TaigaResourcePermission):
csv_perms = AllowAny() csv_perms = AllowAny()
bulk_create_perms = HasProjectPerm('add_epic') bulk_create_perms = HasProjectPerm('add_epic')
bulk_update_order_perms = HasProjectPerm('modify_epic') bulk_update_order_perms = HasProjectPerm('modify_epic')
bulk_create_userstories_perms = HasProjectPerm('modify_epic') & (HasProjectPerm('add_us_to_project') | HasProjectPerm('add_us'))
upvote_perms = IsAuthenticated() & HasProjectPerm('view_epics') upvote_perms = IsAuthenticated() & HasProjectPerm('view_epics')
downvote_perms = IsAuthenticated() & HasProjectPerm('view_epics') downvote_perms = IsAuthenticated() & HasProjectPerm('view_epics')
watch_perms = IsAuthenticated() & HasProjectPerm('view_epics') watch_perms = IsAuthenticated() & HasProjectPerm('view_epics')

View File

@ -26,10 +26,12 @@ from django.db import connection
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from taiga.base.utils import db, text from taiga.base.utils import db, text
from taiga.projects.history.services import take_snapshot
from taiga.projects.services import apply_order_updates from taiga.projects.services import apply_order_updates
from taiga.projects.epics.apps import connect_epics_signals from taiga.projects.epics.apps import connect_epics_signals
from taiga.projects.epics.apps import disconnect_epics_signals from taiga.projects.epics.apps import disconnect_epics_signals
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
from taiga.events import events from taiga.events import events
from taiga.projects.votes.utils import attach_total_voters_to_queryset from taiga.projects.votes.utils import attach_total_voters_to_queryset
from taiga.projects.notifications.utils import attach_watchers_to_queryset from taiga.projects.notifications.utils import attach_watchers_to_queryset
@ -96,6 +98,35 @@ def update_epics_order_in_bulk(bulk_data: list, field: str, project: object):
return epic_orders return epic_orders
def create_related_userstories_in_bulk(bulk_data, epic, **additional_fields):
"""Create user stories from `bulk_data`.
:param epic: Element where all the user stories will be contained
:param bulk_data: List of user stories in bulk format.
:param additional_fields: Additional fields when instantiating each user story.
:return: List of created `Task` instances.
"""
userstories = get_userstories_from_bulk(bulk_data, **additional_fields)
disconnect_userstories_signals()
try:
db.save_in_bulk(userstories)
related_userstories = []
for userstory in userstories:
related_userstories.append(
models.RelatedUserStory(
user_story=userstory,
epic=epic
)
)
db.save_in_bulk(related_userstories)
finally:
connect_userstories_signals()
return userstories
##################################################### #####################################################
# CSV # CSV
##################################################### #####################################################

View File

@ -55,6 +55,11 @@ class EpicsBulkValidator(ProjectExistsValidator, EpicExistsValidator,
bulk_epics = serializers.CharField() bulk_epics = serializers.CharField()
class CrateRelatedUserStoriesBulkValidator(ProjectExistsValidator, EpicExistsValidator,
validators.Validator):
userstories = serializers.CharField()
# Order bulk validators # Order bulk validators
class _EpicOrderBulkValidator(EpicExistsValidator, validators.Validator): class _EpicOrderBulkValidator(EpicExistsValidator, validators.Validator):

View File

@ -664,6 +664,34 @@ def test_epic_action_bulk_create(client, data):
assert results == [401, 403, 403, 451, 451] assert results == [401, 403, 403, 451, 451]
def test_bulk_create_related_userstories(client, data):
public_url = reverse('epics-bulk-create-related-userstories', kwargs={"pk": data.public_epic.pk})
private_url1 = reverse('epics-bulk-create-related-userstories', kwargs={"pk": data.private_epic1.pk})
private_url2 = reverse('epics-bulk-create-related-userstories', kwargs={"pk": data.private_epic2.pk})
blocked_url = reverse('epics-bulk-create-related-userstories', kwargs={"pk": data.blocked_epic.pk})
users = [
None,
data.registered_user,
data.project_member_without_perms,
data.project_member_with_perms,
data.project_owner
]
bulk_data = json.dumps({
"userstories": "test1\ntest2",
})
results = helper_test_http_method(client, 'post', public_url, bulk_data, users)
assert results == [401, 403, 403, 200, 200]
results = helper_test_http_method(client, 'post', private_url1, bulk_data, users)
assert results == [401, 403, 403, 200, 200]
results = helper_test_http_method(client, 'post', private_url2, bulk_data, users)
assert results == [404, 404, 404, 200, 200]
results = helper_test_http_method(client, 'post', blocked_url, bulk_data, users)
assert results == [404, 404, 404, 451, 451]
def test_epic_action_upvote(client, data): def test_epic_action_upvote(client, data):
public_url = reverse('epics-upvote', kwargs={"pk": data.public_epic.pk}) public_url = reverse('epics-upvote', kwargs={"pk": data.public_epic.pk})
private_url1 = reverse('epics-upvote', kwargs={"pk": data.private_epic1.pk}) private_url1 = reverse('epics-upvote', kwargs={"pk": data.private_epic1.pk})

View File

@ -66,3 +66,21 @@ def test_custom_fields_csv_generation():
assert row[17] == attr.name assert row[17] == attr.name
row = next(reader) row = next(reader)
assert row[17] == "val1" assert row[17] == "val1"
def test_bulk_create_related_userstories(client):
user = f.UserFactory.create()
project = f.ProjectFactory.create(owner=user)
epic = f.EpicFactory.create(project=project)
f.MembershipFactory.create(project=project, user=user, is_admin=True)
url = reverse('epics-bulk-create-related-userstories', kwargs={"pk": epic.pk})
data = {
"userstories": "test1\ntest2"
}
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': 2, 'closed': 0}