entry creation serializer/validation + test
This commit is contained in:
parent
5d619698c4
commit
f68a9c4154
|
@ -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
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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>.+)/$",
|
||||||
|
|
|
@ -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"
|
||||||
|
|
Loading…
Reference in New Issue