general: add possibility to override trace emails recipients (#24591)

This commit is contained in:
Frédéric Péters 2018-11-25 15:08:19 +01:00
parent f924283083
commit d1fbd69e09
6 changed files with 144 additions and 11 deletions

View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.12 on 2018-11-25 13:55
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('base', '0009_auto_20181118_0807'),
]
operations = [
migrations.AddField(
model_name='loggingparameters',
name='trace_emails',
field=models.TextField(blank=True, help_text='One address per line (empty for site administrators)', verbose_name='Emails to receive error and critical traces'),
),
]

View File

@ -15,6 +15,7 @@ from django.core.exceptions import ValidationError, ObjectDoesNotExist, Permissi
from django.core.urlresolvers import reverse
from django.db import models, transaction
from django.db.models import Q
from django.test import override_settings
from django.utils.text import slugify
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
@ -154,19 +155,23 @@ class BaseResource(models.Model):
return passerelle.utils.Request(resource=self, logger=self.logger)
@property
def log_level(self):
def logging_parameters(self):
resource_type = ContentType.objects.get_for_model(self)
try:
return LoggingParameters.objects.get(
resource_type=resource_type,
resource_pk=self.id).log_level
resource_pk=self.id)
except LoggingParameters.DoesNotExist:
return 'INFO'
return LoggingParameters(
resource_type=resource_type,
resource_pk=self.id)
@property
def log_level(self):
return self.logging_parameters.log_level
def set_log_level(self, value):
resource_type = ContentType.objects.get_for_model(self)
parameters, created = LoggingParameters.objects.get_or_create(
resource_type=resource_type, resource_pk=self.pk)
parameters = self.logging_parameters
parameters.log_level = value
parameters.save()
@ -461,6 +466,11 @@ class LoggingParameters(models.Model):
),
default='INFO'
)
trace_emails = models.TextField(
verbose_name=_('Emails to receive error and critical traces'),
help_text=_('One address per line (empty for site administrators)'),
blank=True
)
class Meta:
unique_together = (('resource_type', 'resource_pk'))
@ -567,7 +577,12 @@ class ProxyLogger(object):
ResourceLog.objects.create(**attr)
getattr(self._logger, levelname.lower())(message, *args, **kwargs)
admins = settings.ADMINS
logging_parameters = self.connector.logging_parameters
if logging_parameters.trace_emails:
admins = [('', x) for x in logging_parameters.trace_emails.splitlines()]
with override_settings(ADMINS=admins):
getattr(self._logger, levelname.lower())(message, *args, **kwargs)
def exception(self, message, *args, **kwargs):
kwargs['exc_info'] = 1

View File

@ -108,15 +108,19 @@ class LoggingParametersUpdateView(FormView):
return context
def get_form_class(self):
return model_forms.modelform_factory(
form_class = model_forms.modelform_factory(
LoggingParameters,
fields=['log_level'])
fields=['log_level', 'trace_emails'])
form_class.base_fields['trace_emails'].widget.attrs['rows'] = '3'
return form_class
def get_initial(self):
d = self.initial.copy()
d['resource_type'] = self.kwargs['resource_type']
d['resource_pk'] = self.kwargs['resource_pk']
d['log_level'] = self.get_resource().log_level
parameters = self.get_resource().logging_parameters
d['log_level'] = parameters.log_level
d['trace_emails'] = parameters.trace_emails
return d
def get_resource(self):
@ -127,5 +131,8 @@ class LoggingParametersUpdateView(FormView):
return self.get_resource().get_absolute_url()
def form_valid(self, form):
self.get_resource().set_log_level(form.cleaned_data['log_level'])
parameters = self.get_resource().logging_parameters
parameters.log_level = form.cleaned_data['log_level']
parameters.trace_emails = form.cleaned_data['trace_emails']
parameters.save()
return super(LoggingParametersUpdateView, self).form_valid(form)

View File

@ -3,6 +3,11 @@ from httmock import urlmatch, HTTMock, response
import django_webtest
from django.core.files import File
from io import BytesIO
from .utils import make_resource
@pytest.fixture
def app(request):
@ -109,3 +114,29 @@ def internal_server_error(url, request):
def mock_500():
with HTTMock(internal_server_error):
yield None
@pytest.fixture
def dummy_csv_datasource(db):
from passerelle.apps.csvdatasource.models import CsvDataSource, Query
data = '''id,label
1,a
2,b
3,c'''
obj = make_resource(
CsvDataSource,
slug='dummy-slug',
title='Dummy Title',
description='dummy description',
csv_file=File(BytesIO(data), 'dummy.csv'))
Query.objects.create(
resource=obj,
slug='dummy-query',
structure='array',
label='Dummy Query',
description='dummy query description',
projections='id:int(id)\ntext:label')
return obj

View File

@ -187,3 +187,13 @@ def test_logging_parameters(app, admin_user):
resp.form['log_level'] = 'DEBUG'
resp = resp.form.submit()
assert CsvDataSource.objects.get(id=csv.id).log_level == 'DEBUG'
resp = app.get(csv.get_absolute_url())
resp = resp.click('logging parameters')
resp.form['trace_emails'] = 'fred@localhost'
resp = resp.form.submit()
assert CsvDataSource.objects.get(id=csv.id).logging_parameters.trace_emails == 'fred@localhost'
resp = app.get(csv.get_absolute_url())
resp = resp.click('logging parameters')
assert resp.form['trace_emails'].value == 'fred@localhost'

View File

@ -1,5 +1,6 @@
import datetime
import pytest
from mock import patch
from django.utils import timezone
@ -18,6 +19,7 @@ def test_get_description_url_fields(db):
connector = OpenGIS(slug='plop', wms_service_url='http://username@example.net:secret@www.example.net')
assert 'http://***:***@www.example.net' in [x[1] for x in connector.get_description_fields()]
def test_get_description_secret_fields(db):
connector = ClicRdv(slug='plop', apikey='secret1', username='plop', password='secret2')
assert not 'secret1' in [x[1] for x in connector.get_description_fields()]
@ -38,3 +40,51 @@ def test_log_cleaning(db):
connector.daily()
assert ResourceLog.objects.all().count() == 1
@pytest.fixture
def email_handler():
import logging
from django.utils.log import AdminEmailHandler
root = logging.getLogger()
handler = AdminEmailHandler(include_html=True)
handler.level = logging.ERROR
root.handlers.append(handler)
try:
yield
finally:
root.handlers.remove(handler)
def test_trace_emails(app, settings, dummy_csv_datasource, email_handler, mailoutbox):
from .utils import generic_endpoint_url
settings.ADMINS = [('admin', 'admin@example.net')]
logging_parameters = dummy_csv_datasource.logging_parameters
logging_parameters.save()
assert not mailoutbox
with patch.object(dummy_csv_datasource.__class__,
'execute_query',
side_effect=ValueError('coin'), autospec=True):
app.get(generic_endpoint_url(connector='csvdatasource',
endpoint='query/dummy-query/',
slug=dummy_csv_datasource.slug),
status=500)
assert len(mailoutbox) == 1
assert mailoutbox[0].to == ['admin@example.net']
logging_parameters.trace_emails = 'john.doe@example.net'
logging_parameters.save()
app.get(generic_endpoint_url(connector='csvdatasource',
endpoint='query/dummy-query/',
slug=dummy_csv_datasource.slug),
status=500)
assert len(mailoutbox) == 2
assert mailoutbox[0].to == ['admin@example.net']
assert mailoutbox[1].to == ['john.doe@example.net']