diff --git a/requirements-devel.txt b/requirements-devel.txt index b506a70d..da2b1cd1 100644 --- a/requirements-devel.txt +++ b/requirements-devel.txt @@ -8,4 +8,4 @@ pytest-pythonpath==0.3 coverage==3.7.1 coveralls==0.4.2 -django-testclient-extensions==0.1.1 +django-testclient-extensions==0.1.2 diff --git a/taiga/projects/api.py b/taiga/projects/api.py index a9bf87f2..823991b0 100644 --- a/taiga/projects/api.py +++ b/taiga/projects/api.py @@ -171,6 +171,17 @@ class MembershipViewSet(ModelCrudViewSet): return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + @list_route(methods=["POST"]) + def bulk_create(self, request, **kwargs): + bulk_members = request.DATA.get('bulkMembers', None) + if bulk_members is None: + raise exc.BadRequest(_('bulkMembers parameter is mandatory')) + + members = services.create_members_in_bulk(bulk_members, callback=self.post_save) + + members_serialized = self.serializer_class(members, many=True) + return Response(data=members_serialized.data) + def pre_save(self, object): # Only assign new token if a current token value is empty. if not object.token: diff --git a/taiga/projects/services/__init__.py b/taiga/projects/services/__init__.py index 5ee8e086..0276e3c5 100644 --- a/taiga/projects/services/__init__.py +++ b/taiga/projects/services/__init__.py @@ -30,3 +30,6 @@ from .filters import get_issues_filters_data from .stats import get_stats_for_project_issues from .stats import get_stats_for_project + +from .members import create_members_in_bulk +from .members import get_members_from_bulk diff --git a/taiga/projects/services/members.py b/taiga/projects/services/members.py new file mode 100644 index 00000000..e35635f0 --- /dev/null +++ b/taiga/projects/services/members.py @@ -0,0 +1,33 @@ +from taiga.base.utils import db, text + +from .. import models + + +def get_members_from_bulk(bulk_data, **additional_fields): + """Convert `bulk_data` into a list of members. + + :param bulk_data: List of members in bulk format. + :param additional_fields: Additional fields when instantiating each task. + + :return: List of `Member` instances. + """ + members = [] + for data in bulk_data: + data_copy = data.copy() + data_copy.update(additional_fields) + members.append(models.Membership(**data_copy)) + return members + + +def create_members_in_bulk(bulk_data, callback=None, **additional_fields): + """Create members from `bulk_data`. + + :param bulk_data: List of dicts `{"project_id": <>, "role_id": <>, "email": <>}`. + :param callback: Callback to execute after each task save. + :param additional_fields: Additional fields when instantiating each task. + + :return: List of created `Member` instances. + """ + members = get_members_from_bulk(bulk_data, **additional_fields) + db.save_in_bulk(members, callback) + return members diff --git a/tests/integration/test_memberships.py b/tests/integration/test_memberships.py new file mode 100644 index 00000000..f7377661 --- /dev/null +++ b/tests/integration/test_memberships.py @@ -0,0 +1,53 @@ +from unittest import mock + +import pytest + +from django.core.urlresolvers import reverse + +from taiga.projects import services + +from .. import factories as f + +pytestmark = pytest.mark.django_db + + +def test_get_members_from_bulk(): + data = [{"project_id": "1", "role_id": "1", "email": "member1@email.com"}, + {"project_id": "1", "role_id": "1", "email": "member2@email.com"}] + members = services.get_members_from_bulk(data) + + assert len(members) == 2 + assert members[0].email == "member1@email.com" + assert members[1].email == "member2@email.com" + + +@mock.patch("taiga.projects.services.members.db") +def test_create_members_in_bulk(db): + data = [{"project_id": "1", "role_id": "1", "email": "member1@email.com"}, + {"project_id": "1", "role_id": "1", "email": "member2@email.com"}] + members = services.create_members_in_bulk(data) + + db.save_in_bulk.assert_called_once_with(members, None) + + +def test_api_create_bulk_members(client): + project = f.ProjectFactory() + john = f.UserFactory.create() + joseph = f.UserFactory.create() + tester = f.RoleFactory(project=project, name="Tester") + gamer = f.RoleFactory(project=project, name="Gamer") + + url = reverse("memberships-bulk-create") + + data = { + "bulkMembers": [ + {"project_id": project.pk, "role_id": tester.pk, "email": john.email}, + {"project_id": project.pk, "role_id": gamer.pk, "email": joseph.email}, + ] + } + client.login(project.owner) + response = client.json.post(url, data) + + assert response.status_code == 200 + assert response.data[0]["email"] == john.email + assert response.data[1]["email"] == joseph.email