mirror of
https://github.com/daniviga/bite.git
synced 2024-11-22 21:16:12 +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:
parent
b15b88aab4
commit
165ae3d3c0
|
@ -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
|
||||||
|
|
|
@ -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)
|
|
||||||
|
|
24
bite/bite/templates/swagger.html
Normal file
24
bite/bite/templates/swagger.html
Normal 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>
|
|
@ -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'),
|
||||||
|
]
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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'}),
|
||||||
|
|
|
@ -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])
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue
Block a user