WIP: add logfile and local IDs
diff --git a/crashreports/admin.py b/crashreports/admin.py
index 1894034..66e2994 100644
--- a/crashreports/admin.py
+++ b/crashreports/admin.py
@@ -1,6 +1,7 @@
from django.contrib import admin
from crashreports.models import Crashreport
+
@admin.register(Crashreport)
class CrashreportAdmin(admin.ModelAdmin):
pass
diff --git a/crashreports/migrations/0001_initial.py b/crashreports/migrations/0001_initial.py
index 203f616..78c5144 100644
--- a/crashreports/migrations/0001_initial.py
+++ b/crashreports/migrations/0001_initial.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Generated by Django 1.10.2 on 2016-10-11 14:04
+# Generated by Django 1.10.2 on 2016-10-13 09:07
from __future__ import unicode_literals
import crashreports.models
@@ -32,16 +32,20 @@
('power_on_reason', models.CharField(max_length=200)),
('power_off_reason', models.CharField(max_length=200)),
('date', models.DateTimeField()),
+ ('device_local_id', models.IntegerField()),
],
),
migrations.CreateModel(
name='Device',
fields=[
- ('uuid', models.CharField(default=uuid.uuid4, editable=False, max_length=32, primary_key=True, serialize=False)),
- ('iemi', models.CharField(blank=True, max_length=32, null=True)),
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('uuid', models.CharField(default=uuid.uuid4, editable=False, max_length=64, unique=True)),
+ ('imei', models.CharField(blank=True, max_length=32, null=True)),
('board_date', models.DateTimeField(blank=True, null=True)),
+ ('chipset', models.CharField(blank=True, max_length=200, null=True)),
('last_heartbeat', models.DateTimeField(blank=True, null=True)),
('token', models.CharField(blank=True, max_length=200, null=True)),
+ ('next_per_device_key', models.PositiveIntegerField(default=1)),
('tags', taggit.managers.TaggableManager(blank=True, help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags')),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='Hiccup_Device', to=settings.AUTH_USER_MODEL)),
],
@@ -61,10 +65,9 @@
name='LogFile',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('logfile_type', models.TextField(max_length=36)),
- ('crashreport_file', models.FileField(upload_to=crashreports.models.crashreport_file_name)),
+ ('logfile_type', models.TextField(default='last_kmsg', max_length=36)),
+ ('logfile', models.FileField(upload_to=crashreports.models.crashreport_file_name)),
('crashreport', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='crashreports.Crashreport')),
- ('device', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='crashreports.Device')),
],
),
migrations.AddField(
diff --git a/crashreports/models.py b/crashreports/models.py
index 449db3c..96507b0 100644
--- a/crashreports/models.py
+++ b/crashreports/models.py
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
from django.db import models
+from django.db import transaction
from django.contrib.auth.models import User
from taggit.managers import TaggableManager
@@ -20,6 +21,22 @@
tags = TaggableManager(blank=True)
last_heartbeat = models.DateTimeField(null=True, blank=True)
token = models.CharField(max_length=200, null=True, blank=True)
+ next_per_crashreport_key = models.PositiveIntegerField(default=1)
+ next_per_heartbeat_key = models.PositiveIntegerField(default=1)
+
+ @transaction.atomic
+ def get_crashreport_key(self):
+ ret = self.next_per_crashreport_key
+ self.next_per_crashreport_key = self.next_per_crashreport_key + 1
+ self.save()
+ return ret
+
+ @transaction.atomic
+ def get_heartbeat_key(self):
+ ret = self.next_per_heartbeat_key
+ self.next_per_crashreport_key = self.next_per_heartbeat_key + 1
+ self.save()
+ return ret
def crashreport_file_name(instance, filename):
@@ -42,6 +59,20 @@
power_off_reason = models.CharField(max_length=200)
date = models.DateTimeField()
tags = TaggableManager(blank=True)
+ device_local_id = models.PositiveIntegerField(blank=True)
+ next_crashreport_key = models.PositiveIntegerField(default=1)
+
+ @transaction.atomic
+ def get_logfile_key(self):
+ ret = self.next_logfile_key
+ self.next_logfile_key = self.next_logfile_key + 1
+ self.save()
+ return ret
+
+ def save(self, *args, **kwargs):
+ if not self.device_local_id:
+ self.device_local_id = self.device.get_crashreport_key()
+ super(Crashreport, self).save(*args, **kwargs)
def _get_uuid(self):
"Returns the person's full name."
@@ -53,6 +84,12 @@
logfile_type = models.TextField(max_length=36, default="last_kmsg")
crashreport = models.ForeignKey(Crashreport, on_delete=models.CASCADE)
logfile = models.FileField(upload_to=crashreport_file_name)
+ crashreport_local_id = models.PositiveIntegerField(blank=True)
+
+ def save(self, *args, **kwargs):
+ if not self.device_local_id:
+ self.device_local_id = self.crashreport.next_logfile_key()
+ super(LogFile, self).save(*args, **kwargs)
class HeartBeat(models.Model):
@@ -61,6 +98,12 @@
uptime = models.CharField(max_length=200)
build_fingerprint = models.CharField(max_length=200)
date = models.DateTimeField()
+ device_local_id = models.PositiveIntegerField(blank=True)
+
+ def save(self, *args, **kwargs):
+ if not self.device_local_id:
+ self.device_local_id = self.device.get_heartbeat_key()
+ super(HeartBeat, self).save(*args, **kwargs)
def _get_uuid(self):
"Returns the person's full name."
diff --git a/crashreports/serializers.py b/crashreports/serializers.py
index 1ef8dbd..99d3e43 100644
--- a/crashreports/serializers.py
+++ b/crashreports/serializers.py
@@ -1,15 +1,31 @@
from rest_framework import serializers
+from rest_framework.utils.serializer_helpers import ReturnDict
from rest_framework.exceptions import NotFound
from crashreports.models import Crashreport
from crashreports.models import Device
from crashreports.models import HeartBeat
from rest_framework import permissions
+from crashreports.permissions import user_is_hiccup_staff
+
+
+class PrivateField(serializers.ReadOnlyField):
+
+ def get_attribute(self, instance):
+ """
+ Given the *outgoing* object instance, return the primitive value
+ that should be used for this field.
+ """
+ if user_is_hiccup_staff(self.context['request'].user):
+ return super(PrivateField, self).get_attribute(instance)
+ return -1
class CrashReportSerializer(serializers.ModelSerializer):
permission_classes = (permissions.AllowAny,)
uuid = serializers.CharField(max_length=64)
+ id = PrivateField()
+ device_local_id = serializers.IntegerField(required=False)
class Meta:
model = Crashreport
@@ -30,7 +46,9 @@
class HeartBeatSerializer(serializers.ModelSerializer):
permission_classes = (permissions.AllowAny,)
uuid = serializers.CharField(max_length=64)
-
+ id = PrivateField()
+ device_local_id = serializers.IntegerField(required=False)
+
class Meta:
model = HeartBeat
exclude = ('device',)
diff --git a/crashreports/tests.py b/crashreports/tests.py
index 43acf1d..53bdfcf 100644
--- a/crashreports/tests.py
+++ b/crashreports/tests.py
@@ -80,19 +80,31 @@
class HeartbeatListTestCase(APITestCase):
def setUp(self):
+ self.setup_users()
+ self.data = self.create_dummy_data(self.uuid)
+ self.url = "/hiccup/api/v1/heartbeats/"
+
+
+ def setup_users(self):
self.password = "test"
- self.admin = User.objects.create_superuser(
- 'myuser', 'myemail@test.com', self.password)
- # we need a device
request = self.client.post("/hiccup/api/v1/devices/register/", {})
self.uuid = request.data['uuid']
self.token = request.data['token']
request = self.client.post("/hiccup/api/v1/devices/register/", {})
self.other_uuid = request.data['uuid']
self.other_token = request.data['token']
- self.data = self.create_dummy_data(self.uuid)
- self.url = "/hiccup/api/v1/heartbeats/"
+ self.admin = User.objects.create_superuser(
+ 'myuser', 'myemail@test.com', self.password)
+ self.admin = APIClient()
+ self.admin.login(username='myuser', password='test')
+ self.user = APIClient()
+ self.other_user = APIClient()
+ self.user.credentials(HTTP_AUTHORIZATION='Token ' + self.token)
+ self.other_user.credentials(HTTP_AUTHORIZATION='Token '
+ + self.other_token)
+ self.noauth_client = APIClient()
+
def create_dummy_data(self, uuid="not set"):
return {
'uuid': uuid,
@@ -103,99 +115,111 @@
}
def test_create_no_auth(self):
- client = APIClient()
- request = client.post(self.url, self.data)
+ request = self.noauth_client.post(self.url, self.data)
self.assertEqual(request.status_code, 401)
def test_create_as_admin(self):
- client = APIClient()
- client.login(username='myuser', password='test')
- request = client.post(self.url, self.data)
+ request = self.admin.post(self.url, self.data)
self.assertEqual(request.status_code, 201)
- client.logout()
+ self.assertTrue(request.data['id'] > 0)
def test_create_as_admin_not_existing_device(self):
- client = APIClient()
- client.login(username='myuser', password='test')
- request = client.post(self.url,
- self.create_dummy_data())
+ request = self.admin.post(self.url,
+ self.create_dummy_data())
self.assertEqual(request.status_code, 404)
- client.logout()
def test_create_as_uuid_owner(self):
- client = APIClient()
- client.credentials(HTTP_AUTHORIZATION='Token ' + self.token)
- request = client.post(self.url,
- self.create_dummy_data(self.uuid))
+ request = self.user.post(self.url,
+ self.create_dummy_data(self.uuid))
self.assertEqual(request.status_code, 201)
- client.credentials()
+ self.assertTrue(request.data['id'] == -1)
def test_create_as_uuid_not_owner(self):
- client = APIClient()
- client.credentials(HTTP_AUTHORIZATION='Token ' + self.token)
- request = client.post(self.url,
- self.create_dummy_data(self.other_uuid))
+ request = self.user.post(self.url,
+ self.create_dummy_data(self.other_uuid))
self.assertEqual(request.status_code, 403)
- client.credentials()
- def post_heartbeats(self, client, heartbeat, count=5):
+ def post_multiple(self, client, data, count=5):
for i in range(count):
- client.post(self.url, heartbeat)
+ client.post(self.url, data)
def test_list(self):
count = 5
- client = APIClient()
- client.login(username='myuser', password='test')
- self.post_heartbeats(client, self.data, count)
- request = client.get(self.url)
+ self.post_multiple(self.user, self.data, count)
+ request = self.admin.get(self.url)
self.assertEqual(request.status_code, 200)
self.assertTrue(len(request.data) >= count)
- client.logout()
-
+
def test_list_noauth(self):
count = 5
- client = APIClient()
- client.login(username='myuser', password='test')
- self.post_heartbeats(client, self.data, count)
- client.logout()
- request = client.get(self.url)
+ self.post_multiple(self.user, self.data, count)
+ request = self.noauth_client.get(self.url)
self.assertEqual(request.status_code, 401)
def test_list_device_owner(self):
count = 5
- client = APIClient()
- client.credentials(HTTP_AUTHORIZATION='Token ' + self.token)
- self.post_heartbeats(client, self.data, count)
- request = client.get(self.url)
- client.logout()
+ self.post_multiple(self.user, self.data, count)
+ request = self.user.get(self.url)
self.assertEqual(request.status_code, 403)
+def create_crashreport(uuid="not set"):
+ return {
+ 'uuid': uuid,
+ 'is_fake_report': 0,
+ 'app_version': 2,
+ 'uptime': "2 Hours",
+ 'build_fingerprint': "models.CharField(max_length=200)",
+ 'boot_reason': "models.CharField(max_length=200)",
+ 'power_on_reason': "models.CharField(max_length=200)",
+ 'power_off_reason': "models.CharField(max_length=200)",
+ 'date': str(datetime.datetime(year=2016, month=1, day=1))
+ }
+
+
class CrashreportListTestCase(HeartbeatListTestCase):
def setUp(self):
- self.password = "test"
- self.admin = User.objects.create_superuser(
- 'myuser', 'myemail@test.com', self.password)
+ self.setup_users()
+ self.data = self.create_dummy_data(self.uuid)
+ self.url = "/hiccup/api/v1/crashreports/"
+
+
+ def create_dummy_data(self, uuid="not set"):
+ return create_crashreport(uuid)
+
+
+class LogfileUploadTest(APITestCase):
+ def setUp(self):
+ self.setup_users()
# we need a device
+ self.user.post("/hiccup/api/v1/crashreports/",
+ create_crashreport(self.uuid))
+ self.user.post("/hiccup/api/v1/crashreports/",
+ create_crashreport(self.uuid))
+ self.user.post("/hiccup/api/v1/crashreports/",
+ create_crashreport(self.other_uuid))
+ self.user.post("/hiccup/api/v1/crashreports/",
+ create_crashreport(self.other_uuid))
+
+ def setup_users(self):
+ self.password = "test"
request = self.client.post("/hiccup/api/v1/devices/register/", {})
self.uuid = request.data['uuid']
self.token = request.data['token']
request = self.client.post("/hiccup/api/v1/devices/register/", {})
self.other_uuid = request.data['uuid']
self.other_token = request.data['token']
- self.data = self.create_dummy_data(self.uuid)
- self.url = "/hiccup/api/v1/crashreports/"
+ self.admin = User.objects.create_superuser(
+ 'myuser', 'myemail@test.com', self.password)
+ self.admin = APIClient()
+ self.admin.login(username='myuser', password='test')
+ self.user = APIClient()
+ self.other_user = APIClient()
+ self.user.credentials(HTTP_AUTHORIZATION='Token ' + self.token)
+ self.other_user.credentials(HTTP_AUTHORIZATION='Token '
+ + self.other_token)
- def create_dummy_data(self, uuid="not set"):
- return {
- 'uuid': uuid,
- 'is_fake_report': 0,
- 'app_version': 2,
- 'uptime': "2 Hours",
- 'build_fingerprint': "models.CharField(max_length=200)",
- 'boot_reason': "models.CharField(max_length=200)",
- 'power_on_reason': "models.CharField(max_length=200)",
- 'power_off_reason': "models.CharField(max_length=200)",
- 'date': str(datetime.datetime(year=2016, month=1, day=1))
- }
+ def test_Logfile_upload_as_admin(self):
+ client = APIClient()
+ client.login(username='myuser', password='test')