Adding project info on about slots when import/create/edit fails
parent
e4ba94f67f
commit
2457e8b705
|
@ -433,7 +433,9 @@ REST_FRAMEWORK = {
|
|||
# Extra expose header related to Taiga APP (see taiga.base.middleware.cors=)
|
||||
APP_EXTRA_EXPOSE_HEADERS = [
|
||||
"taiga-info-total-opened-milestones",
|
||||
"taiga-info-total-closed-milestones"
|
||||
"taiga-info-total-closed-milestones",
|
||||
"taiga-info-project-memberships",
|
||||
"taiga-info-project-is-private"
|
||||
]
|
||||
|
||||
DEFAULT_PROJECT_TEMPLATE = "scrum"
|
||||
|
|
|
@ -202,10 +202,27 @@ class NotAuthenticated(NotAuthenticated):
|
|||
|
||||
|
||||
class Blocked(APIException):
|
||||
"""
|
||||
Exception used on blocked projects
|
||||
"""
|
||||
status_code = status.HTTP_451_BLOCKED
|
||||
default_detail = _("Blocked element")
|
||||
|
||||
|
||||
class NotEnoughSlotsForProject(BaseException):
|
||||
"""
|
||||
Exception used on import/edition/creation project errors where the user
|
||||
hasn't slots enough
|
||||
"""
|
||||
default_detail = _("Not enough slots for project.")
|
||||
|
||||
def __init__(self, is_private, total_memberships, detail=None):
|
||||
self.detail = detail or self.default_detail
|
||||
self.project_data = {
|
||||
"is_private": is_private,
|
||||
"total_memberships": total_memberships
|
||||
}
|
||||
|
||||
def format_exception(exc):
|
||||
if isinstance(exc.detail, (dict, list, tuple,)):
|
||||
detail = exc.detail
|
||||
|
@ -237,6 +254,9 @@ def exception_handler(exc):
|
|||
headers["WWW-Authenticate"] = exc.auth_header
|
||||
if getattr(exc, "wait", None):
|
||||
headers["X-Throttle-Wait-Seconds"] = "%d" % exc.wait
|
||||
if getattr(exc, "project_data", None):
|
||||
headers["Taiga-Info-Project-Memberships"] = exc.project_data["total_memberships"]
|
||||
headers["Taiga-Info-Project-Is-Private"] = exc.project_data["is_private"]
|
||||
|
||||
detail = format_exception(exc)
|
||||
return response.Response(detail, status=exc.status_code, headers=headers)
|
||||
|
|
|
@ -97,7 +97,7 @@ class ProjectImporterViewSet(mixins.ImportThrottlingPolicyMixin, CreateModelMixi
|
|||
project=Project(is_private=is_private, id=None)
|
||||
)
|
||||
if not enough_slots:
|
||||
raise exc.BadRequest(not_enough_slots_error)
|
||||
raise exc.NotEnoughSlotsForProject(is_private, 1, not_enough_slots_error)
|
||||
|
||||
# Create Project
|
||||
project_serialized = service.store_project(data)
|
||||
|
@ -122,7 +122,7 @@ class ProjectImporterViewSet(mixins.ImportThrottlingPolicyMixin, CreateModelMixi
|
|||
members=max(members, 1)
|
||||
)
|
||||
if not enough_slots:
|
||||
raise exc.BadRequest(not_enough_slots_error)
|
||||
raise exc.NotEnoughSlotsForProject(is_private, max(members, 1), not_enough_slots_error)
|
||||
service.store_memberships(project_serialized.object, data)
|
||||
|
||||
try:
|
||||
|
@ -224,13 +224,6 @@ class ProjectImporterViewSet(mixins.ImportThrottlingPolicyMixin, CreateModelMixi
|
|||
raise exc.WrongArguments(_("Invalid dump format"))
|
||||
|
||||
user = request.user
|
||||
(enough_slots, not_enough_slots_error) = users_service.has_available_slot_for_project(
|
||||
user,
|
||||
project=Project(is_private=is_private, id=None)
|
||||
)
|
||||
if not enough_slots:
|
||||
raise exc.BadRequest(not_enough_slots_error)
|
||||
|
||||
slug = dump.get('slug', None)
|
||||
if slug is not None and Project.objects.filter(slug=slug).exists():
|
||||
del dump['slug']
|
||||
|
@ -242,7 +235,7 @@ class ProjectImporterViewSet(mixins.ImportThrottlingPolicyMixin, CreateModelMixi
|
|||
members=max(members, 1)
|
||||
)
|
||||
if not enough_slots:
|
||||
raise exc.BadRequest(not_enough_slots_error)
|
||||
raise exc.NotEnoughSlotsForProject(is_private, max(members, 1), not_enough_slots_error)
|
||||
|
||||
if settings.CELERY_ENABLED:
|
||||
task = tasks.load_project_dump.delay(user, dump)
|
||||
|
|
|
@ -378,7 +378,7 @@ class ProjectViewSet(LikedResourceMixin, HistoryResourceMixin,
|
|||
members=members
|
||||
)
|
||||
if not enough_slots:
|
||||
raise exc.BadRequest(not_enough_slots_error)
|
||||
raise exc.NotEnoughSlotsForProject(project.is_private, members, not_enough_slots_error)
|
||||
|
||||
reason = request.DATA.get('reason', None)
|
||||
services.accept_project_transfer(project, request.user, token, reason)
|
||||
|
@ -414,8 +414,9 @@ class ProjectViewSet(LikedResourceMixin, HistoryResourceMixin,
|
|||
def pre_save(self, obj):
|
||||
user = self.request.user
|
||||
(enough_slots, not_enough_slots_error) = users_service.has_available_slot_for_project(user, project=obj)
|
||||
members = max(obj.memberships.count(), 1)
|
||||
if not enough_slots:
|
||||
raise exc.BadRequest(not_enough_slots_error)
|
||||
raise exc.NotEnoughSlotsForProject(obj.is_private, members, not_enough_slots_error)
|
||||
|
||||
if not obj.id:
|
||||
obj.owner = user
|
||||
|
@ -624,13 +625,14 @@ class MembershipViewSet(BlockedByProjectMixin, ModelCrudViewSet):
|
|||
# of handling explicit exception catchin here.
|
||||
|
||||
if "bulk_memberships" in data and isinstance(data["bulk_memberships"], list):
|
||||
members = len(data["bulk_memberships"])
|
||||
(enough_slots, not_enough_slots_error) = users_service.has_available_slot_for_project(
|
||||
request.user,
|
||||
project=project,
|
||||
members=len(data["bulk_memberships"])
|
||||
members=members
|
||||
)
|
||||
if not enough_slots:
|
||||
raise exc.BadRequest(not_enough_slots_error)
|
||||
raise exc.NotEnoughSlotsForProject(project.is_private, members, not_enough_slots_error)
|
||||
|
||||
try:
|
||||
members = services.create_members_in_bulk(data["bulk_memberships"],
|
||||
|
@ -660,13 +662,14 @@ class MembershipViewSet(BlockedByProjectMixin, ModelCrudViewSet):
|
|||
|
||||
def pre_save(self, obj):
|
||||
if not obj.id:
|
||||
members = 1
|
||||
(enough_slots, not_enough_slots_error) = users_service.has_available_slot_for_project(
|
||||
self.request.user,
|
||||
project=obj.project,
|
||||
members=1
|
||||
members=members
|
||||
)
|
||||
if not enough_slots:
|
||||
raise exc.BadRequest(not_enough_slots_error)
|
||||
raise exc.NotEnoughSlotsForProject(obj.project.is_private, members, not_enough_slots_error)
|
||||
|
||||
if not obj.token:
|
||||
obj.token = str(uuid.uuid1())
|
||||
|
|
|
@ -91,6 +91,8 @@ def test_valid_project_without_enough_public_projects_slots(client):
|
|||
assert response.status_code == 400
|
||||
assert "can't have more public projects" in response.data["_error_message"]
|
||||
assert Project.objects.filter(slug="public-project-without-slots").count() == 0
|
||||
assert response["Taiga-Info-Project-Memberships"] == "1"
|
||||
assert response["Taiga-Info-Project-Is-Private"] == "False"
|
||||
|
||||
|
||||
def test_valid_project_without_enough_private_projects_slots(client):
|
||||
|
@ -110,6 +112,8 @@ def test_valid_project_without_enough_private_projects_slots(client):
|
|||
|
||||
assert response.status_code == 400
|
||||
assert "can't have more private projects" in response.data["_error_message"]
|
||||
assert response["Taiga-Info-Project-Memberships"] == "1"
|
||||
assert response["Taiga-Info-Project-Is-Private"] == "True"
|
||||
assert Project.objects.filter(slug="private-project-without-slots").count() == 0
|
||||
|
||||
|
||||
|
@ -1249,6 +1253,8 @@ def test_valid_dump_import_without_enough_public_projects_slots(client):
|
|||
response = client.post(url, {'dump': data})
|
||||
assert response.status_code == 400
|
||||
assert "can't have more public projects" in response.data["_error_message"]
|
||||
assert response["Taiga-Info-Project-Memberships"] == "1"
|
||||
assert response["Taiga-Info-Project-Is-Private"] == "False"
|
||||
assert Project.objects.filter(slug="public-project-without-slots").count() == 0
|
||||
|
||||
|
||||
|
@ -1269,6 +1275,8 @@ def test_valid_dump_import_without_enough_private_projects_slots(client):
|
|||
response = client.post(url, {'dump': data})
|
||||
assert response.status_code == 400
|
||||
assert "can't have more private projects" in response.data["_error_message"]
|
||||
assert response["Taiga-Info-Project-Memberships"] == "1"
|
||||
assert response["Taiga-Info-Project-Is-Private"] == "True"
|
||||
assert Project.objects.filter(slug="private-project-without-slots").count() == 0
|
||||
|
||||
|
||||
|
|
|
@ -70,6 +70,8 @@ def test_create_private_project_without_enough_private_projects_slots(client):
|
|||
|
||||
assert response.status_code == 400
|
||||
assert "can't have more private projects" in response.data["_error_message"]
|
||||
assert response["Taiga-Info-Project-Memberships"] == "1"
|
||||
assert response["Taiga-Info-Project-Is-Private"] == "True"
|
||||
|
||||
|
||||
def test_create_public_project_without_enough_public_projects_slots(client):
|
||||
|
@ -86,6 +88,8 @@ def test_create_public_project_without_enough_public_projects_slots(client):
|
|||
|
||||
assert response.status_code == 400
|
||||
assert "can't have more public projects" in response.data["_error_message"]
|
||||
assert response["Taiga-Info-Project-Memberships"] == "1"
|
||||
assert response["Taiga-Info-Project-Is-Private"] == "False"
|
||||
|
||||
|
||||
def test_change_project_from_private_to_public_without_enough_public_projects_slots(client):
|
||||
|
@ -102,6 +106,8 @@ def test_change_project_from_private_to_public_without_enough_public_projects_sl
|
|||
|
||||
assert response.status_code == 400
|
||||
assert "can't have more public projects" in response.data["_error_message"]
|
||||
assert response["Taiga-Info-Project-Memberships"] == "1"
|
||||
assert response["Taiga-Info-Project-Is-Private"] == "False"
|
||||
|
||||
|
||||
def test_change_project_from_public_to_private_without_enough_private_projects_slots(client):
|
||||
|
@ -118,6 +124,8 @@ def test_change_project_from_public_to_private_without_enough_private_projects_s
|
|||
|
||||
assert response.status_code == 400
|
||||
assert "can't have more private projects" in response.data["_error_message"]
|
||||
assert response["Taiga-Info-Project-Memberships"] == "1"
|
||||
assert response["Taiga-Info-Project-Is-Private"] == "True"
|
||||
|
||||
|
||||
def test_create_private_project_with_enough_private_projects_slots(client):
|
||||
|
|
Loading…
Reference in New Issue