UPDATE
This commit is contained in:
@@ -0,0 +1,317 @@
|
||||
# مستند API لاگ درخواست های سنسور خارجی
|
||||
|
||||
این فایل نحوه کار endpoint زیر را توضیح می دهد:
|
||||
|
||||
`GET /sensor_external_api/logs/`
|
||||
|
||||
مسیر مربوطه در فایل `sensor_external_api/urls.py` به این صورت ثبت شده است:
|
||||
|
||||
```python
|
||||
path("logs/", SensorExternalRequestLogListAPIView.as_view(), name="sensor-external-api-log-list")
|
||||
```
|
||||
|
||||
## هدف API
|
||||
|
||||
این API برای مشاهده لیست لاگ درخواست هایی استفاده می شود که از سنسورهای خارجی برای یک مزرعه مشخص ثبت شده اند.
|
||||
|
||||
هر لاگ شامل اطلاعات زیر است:
|
||||
- شناسه لاگ
|
||||
- `farm_uuid`
|
||||
- `sensor_catalog_uuid`
|
||||
- `physical_device_uuid`
|
||||
- `payload` دریافتی از سنسور
|
||||
- زمان ثبت لاگ
|
||||
- اطلاعات سنسور مزرعه (`farm_sensor`)
|
||||
- اطلاعات کاتالوگ سنسور (`sensor_catalog`)
|
||||
|
||||
## کلاس View
|
||||
|
||||
این endpoint در کلاس `SensorExternalRequestLogListAPIView` داخل فایل `sensor_external_api/views.py` پیاده سازی شده است.
|
||||
|
||||
ویژگی های مهم این View:
|
||||
- فقط متد `GET` را پشتیبانی می کند.
|
||||
- نیاز به احراز هویت دارد.
|
||||
- از صفحه بندی استفاده می کند.
|
||||
- لاگ ها را بر اساس `farm_uuid` فیلتر می کند.
|
||||
|
||||
## احراز هویت و دسترسی
|
||||
|
||||
در این View مقدار زیر تعریف شده است:
|
||||
|
||||
```python
|
||||
permission_classes = [IsAuthenticated]
|
||||
```
|
||||
|
||||
یعنی کاربر باید authenticated باشد تا بتواند این API را صدا بزند.
|
||||
|
||||
نکته مهم:
|
||||
در تست ها، اگر درخواست بدون اعتبارنامه ارسال شود، پاسخ `401 Unauthorized` برمی گردد.
|
||||
|
||||
## پارامترهای ورودی
|
||||
|
||||
این API پارامترهای query string زیر را دریافت می کند:
|
||||
|
||||
- `farm_uuid` اجباری
|
||||
- `page` اختیاری، پیش فرض `1`
|
||||
- `page_size` اختیاری، پیش فرض `20` و حداکثر `100`
|
||||
|
||||
اعتبارسنجی پارامترها توسط `SensorExternalRequestLogQuerySerializer` در فایل `sensor_external_api/serializers.py` انجام می شود:
|
||||
|
||||
```python
|
||||
class SensorExternalRequestLogQuerySerializer(serializers.Serializer):
|
||||
farm_uuid = serializers.UUIDField()
|
||||
page = serializers.IntegerField(required=False, min_value=1, default=1)
|
||||
page_size = serializers.IntegerField(required=False, min_value=1, max_value=100, default=20)
|
||||
```
|
||||
|
||||
### نمونه درخواست
|
||||
|
||||
```http
|
||||
GET /api/sensor-external-api/logs/?farm_uuid=11111111-1111-1111-1111-111111111111&page=1&page_size=20
|
||||
```
|
||||
|
||||
## روند اجرای API
|
||||
|
||||
### 1) اعتبارسنجی query params
|
||||
ابتدا `request.query_params` توسط serializer اعتبارسنجی می شود:
|
||||
|
||||
```python
|
||||
serializer = SensorExternalRequestLogQuerySerializer(data=request.query_params)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
```
|
||||
|
||||
اگر `farm_uuid` معتبر نباشد یا `page_size` خارج از بازه باشد، پاسخ خطای validation از DRF برمی گردد.
|
||||
|
||||
### 2) گرفتن لاگ های مربوط به مزرعه
|
||||
سپس سرویس `get_sensor_external_request_logs_for_farm` فراخوانی می شود:
|
||||
|
||||
```python
|
||||
queryset = get_sensor_external_request_logs_for_farm(
|
||||
farm_uuid=serializer.validated_data["farm_uuid"],
|
||||
)
|
||||
```
|
||||
|
||||
این سرویس در فایل `sensor_external_api/services.py` تعریف شده و لاگ ها را از جدول `sensor_external_request_logs` می خواند:
|
||||
|
||||
```python
|
||||
SensorExternalRequestLog.objects.filter(farm_uuid=farm_uuid).order_by("-created_at", "-id")
|
||||
```
|
||||
|
||||
پس ترتیب خروجی به این صورت است:
|
||||
- جدیدترین لاگ ها اول نمایش داده می شوند.
|
||||
- اگر `created_at` برابر باشد، لاگ با `id` بزرگ تر زودتر می آید.
|
||||
|
||||
### 3) مدیریت خطای migration
|
||||
اگر جدول های لازم هنوز migrate نشده باشند، سرویس خطا را به `ValueError` تبدیل می کند و View این پاسخ را برمی گرداند:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 503,
|
||||
"msg": "Required tables are not ready. Run migrations."
|
||||
}
|
||||
```
|
||||
|
||||
با status code برابر با `503 Service Unavailable`.
|
||||
|
||||
### 4) صفحه بندی نتایج
|
||||
این View از paginator زیر استفاده می کند:
|
||||
|
||||
```python
|
||||
class SensorExternalRequestLogPagination(PageNumberPagination):
|
||||
page_size = 20
|
||||
page_size_query_param = "page_size"
|
||||
max_page_size = 100
|
||||
```
|
||||
|
||||
و در View نیز `page_size` از داده معتبرشده serializer روی paginator اعمال می شود:
|
||||
|
||||
```python
|
||||
paginator = self.pagination_class()
|
||||
paginator.page_size = serializer.validated_data["page_size"]
|
||||
page = paginator.paginate_queryset(queryset, request, view=self)
|
||||
```
|
||||
|
||||
### 5) ساخت map از سنسورهای مزرعه
|
||||
برای اینکه هر لاگ همراه با اطلاعات سنسور مزرعه و کاتالوگ سنسور برگردد، این سرویس صدا زده می شود:
|
||||
|
||||
```python
|
||||
farm_sensor_map = get_farm_sensor_map_for_logs(logs=page)
|
||||
```
|
||||
|
||||
این سرویس:
|
||||
- لاگ های همان page را می گیرد.
|
||||
- `FarmSensor` های متناظر را از دیتابیس پیدا می کند.
|
||||
- یک map با کلید زیر می سازد:
|
||||
|
||||
```python
|
||||
(farm_uuid, sensor_catalog_uuid, physical_device_uuid)
|
||||
```
|
||||
|
||||
به کمک این map، serializer می تواند برای هر لاگ اطلاعات تکمیلی را پر کند.
|
||||
|
||||
## ساختار serializer خروجی
|
||||
|
||||
خروجی هر آیتم با `SensorExternalRequestLogSerializer` ساخته می شود.
|
||||
|
||||
فیلدهای اصلی:
|
||||
|
||||
```python
|
||||
fields = [
|
||||
"id",
|
||||
"farm_uuid",
|
||||
"sensor_catalog_uuid",
|
||||
"physical_device_uuid",
|
||||
"farm_sensor",
|
||||
"sensor_catalog",
|
||||
"payload",
|
||||
"created_at",
|
||||
]
|
||||
```
|
||||
|
||||
### فیلد `farm_sensor`
|
||||
این فیلد از نوع `SerializerMethodField` است.
|
||||
اگر سنسور متناظر پیدا شود، اطلاعاتی مثل موارد زیر را برمی گرداند:
|
||||
- `uuid`
|
||||
- `sensor_catalog_uuid`
|
||||
- `physical_device_uuid`
|
||||
- `name`
|
||||
- `sensor_type`
|
||||
- `is_active`
|
||||
- `specifications`
|
||||
- `power_source`
|
||||
- `created_at`
|
||||
- `updated_at`
|
||||
|
||||
اگر سنسور پیدا نشود، مقدار آن `null` خواهد بود.
|
||||
|
||||
### فیلد `sensor_catalog`
|
||||
این فیلد هم از نوع `SerializerMethodField` است.
|
||||
اگر `farm_sensor` و `sensor_catalog` موجود باشند، اطلاعات کاتالوگ سنسور برمی گردد، مثل:
|
||||
- `uuid`
|
||||
- `code`
|
||||
- `name`
|
||||
- `description`
|
||||
- `customizable_fields`
|
||||
- `supported_power_sources`
|
||||
- `returned_data_fields`
|
||||
- `sample_payload`
|
||||
- `is_active`
|
||||
- `created_at`
|
||||
- `updated_at`
|
||||
|
||||
اگر داده متناظر وجود نداشته باشد، مقدار آن `null` خواهد بود.
|
||||
|
||||
## مدل لاگ
|
||||
|
||||
لاگ ها در مدل `SensorExternalRequestLog` ذخیره می شوند:
|
||||
|
||||
```python
|
||||
class SensorExternalRequestLog(models.Model):
|
||||
farm_uuid = models.UUIDField(db_index=True)
|
||||
sensor_catalog_uuid = models.UUIDField(null=True, blank=True, db_index=True)
|
||||
physical_device_uuid = models.UUIDField(db_index=True)
|
||||
payload = models.JSONField(default=dict, blank=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
```
|
||||
|
||||
ویژگی مهم مدل:
|
||||
- جدول دیتابیس: `sensor_external_request_logs`
|
||||
- ترتیب پیش فرض: `ordering = ["-created_at", "-id"]`
|
||||
|
||||
## نمونه پاسخ موفق
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "success",
|
||||
"count": 2,
|
||||
"next": "http://example.com/api/sensor-external-api/logs/?farm_uuid=11111111-1111-1111-1111-111111111111&page=2&page_size=1",
|
||||
"previous": null,
|
||||
"data": [
|
||||
{
|
||||
"id": 12,
|
||||
"farm_uuid": "11111111-1111-1111-1111-111111111111",
|
||||
"sensor_catalog_uuid": "22222222-2222-2222-2222-222222222222",
|
||||
"physical_device_uuid": "55555555-5555-5555-5555-555555555555",
|
||||
"farm_sensor": {
|
||||
"uuid": "99999999-9999-9999-9999-999999999999",
|
||||
"sensor_catalog_uuid": "22222222-2222-2222-2222-222222222222",
|
||||
"physical_device_uuid": "55555555-5555-5555-5555-555555555555",
|
||||
"name": "External device 2",
|
||||
"sensor_type": "soil_sensor",
|
||||
"is_active": true,
|
||||
"specifications": {
|
||||
"model": "FH-2"
|
||||
},
|
||||
"power_source": {
|
||||
"type": "solar"
|
||||
},
|
||||
"created_at": "2024-01-01T10:00:00Z",
|
||||
"updated_at": "2024-01-01T10:00:00Z"
|
||||
},
|
||||
"sensor_catalog": {
|
||||
"uuid": "22222222-2222-2222-2222-222222222222",
|
||||
"code": "ext-sensor-log-2",
|
||||
"name": "External Sensor Log 2",
|
||||
"description": "Sensor catalog for second log",
|
||||
"customizable_fields": [],
|
||||
"supported_power_sources": [],
|
||||
"returned_data_fields": ["humidity"],
|
||||
"sample_payload": {},
|
||||
"is_active": true,
|
||||
"created_at": "2024-01-01T10:00:00Z",
|
||||
"updated_at": "2024-01-01T10:00:00Z"
|
||||
},
|
||||
"payload": {
|
||||
"temp": 18
|
||||
},
|
||||
"created_at": "2024-01-02T10:00:00Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## خطاهای احتمالی
|
||||
|
||||
### 401 Unauthorized
|
||||
اگر کاربر احراز هویت نشده باشد:
|
||||
|
||||
```json
|
||||
{
|
||||
"detail": "Authentication credentials were not provided."
|
||||
}
|
||||
```
|
||||
|
||||
### 400 Bad Request
|
||||
اگر پارامترها نامعتبر باشند، مثلا `farm_uuid` فرمت درست نداشته باشد یا `page_size` بیشتر از `100` باشد، خطای validation برگردانده می شود.
|
||||
|
||||
### 503 Service Unavailable
|
||||
اگر migration های مربوط به جدول ها اجرا نشده باشند:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 503,
|
||||
"msg": "Required tables are not ready. Run migrations."
|
||||
}
|
||||
```
|
||||
|
||||
## نکات مهم پیاده سازی
|
||||
|
||||
- این API فقط لاگ های مربوط به یک `farm_uuid` را برمی گرداند.
|
||||
- پاسخ به صورت paginated است.
|
||||
- داده های `farm_sensor` و `sensor_catalog` به صورت enrich شده به هر لاگ اضافه می شوند.
|
||||
- اگر برای یک لاگ، سنسور متناظر در `FarmSensor` پیدا نشود، فیلدهای `farm_sensor` و `sensor_catalog` ممکن است `null` باشند.
|
||||
- ترتیب نمایش لاگ ها نزولی و از جدیدترین به قدیمی ترین است.
|
||||
|
||||
## فایل های مرتبط
|
||||
|
||||
- `sensor_external_api/urls.py`
|
||||
- `sensor_external_api/views.py`
|
||||
- `sensor_external_api/serializers.py`
|
||||
- `sensor_external_api/services.py`
|
||||
- `sensor_external_api/models.py`
|
||||
- `sensor_external_api/tests.py`
|
||||
|
||||
## جمع بندی
|
||||
|
||||
API مربوط به `logs/` برای گزارش گیری و مشاهده تاریخچه درخواست های ورودی از سنسورهای خارجی یک مزرعه استفاده می شود. این endpoint با دریافت `farm_uuid`، لاگ های مرتبط را از دیتابیس می خواند، آن ها را صفحه بندی می کند، اطلاعات سنسور و کاتالوگ را به خروجی اضافه می کند و در نهایت پاسخ استاندارد برمی گرداند.
|
||||
@@ -1,3 +1,5 @@
|
||||
import requests
|
||||
from django.conf import settings
|
||||
from django.db import OperationalError, ProgrammingError, transaction
|
||||
|
||||
from farm_hub.models import FarmSensor
|
||||
@@ -6,6 +8,10 @@ from notifications.services import create_notification_for_farm_uuid
|
||||
from .models import SensorExternalRequestLog
|
||||
|
||||
|
||||
class FarmDataForwardError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def get_sensor_external_request_logs_for_farm(*, farm_uuid):
|
||||
try:
|
||||
return SensorExternalRequestLog.objects.filter(farm_uuid=farm_uuid).order_by("-created_at", "-id")
|
||||
@@ -60,7 +66,7 @@ def get_latest_sensor_external_request_log(*, farm_uuid, sensor_catalog_uuid, ph
|
||||
def create_sensor_external_notification(*, physical_device_uuid, payload=None):
|
||||
payload = payload or {}
|
||||
sensor = (
|
||||
FarmSensor.objects.select_related("farm", "sensor_catalog")
|
||||
FarmSensor.objects.select_related("farm", "farm__current_crop_area", "sensor_catalog")
|
||||
.filter(physical_device_uuid=physical_device_uuid)
|
||||
.first()
|
||||
)
|
||||
@@ -89,3 +95,79 @@ def create_sensor_external_notification(*, physical_device_uuid, payload=None):
|
||||
)
|
||||
except (ProgrammingError, OperationalError) as exc:
|
||||
raise ValueError("Sensor external API tables are not migrated.") from exc
|
||||
|
||||
|
||||
def forward_sensor_payload_to_farm_data(*, physical_device_uuid, payload=None):
|
||||
payload = payload or {}
|
||||
sensor = (
|
||||
FarmSensor.objects.select_related("farm", "farm__current_crop_area")
|
||||
.filter(physical_device_uuid=physical_device_uuid)
|
||||
.first()
|
||||
)
|
||||
if sensor is None:
|
||||
raise ValueError("Physical device not found.")
|
||||
|
||||
farm_boundary = _get_farm_boundary(sensor=sensor)
|
||||
url = _build_farm_data_url()
|
||||
api_key = getattr(settings, "FARM_DATA_API_KEY", "")
|
||||
if not api_key:
|
||||
raise FarmDataForwardError("FARM_DATA_API_KEY is not configured.")
|
||||
|
||||
request_payload = {
|
||||
"farm_uuid": str(sensor.farm.farm_uuid),
|
||||
"farm_boundary": farm_boundary,
|
||||
"sensor_payload": {
|
||||
sensor.name or str(sensor.physical_device_uuid): payload,
|
||||
},
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.post(
|
||||
url,
|
||||
json=request_payload,
|
||||
headers={
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json",
|
||||
"X-API-Key": api_key,
|
||||
"Authorization": f"Api-Key {api_key}",
|
||||
},
|
||||
timeout=getattr(settings, "FARM_DATA_API_TIMEOUT", 30),
|
||||
)
|
||||
except requests.RequestException as exc:
|
||||
raise FarmDataForwardError(f"Farm data API request failed: {exc}") from exc
|
||||
|
||||
if response.status_code >= 400:
|
||||
try:
|
||||
response_body = response.json()
|
||||
except ValueError:
|
||||
response_body = response.text
|
||||
raise FarmDataForwardError(
|
||||
f"Farm data API returned status {response.status_code}: {response_body}"
|
||||
)
|
||||
|
||||
return request_payload
|
||||
|
||||
|
||||
def _get_farm_boundary(*, sensor):
|
||||
crop_area = sensor.farm.current_crop_area or sensor.farm.crop_areas.order_by("-created_at", "-id").first()
|
||||
if crop_area is None:
|
||||
raise FarmDataForwardError("Farm boundary is not configured for this farm.")
|
||||
|
||||
geometry = crop_area.geometry or {}
|
||||
if geometry.get("type") == "Feature":
|
||||
geometry = geometry.get("geometry") or {}
|
||||
|
||||
if geometry.get("type") != "Polygon":
|
||||
raise FarmDataForwardError("Farm boundary geometry must be a Polygon.")
|
||||
|
||||
return geometry
|
||||
|
||||
|
||||
def _build_farm_data_url():
|
||||
base_url = getattr(settings, "AI_SERVICE_BASE_URL", "").rstrip("/")
|
||||
path = "/api/farm-data/"
|
||||
|
||||
if not base_url:
|
||||
raise FarmDataForwardError("FARM_DATA_API_HOST is not configured.")
|
||||
|
||||
return f"{base_url}/{path.lstrip('/')}"
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import requests
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.test import TestCase, override_settings
|
||||
from rest_framework.test import APIRequestFactory
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
from crop_zoning.models import CropArea
|
||||
from farm_hub.models import FarmHub, FarmSensor, FarmType
|
||||
from notifications.models import FarmNotification
|
||||
from sensor_catalog.models import SensorCatalog
|
||||
@@ -11,7 +14,12 @@ from .services import get_latest_sensor_external_request_log
|
||||
from .views import SensorExternalAPIView, SensorExternalRequestLogListAPIView
|
||||
|
||||
|
||||
@override_settings(SENSOR_EXTERNAL_API_KEY="12345")
|
||||
@override_settings(
|
||||
SENSOR_EXTERNAL_API_KEY="12345",
|
||||
FARM_DATA_API_HOST="http://localhost",
|
||||
FARM_DATA_API_PORT="8020",
|
||||
FARM_DATA_API_KEY="farm-data-key",
|
||||
)
|
||||
class SensorExternalAPIViewTests(TestCase):
|
||||
def setUp(self):
|
||||
self.factory = APIRequestFactory()
|
||||
@@ -31,11 +39,38 @@ class SensorExternalAPIViewTests(TestCase):
|
||||
code="ext-sensor-v1",
|
||||
name="External Sensor",
|
||||
)
|
||||
self.crop_area = CropArea.objects.create(
|
||||
farm=self.farm,
|
||||
geometry={
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[51.39, 35.7],
|
||||
[51.41, 35.7],
|
||||
[51.41, 35.72],
|
||||
[51.39, 35.72],
|
||||
[51.39, 35.7],
|
||||
]
|
||||
],
|
||||
},
|
||||
points=[
|
||||
[51.39, 35.7],
|
||||
[51.41, 35.7],
|
||||
[51.41, 35.72],
|
||||
[51.39, 35.72],
|
||||
],
|
||||
center={"lat": 35.71, "lng": 51.4},
|
||||
area_sqm=1000,
|
||||
area_hectares=0.1,
|
||||
chunk_area_sqm=1000,
|
||||
)
|
||||
self.farm.current_crop_area = self.crop_area
|
||||
self.farm.save(update_fields=["current_crop_area"])
|
||||
self.sensor = FarmSensor.objects.create(
|
||||
farm=self.farm,
|
||||
sensor_catalog=self.sensor_catalog,
|
||||
physical_device_uuid="11111111-1111-1111-1111-111111111111",
|
||||
name="External device",
|
||||
name="sensor-7-1",
|
||||
sensor_type="weather_station",
|
||||
)
|
||||
|
||||
@@ -50,7 +85,9 @@ class SensorExternalAPIViewTests(TestCase):
|
||||
|
||||
self.assertEqual(response.status_code, 401)
|
||||
|
||||
def test_creates_notification_and_request_log_for_device_uuid(self):
|
||||
@patch("sensor_external_api.services.requests.post")
|
||||
def test_creates_notification_and_request_log_for_device_uuid(self, mock_post):
|
||||
mock_post.return_value = Mock(status_code=201)
|
||||
request = self.factory.post(
|
||||
"/api/sensor-external-api/",
|
||||
{"uuid": str(self.sensor.physical_device_uuid), "payload": {"temp": 12}},
|
||||
@@ -75,6 +112,23 @@ class SensorExternalAPIViewTests(TestCase):
|
||||
payload={"temp": 12},
|
||||
).exists()
|
||||
)
|
||||
mock_post.assert_called_once_with(
|
||||
"http://localhost:8020/api/farm-data/",
|
||||
json={
|
||||
"farm_uuid": str(self.farm.farm_uuid),
|
||||
"farm_boundary": self.crop_area.geometry,
|
||||
"sensor_payload": {
|
||||
"sensor-7-1": {"temp": 12},
|
||||
},
|
||||
},
|
||||
headers={
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json",
|
||||
"X-API-Key": "farm-data-key",
|
||||
"Authorization": "Api-Key farm-data-key",
|
||||
},
|
||||
timeout=30,
|
||||
)
|
||||
|
||||
def test_returns_404_for_unknown_device_uuid(self):
|
||||
request = self.factory.post(
|
||||
@@ -88,6 +142,23 @@ class SensorExternalAPIViewTests(TestCase):
|
||||
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
@patch("sensor_external_api.services.requests.post")
|
||||
def test_returns_503_when_farm_data_api_is_unavailable(self, mock_post):
|
||||
mock_post.side_effect = requests.RequestException("connection error")
|
||||
|
||||
request = self.factory.post(
|
||||
"/api/sensor-external-api/",
|
||||
{"uuid": str(self.sensor.physical_device_uuid), "payload": {"temp": 12}},
|
||||
format="json",
|
||||
HTTP_X_API_KEY="12345",
|
||||
)
|
||||
|
||||
response = SensorExternalAPIView.as_view()(request)
|
||||
|
||||
self.assertEqual(response.status_code, 503)
|
||||
self.assertEqual(response.data["code"], 503)
|
||||
self.assertIn("Farm data API request failed", response.data["msg"])
|
||||
|
||||
|
||||
class SensorExternalServiceTests(TestCase):
|
||||
def test_get_latest_sensor_external_request_log_returns_latest_matching_record(self):
|
||||
|
||||
@@ -16,7 +16,9 @@ from .serializers import (
|
||||
SensorExternalRequestSerializer,
|
||||
)
|
||||
from .services import (
|
||||
FarmDataForwardError,
|
||||
create_sensor_external_notification,
|
||||
forward_sensor_payload_to_farm_data,
|
||||
get_farm_sensor_map_for_logs,
|
||||
get_sensor_external_request_logs_for_farm,
|
||||
)
|
||||
@@ -61,6 +63,10 @@ class SensorExternalAPIView(APIView):
|
||||
physical_device_uuid=serializer.validated_data["uuid"],
|
||||
payload=serializer.validated_data.get("payload"),
|
||||
)
|
||||
forward_sensor_payload_to_farm_data(
|
||||
physical_device_uuid=serializer.validated_data["uuid"],
|
||||
payload=serializer.validated_data.get("payload"),
|
||||
)
|
||||
except ValueError as exc:
|
||||
if "not migrated" in str(exc):
|
||||
return Response(
|
||||
@@ -68,6 +74,11 @@ class SensorExternalAPIView(APIView):
|
||||
status=status.HTTP_503_SERVICE_UNAVAILABLE,
|
||||
)
|
||||
return Response({"code": 404, "msg": "Physical device not found."}, status=status.HTTP_404_NOT_FOUND)
|
||||
except FarmDataForwardError as exc:
|
||||
return Response(
|
||||
{"code": 503, "msg": str(exc)},
|
||||
status=status.HTTP_503_SERVICE_UNAVAILABLE,
|
||||
)
|
||||
|
||||
data = FarmNotificationSerializer(notification).data
|
||||
return Response({"code": 201, "msg": "success", "data": data}, status=status.HTTP_201_CREATED)
|
||||
|
||||
Reference in New Issue
Block a user