1
0
mirror of https://github.com/daniviga/bite.git synced 2024-11-23 05:16:13 +01:00

API improvements (#22)

* Add swagger ui

* API improvements

* Fix nginx and pre-compile python to speedup start

* Add flake8 testing
This commit is contained in:
Daniele Viganò 2020-06-26 19:27:47 +02:00 committed by GitHub
parent b15b88aab4
commit 165ae3d3c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 93 additions and 22 deletions

View File

@ -42,6 +42,11 @@ jobs:
- docker-compose -f docker/docker-compose.yml up -d - docker-compose -f docker/docker-compose.yml up -d
script: script:
- docker-compose -f docker/docker-compose.yml exec bite python manage.py test - docker-compose -f docker/docker-compose.yml exec bite python manage.py test
- stage: django
install:
- pip3 -q install flake8
script:
- flake8 bite --exclude migrations,settings.py
- <<: *iot-simulator - <<: *iot-simulator
env: IOT_TL=http env: IOT_TL=http
- <<: *iot-simulator - <<: *iot-simulator

View File

@ -26,16 +26,3 @@ from api.serializers import DeviceSerializer
class APISubscribe(ModelViewSet): class APISubscribe(ModelViewSet):
queryset = Device.objects.all() queryset = Device.objects.all()
serializer_class = DeviceSerializer serializer_class = DeviceSerializer
# def post(self, request):
# serializer = DeviceSerializer(data=request.data)
# if serializer.is_valid():
# serializer.save()
# return Response(serializer.data, status=status.HTTP_201_CREATED)
# return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
# def get(self, request):
# devices = Device.objects.all()
# import pdb; pdb.set_trace()
# serializer = DeviceSerializer(devices)
# return Response(serializer.serial)

View File

@ -0,0 +1,24 @@
<!DOCTYPE html>
<html>
<head>
<title>Swagger</title>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" href="//unpkg.com/swagger-ui-dist@3/swagger-ui.css" />
</head>
<body>
<div id="swagger-ui"></div>
<script src="//unpkg.com/swagger-ui-dist@3/swagger-ui-bundle.js"></script>
<script>
const ui = SwaggerUIBundle({
url: "{% url schema_url %}",
dom_id: '#swagger-ui',
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIBundle.SwaggerUIStandalonePreset
],
layout: "BaseLayout"
})
</script>
</body>
</html>

View File

@ -34,6 +34,7 @@ Including another URLconf
""" """
from django.contrib import admin from django.contrib import admin
from django.conf import settings
from django.urls import include, path from django.urls import include, path
from api import urls as api_urls from api import urls as api_urls
@ -45,3 +46,19 @@ urlpatterns = [
path('api/', include(api_urls)), path('api/', include(api_urls)),
path('telemetry/', include(telemetry_urls)), path('telemetry/', include(telemetry_urls)),
] ]
if settings.DEBUG:
from django.views.generic import TemplateView
from rest_framework.schemas import get_schema_view
urlpatterns += [
path('swagger/', TemplateView.as_view(
template_name='swagger.html',
extra_context={'schema_url': 'openapi-schema'}
), name='swagger'),
path('openapi', get_schema_view(
title="BITE - A Basic/IoT/Example",
description="BITE API for IoT",
version="1.0.0"
), name='openapi-schema'),
]

View File

