entry creation serializer/validation + test

This commit is contained in:
Ethan Roseman 2022-08-27 21:28:16 +09:00
parent 5d619698c4
commit f68a9c4154
No known key found for this signature in database
GPG Key ID: 27F9FCEB8E4969BD
5 changed files with 95 additions and 21 deletions

View File

@ -1,3 +1,4 @@
from datetime import datetime
from django.db import models from django.db import models
from django.utils.crypto import get_random_string from django.utils.crypto import get_random_string
@ -78,7 +79,10 @@ class Entry(models.Model):
ordering = ["-timestamp"] ordering = ["-timestamp"]
def __str__(self) -> str: def __str__(self) -> str:
return f"{self.category} {self.timestamp}" time_string = datetime.utcfromtimestamp(self.timestamp).strftime(
"%Y-%m-%d %H:%M:%S"
)
return f"{self.category} {time_string}"
# A measure (total bytes, bytes matched, functions matched, bytes decompiled, etc) tied to an Entry # A measure (total bytes, bytes matched, functions matched, bytes decompiled, etc) tied to an Entry

View File

@ -7,6 +7,22 @@ class ApiKeySerializer(serializers.CharField):
max_length = AUTH_KEY_LEN max_length = AUTH_KEY_LEN
# Classes for valdating requests to create new entries
class CreateEntrySerializer(serializers.Serializer): # type:ignore
timestamp = serializers.IntegerField()
git_hash = serializers.CharField(max_length=40)
categories = serializers.DictField(
child=serializers.DictField(child=serializers.IntegerField())
)
class CreateEntriesSerializer(serializers.Serializer): # type:ignore
api_key = ApiKeySerializer()
entries = serializers.ListField(
child=CreateEntrySerializer(), required=True, allow_empty=False
)
class CreateCategoriesSerializer(serializers.Serializer): # type:ignore class CreateCategoriesSerializer(serializers.Serializer): # type:ignore
api_key = ApiKeySerializer() api_key = ApiKeySerializer()
categories = serializers.DictField( categories = serializers.DictField(

View File

@ -2,7 +2,7 @@ from django.urls import reverse
from rest_framework import status from rest_framework import status
from rest_framework.test import APITestCase from rest_framework.test import APITestCase
from frog_api.models import Category, Project, Version from frog_api.models import Category, Entry, Measure, Project, Version
class CreateCategoriesTests(APITestCase): class CreateCategoriesTests(APITestCase):
@ -26,8 +26,6 @@ class CreateCategoriesTests(APITestCase):
version = Version(slug="us", name="US", project=project) version = Version(slug="us", name="US", project=project)
version.save() version.save()
self.assertEqual(Category.objects.count(), 0)
response = self.client.post( response = self.client.post(
reverse("category-structure", args=[project.slug, version.slug]), reverse("category-structure", args=[project.slug, version.slug]),
create_json, create_json,
@ -37,3 +35,60 @@ class CreateCategoriesTests(APITestCase):
# Confirm we created the categories and that they are in the DB # Confirm we created the categories and that they are in the DB
self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(Category.objects.count(), len(create_json["categories"])) self.assertEqual(Category.objects.count(), len(create_json["categories"]))
class CreateEntriesTests(APITestCase):
def test_create_entries(self) -> None:
"""
Ensure that the entry creation endpoint works
"""
create_json = {
"api_key": "test_key_123",
"entries": [
{
"categories": {
"default": {
"code_matching": 103860,
"code_total": 4747584,
"asm": 4597948,
"nonmatching_functions_count": 49,
"assets_identified": 0,
"assets_total": 40816656,
"code_decompiled": 120152,
"assets_debinarised": 0,
},
"actors": {
"code_matching": 103860,
"code_total": 4747584,
},
},
"timestamp": 1615435438,
"git_hash": "e788bfecbfb10afd4182332db99bb562ea75b1de",
}
],
}
# Create a test Project, Version, and Categories
project = Project(slug="oot", name="Ocarina of Time", auth_key="test_key_123")
project.save()
version = Version(slug="us", name="US", project=project)
version.save()
category1 = Category(slug="default", name="Default", version=version)
category1.save()
category2 = Category(slug="actors", name="Actors", version=version)
category2.save()
response = self.client.post(
reverse("version-data", args=[project.slug, version.slug]),
create_json,
format="json",
)
# Confirm we created the entries and that they are in the DB
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(Entry.objects.count(), 2)
self.assertEqual(Measure.objects.count(), 10)

View File

@ -20,6 +20,7 @@ urlpatterns = [
re_path( re_path(
"data/(?P<project_slug>.+)/(?P<version_slug>.+)/$", "data/(?P<project_slug>.+)/(?P<version_slug>.+)/$",
data.VersionDataView.as_view(), data.VersionDataView.as_view(),
name="version-data",
), ),
re_path( re_path(
"data/(?P<project_slug>.+)/$", "data/(?P<project_slug>.+)/$",

View File

@ -1,13 +1,13 @@
from typing import Any, List from typing import Any
from django.db import models from django.db import models
from frog_api.exceptions import ( from frog_api.exceptions import (
InvalidDataException, InvalidDataException,
MissingAPIKeyException,
NoEntriesException, NoEntriesException,
) )
from frog_api.models import Entry, Measure, Project, Version from frog_api.models import Entry, Measure, Project, Version
from frog_api.serializers.model_serializers import EntrySerializer from frog_api.serializers.model_serializers import EntrySerializer
from frog_api.serializers.request_serializers import CreateEntriesSerializer
from frog_api.views.common import ( from frog_api.views.common import (
get_category, get_category,
get_project, get_project,
@ -93,32 +93,30 @@ class VersionDataView(APIView):
def create_entries( def create_entries(
req_data: dict[str, Any], project_slug: str, version_slug: str req_data: dict[str, Any], project_slug: str, version_slug: str
) -> int: ) -> int:
request_ser = CreateEntriesSerializer(data=req_data)
request_ser.is_valid(raise_exception=True)
data = request_ser.data
project = get_project(project_slug) project = get_project(project_slug)
validate_api_key(data["api_key"], project)
version = get_version(version_slug, project) version = get_version(version_slug, project)
if "api_key" not in req_data: to_save: list[models.Model] = []
raise MissingAPIKeyException() for entry in data["entries"]:
validate_api_key(req_data["api_key"], project)
to_save: List[models.Model] = []
for entry in req_data["data"]:
timestamp = entry["timestamp"] timestamp = entry["timestamp"]
git_hash = entry["git_hash"] git_hash = entry["git_hash"]
for cat in entry: categories = entry["categories"]
if cat in ["timestamp", "git_hash"]: for cat in categories:
continue
if type(entry[cat]) is not dict:
continue
category = get_category(cat, version) category = get_category(cat, version)
entry = Entry(category=category, timestamp=timestamp, git_hash=git_hash) entry = Entry(category=category, timestamp=timestamp, git_hash=git_hash)
to_save.append(entry) to_save.append(entry)
for measure_type in entry[cat]: for measure_type in categories[cat]:
value = entry[cat][measure_type] value = categories[cat][measure_type]
if type(value) != int: if type(value) != int:
raise InvalidDataException( raise InvalidDataException(
f"{cat}:{measure_type} must be an integer" f"{cat}:{measure_type} must be an integer"