diff --git a/taiga/auth/api.py b/taiga/auth/api.py
index e2393868..f2ed0161 100644
--- a/taiga/auth/api.py
+++ b/taiga/auth/api.py
@@ -20,13 +20,13 @@ from enum import Enum
from django.utils.translation import ugettext_lazy as _
from django.conf import settings
-from rest_framework.response import Response
-from rest_framework import status
from rest_framework import serializers
from taiga.base.api import viewsets
from taiga.base.decorators import list_route
from taiga.base import exceptions as exc
+from taiga.base import response
+
from taiga.users.services import get_and_validate_user
from .serializers import PublicRegisterSerializer
@@ -108,7 +108,7 @@ class AuthViewSet(viewsets.ViewSet):
raise exc.BadRequest(e.detail)
data = make_auth_response_data(user)
- return Response(data, status=status.HTTP_201_CREATED)
+ return response.Created(data)
def _private_register(self, request):
register_type = parse_register_type(request.DATA)
@@ -121,7 +121,7 @@ class AuthViewSet(viewsets.ViewSet):
user = private_register_for_new_user(**data)
data = make_auth_response_data(user)
- return Response(data, status=status.HTTP_201_CREATED)
+ return response.Created(data)
@list_route(methods=["POST"])
def register(self, request, **kwargs):
@@ -134,7 +134,6 @@ class AuthViewSet(viewsets.ViewSet):
return self._private_register(request)
raise exc.BadRequest(_("invalid register type"))
-
# Login view: /api/v1/auth
def create(self, request, **kwargs):
self.check_permissions(request, 'create', None)
diff --git a/taiga/auth/services.py b/taiga/auth/services.py
index 19bb6ebe..238f229f 100644
--- a/taiga/auth/services.py
+++ b/taiga/auth/services.py
@@ -29,12 +29,10 @@ from django.db import transaction as tx
from django.db import IntegrityError
from django.utils.translation import ugettext as _
-from rest_framework.response import Response
-from rest_framework import status
-
from djmail.template_mail import MagicMailBuilder, InlineCSSTemplateMail
from taiga.base import exceptions as exc
+from taiga.base import response
from taiga.users.serializers import UserSerializer
from taiga.users.services import get_and_validate_user
from taiga.base.utils.slug import slugify_uniquely
@@ -203,7 +201,7 @@ def normal_login_func(request):
user = get_and_validate_user(username=username, password=password)
data = make_auth_response_data(user)
- return Response(data, status=status.HTTP_200_OK)
+ return response.Ok(data)
register_auth_plugin("normal", normal_login_func);
diff --git a/taiga/base/api/mixins.py b/taiga/base/api/mixins.py
index bca89e63..ed2b2f24 100644
--- a/taiga/base/api/mixins.py
+++ b/taiga/base/api/mixins.py
@@ -23,9 +23,7 @@ from django.core.exceptions import ValidationError
from django.http import Http404
from django.db import transaction as tx
-from rest_framework import status
-from rest_framework.response import Response
-from rest_framework.request import clone_request
+from taiga.base import response
from rest_framework.settings import api_settings
from .utils import get_object_or_404
@@ -73,10 +71,9 @@ class CreateModelMixin(object):
self.object = serializer.save(force_insert=True)
self.post_save(self.object, created=True)
headers = self.get_success_headers(serializer.data)
- return Response(serializer.data, status=status.HTTP_201_CREATED,
- headers=headers)
+ return response.Created(serializer.data, headers=headers)
- return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
+ return response.BadRequest(serializer.errors)
def get_success_headers(self, data):
try:
@@ -114,7 +111,7 @@ class ListModelMixin(object):
else:
serializer = self.get_serializer(self.object_list, many=True)
- return Response(serializer.data)
+ return response.Ok(serializer.data)
class RetrieveModelMixin(object):
@@ -130,7 +127,7 @@ class RetrieveModelMixin(object):
raise Http404
serializer = self.get_serializer(self.object)
- return Response(serializer.data)
+ return response.Ok(serializer.data)
class UpdateModelMixin(object):
@@ -149,7 +146,7 @@ class UpdateModelMixin(object):
files=request.FILES, partial=partial)
if not serializer.is_valid():
- return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
+ return response.BadRequest(serializer.errors)
# Hooks
try:
@@ -158,16 +155,16 @@ class UpdateModelMixin(object):
except ValidationError as err:
# full_clean on model instance may be called in pre_save,
# so we have to handle eventual errors.
- return Response(err.message_dict, status=status.HTTP_400_BAD_REQUEST)
+ return response.BadRequest(err.message_dict)
if self.object is None:
self.object = serializer.save(force_insert=True)
self.post_save(self.object, created=True)
- return Response(serializer.data, status=status.HTTP_201_CREATED)
+ return response.Created(serializer.data)
self.object = serializer.save(force_update=True)
self.post_save(self.object, created=False)
- return Response(serializer.data, status=status.HTTP_200_OK)
+ return response.Ok(serializer.data)
def partial_update(self, request, *args, **kwargs):
kwargs['partial'] = True
@@ -216,4 +213,4 @@ class DestroyModelMixin(object):
self.pre_conditions_on_delete(obj)
obj.delete()
self.post_delete(obj)
- return Response(status=status.HTTP_204_NO_CONTENT)
+ return response.NoContent()
diff --git a/taiga/base/api/views.py b/taiga/base/api/views.py
index cde7113c..638245a0 100644
--- a/taiga/base/api/views.py
+++ b/taiga/base/api/views.py
@@ -27,10 +27,13 @@ from django.views.decorators.csrf import csrf_exempt
from rest_framework import status, exceptions
from rest_framework.compat import smart_text, HttpResponseBase, View
from rest_framework.request import Request
-from rest_framework.response import Response
from rest_framework.settings import api_settings
from rest_framework.utils import formatting
+from taiga.base.response import Response
+from taiga.base.response import Ok
+from taiga.base.response import NotFound
+from taiga.base.response import Forbidden
from taiga.base.utils.iterators import as_tuple
from django.conf import settings
@@ -89,12 +92,10 @@ def exception_handler(exc):
headers=headers)
elif isinstance(exc, Http404):
- return Response({'detail': 'Not found'},
- status=status.HTTP_404_NOT_FOUND)
+ return NotFound({'detail': 'Not found'})
elif isinstance(exc, PermissionDenied):
- return Response({'detail': 'Permission denied'},
- status=status.HTTP_403_FORBIDDEN)
+ return Forbidden({'detail': 'Permission denied'})
# Note: Unhandled exceptions will raise a 500 error.
return None
@@ -425,7 +426,7 @@ class APIView(View):
We may as well implement this as Django will otherwise provide
a less useful default implementation.
"""
- return Response(self.metadata(request), status=status.HTTP_200_OK)
+ return Ok(self.metadata(request))
def metadata(self, request):
"""
diff --git a/taiga/base/exceptions.py b/taiga/base/exceptions.py
index 53a56345..3eb05f5f 100644
--- a/taiga/base/exceptions.py
+++ b/taiga/base/exceptions.py
@@ -14,17 +14,16 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
-
from rest_framework import exceptions
from rest_framework import status
-from rest_framework.response import Response
from django.core.exceptions import PermissionDenied as DjangoPermissionDenied
from django.utils.encoding import force_text
from django.utils.translation import ugettext_lazy as _
from django.http import Http404
-from .utils.json import to_json
+from taiga.base import response
+from taiga.base.utils.json import to_json
class BaseException(exceptions.APIException):
@@ -129,15 +128,13 @@ def exception_handler(exc):
headers["X-Throttle-Wait-Seconds"] = "%d" % exc.wait
detail = format_exception(exc)
- return Response(detail, status=exc.status_code, headers=headers)
+ return response.Response(detail, status=exc.status_code, headers=headers)
elif isinstance(exc, Http404):
- return Response({'_error_message': str(exc)},
- status=status.HTTP_404_NOT_FOUND)
+ return response.NotFound({'_error_message': str(exc)})
elif isinstance(exc, DjangoPermissionDenied):
- return Response({"_error_message": str(exc)},
- status=status.HTTP_403_FORBIDDEN)
+ return response.Forbidden({"_error_message": str(exc)})
# Note: Unhandled exceptions will raise a 500 error.
return None
diff --git a/taiga/base/routers.py b/taiga/base/routers.py
index 407b40a5..3a3a5b8a 100644
--- a/taiga/base/routers.py
+++ b/taiga/base/routers.py
@@ -23,7 +23,7 @@ from django.core.exceptions import ImproperlyConfigured
from django.core.urlresolvers import NoReverseMatch
from rest_framework import views
-from rest_framework.response import Response
+from taiga.base import response
from rest_framework.reverse import reverse
from rest_framework.urlpatterns import format_suffix_patterns
@@ -292,7 +292,7 @@ class DRFDefaultRouter(SimpleRouter):
except NoReverseMatch:
# Support resources that are prefixed by a parametrized url
ret[key] = request.build_absolute_uri() + key
- return Response(ret)
+ return response.Response(ret)
return APIRoot.as_view()
diff --git a/taiga/export_import/api.py b/taiga/export_import/api.py
index a993b6af..edf8911d 100644
--- a/taiga/export_import/api.py
+++ b/taiga/export_import/api.py
@@ -18,10 +18,6 @@ import json
import codecs
import uuid
-from rest_framework.response import Response
-from rest_framework.decorators import throttle_classes
-from rest_framework import status
-
from django.utils.decorators import method_decorator
from django.utils.translation import ugettext_lazy as _
from django.db.transaction import atomic
@@ -30,10 +26,13 @@ from django.conf import settings
from django.core.files.storage import default_storage
from django.core.files.base import ContentFile
-from taiga.base.api.mixins import CreateModelMixin
-from taiga.base.api.viewsets import GenericViewSet
+from rest_framework.decorators import throttle_classes
+
from taiga.base.decorators import detail_route, list_route
from taiga.base import exceptions as exc
+from taiga.base import response
+from taiga.base.api.mixins import CreateModelMixin
+from taiga.base.api.viewsets import GenericViewSet
from taiga.projects.models import Project, Membership
from taiga.projects.issues.models import Issue
from taiga.projects.serializers import ProjectSerializer
@@ -65,8 +64,9 @@ class ProjectExporterViewSet(mixins.ImportThrottlingPolicyMixin, GenericViewSet)
if settings.CELERY_ENABLED:
task = tasks.dump_project.delay(request.user, project)
- tasks.delete_project_dump.apply_async((project.pk, project.slug), countdown=settings.EXPORTS_TTL)
- return Response({"export_id": task.id}, status=status.HTTP_202_ACCEPTED)
+ tasks.delete_project_dump.apply_async((project.pk, project.slug),
+ countdown=settings.EXPORTS_TTL)
+ return response.Accepted({"export_id": task.id})
path = "exports/{}/{}-{}.json".format(project.pk, project.slug, uuid.uuid4().hex)
content = ContentFile(ExportRenderer().render(service.project_to_dict(project),
@@ -76,7 +76,7 @@ class ProjectExporterViewSet(mixins.ImportThrottlingPolicyMixin, GenericViewSet)
response_data = {
"url": default_storage.url(path)
}
- return Response(response_data, status=status.HTTP_200_OK)
+ return response.Ok(response_data)
class ProjectImporterViewSet(mixins.ImportThrottlingPolicyMixin, CreateModelMixin, GenericViewSet):
@@ -152,7 +152,7 @@ class ProjectImporterViewSet(mixins.ImportThrottlingPolicyMixin, CreateModelMixi
response_data = project_serialized.data
response_data['id'] = project_serialized.object.id
headers = self.get_success_headers(response_data)
- return Response(response_data, status=status.HTTP_201_CREATED, headers=headers)
+ return response.Created(response_data, headers=headers)
@list_route(methods=["POST"])
@method_decorator(atomic)
@@ -181,11 +181,11 @@ class ProjectImporterViewSet(mixins.ImportThrottlingPolicyMixin, CreateModelMixi
if settings.CELERY_ENABLED:
task = tasks.load_project_dump.delay(request.user, dump)
- return Response({"import_id": task.id}, status=status.HTTP_202_ACCEPTED)
+ return response.Accepted({"import_id": task.id})
project = dump_service.dict_to_project(dump, request.user.email)
response_data = ProjectSerializer(project).data
- return Response(response_data, status=status.HTTP_201_CREATED)
+ return response.Created(response_data)
@detail_route(methods=['post'])
@@ -204,7 +204,7 @@ class ProjectImporterViewSet(mixins.ImportThrottlingPolicyMixin, CreateModelMixi
raise exc.BadRequest(errors)
headers = self.get_success_headers(issue.data)
- return Response(issue.data, status=status.HTTP_201_CREATED, headers=headers)
+ return response.Created(issue.data, headers=headers)
@detail_route(methods=['post'])
@method_decorator(atomic)
@@ -219,7 +219,7 @@ class ProjectImporterViewSet(mixins.ImportThrottlingPolicyMixin, CreateModelMixi
raise exc.BadRequest(errors)
headers = self.get_success_headers(task.data)
- return Response(task.data, status=status.HTTP_201_CREATED, headers=headers)
+ return response.Created(task.data, headers=headers)
@detail_route(methods=['post'])
@method_decorator(atomic)
@@ -234,7 +234,7 @@ class ProjectImporterViewSet(mixins.ImportThrottlingPolicyMixin, CreateModelMixi
raise exc.BadRequest(errors)
headers = self.get_success_headers(us.data)
- return Response(us.data, status=status.HTTP_201_CREATED, headers=headers)
+ return response.Created(us.data, headers=headers)
@detail_route(methods=['post'])
@method_decorator(atomic)
@@ -249,7 +249,7 @@ class ProjectImporterViewSet(mixins.ImportThrottlingPolicyMixin, CreateModelMixi
raise exc.BadRequest(errors)
headers = self.get_success_headers(milestone.data)
- return Response(milestone.data, status=status.HTTP_201_CREATED, headers=headers)
+ return response.Created(milestone.data, headers=headers)
@detail_route(methods=['post'])
@method_decorator(atomic)
@@ -264,7 +264,7 @@ class ProjectImporterViewSet(mixins.ImportThrottlingPolicyMixin, CreateModelMixi
raise exc.BadRequest(errors)
headers = self.get_success_headers(wiki_page.data)
- return Response(wiki_page.data, status=status.HTTP_201_CREATED, headers=headers)
+ return response.Created(wiki_page.data, headers=headers)
@detail_route(methods=['post'])
@method_decorator(atomic)
@@ -279,4 +279,4 @@ class ProjectImporterViewSet(mixins.ImportThrottlingPolicyMixin, CreateModelMixi
raise exc.BadRequest(errors)
headers = self.get_success_headers(wiki_link.data)
- return Response(wiki_link.data, status=status.HTTP_201_CREATED, headers=headers)
+ return response.Created(wiki_link.data, headers=headers)
diff --git a/taiga/hooks/api.py b/taiga/hooks/api.py
index 105d0189..a95d6fbe 100644
--- a/taiga/hooks/api.py
+++ b/taiga/hooks/api.py
@@ -14,11 +14,11 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
-from rest_framework.response import Response
from django.utils.translation import ugettext_lazy as _
-from taiga.base.api.viewsets import GenericViewSet
from taiga.base import exceptions as exc
+from taiga.base import response
+from taiga.base.api.viewsets import GenericViewSet
from taiga.base.utils import json
from taiga.projects.models import Project
@@ -75,4 +75,4 @@ class BaseWebhookApiViewSet(GenericViewSet):
except ActionSyntaxException as e:
raise exc.BadRequest(e)
- return Response({})
+ return response.NoContent()
diff --git a/taiga/hooks/bitbucket/api.py b/taiga/hooks/bitbucket/api.py
index cb1c75e8..a73e9958 100644
--- a/taiga/hooks/bitbucket/api.py
+++ b/taiga/hooks/bitbucket/api.py
@@ -14,18 +14,14 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
-from rest_framework.response import Response
from django.utils.translation import ugettext_lazy as _
from django.conf import settings
-from taiga.base.api.viewsets import GenericViewSet
from taiga.base import exceptions as exc
-from taiga.base.utils import json
from taiga.projects.models import Project
from taiga.hooks.api import BaseWebhookApiViewSet
from . import event_hooks
-from ..exceptions import ActionSyntaxException
from urllib.parse import parse_qs
from ipware.ip import get_real_ip
@@ -61,7 +57,9 @@ class BitBucketViewSet(BaseWebhookApiViewSet):
if not project_secret:
return False
- valid_origin_ips = project.modules_config.config.get("bitbucket", {}).get("valid_origin_ips", settings.BITBUCKET_VALID_ORIGIN_IPS)
+ bitbucket_config = project.modules_config.config.get("bitbucket", {})
+ valid_origin_ips = bitbucket_config.get("valid_origin_ips",
+ settings.BITBUCKET_VALID_ORIGIN_IPS)
origin_ip = get_real_ip(request)
if valid_origin_ips and (not origin_ip or not origin_ip in valid_origin_ips):
return False
diff --git a/taiga/hooks/github/api.py b/taiga/hooks/github/api.py
index c0f32c16..767aefd4 100644
--- a/taiga/hooks/github/api.py
+++ b/taiga/hooks/github/api.py
@@ -14,13 +14,6 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
-from rest_framework.response import Response
-from django.utils.translation import ugettext_lazy as _
-
-from taiga.base.api.viewsets import GenericViewSet
-from taiga.base import exceptions as exc
-from taiga.base.utils import json
-from taiga.projects.models import Project
from taiga.hooks.api import BaseWebhookApiViewSet
from . import event_hooks
@@ -51,7 +44,8 @@ class GitHubViewSet(BaseWebhookApiViewSet):
if project.modules_config.config is None:
return False
- secret = bytes(project.modules_config.config.get("github", {}).get("secret", "").encode("utf-8"))
+ secret = project.modules_config.config.get("github", {}).get("secret", "")
+ secret = bytes(secret.encode("utf-8"))
mac = hmac.new(secret, msg=request.body,digestmod=hashlib.sha1)
return hmac.compare_digest(mac.hexdigest(), signature)
diff --git a/taiga/hooks/gitlab/api.py b/taiga/hooks/gitlab/api.py
index a7596910..1f776b8e 100644
--- a/taiga/hooks/gitlab/api.py
+++ b/taiga/hooks/gitlab/api.py
@@ -14,20 +14,18 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
-from rest_framework.response import Response
from django.utils.translation import ugettext_lazy as _
from django.conf import settings
-from taiga.base.api.viewsets import GenericViewSet
-from taiga.base import exceptions as exc
+from ipware.ip import get_real_ip
+
from taiga.base.utils import json
+
from taiga.projects.models import Project
from taiga.hooks.api import BaseWebhookApiViewSet
from . import event_hooks
-from ipware.ip import get_real_ip
-
class GitLabViewSet(BaseWebhookApiViewSet):
event_hook_classes = {
@@ -51,7 +49,8 @@ class GitLabViewSet(BaseWebhookApiViewSet):
if not project_secret:
return False
- valid_origin_ips = project.modules_config.config.get("gitlab", {}).get("valid_origin_ips", settings.GITLAB_VALID_ORIGIN_IPS)
+ gitlab_config = project.modules_config.config.get("gitlab", {})
+ valid_origin_ips = gitlab_config.get("valid_origin_ips", settings.GITLAB_VALID_ORIGIN_IPS)
origin_ip = get_real_ip(request)
if valid_origin_ips and (not origin_ip or origin_ip not in valid_origin_ips):
return False
diff --git a/taiga/projects/api.py b/taiga/projects/api.py
index f9f605a5..0f17c668 100644
--- a/taiga/projects/api.py
+++ b/taiga/projects/api.py
@@ -74,7 +74,7 @@ class ProjectViewSet(ModelCrudViewSet):
modules_config = services.get_modules_config(project)
if request.method == "GET":
- return response.Ok(data=modules_config.config)
+ return response.Ok(modules_config.config)
else:
modules_config.config.update(request.DATA)
@@ -85,31 +85,31 @@ class ProjectViewSet(ModelCrudViewSet):
def stats(self, request, pk=None):
project = self.get_object()
self.check_permissions(request, "stats", project)
- return response.Ok(data=services.get_stats_for_project(project))
+ return response.Ok(services.get_stats_for_project(project))
@detail_route(methods=["GET"])
def member_stats(self, request, pk=None):
project = self.get_object()
self.check_permissions(request, "member_stats", project)
- return response.Ok(data=services.get_member_stats_for_project(project))
+ return response.Ok(services.get_member_stats_for_project(project))
@detail_route(methods=["GET"])
def issues_stats(self, request, pk=None):
project = self.get_object()
self.check_permissions(request, "issues_stats", project)
- return response.Ok(data=services.get_stats_for_project_issues(project))
+ return response.Ok(services.get_stats_for_project_issues(project))
@detail_route(methods=["GET"])
def issue_filters_data(self, request, pk=None):
project = self.get_object()
self.check_permissions(request, "issues_filters_data", project)
- return response.Ok(data=services.get_issues_filters_data(project))
+ return response.Ok(services.get_issues_filters_data(project))
@detail_route(methods=["GET"])
def tags_colors(self, request, pk=None):
project = self.get_object()
self.check_permissions(request, "tags_colors", project)
- return response.Ok(data=dict(project.tags_colors))
+ return response.Ok(dict(project.tags_colors))
@detail_route(methods=["POST"])
def star(self, request, pk=None):
@@ -132,7 +132,7 @@ class ProjectViewSet(ModelCrudViewSet):
voters = votes_service.get_voters(project)
voters_data = votes_serializers.VoterSerializer(voters, many=True)
- return response.Ok(data=voters_data.data)
+ return response.Ok(voters_data.data)
@detail_route(methods=["POST"])
def create_template(self, request, **kwargs):
@@ -359,7 +359,7 @@ class MembershipViewSet(ModelCrudViewSet):
return response.BadRequest(err.message_dict)
members_serialized = self.serializer_class(members, many=True)
- return response.Ok(data=members_serialized.data)
+ return response.Ok(members_serialized.data)
@detail_route(methods=["POST"])
def resend_invitation(self, request, **kwargs):
diff --git a/taiga/projects/attachments/api.py b/taiga/projects/attachments/api.py
index 2c89d57f..383dcbba 100644
--- a/taiga/projects/attachments/api.py
+++ b/taiga/projects/attachments/api.py
@@ -21,14 +21,15 @@ import mimetypes
mimetypes.init()
from django.contrib.contenttypes.models import ContentType
-from taiga.base.api.utils import get_object_or_404
from django.conf import settings
from django import http
-from taiga.base.api import ModelCrudViewSet
-from taiga.base.api import generics
from taiga.base import filters
from taiga.base import exceptions as exc
+from taiga.base.api import generics
+from taiga.base.api import ModelCrudViewSet
+from taiga.base.api.utils import get_object_or_404
+
from taiga.users.models import User
from taiga.projects.notifications.mixins import WatchedResourceMixin
diff --git a/taiga/projects/history/api.py b/taiga/projects/history/api.py
index 078f1f63..49023f94 100644
--- a/taiga/projects/history/api.py
+++ b/taiga/projects/history/api.py
@@ -17,12 +17,10 @@
from django.contrib.contenttypes.models import ContentType
from django.utils import timezone
-from rest_framework.response import Response
-from rest_framework import status
-
-from taiga.base.api.utils import get_object_or_404
+from taiga.base import response
from taiga.base.decorators import detail_route
from taiga.base.api import ReadOnlyListViewSet
+from taiga.base.api.utils import get_object_or_404
from . import permissions
from . import serializers
@@ -54,7 +52,7 @@ class HistoryViewSet(ReadOnlyListViewSet):
else:
serializer = self.get_serializer(queryset, many=True)
- return Response(serializer.data)
+ return response.Ok(serializer.data)
@detail_route(methods=['post'])
def delete_comment(self, request, pk):
@@ -65,15 +63,15 @@ class HistoryViewSet(ReadOnlyListViewSet):
self.check_permissions(request, 'delete_comment', comment)
if comment is None:
- return Response(status=status.HTTP_404_NOT_FOUND)
+ return response.NotFound()
if comment.delete_comment_date or comment.delete_comment_user:
- return Response({"error": "Comment already deleted"}, status=status.HTTP_400_BAD_REQUEST)
+ return response.BadRequest({"error": "Comment already deleted"})
comment.delete_comment_date = timezone.now()
comment.delete_comment_user = {"pk": request.user.pk, "name": request.user.get_full_name()}
comment.save()
- return Response(status=status.HTTP_200_OK)
+ return response.Ok()
@detail_route(methods=['post'])
def undelete_comment(self, request, pk):
@@ -84,20 +82,20 @@ class HistoryViewSet(ReadOnlyListViewSet):
self.check_permissions(request, 'undelete_comment', comment)
if comment is None:
- return Response(status=status.HTTP_404_NOT_FOUND)
+ return response.NotFound()
if not comment.delete_comment_date and not comment.delete_comment_user:
- return Response({"error": "Comment not deleted"}, status=status.HTTP_400_BAD_REQUEST)
+ return response.BadRequest({"error": "Comment not deleted"})
comment.delete_comment_date = None
comment.delete_comment_user = None
comment.save()
- return Response(status=status.HTTP_200_OK)
+ return response.Ok()
# Just for restframework! Because it raises
# 404 on main api root if this method not exists.
def list(self, request):
- return Response({})
+ return response.NotFound()
def retrieve(self, request, pk):
obj = self.get_object()
diff --git a/taiga/projects/issues/api.py b/taiga/projects/issues/api.py
index eb2dedcc..c09ad0f4 100644
--- a/taiga/projects/issues/api.py
+++ b/taiga/projects/issues/api.py
@@ -18,14 +18,12 @@ from django.utils.translation import ugettext_lazy as _
from django.db.models import Q
from django.http import Http404
-from rest_framework.response import Response
-from rest_framework import status
-
-from taiga.base.api.utils import get_object_or_404
-from taiga.base import filters, response
+from taiga.base import filters
from taiga.base import exceptions as exc
+from taiga.base import response
from taiga.base.decorators import detail_route, list_route
from taiga.base.api import ModelCrudViewSet, ModelListViewSet
+from taiga.base.api.utils import get_object_or_404
from taiga.base import tags
from taiga.users.models import User
@@ -139,19 +137,24 @@ class IssueViewSet(OCCResourceMixin, HistoryResourceMixin, WatchedResourceMixin,
super().pre_conditions_on_save(obj)
if obj.milestone and obj.milestone.project != obj.project:
- raise exc.PermissionDenied(_("You don't have permissions to set this milestone to this issue."))
+ raise exc.PermissionDenied(_("You don't have permissions to set this sprint "
+ "to this issue."))
if obj.status and obj.status.project != obj.project:
- raise exc.PermissionDenied(_("You don't have permissions to set this status to this issue."))
+ raise exc.PermissionDenied(_("You don't have permissions to set this status "
+ "to this issue."))
if obj.severity and obj.severity.project != obj.project:
- raise exc.PermissionDenied(_("You don't have permissions to set this severity to this issue."))
+ raise exc.PermissionDenied(_("You don't have permissions to set this severity "
+ "to this issue."))
if obj.priority and obj.priority.project != obj.project:
- raise exc.PermissionDenied(_("You don't have permissions to set this priority to this issue."))
+ raise exc.PermissionDenied(_("You don't have permissions to set this priority "
+ "to this issue."))
if obj.type and obj.type.project != obj.project:
- raise exc.PermissionDenied(_("You don't have permissions to set this type to this issue."))
+ raise exc.PermissionDenied(_("You don't have permissions to set this type "
+ "to this issue."))
@list_route(methods=["GET"])
def by_ref(self, request):
@@ -185,7 +188,7 @@ class IssueViewSet(OCCResourceMixin, HistoryResourceMixin, WatchedResourceMixin,
self.check_permissions(request, 'upvote', issue)
votes_service.add_vote(issue, user=request.user)
- return Response(status=status.HTTP_200_OK)
+ return response.Ok()
@detail_route(methods=['post'])
def downvote(self, request, pk=None):
@@ -194,7 +197,7 @@ class IssueViewSet(OCCResourceMixin, HistoryResourceMixin, WatchedResourceMixin,
self.check_permissions(request, 'downvote', issue)
votes_service.remove_vote(issue, user=request.user)
- return Response(status=status.HTTP_200_OK)
+ return response.Ok()
class VotersViewSet(ModelListViewSet):
@@ -215,7 +218,7 @@ class VotersViewSet(ModelListViewSet):
raise Http404
serializer = self.get_serializer(self.object)
- return Response(serializer.data)
+ return response.Ok(serializer.data)
def list(self, request, *args, **kwargs):
issue_id = kwargs.get("issue_id", None)
diff --git a/taiga/projects/milestones/api.py b/taiga/projects/milestones/api.py
index 3e26309d..132f9bf2 100644
--- a/taiga/projects/milestones/api.py
+++ b/taiga/projects/milestones/api.py
@@ -14,16 +14,11 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
-from django.utils.translation import ugettext_lazy as _
-
-from rest_framework.permissions import IsAuthenticated
-from rest_framework.response import Response
-
-from taiga.base.api.utils import get_object_or_404
from taiga.base import filters
-from taiga.base import exceptions as exc
+from taiga.base import response
from taiga.base.decorators import detail_route
from taiga.base.api import ModelCrudViewSet
+from taiga.base.api.utils import get_object_or_404
from taiga.projects.notifications.mixins import WatchedResourceMixin
from taiga.projects.history.mixins import HistoryResourceMixin
@@ -97,4 +92,4 @@ class MilestoneViewSet(HistoryResourceMixin, WatchedResourceMixin, ModelCrudView
current_date = current_date + datetime.timedelta(days=1)
optimal_points -= optimal_points_per_day
- return Response(milestone_stats)
+ return response.Ok(milestone_stats)
diff --git a/taiga/projects/notifications/api.py b/taiga/projects/notifications/api.py
index e2c0e91e..f0dfbf8f 100644
--- a/taiga/projects/notifications/api.py
+++ b/taiga/projects/notifications/api.py
@@ -14,20 +14,10 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
-from django.utils.translation import ugettext_lazy as _
from django.db.models import Q
-from rest_framework.permissions import IsAuthenticated
-from rest_framework.response import Response
-
-from taiga.base.api.utils import get_object_or_404
-from taiga.base import filters
-from taiga.base import exceptions as exc
-from taiga.base.decorators import detail_route
from taiga.base.api import ModelCrudViewSet
-from taiga.projects.models import Project
-
from taiga.projects.notifications.choices import NotifyLevel
from . import serializers
diff --git a/taiga/projects/references/api.py b/taiga/projects/references/api.py
index 8e53e179..97b41de4 100644
--- a/taiga/projects/references/api.py
+++ b/taiga/projects/references/api.py
@@ -16,14 +16,13 @@
from django.apps import apps
-from rest_framework.response import Response
-
-from taiga.base.api.utils import get_object_or_404
from taiga.base import exceptions as exc
+from taiga.base import response
from taiga.base.api import viewsets
-from .serializers import ResolverSerializer
+from taiga.base.api.utils import get_object_or_404
from taiga.permissions.service import user_has_perm
+from .serializers import ResolverSerializer
from . import permissions
@@ -42,19 +41,22 @@ class ResolverViewSet(viewsets.ViewSet):
self.check_permissions(request, "list", project)
- result = {
- "project": project.pk
- }
+ result = {"project": project.pk}
if data["us"] and user_has_perm(request.user, "view_us", project):
- result["us"] = get_object_or_404(project.user_stories.all(), ref=data["us"]).pk
+ result["us"] = get_object_or_404(project.user_stories.all(),
+ ref=data["us"]).pk
if data["task"] and user_has_perm(request.user, "view_tasks", project):
- result["task"] = get_object_or_404(project.tasks.all(), ref=data["task"]).pk
+ result["task"] = get_object_or_404(project.tasks.all(),
+ ref=data["task"]).pk
if data["issue"] and user_has_perm(request.user, "view_issues", project):
- result["issue"] = get_object_or_404(project.issues.all(), ref=data["issue"]).pk
+ result["issue"] = get_object_or_404(project.issues.all(),
+ ref=data["issue"]).pk
if data["milestone"] and user_has_perm(request.user, "view_milestones", project):
- result["milestone"] = get_object_or_404(project.milestones.all(), slug=data["milestone"]).pk
+ result["milestone"] = get_object_or_404(project.milestones.all(),
+ slug=data["milestone"]).pk
if data["wikipage"] and user_has_perm(request.user, "view_wiki_pages", project):
- result["wikipage"] = get_object_or_404(project.wiki_pages.all(), slug=data["wikipage"]).pk
+ result["wikipage"] = get_object_or_404(project.wiki_pages.all(),
+ slug=data["wikipage"]).pk
- return Response(result)
+ return response.Ok(result)
diff --git a/taiga/projects/userstories/api.py b/taiga/projects/userstories/api.py
index b0725f68..32d40242 100644
--- a/taiga/projects/userstories/api.py
+++ b/taiga/projects/userstories/api.py
@@ -16,19 +16,19 @@
from contextlib import suppress
+from rest_framework import status
+
from django.apps import apps
from django.db import transaction
from django.utils.translation import ugettext as _
from django.core.exceptions import ObjectDoesNotExist
-from rest_framework.response import Response
-from rest_framework import status
-
-from taiga.base.api.utils import get_object_or_404
-from taiga.base import filters, response
+from taiga.base import filters
from taiga.base import exceptions as exc
+from taiga.base import response
from taiga.base.decorators import list_route
from taiga.base.api import ModelCrudViewSet
+from taiga.base.api.utils import get_object_or_404
from taiga.projects.notifications.mixins import WatchedResourceMixin
from taiga.projects.history.mixins import HistoryResourceMixin
diff --git a/taiga/projects/wiki/api.py b/taiga/projects/wiki/api.py
index 45b7f5a2..f67150bf 100644
--- a/taiga/projects/wiki/api.py
+++ b/taiga/projects/wiki/api.py
@@ -17,13 +17,12 @@
from django.utils.translation import ugettext_lazy as _
from rest_framework.permissions import IsAuthenticated
-from rest_framework.response import Response
-from rest_framework import status
-from taiga.base.api.utils import get_object_or_404
from taiga.base import filters
from taiga.base import exceptions as exc
+from taiga.base import response
from taiga.base.api import ModelCrudViewSet
+from taiga.base.api.utils import get_object_or_404
from taiga.base.decorators import list_route
from taiga.projects.models import Project
from taiga.mdrender.service import render as mdrender
@@ -58,17 +57,17 @@ class WikiViewSet(OCCResourceMixin, HistoryResourceMixin, WatchedResourceMixin,
project_id = request.DATA.get("project_id", None)
if not content:
- raise exc.WrongArguments({"content": "No content parameter"})
+ raise exc.WrongArguments({"content": _("No content parameter")})
if not project_id:
- raise exc.WrongArguments({"project_id": "No project_id parameter"})
+ raise exc.WrongArguments({"project_id": _("No project_id parameter")})
project = get_object_or_404(Project, pk=project_id)
self.check_permissions(request, "render", project)
data = mdrender(project, content)
- return Response({"data": data})
+ return response.Ok({"data": data})
def pre_save(self, obj):
if not obj.owner:
diff --git a/taiga/searches/api.py b/taiga/searches/api.py
index 182e8213..0a8bb788 100644
--- a/taiga/searches/api.py
+++ b/taiga/searches/api.py
@@ -16,10 +16,9 @@
from django.apps import apps
-from rest_framework.response import Response
from rest_framework import viewsets
-from taiga.base import exceptions as excp
+from taiga.base import response
from taiga.base.api.utils import get_object_or_404
from taiga.projects.userstories.serializers import UserStorySerializer
from taiga.projects.tasks.serializers import TaskSerializer
@@ -48,7 +47,7 @@ class SearchViewSet(viewsets.ViewSet):
result["wikipages"] = self._search_wiki_pages(project, text)
result["count"] = sum(map(lambda x: len(x), result.values()))
- return Response(result)
+ return response.Ok(result)
def _get_project(self, project_id):
project_model = apps.get_model("projects", "Project")
diff --git a/taiga/timeline/api.py b/taiga/timeline/api.py
index 10795cef..4d907a69 100644
--- a/taiga/timeline/api.py
+++ b/taiga/timeline/api.py
@@ -16,8 +16,7 @@
from django.contrib.contenttypes.models import ContentType
-from rest_framework.response import Response
-
+from taiga.base import response
from taiga.base.api.utils import get_object_or_404
from taiga.base.api import GenericViewSet
@@ -52,12 +51,12 @@ class TimelineViewSet(GenericViewSet):
else:
serializer = self.get_serializer(queryset, many=True)
- return Response(serializer.data)
+ return response.Ok(serializer.data)
# Just for restframework! Because it raises
# 404 on main api root if this method not exists.
def list(self, request):
- return Response({})
+ return response.NotFound()
def retrieve(self, request, pk):
obj = self.get_object()
diff --git a/taiga/users/api.py b/taiga/users/api.py
index a446de8d..547e99c6 100644
--- a/taiga/users/api.py
+++ b/taiga/users/api.py
@@ -23,25 +23,23 @@ from django.core.validators import validate_email
from django.core.exceptions import ValidationError
from django.conf import settings
-from easy_thumbnails.source_generators import pil_image
-
-from rest_framework.response import Response
-from rest_framework import status
-
-from djmail.template_mail import MagicMailBuilder
-from djmail.template_mail import InlineCSSTemplateMail
-
+from taiga.base import exceptions as exc
+from taiga.base import filters
+from taiga.base import response
from taiga.auth.tokens import get_user_for_token
from taiga.base.decorators import list_route
from taiga.base.decorators import detail_route
-from taiga.base import exceptions as exc
-from taiga.base import filters
from taiga.base.api import ModelCrudViewSet
from taiga.base.api.utils import get_object_or_404
from taiga.base.filters import MembersFilterBackend
from taiga.projects.votes import services as votes_service
from taiga.projects.serializers import StarredSerializer
+from easy_thumbnails.source_generators import pil_image
+
+from djmail.template_mail import MagicMailBuilder
+from djmail.template_mail import InlineCSSTemplateMail
+
from . import models
from . import serializers
from . import permissions
@@ -71,7 +69,7 @@ class UsersViewSet(ModelCrudViewSet):
else:
serializer = self.get_serializer(self.object_list, many=True)
- return Response(serializer.data)
+ return response.Ok(serializer.data)
@list_route(methods=["POST"])
def password_recovery(self, request, pk=None):
@@ -96,8 +94,8 @@ class UsersViewSet(ModelCrudViewSet):
email = mbuilder.password_recovery(user.email, {"user": user})
email.send()
- return Response({"detail": _("Mail sended successful!"),
- "email": user.email})
+ return response.Ok({"detail": _("Mail sended successful!"),
+ "email": user.email})
@list_route(methods=["POST"])
def change_password_from_recovery(self, request, pk=None):
@@ -120,7 +118,7 @@ class UsersViewSet(ModelCrudViewSet):
user.token = None
user.save(update_fields=["password", "token"])
- return Response(status=status.HTTP_204_NO_CONTENT)
+ return response.NoContent()
@list_route(methods=["POST"])
def change_password(self, request, pk=None):
@@ -148,7 +146,7 @@ class UsersViewSet(ModelCrudViewSet):
request.user.set_password(password)
request.user.save(update_fields=["password"])
- return Response(status=status.HTTP_204_NO_CONTENT)
+ return response.NoContent()
@list_route(methods=["POST"])
def change_avatar(self, request):
@@ -171,7 +169,7 @@ class UsersViewSet(ModelCrudViewSet):
request.user.save(update_fields=["photo"])
user_data = serializers.UserSerializer(request.user).data
- return Response(user_data, status=status.HTTP_200_OK)
+ return response.Ok(user_data)
@list_route(methods=["POST"])
def remove_avatar(self, request):
@@ -182,7 +180,7 @@ class UsersViewSet(ModelCrudViewSet):
request.user.photo = None
request.user.save(update_fields=["photo"])
user_data = serializers.UserSerializer(request.user).data
- return Response(user_data, status=status.HTTP_200_OK)
+ return response.Ok(user_data)
@detail_route(methods=["GET"])
def starred(self, request, pk=None):
@@ -191,7 +189,7 @@ class UsersViewSet(ModelCrudViewSet):
stars = votes_service.get_voted(user.pk, model=apps.get_model('projects', 'Project'))
stars_data = StarredSerializer(stars, many=True)
- return Response(stars_data.data)
+ return response.Ok(stars_data.data)
#TODO: commit_on_success
def partial_update(self, request, *args, **kwargs):
@@ -254,7 +252,7 @@ class UsersViewSet(ModelCrudViewSet):
user.email_token = None
user.save(update_fields=["email", "new_email", "email_token"])
- return Response(status=status.HTTP_204_NO_CONTENT)
+ return response.NoContent()
@list_route(methods=["GET"])
def me(self, request, pk=None):
@@ -263,7 +261,7 @@ class UsersViewSet(ModelCrudViewSet):
"""
self.check_permissions(request, "me", None)
user_data = serializers.UserSerializer(request.user).data
- return Response(user_data, status=status.HTTP_200_OK)
+ return response.Ok(user_data)
@list_route(methods=["POST"])
def cancel(self, request, pk=None):
@@ -286,7 +284,7 @@ class UsersViewSet(ModelCrudViewSet):
raise exc.WrongArguments(_("Invalid, are you sure the token is correct?"))
user.cancel()
- return Response(status=status.HTTP_204_NO_CONTENT)
+ return response.NoContent()
def destroy(self, request, pk=None):
user = self.get_object()
@@ -295,7 +293,7 @@ class UsersViewSet(ModelCrudViewSet):
request_data = stream is not None and stream.GET or None
user_cancel_account_signal.send(sender=user.__class__, user=user, request_data=request_data)
user.cancel()
- return Response(status=status.HTTP_204_NO_CONTENT)
+ return response.NoContent()
######################################################
diff --git a/taiga/webhooks/api.py b/taiga/webhooks/api.py
index 31242102..55f3fca4 100644
--- a/taiga/webhooks/api.py
+++ b/taiga/webhooks/api.py
@@ -16,10 +16,11 @@
import json
-from rest_framework.response import Response
-
from taiga.base import filters
-from taiga.base.api import ModelCrudViewSet, ModelListViewSet
+from taiga.base import response
+from taiga.base.api import ModelCrudViewSet
+from taiga.base.api import ModelListViewSet
+
from taiga.base.api.utils import get_object_or_404
from taiga.base.decorators import detail_route
@@ -44,7 +45,8 @@ class WebhookViewSet(ModelCrudViewSet):
webhooklog = tasks.test_webhook(webhook.id, webhook.url, webhook.key)
log = serializers.WebhookLogSerializer(webhooklog)
- return Response(log.data)
+ return response.Ok(log.data)
+
class WebhookLogViewSet(ModelListViewSet):
model = models.WebhookLog
@@ -60,7 +62,9 @@ class WebhookLogViewSet(ModelListViewSet):
webhook = webhooklog.webhook
- webhooklog = tasks.resend_webhook(webhook.id, webhook.url, webhook.key, webhooklog.request_data)
+ webhooklog = tasks.resend_webhook(webhook.id, webhook.url, webhook.key,
+ webhooklog.request_data)
+
log = serializers.WebhookLogSerializer(webhooklog)
- return Response(log.data)
+ return response.Ok(log.data)
diff --git a/tests/integration/test_hooks_bitbucket.py b/tests/integration/test_hooks_bitbucket.py
index f071400e..e5e0416b 100644
--- a/tests/integration/test_hooks_bitbucket.py
+++ b/tests/integration/test_hooks_bitbucket.py
@@ -55,7 +55,7 @@ def test_ok_signature(client):
urllib.parse.urlencode(data, True),
content_type="application/x-www-form-urlencoded",
REMOTE_ADDR=settings.BITBUCKET_VALID_ORIGIN_IPS[0])
- assert response.status_code == 200
+ assert response.status_code == 204
def test_invalid_ip(client):
project=f.ProjectFactory()
@@ -91,7 +91,7 @@ def test_not_ip_filter(client):
urllib.parse.urlencode(data, True),
content_type="application/x-www-form-urlencoded",
REMOTE_ADDR="111.111.111.112")
- assert response.status_code == 200
+ assert response.status_code == 204
def test_push_event_detected(client):
@@ -108,7 +108,7 @@ def test_push_event_detected(client):
assert process_event_mock.call_count == 1
- assert response.status_code == 200
+ assert response.status_code == 204
def test_push_event_issue_processing(client):
diff --git a/tests/integration/test_hooks_github.py b/tests/integration/test_hooks_github.py
index 08a33bbd..5115be6e 100644
--- a/tests/integration/test_hooks_github.py
+++ b/tests/integration/test_hooks_github.py
@@ -50,7 +50,7 @@ def test_ok_signature(client):
HTTP_X_HUB_SIGNATURE="sha1=3c8e83fdaa266f81c036ea0b71e98eb5e054581a",
content_type="application/json")
- assert response.status_code == 200
+ assert response.status_code == 204
def test_push_event_detected(client):
@@ -70,7 +70,7 @@ def test_push_event_detected(client):
assert process_event_mock.call_count == 1
- assert response.status_code == 200
+ assert response.status_code == 204
def test_push_event_issue_processing(client):
diff --git a/tests/integration/test_hooks_gitlab.py b/tests/integration/test_hooks_gitlab.py
index 2bb4305a..888d745a 100644
--- a/tests/integration/test_hooks_gitlab.py
+++ b/tests/integration/test_hooks_gitlab.py
@@ -55,7 +55,7 @@ def test_ok_signature(client):
content_type="application/json",
REMOTE_ADDR="111.111.111.111")
- assert response.status_code == 200
+ assert response.status_code == 204
def test_invalid_ip(client):
@@ -95,7 +95,7 @@ def test_not_ip_filter(client):
content_type="application/json",
REMOTE_ADDR="111.111.111.111")
- assert response.status_code == 200
+ assert response.status_code == 204
def test_push_event_detected(client):
@@ -115,7 +115,7 @@ def test_push_event_detected(client):
assert process_event_mock.call_count == 1
- assert response.status_code == 200
+ assert response.status_code == 204
def test_push_event_issue_processing(client):
@@ -340,7 +340,6 @@ def test_issues_event_bad_issue(client):
assert len(mail.outbox) == 0
-
def test_api_get_project_modules(client):
project = f.create_project()
membership = f.MembershipFactory(project=project, user=project.owner, is_owner=True)