@ -22,6 +22,17 @@ from api.models import Device
from telemetry.models import Telemetry from telemetry.models import Telemetry
class TelemetryStatsSerializer(serializers.Serializer):
count_samples = serializers.IntegerField()
first_sample = serializers.DateTimeField()
last_sample = serializers.DateTimeField()
class TelemetrySummarySerializer(serializers.Serializer):
device = serializers.CharField()
stats = TelemetryStatsSerializer()
class TelemetrySerializer(serializers.ModelSerializer): class TelemetrySerializer(serializers.ModelSerializer):
device = serializers.SlugRelatedField( device = serializers.SlugRelatedField(
slug_field='serial', slug_field='serial',

View File

@ -33,14 +33,15 @@ Including another URLconf
2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
""" """
from django.urls import path from django.urls import path
from telemetry.views import TelemetryView, TelemetryLatest, TelemetryRange from telemetry.views import (TelemetryView, TelemetrySummaryView,
TelemetryLatest, TelemetryRange)
urlpatterns = [ urlpatterns = [
path('', path('',
TelemetryView.as_view({'post': 'create'}), TelemetryView.as_view({'post': 'create'}),
name='telemetry'), name='telemetry'),
path('<str:device>/', path('<str:device>/',
TelemetryView.as_view({'get': 'list'}), TelemetrySummaryView.as_view(),
name='device-telemetry'), name='device-telemetry'),
path('<str:device>/last/', path('<str:device>/last/',
TelemetryLatest.as_view({'get': 'retrieve'}), TelemetryLatest.as_view({'get': 'retrieve'}),

View File

@ -19,10 +19,12 @@
from datetime import datetime from datetime import datetime
from django.http import Http404 from django.http import Http404
from rest_framework.views import APIView
from rest_framework.viewsets import ModelViewSet from rest_framework.viewsets import ModelViewSet
from telemetry.models import Telemetry from telemetry.models import Telemetry
from telemetry.serializers import TelemetrySerializer from telemetry.serializers import (TelemetrySerializer,
TelemetrySummarySerializer)
from rest_framework.response import Response from rest_framework.response import Response
@ -39,15 +41,36 @@ class TelemetryView(ModelViewSet):
return Response(serializer.data) return Response(serializer.data)
class TelemetrySummaryView(APIView):
def get(self, request, device, format=None):
count = Telemetry.objects.filter(device__serial=device).count()
if count == 0:
raise Http404
first = Telemetry.objects.filter(
device__serial=device).order_by('-time')[:1][0]
last = Telemetry.objects.filter(
device__serial=device).order_by('time')[:1][0]
data = {
'device': device,
'stats': {
'count_samples': count,
'first_sample': first.time,
'last_sample': last.time}
}
serializer = TelemetrySummarySerializer(data)
return Response(serializer.data)
class TelemetryRange(ModelViewSet): class TelemetryRange(ModelViewSet):
queryset = Telemetry.objects.all() queryset = Telemetry.objects.all()
serializer_class = TelemetrySerializer serializer_class = TelemetrySerializer
lookup_field = 'device' lookup_field = 'device'
def list(self, request, device, time_from, time_to=None): def list(self, request, device, time_from, time_to=None):
time_to = datetime.now() if time_to is None else time_to
queryset = Telemetry.objects.filter( queryset = Telemetry.objects.filter(
device__serial=device, device__serial=device,
time__range=[time_from, datetime.now()]) time__range=[time_from, time_to])
if not queryset: if not queryset:
raise Http404 raise Http404
serializer = TelemetrySerializer(queryset, many=True) serializer = TelemetrySerializer(queryset, many=True)
@ -61,7 +84,7 @@ class TelemetryLatest(ModelViewSet):
def retrieve(self, request, device=None): def retrieve(self, request, device=None):
queryset = Telemetry.objects.filter( queryset = Telemetry.objects.filter(
device__serial=device).order_by('-time') device__serial=device).order_by('-time')[:1]
if not queryset: if not queryset:
raise Http404 raise Http404
serializer = TelemetrySerializer(queryset[0]) serializer = TelemetrySerializer(queryset[0])

View File

@ -35,6 +35,7 @@ COPY --chown=1000:1000 bite /srv/app/bite
COPY --chown=1000:1000 requirements.txt /srv/app/bite/requirements.txt COPY --chown=1000:1000 requirements.txt /srv/app/bite/requirements.txt
RUN pip3 install -r /srv/app/bite/requirements.txt \ RUN pip3 install -r /srv/app/bite/requirements.txt \
&& python3 -m compileall -q /srv/app/bite \
&& mkdir -p /srv/appdata/bite/static \ && mkdir -p /srv/appdata/bite/static \
&& chown -R 1000:1000 /srv/appdata/bite && chown -R 1000:1000 /srv/appdata/bite

View File

@ -48,13 +48,13 @@ http {
'' close; '' close;
} }
upstream bite { upstream django {
# We point to the Docker 'service' instead of directly to the container # We point to the Docker 'service' instead of directly to the container
# Docker does then a DNS round-robin internally # Docker does then a DNS round-robin internally
server bite:8000; server bite:8000;
} }
upstream broker { upstream mqtt {
# We point to the Docker 'service' instead of directly to the container # We point to the Docker 'service' instead of directly to the container
# Docker does then a DNS round-robin internally # Docker does then a DNS round-robin internally
server broker:9001; server broker:9001;
@ -69,7 +69,7 @@ http {
keepalive_timeout 60 60; keepalive_timeout 60 60;
location / { location / {
proxy_pass http://bite; proxy_pass http://django;
proxy_http_version 1.1; proxy_http_version 1.1;
proxy_set_header Host $http_host; proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
@ -85,7 +85,7 @@ http {
} }
location /mqtt { location /mqtt {
proxy_pass http://broker; proxy_pass http://mqtt;
proxy_http_version 1.1; proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade; proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade; proxy_set_header Connection $connection_upgrade;

View File

@ -4,5 +4,7 @@ django-health-check
psycopg2-binary psycopg2-binary
paho-mqtt==1.5.0 paho-mqtt==1.5.0
asyncio-mqtt==0.5.0 asyncio-mqtt==0.5.0
PyYAML
uritemplate
pygments pygments
gunicorn gunicorn