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 save_options: Additional options to use when saving each instance.
"""
ret = []
if callback is None:
callback = functions.noop
@ -98,6 +99,7 @@ def save_in_bulk(instances, callback=None, precall=None, **save_options):
instance.save(**save_options)
callback(instance, created=created)
return ret
@transaction.atomic
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 import filters, response
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.mixins import BlockedByProjectMixin
@ -210,6 +210,29 @@ class EpicViewSet(OCCResourceMixin, VotedResourceMixin, HistoryResourceMixin,
def bulk_update_epics_order(self, 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):
permission_classes = (permissions.EpicVotersPermission,)

View File

@ -16,8 +16,8 @@
# 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/>.
from taiga.base.api.permissions import TaigaResourcePermission, AllowAny, IsAuthenticated, IsSuperUser
from taiga.permissions.permissions import HasProjectPerm, IsProjectAdmin
from taiga.base.api.permissions import TaigaResourcePermission, AllowAny, IsAuthenticated
from taiga.base.api.permissions import IsSuperUser, HasProjectPerm, IsProjectAdmin
from taiga.permissions.permissions import CommentAndOrUpdatePerm
@ -35,6 +35,7 @@ class EpicPermission(TaigaResourcePermission):
csv_perms = AllowAny()
bulk_create_perms = HasProjectPerm('add_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')
downvote_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 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.epics.apps import connect_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.projects.votes.utils import attach_total_voters_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
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
#####################################################

View File

@ -55,6 +55,11 @@ class EpicsBulkValidator(ProjectExistsValidator, EpicExistsValidator,
bulk_epics = serializers.CharField()
class CrateRelatedUserStoriesBulkValidator(ProjectExistsValidator, EpicExistsValidator,
validators.Validator):
userstories = serializers.CharField()
# Order bulk validators
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]
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):
public_url = reverse('epics-upvote', kwargs={"pk": data.public_epic.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
row = next(reader)
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}