Adding project info on about slots when import/create/edit fails

remotes/origin/issue/4795/notification_even_they_are_disabled
Alejandro Alonso 2016-03-01 09:39:08 +01:00
parent e4ba94f67f
commit 2457e8b705
6 changed files with 51 additions and 17 deletions

View File

@ -433,7 +433,9 @@ REST_FRAMEWORK = {
# Extra expose header related to Taiga APP (see taiga.base.middleware.cors=) # Extra expose header related to Taiga APP (see taiga.base.middleware.cors=)
APP_EXTRA_EXPOSE_HEADERS = [ APP_EXTRA_EXPOSE_HEADERS = [
"taiga-info-total-opened-milestones", "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" DEFAULT_PROJECT_TEMPLATE = "scrum"

View File

@ -202,10 +202,27 @@ class NotAuthenticated(NotAuthenticated):
class Blocked(APIException): class Blocked(APIException):
"""
Exception used on blocked projects
"""
status_code = status.HTTP_451_BLOCKED status_code = status.HTTP_451_BLOCKED
default_detail = _("Blocked element") 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): def format_exception(exc):
if isinstance(exc.detail, (dict, list, tuple,)): if isinstance(exc.detail, (dict, list, tuple,)):
detail = exc.detail detail = exc.detail
@ -237,6 +254,9 @@ def exception_handler(exc):
headers["WWW-Authenticate"] = exc.auth_header headers["WWW-Authenticate"] = exc.auth_header
if getattr(exc, "wait", None): if getattr(exc, "wait", None):
headers["X-Throttle-Wait-Seconds"] = "%d" % exc.wait 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) detail = format_exception(exc)
return response.Response(detail, status=exc.status_code, headers=headers) return response.Response(detail, status=exc.status_code, headers=headers)

View File

@ -97,7 +97,7 @@ class ProjectImporterViewSet(mixins.ImportThrottlingPolicyMixin, CreateModelMixi
project=Project(is_private=is_private, id=None) project=Project(is_private=is_private, id=None)
) )
if not enough_slots: if not enough_slots:
raise exc.BadRequest(not_enough_slots_error) raise exc.NotEnoughSlotsForProject(is_private, 1, not_enough_slots_error)
# Create Project # Create Project
project_serialized = service.store_project(data) project_serialized = service.store_project(data)
@ -122,7 +122,7 @@ class ProjectImporterViewSet(mixins.ImportThrottlingPolicyMixin, CreateModelMixi
members=max(members, 1) members=max(members, 1)
) )
if not enough_slots: 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) service.store_memberships(project_serialized.object, data)
try: try:
@ -224,13 +224,6 @@ class ProjectImporterViewSet(mixins.ImportThrottlingPolicyMixin, CreateModelMixi
raise exc.WrongArguments(_("Invalid dump format")) raise exc.WrongArguments(_("Invalid dump format"))
user = request.user 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) slug = dump.get('slug', None)
if slug is not None and Project.objects.filter(slug=slug).exists(): if slug is not None and Project.objects.filter(slug=slug).exists():
del dump['slug'] del dump['slug']
@ -242,7 +235,7 @@ class ProjectImporterViewSet(mixins.ImportThrottlingPolicyMixin, CreateModelMixi
members=max(members, 1) members=max(members, 1)
) )
if not enough_slots: 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: if settings.CELERY_ENABLED:
task = tasks.load_project_dump.delay(user, dump) task = tasks.load_project_dump.delay(user, dump)

View File

@ -378,7 +378,7 @@ class ProjectViewSet(LikedResourceMixin, HistoryResourceMixin,
members=members members=members
) )
if not enough_slots: 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) reason = request.DATA.get('reason', None)
services.accept_project_transfer(project, request.user, token, reason) services.accept_project_transfer(project, request.user, token, reason)
@ -414,8 +414,9 @@ class ProjectViewSet(LikedResourceMixin, HistoryResourceMixin,
def pre_save(self, obj): def pre_save(self, obj):
user = self.request.user user = self.request.user
(enough_slots, not_enough_slots_error) = users_service.has_available_slot_for_project(user, project=obj) (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: 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: if not obj.id:
obj.owner = user obj.owner = user
@ -624,13 +625,14 @@ class MembershipViewSet(BlockedByProjectMixin, ModelCrudViewSet):
# of handling explicit exception catchin here. # of handling explicit exception catchin here.
if "bulk_memberships" in data and isinstance(data["bulk_memberships"], list): 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( (enough_slots, not_enough_slots_error) = users_service.has_available_slot_for_project(
request.user, request.user,
project=project, project=project,
members=len(data["bulk_memberships"]) members=members
) )
if not enough_slots: if not enough_slots:
raise exc.BadRequest(not_enough_slots_error) raise exc.NotEnoughSlotsForProject(project.is_private, members, not_enough_slots_error)
try: try:
members = services.create_members_in_bulk(data["bulk_memberships"], members = services.create_members_in_bulk(data["bulk_memberships"],
@ -660,13 +662,14 @@ class MembershipViewSet(BlockedByProjectMixin, ModelCrudViewSet):
def pre_save(self, obj): def pre_save(self, obj):
if not obj.id: if not obj.id:
members = 1
(enough_slots, not_enough_slots_error) = users_service.has_available_slot_for_project( (enough_slots, not_enough_slots_error) = users_service.has_available_slot_for_project(
self.request.user, self.request.user,
project=obj.project, project=obj.project,
members=1 members=members
) )
if not enough_slots: 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: if not obj.token:
obj.token = str(uuid.uuid1()) obj.token = str(uuid.uuid1())

View File

@ -91,6 +91,8 @@ def test_valid_project_without_enough_public_projects_slots(client):
assert response.status_code == 400 assert response.status_code == 400
assert "can't have more public projects" in response.data["_error_message"] assert "can't have more public projects" in response.data["_error_message"]
assert Project.objects.filter(slug="public-project-without-slots").count() == 0 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): 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 response.status_code == 400
assert "can't have more private projects" in response.data["_error_message"] 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 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}) response = client.post(url, {'dump': data})
assert response.status_code == 400 assert response.status_code == 400
assert "can't have more public projects" in response.data["_error_message"] 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 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}) response = client.post(url, {'dump': data})
assert response.status_code == 400 assert response.status_code == 400
assert "can't have more private projects" in response.data["_error_message"] 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 assert Project.objects.filter(slug="private-project-without-slots").count() == 0

View File

@ -70,6 +70,8 @@ def test_create_private_project_without_enough_private_projects_slots(client):
assert response.status_code == 400 assert response.status_code == 400
assert "can't have more private projects" in response.data["_error_message"] 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): 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 response.status_code == 400
assert "can't have more public projects" in response.data["_error_message"] 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): 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 response.status_code == 400
assert "can't have more public projects" in response.data["_error_message"] 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): 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 response.status_code == 400
assert "can't have more private projects" in response.data["_error_message"] 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): def test_create_private_project_with_enough_private_projects_slots(client):