-
Notifications
You must be signed in to change notification settings - Fork 109
Add /api/metrics/testpilottest POST resource #952
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| fetch('https://testpilot.firefox.com/api/metrics/ping/testpilottest', { | ||
| method: 'POST', | ||
| mode: 'cors', | ||
| headers: {'Content-Type': 'application/json'}, | ||
| body: JSON.stringify({ | ||
| "some": "random", | ||
| "metrics": "ping", | ||
| "payload": "here" | ||
| }) | ||
| }).then(resp => { | ||
| console.log('metrics ping success', resp); | ||
| }).catch(e => { | ||
| console.log('problem sending metrics ping', e); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| { | ||
| "manifest_version": 2, | ||
| "name": "Test Pilot WebExtension Example", | ||
| "version": "1.0", | ||
| "description": "This is a WebExtension built as an example Test Pilot experiment", | ||
| "icons": { | ||
| "32": "icons/icon-32.png" | ||
| }, | ||
| "permissions": ["background"], | ||
| "applications": { | ||
| "gecko": { | ||
| "id": "testpilotexample1@mozilla.org", | ||
| "strict_min_version": "45.0" | ||
| } | ||
| }, | ||
| "background": { | ||
| "scripts": ["background.js"] | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| from django.test.utils import override_settings | ||
| from django.core.urlresolvers import reverse | ||
| from django.contrib.auth.models import User | ||
|
|
||
| import json | ||
|
|
||
| from mozilla_cloud_services_logger.formatters import JsonLogFormatter | ||
|
|
||
| from testfixtures import LogCapture | ||
| from ..utils import TestCase | ||
|
|
||
|
|
||
| class LogPingTestCase(TestCase): | ||
|
|
||
| def setUp(self): | ||
| super(LogPingTestCase, self).setUp() | ||
|
|
||
| self.handler = LogCapture() | ||
|
|
||
| self.username = 'johndoe2' | ||
| self.password = 'trustno1' | ||
| self.email = '%s@example.com' % self.username | ||
|
|
||
| self.user = User.objects.create_user( | ||
| username=self.username, | ||
| email=self.email, | ||
| password=self.password) | ||
|
|
||
| def tearDown(self): | ||
| self.handler.uninstall() | ||
|
|
||
| def test_404_ping(self): | ||
| """POST to /api/metrics/foobar should be a 404""" | ||
| self.client.login(username=self.username, | ||
| password=self.password) | ||
| log_name = 'foobar' | ||
| url = reverse('log-ping', args=(log_name,)) | ||
| resp = self.client.post(url, {}) | ||
| self.assertEqual(404, resp.status_code) | ||
|
|
||
| def test_unauth_ping(self): | ||
| log_name = 'testpilottest' | ||
| data = { | ||
| "service": "wayforwardthing", | ||
| "agent": "frobnitz browser" | ||
| } | ||
| url = reverse('log-ping', args=(log_name,)) | ||
|
|
||
| self.handler.records = [] | ||
| resp = self.client.post( | ||
| url, | ||
| content_type='application/json', | ||
| data=json.dumps(data)) | ||
|
|
||
| self.assertEqual(200, resp.status_code) | ||
|
|
||
| record = self.handler.records[0] | ||
| formatter = JsonLogFormatter(logger_name=record.name) | ||
| details = json.loads(formatter.format(record)) | ||
| fields = details['Fields'] | ||
|
|
||
| self.assertEqual(log_name, record.name) | ||
| self.assertEqual(fields['service'], data['service']) | ||
| self.assertEqual(fields['agent'], data['agent']) | ||
|
|
||
| @override_settings(LOG_PING_WHITELIST=['alpha', 'beta', 'delta']) | ||
| def test_auth_ping(self): | ||
| """POST to /api/metrics/{name} should result in expected log event""" | ||
| self.client.login(username=self.username, | ||
| password=self.password) | ||
|
|
||
| for log_name in ['alpha', 'beta', 'delta']: | ||
| url = reverse('log-ping', args=(log_name,)) | ||
|
|
||
| self.handler.records = [] | ||
| resp = self.client.post( | ||
| url, | ||
| content_type='application/json', | ||
| data=json.dumps({'context': 'wheee'})) | ||
|
|
||
| self.assertEqual(200, resp.status_code) | ||
|
|
||
| record = self.handler.records[0] | ||
| formatter = JsonLogFormatter(logger_name=record.name) | ||
| details = json.loads(formatter.format(record)) | ||
| fields = details['Fields'] | ||
|
|
||
| self.assertEqual(log_name, record.name) | ||
| self.assertEqual(fields['uid'], self.user.id) | ||
| self.assertEqual(fields['context'], 'wheee') |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| from django.conf.urls import url, patterns | ||
|
|
||
| from . import views | ||
|
|
||
| urlpatterns = patterns( | ||
| '', | ||
| url(r'^ping/(?P<logger_name>.+)', views.log_ping, name='log-ping'), | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| import logging | ||
|
|
||
| from django.conf import settings | ||
| from django.http import Http404 | ||
|
|
||
| from django.views.decorators.csrf import csrf_exempt | ||
| from rest_framework.response import Response | ||
| from rest_framework.decorators import permission_classes, api_view | ||
|
|
||
| DEFAULT_LOG_PING_WHITELIST = [ | ||
| 'testpilottest' | ||
| ] | ||
|
|
||
|
|
||
| @csrf_exempt | ||
| @api_view(['POST']) | ||
| @permission_classes([]) | ||
| def log_ping(request, logger_name): | ||
| """Accept and log metrics pings""" | ||
| whitelist = getattr(settings, 'LOG_PING_WHITELIST', DEFAULT_LOG_PING_WHITELIST) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So we will need to add logger names for each experiment that uses this? I.e., for 404s and my "blok" add-on/experiment, we would need to have: ?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, this should be like the general
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FWIW, the whitelist is kind of a leftover from when we thought we'd be doing more of our own metrics with events like
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we update the Or whatever the add-ons need to POST that isn't auto-filled by the server? |
||
| if logger_name not in whitelist: | ||
| raise Http404 | ||
|
|
||
| extra = {} | ||
| if request.user.is_authenticated(): | ||
| extra['uid'] = request.user.id | ||
| extra.update(request.data) | ||
| logging.getLogger(logger_name).info('', extra=extra) | ||
|
|
||
| return Response({'status': 'ok'}) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How does
.catch().then()work?I can't remember if that means if you did catch an error it'd fall through and, you'd get both messages:
Or should we reverse the order so it's
.then().catch()?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it bails on the catch, but I swapped the order anyway
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Turns out the
catch()in the middle lets you handle the error and continue, so it'd have done both.catch()and.then():