WIP
parent
7df6450fae
commit
ccd928822a
|
@ -358,7 +358,17 @@ LOGGING = {
|
||||||
"level": "ERROR",
|
"level": "ERROR",
|
||||||
"filters": ["require_debug_false"],
|
"filters": ["require_debug_false"],
|
||||||
"class": "django.utils.log.AdminEmailHandler",
|
"class": "django.utils.log.AdminEmailHandler",
|
||||||
}
|
},
|
||||||
|
'logstash': {
|
||||||
|
'level': 'INFO',
|
||||||
|
'class': 'logstash.TCPLogstashHandler',
|
||||||
|
'host': 'localhost',
|
||||||
|
'port': 5000,
|
||||||
|
'version': 1, # Version of logstash event schema. Default value: 0 (for backward compatibility of the library)
|
||||||
|
'message_type': 'taiga-bi', # 'type' field in logstash message. Default value: 'logstash'.
|
||||||
|
# 'fqdn': False, # Fully qualified domain name. Default value: false.
|
||||||
|
#'tags': ['tag1', 'tag2'], # list of tags. Default: None.
|
||||||
|
},
|
||||||
},
|
},
|
||||||
"loggers": {
|
"loggers": {
|
||||||
"django": {
|
"django": {
|
||||||
|
@ -380,6 +390,11 @@ LOGGING = {
|
||||||
"handlers": ["console"],
|
"handlers": ["console"],
|
||||||
"level": "DEBUG",
|
"level": "DEBUG",
|
||||||
"propagate": False,
|
"propagate": False,
|
||||||
|
},
|
||||||
|
"bi": {
|
||||||
|
"handlers": ["logstash"],
|
||||||
|
"level": "INFO",
|
||||||
|
"propagate": False,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,7 @@ from django.db import transaction as tx
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
from taiga.base import response
|
from taiga.base import response
|
||||||
|
from taiga.base.logger import bilogger
|
||||||
|
|
||||||
from .settings import api_settings
|
from .settings import api_settings
|
||||||
from .utils import get_object_or_404
|
from .utils import get_object_or_404
|
||||||
|
@ -97,7 +98,9 @@ class CreateModelMixin:
|
||||||
self.pre_conditions_on_save(serializer.object)
|
self.pre_conditions_on_save(serializer.object)
|
||||||
self.object = serializer.save(force_insert=True)
|
self.object = serializer.save(force_insert=True)
|
||||||
self.post_save(self.object, created=True)
|
self.post_save(self.object, created=True)
|
||||||
|
|
||||||
headers = self.get_success_headers(serializer.data)
|
headers = self.get_success_headers(serializer.data)
|
||||||
|
bilogger(request, self, obj=self.object)
|
||||||
return response.Created(serializer.data, headers=headers)
|
return response.Created(serializer.data, headers=headers)
|
||||||
|
|
||||||
return response.BadRequest(serializer.errors)
|
return response.BadRequest(serializer.errors)
|
||||||
|
@ -136,6 +139,7 @@ class ListModelMixin:
|
||||||
else:
|
else:
|
||||||
serializer = self.get_serializer(self.object_list, many=True)
|
serializer = self.get_serializer(self.object_list, many=True)
|
||||||
|
|
||||||
|
bilogger(request, self)
|
||||||
return response.Ok(serializer.data)
|
return response.Ok(serializer.data)
|
||||||
|
|
||||||
|
|
||||||
|
@ -152,6 +156,7 @@ class RetrieveModelMixin:
|
||||||
raise Http404
|
raise Http404
|
||||||
|
|
||||||
serializer = self.get_serializer(self.object)
|
serializer = self.get_serializer(self.object)
|
||||||
|
bilogger(request, self, obj=self.object)
|
||||||
return response.Ok(serializer.data)
|
return response.Ok(serializer.data)
|
||||||
|
|
||||||
|
|
||||||
|
@ -189,6 +194,7 @@ class UpdateModelMixin:
|
||||||
|
|
||||||
self.object = serializer.save(force_update=True)
|
self.object = serializer.save(force_update=True)
|
||||||
self.post_save(self.object, created=False)
|
self.post_save(self.object, created=False)
|
||||||
|
bilogger(request, self, obj=self.object)
|
||||||
return response.Ok(serializer.data)
|
return response.Ok(serializer.data)
|
||||||
|
|
||||||
def partial_update(self, request, *args, **kwargs):
|
def partial_update(self, request, *args, **kwargs):
|
||||||
|
@ -238,6 +244,7 @@ class DestroyModelMixin:
|
||||||
self.pre_conditions_on_delete(obj)
|
self.pre_conditions_on_delete(obj)
|
||||||
obj.delete()
|
obj.delete()
|
||||||
self.post_delete(obj)
|
self.post_delete(obj)
|
||||||
|
bilogger(request, self, obj=obj)
|
||||||
return response.NoContent()
|
return response.NoContent()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
import logging
|
||||||
|
import copy
|
||||||
|
from taiga.base.utils import json
|
||||||
|
bi_logger = logging.getLogger("bi")
|
||||||
|
|
||||||
|
|
||||||
|
def bilogger(request, view, obj=None, **kwargs):
|
||||||
|
data = copy.copy(kwargs)
|
||||||
|
|
||||||
|
data["ip"] = request.META.get("REMOTE_ADDR", None)
|
||||||
|
data["user-agent"] = request.META.get("HTTP_USER_AGENT", None)
|
||||||
|
data["path"] = request.get_full_path()
|
||||||
|
|
||||||
|
if "success" not in kwargs:
|
||||||
|
data["success"] = True
|
||||||
|
|
||||||
|
data['user'] = 'anon'
|
||||||
|
if not request.user.is_anonymous():
|
||||||
|
data['user'] = request.user.id
|
||||||
|
data['view'] = view.get_view_name()
|
||||||
|
|
||||||
|
if "action" not in kwargs:
|
||||||
|
data['action'] = view.action
|
||||||
|
|
||||||
|
if obj is not None:
|
||||||
|
data['object-id'] = obj.id
|
||||||
|
if hasattr(obj, 'project_id'):
|
||||||
|
data['project-id'] = obj.project_id
|
||||||
|
|
||||||
|
bi_logger.info("", extra=data)
|
|
@ -59,6 +59,8 @@ from . import permissions
|
||||||
from . import serializers
|
from . import serializers
|
||||||
from . import services
|
from . import services
|
||||||
|
|
||||||
|
from taiga.base.logger import bilogger
|
||||||
|
|
||||||
|
|
||||||
######################################################
|
######################################################
|
||||||
## Project
|
## Project
|
||||||
|
@ -171,6 +173,7 @@ class ProjectViewSet(LikedResourceMixin, HistoryResourceMixin,
|
||||||
self.object.save(update_fields=["logo"])
|
self.object.save(update_fields=["logo"])
|
||||||
|
|
||||||
serializer = self.get_serializer(self.object)
|
serializer = self.get_serializer(self.object)
|
||||||
|
bilogger(request, self, obj=self.object)
|
||||||
return response.Ok(serializer.data)
|
return response.Ok(serializer.data)
|
||||||
|
|
||||||
@detail_route(methods=["POST"])
|
@detail_route(methods=["POST"])
|
||||||
|
@ -185,6 +188,7 @@ class ProjectViewSet(LikedResourceMixin, HistoryResourceMixin,
|
||||||
self.object.save(update_fields=["logo"])
|
self.object.save(update_fields=["logo"])
|
||||||
|
|
||||||
serializer = self.get_serializer(self.object)
|
serializer = self.get_serializer(self.object)
|
||||||
|
bilogger(request, self, obj=self.object)
|
||||||
return response.Ok(serializer.data)
|
return response.Ok(serializer.data)
|
||||||
|
|
||||||
@detail_route(methods=["POST"])
|
@detail_route(methods=["POST"])
|
||||||
|
@ -194,6 +198,7 @@ class ProjectViewSet(LikedResourceMixin, HistoryResourceMixin,
|
||||||
self.pre_conditions_on_save(project)
|
self.pre_conditions_on_save(project)
|
||||||
notify_level = request.DATA.get("notify_level", NotifyLevel.involved)
|
notify_level = request.DATA.get("notify_level", NotifyLevel.involved)
|
||||||
project.add_watcher(self.request.user, notify_level=notify_level)
|
project.add_watcher(self.request.user, notify_level=notify_level)
|
||||||
|
bilogger(request, self, obj=project)
|
||||||
return response.Ok()
|
return response.Ok()
|
||||||
|
|
||||||
@detail_route(methods=["POST"])
|
@detail_route(methods=["POST"])
|
||||||
|
@ -203,6 +208,7 @@ class ProjectViewSet(LikedResourceMixin, HistoryResourceMixin,
|
||||||
self.pre_conditions_on_save(project)
|
self.pre_conditions_on_save(project)
|
||||||
user = self.request.user
|
user = self.request.user
|
||||||
project.remove_watcher(user)
|
project.remove_watcher(user)
|
||||||
|
bilogger(request, self, obj=project)
|
||||||
return response.Ok()
|
return response.Ok()
|
||||||
|
|
||||||
@list_route(methods=["POST"])
|
@list_route(methods=["POST"])
|
||||||
|
@ -216,6 +222,7 @@ class ProjectViewSet(LikedResourceMixin, HistoryResourceMixin,
|
||||||
|
|
||||||
data = serializer.data
|
data = serializer.data
|
||||||
services.update_projects_order_in_bulk(data, "user_order", request.user)
|
services.update_projects_order_in_bulk(data, "user_order", request.user)
|
||||||
|
bilogger(request, self)
|
||||||
return response.NoContent(data=None)
|
return response.NoContent(data=None)
|
||||||
|
|
||||||
@detail_route(methods=["POST"])
|
@detail_route(methods=["POST"])
|
||||||
|
@ -223,16 +230,18 @@ class ProjectViewSet(LikedResourceMixin, HistoryResourceMixin,
|
||||||
template_name = request.DATA.get('template_name', None)
|
template_name = request.DATA.get('template_name', None)
|
||||||
template_description = request.DATA.get('template_description', None)
|
template_description = request.DATA.get('template_description', None)
|
||||||
|
|
||||||
|
project = self.get_object()
|
||||||
|
|
||||||
if not template_name:
|
if not template_name:
|
||||||
|
bilogger(request, self, obj=project, success=False, error="invalid-template-name")
|
||||||
raise response.BadRequest(_("Not valid template name"))
|
raise response.BadRequest(_("Not valid template name"))
|
||||||
|
|
||||||
if not template_description:
|
if not template_description:
|
||||||
|
bilogger(request, self, obj=project, success=False, error="invalid-template-description")
|
||||||
raise response.BadRequest(_("Not valid template description"))
|
raise response.BadRequest(_("Not valid template description"))
|
||||||
|
|
||||||
template_slug = slugify_uniquely(template_name, models.ProjectTemplate)
|
template_slug = slugify_uniquely(template_name, models.ProjectTemplate)
|
||||||
|
|
||||||
project = self.get_object()
|
|
||||||
|
|
||||||
self.check_permissions(request, 'create_template', project)
|
self.check_permissions(request, 'create_template', project)
|
||||||
|
|
||||||
template = models.ProjectTemplate(
|
template = models.ProjectTemplate(
|
||||||
|
@ -243,6 +252,7 @@ class ProjectViewSet(LikedResourceMixin, HistoryResourceMixin,
|
||||||
|
|
||||||
template.load_data_from_project(project)
|
template.load_data_from_project(project)
|
||||||
template.save()
|
template.save()
|
||||||
|
bilogger(request, self, obj=project)
|
||||||
return response.Created(serializers.ProjectTemplateSerializer(template).data)
|
return response.Created(serializers.ProjectTemplateSerializer(template).data)
|
||||||
|
|
||||||
@detail_route(methods=['POST'])
|
@detail_route(methods=['POST'])
|
||||||
|
@ -251,6 +261,7 @@ class ProjectViewSet(LikedResourceMixin, HistoryResourceMixin,
|
||||||
self.check_permissions(request, 'leave', project)
|
self.check_permissions(request, 'leave', project)
|
||||||
self.pre_conditions_on_save(project)
|
self.pre_conditions_on_save(project)
|
||||||
services.remove_user_from_project(request.user, project)
|
services.remove_user_from_project(request.user, project)
|
||||||
|
bilogger(request, self, obj=project)
|
||||||
return response.Ok()
|
return response.Ok()
|
||||||
|
|
||||||
@detail_route(methods=["POST"])
|
@detail_route(methods=["POST"])
|
||||||
|
@ -259,6 +270,7 @@ class ProjectViewSet(LikedResourceMixin, HistoryResourceMixin,
|
||||||
self.check_permissions(request, "regenerate_userstories_csv_uuid", project)
|
self.check_permissions(request, "regenerate_userstories_csv_uuid", project)
|
||||||
self.pre_conditions_on_save(project)
|
self.pre_conditions_on_save(project)
|
||||||
data = {"uuid": self._regenerate_csv_uuid(project, "userstories_csv_uuid")}
|
data = {"uuid": self._regenerate_csv_uuid(project, "userstories_csv_uuid")}
|
||||||
|
bilogger(request, self, obj=project)
|
||||||
return response.Ok(data)
|
return response.Ok(data)
|
||||||
|
|
||||||
@detail_route(methods=["POST"])
|
@detail_route(methods=["POST"])
|
||||||
|
@ -267,6 +279,7 @@ class ProjectViewSet(LikedResourceMixin, HistoryResourceMixin,
|
||||||
self.check_permissions(request, "regenerate_issues_csv_uuid", project)
|
self.check_permissions(request, "regenerate_issues_csv_uuid", project)
|
||||||
self.pre_conditions_on_save(project)
|
self.pre_conditions_on_save(project)
|
||||||
data = {"uuid": self._regenerate_csv_uuid(project, "issues_csv_uuid")}
|
data = {"uuid": self._regenerate_csv_uuid(project, "issues_csv_uuid")}
|
||||||
|
bilogger(request, self, obj=project)
|
||||||
return response.Ok(data)
|
return response.Ok(data)
|
||||||
|
|
||||||
@detail_route(methods=["POST"])
|
@detail_route(methods=["POST"])
|
||||||
|
@ -275,6 +288,7 @@ class ProjectViewSet(LikedResourceMixin, HistoryResourceMixin,
|
||||||
self.check_permissions(request, "regenerate_tasks_csv_uuid", project)
|
self.check_permissions(request, "regenerate_tasks_csv_uuid", project)
|
||||||
self.pre_conditions_on_save(project)
|
self.pre_conditions_on_save(project)
|
||||||
data = {"uuid": self._regenerate_csv_uuid(project, "tasks_csv_uuid")}
|
data = {"uuid": self._regenerate_csv_uuid(project, "tasks_csv_uuid")}
|
||||||
|
bilogger(request, self, obj=project)
|
||||||
return response.Ok(data)
|
return response.Ok(data)
|
||||||
|
|
||||||
@list_route(methods=["GET"])
|
@list_route(methods=["GET"])
|
||||||
|
@ -290,18 +304,21 @@ class ProjectViewSet(LikedResourceMixin, HistoryResourceMixin,
|
||||||
modules_config = services.get_modules_config(project)
|
modules_config = services.get_modules_config(project)
|
||||||
|
|
||||||
if request.method == "GET":
|
if request.method == "GET":
|
||||||
|
bilogger(request, self, action="modules-get", obj=project)
|
||||||
return response.Ok(modules_config.config)
|
return response.Ok(modules_config.config)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.pre_conditions_on_save(project)
|
self.pre_conditions_on_save(project)
|
||||||
modules_config.config.update(request.DATA)
|
modules_config.config.update(request.DATA)
|
||||||
modules_config.save()
|
modules_config.save()
|
||||||
|
bilogger(request, self, action="modules-update", obj=project)
|
||||||
return response.NoContent()
|
return response.NoContent()
|
||||||
|
|
||||||
@detail_route(methods=["GET"])
|
@detail_route(methods=["GET"])
|
||||||
def stats(self, request, pk=None):
|
def stats(self, request, pk=None):
|
||||||
project = self.get_object()
|
project = self.get_object()
|
||||||
self.check_permissions(request, "stats", project)
|
self.check_permissions(request, "stats", project)
|
||||||
|
bilogger(request, self, id=project.id)
|
||||||
return response.Ok(services.get_stats_for_project(project))
|
return response.Ok(services.get_stats_for_project(project))
|
||||||
|
|
||||||
def _regenerate_csv_uuid(self, project, field):
|
def _regenerate_csv_uuid(self, project, field):
|
||||||
|
@ -314,18 +331,21 @@ class ProjectViewSet(LikedResourceMixin, HistoryResourceMixin,
|
||||||
def member_stats(self, request, pk=None):
|
def member_stats(self, request, pk=None):
|
||||||
project = self.get_object()
|
project = self.get_object()
|
||||||
self.check_permissions(request, "member_stats", project)
|
self.check_permissions(request, "member_stats", project)
|
||||||
|
bilogger(request, self, obj=project)
|
||||||
return response.Ok(services.get_member_stats_for_project(project))
|
return response.Ok(services.get_member_stats_for_project(project))
|
||||||
|
|
||||||
@detail_route(methods=["GET"])
|
@detail_route(methods=["GET"])
|
||||||
def issues_stats(self, request, pk=None):
|
def issues_stats(self, request, pk=None):
|
||||||
project = self.get_object()
|
project = self.get_object()
|
||||||
self.check_permissions(request, "issues_stats", project)
|
self.check_permissions(request, "issues_stats", project)
|
||||||
|
bilogger(request, self, obj=project)
|
||||||
return response.Ok(services.get_stats_for_project_issues(project))
|
return response.Ok(services.get_stats_for_project_issues(project))
|
||||||
|
|
||||||
@detail_route(methods=["GET"])
|
@detail_route(methods=["GET"])
|
||||||
def tags_colors(self, request, pk=None):
|
def tags_colors(self, request, pk=None):
|
||||||
project = self.get_object()
|
project = self.get_object()
|
||||||
self.check_permissions(request, "tags_colors", project)
|
self.check_permissions(request, "tags_colors", project)
|
||||||
|
bilogger(request, self, obj=project)
|
||||||
return response.Ok(dict(project.tags_colors))
|
return response.Ok(dict(project.tags_colors))
|
||||||
|
|
||||||
@detail_route(methods=["POST"])
|
@detail_route(methods=["POST"])
|
||||||
|
@ -347,7 +367,6 @@ class ProjectViewSet(LikedResourceMixin, HistoryResourceMixin,
|
||||||
user_model = apps.get_model("users", "User")
|
user_model = apps.get_model("users", "User")
|
||||||
try:
|
try:
|
||||||
user = user_model.objects.get(id=user_id)
|
user = user_model.objects.get(id=user_id)
|
||||||
|
|
||||||
except user_model.DoesNotExist:
|
except user_model.DoesNotExist:
|
||||||
return response.BadRequest(_("The user doesn't exist"))
|
return response.BadRequest(_("The user doesn't exist"))
|
||||||
|
|
||||||
|
@ -390,6 +409,7 @@ class ProjectViewSet(LikedResourceMixin, HistoryResourceMixin,
|
||||||
raise exc.WrongArguments(_("Invalid token"))
|
raise exc.WrongArguments(_("Invalid token"))
|
||||||
|
|
||||||
project = self.get_object()
|
project = self.get_object()
|
||||||
|
|
||||||
self.check_permissions(request, "transfer_reject", project)
|
self.check_permissions(request, "transfer_reject", project)
|
||||||
|
|
||||||
reason = request.DATA.get('reason', None)
|
reason = request.DATA.get('reason', None)
|
||||||
|
@ -432,7 +452,7 @@ class ProjectViewSet(LikedResourceMixin, HistoryResourceMixin,
|
||||||
|
|
||||||
self.pre_delete(obj)
|
self.pre_delete(obj)
|
||||||
self.pre_conditions_on_delete(obj)
|
self.pre_conditions_on_delete(obj)
|
||||||
obj.delete_related_content()
|
obj.delete_related_content()
|
||||||
obj.delete()
|
obj.delete()
|
||||||
self.post_delete(obj)
|
self.post_delete(obj)
|
||||||
return response.NoContent()
|
return response.NoContent()
|
||||||
|
|
Loading…
Reference in New Issue