Adding leave project API
parent
e7b963a674
commit
c9fc2268d0
|
@ -154,6 +154,13 @@ class ProjectViewSet(ModelCrudViewSet):
|
||||||
template.save()
|
template.save()
|
||||||
return Response(serializers.ProjectTemplateSerializer(template).data, status=201)
|
return Response(serializers.ProjectTemplateSerializer(template).data, status=201)
|
||||||
|
|
||||||
|
@detail_route(methods=['post'])
|
||||||
|
def leave(self, request, pk=None):
|
||||||
|
project = self.get_object()
|
||||||
|
self.check_permissions(request, 'leave', project)
|
||||||
|
services.remove_member(project, user=request.user)
|
||||||
|
return Response(status=status.HTTP_200_OK)
|
||||||
|
|
||||||
def pre_save(self, obj):
|
def pre_save(self, obj):
|
||||||
if not obj.id:
|
if not obj.id:
|
||||||
obj.owner = self.request.user
|
obj.owner = self.request.user
|
||||||
|
|
|
@ -13,10 +13,35 @@
|
||||||
# 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 django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from taiga.base.api.permissions import (TaigaResourcePermission, HasProjectPerm,
|
from taiga.base.api.permissions import (TaigaResourcePermission, HasProjectPerm,
|
||||||
IsAuthenticated, IsProjectOwner,
|
IsAuthenticated, IsProjectOwner,
|
||||||
AllowAny, IsSuperUser)
|
AllowAny, IsSuperUser, PermissionComponent)
|
||||||
|
|
||||||
|
from taiga.base import exceptions as exc
|
||||||
|
from taiga.projects.models import Membership
|
||||||
|
|
||||||
|
|
||||||
|
class CanLeaveProject(PermissionComponent):
|
||||||
|
def check_permissions(self, request, view, obj=None):
|
||||||
|
if not obj or not request.user.is_authenticated():
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
membership = Membership.objects.get(user=request.user, project=obj)
|
||||||
|
other_admin_memberships_count = Membership.objects\
|
||||||
|
.exclude(id=membership.id)\
|
||||||
|
.filter(project=obj, is_owner=True)\
|
||||||
|
.count()
|
||||||
|
|
||||||
|
# The project need at least one owner
|
||||||
|
if membership.is_owner and other_admin_memberships_count == 0:
|
||||||
|
raise exc.PermissionDenied(_("You can't leave the project if there are no more owners"))
|
||||||
|
|
||||||
|
return True
|
||||||
|
except Membership.DoesNotExist:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
class ProjectPermission(TaigaResourcePermission):
|
class ProjectPermission(TaigaResourcePermission):
|
||||||
|
@ -37,6 +62,7 @@ class ProjectPermission(TaigaResourcePermission):
|
||||||
tags_colors_perms = HasProjectPerm('view_project')
|
tags_colors_perms = HasProjectPerm('view_project')
|
||||||
fans_perms = HasProjectPerm('view_project')
|
fans_perms = HasProjectPerm('view_project')
|
||||||
create_template_perms = IsSuperUser()
|
create_template_perms = IsSuperUser()
|
||||||
|
leave_perms = CanLeaveProject()
|
||||||
|
|
||||||
|
|
||||||
class MembershipPermission(TaigaResourcePermission):
|
class MembershipPermission(TaigaResourcePermission):
|
||||||
|
|
|
@ -34,6 +34,7 @@ from .stats import get_member_stats_for_project
|
||||||
|
|
||||||
from .members import create_members_in_bulk
|
from .members import create_members_in_bulk
|
||||||
from .members import get_members_from_bulk
|
from .members import get_members_from_bulk
|
||||||
|
from .members import remove_member
|
||||||
|
|
||||||
from .invitations import send_invitation
|
from .invitations import send_invitation
|
||||||
from .invitations import find_invited_user
|
from .invitations import find_invited_user
|
||||||
|
|
|
@ -2,7 +2,6 @@ from taiga.base.utils import db, text
|
||||||
|
|
||||||
from .. import models
|
from .. import models
|
||||||
|
|
||||||
|
|
||||||
def get_members_from_bulk(bulk_data, **additional_fields):
|
def get_members_from_bulk(bulk_data, **additional_fields):
|
||||||
"""Convert `bulk_data` into a list of members.
|
"""Convert `bulk_data` into a list of members.
|
||||||
|
|
||||||
|
@ -31,3 +30,7 @@ def create_members_in_bulk(bulk_data, callback=None, precall=None, **additional_
|
||||||
members = get_members_from_bulk(bulk_data, **additional_fields)
|
members = get_members_from_bulk(bulk_data, **additional_fields)
|
||||||
db.save_in_bulk(members, callback, precall)
|
db.save_in_bulk(members, callback, precall)
|
||||||
return members
|
return members
|
||||||
|
|
||||||
|
|
||||||
|
def remove_member(project, user):
|
||||||
|
models.Membership.objects.get(project=project, user=user).delete()
|
||||||
|
|
|
@ -160,3 +160,35 @@ def test_get_closed_bugs_per_member_stats():
|
||||||
|
|
||||||
assert stats["closed_tasks"][membership_1.user.id] == 1
|
assert stats["closed_tasks"][membership_1.user.id] == 1
|
||||||
assert stats["closed_tasks"][membership_2.user.id] == 0
|
assert stats["closed_tasks"][membership_2.user.id] == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_leave_project_valid_membership(client):
|
||||||
|
user = f.UserFactory.create()
|
||||||
|
project = f.ProjectFactory.create()
|
||||||
|
role = f.RoleFactory.create(project=project, permissions=["view_project"])
|
||||||
|
f.MembershipFactory.create(project=project, user=user, role=role)
|
||||||
|
client.login(user)
|
||||||
|
url = reverse("projects-leave", args=(project.id,))
|
||||||
|
response = client.post(url)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
|
def test_leave_project_valid_membership_only_owner(client):
|
||||||
|
user = f.UserFactory.create()
|
||||||
|
project = f.ProjectFactory.create()
|
||||||
|
role = f.RoleFactory.create(project=project, permissions=["view_project"])
|
||||||
|
f.MembershipFactory.create(project=project, user=user, role=role, is_owner=True)
|
||||||
|
client.login(user)
|
||||||
|
url = reverse("projects-leave", args=(project.id,))
|
||||||
|
response = client.post(url)
|
||||||
|
assert response.status_code == 403
|
||||||
|
assert json.loads(response.content)["_error_message"] == "You can't leave the project if there are no more owners"
|
||||||
|
|
||||||
|
|
||||||
|
def test_leave_project_invalid_membership(client):
|
||||||
|
user = f.UserFactory.create()
|
||||||
|
project = f.ProjectFactory()
|
||||||
|
client.login(user)
|
||||||
|
url = reverse("projects-leave", args=(project.id,))
|
||||||
|
response = client.post(url)
|
||||||
|
assert response.status_code == 404
|
||||||
|
|
Loading…
Reference in New Issue