fix(api): align contracts with frontend md
Some checks failed
CI/CD Pipeline / Code Quality Checks (pull_request) Failing after 3m20s
CI/CD Pipeline / Run Tests (pull_request) Successful in 13m45s
CI/CD Pipeline / Run API Inventory E2E Tests (pull_request) Successful in 22s
CI/CD Pipeline / Telegram Notify Success (pull_request) Has been skipped
Some checks failed
CI/CD Pipeline / Code Quality Checks (pull_request) Failing after 3m20s
CI/CD Pipeline / Run Tests (pull_request) Successful in 13m45s
CI/CD Pipeline / Run API Inventory E2E Tests (pull_request) Successful in 22s
CI/CD Pipeline / Telegram Notify Success (pull_request) Has been skipped
This commit is contained in:
@@ -63,3 +63,9 @@ class BackgroundJobListSerializer(serializers.Serializer):
|
||||
|
||||
def get_status(self, obj):
|
||||
return _get_frontend_job_status(obj.status)
|
||||
|
||||
|
||||
class BackgroundJobListResponseSerializer(serializers.Serializer):
|
||||
"""Frontend-friendly wrapper for jobs list."""
|
||||
|
||||
results = BackgroundJobListSerializer(many=True, read_only=True)
|
||||
|
||||
@@ -13,7 +13,11 @@ import time
|
||||
from typing import Any
|
||||
|
||||
from apps.core.openapi import CommonResponses, ErrorResponses, swagger_tag
|
||||
from apps.core.serializers import BackgroundJobListSerializer, BackgroundJobSerializer
|
||||
from apps.core.serializers import (
|
||||
BackgroundJobListResponseSerializer,
|
||||
BackgroundJobListSerializer,
|
||||
BackgroundJobSerializer,
|
||||
)
|
||||
from django.conf import settings
|
||||
from django.db import connection
|
||||
from django.http import StreamingHttpResponse
|
||||
@@ -369,7 +373,7 @@ class BackgroundJobListView(APIView):
|
||||
"Поддерживает фильтрацию по статусу (status) и лимит (limit)."
|
||||
),
|
||||
responses={
|
||||
200: BackgroundJobListSerializer(many=True),
|
||||
200: BackgroundJobListResponseSerializer,
|
||||
**ErrorResponses.AUTHENTICATED,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Сериализаторы приложения обмена данными."""
|
||||
|
||||
import json
|
||||
import uuid
|
||||
from typing import Any
|
||||
|
||||
from apps.exchange.models import ExchangeConnection
|
||||
@@ -51,6 +52,8 @@ def get_periodic_task_payload(task: PeriodicTask) -> dict[str, Any]:
|
||||
class ExchangeConnectionSerializer(serializers.ModelSerializer):
|
||||
"""Сериализатор подключения без выдачи пароля в ответах."""
|
||||
|
||||
id = serializers.CharField(source="pk", read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = ExchangeConnection
|
||||
fields = [
|
||||
@@ -65,6 +68,19 @@ class ExchangeConnectionSerializer(serializers.ModelSerializer):
|
||||
read_only_fields = fields
|
||||
|
||||
|
||||
class ExchangeConnectionListResponseSerializer(serializers.Serializer):
|
||||
"""Frontend-friendly wrapper for exchange connections list."""
|
||||
|
||||
results = ExchangeConnectionSerializer(many=True, read_only=True)
|
||||
|
||||
|
||||
class ExchangeConnectionTestResponseSerializer(serializers.Serializer):
|
||||
"""Ответ проверки подключения в формате frontend."""
|
||||
|
||||
success = serializers.BooleanField(read_only=True)
|
||||
message = serializers.CharField(read_only=True)
|
||||
|
||||
|
||||
class ExchangeConnectionCreateSerializer(serializers.Serializer):
|
||||
"""Входные данные для создания активного подключения."""
|
||||
|
||||
@@ -107,14 +123,12 @@ class ExchangeCopyRequestSerializer(serializers.Serializer):
|
||||
class ExchangePeriodicTaskSerializer(serializers.ModelSerializer):
|
||||
"""Сериализатор периодической задачи обмена."""
|
||||
|
||||
id = serializers.CharField(source="pk", read_only=True)
|
||||
schedule_type = serializers.SerializerMethodField()
|
||||
interval_every = serializers.SerializerMethodField()
|
||||
interval_period = serializers.SerializerMethodField()
|
||||
crontab_minute = serializers.SerializerMethodField()
|
||||
crontab_hour = serializers.SerializerMethodField()
|
||||
crontab_day_of_week = serializers.SerializerMethodField()
|
||||
crontab_day_of_month = serializers.SerializerMethodField()
|
||||
crontab_month_of_year = serializers.SerializerMethodField()
|
||||
notify_on_error = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
@@ -126,9 +140,6 @@ class ExchangePeriodicTaskSerializer(serializers.ModelSerializer):
|
||||
"interval_period",
|
||||
"crontab_minute",
|
||||
"crontab_hour",
|
||||
"crontab_day_of_week",
|
||||
"crontab_day_of_month",
|
||||
"crontab_month_of_year",
|
||||
"notify_on_error",
|
||||
]
|
||||
read_only_fields = fields
|
||||
@@ -137,7 +148,7 @@ class ExchangePeriodicTaskSerializer(serializers.ModelSerializer):
|
||||
if obj.interval_id:
|
||||
return "interval"
|
||||
if obj.crontab_id:
|
||||
return "crontab"
|
||||
return "daily"
|
||||
return None
|
||||
|
||||
def get_interval_every(self, obj: PeriodicTask) -> int | None:
|
||||
@@ -146,64 +157,46 @@ class ExchangePeriodicTaskSerializer(serializers.ModelSerializer):
|
||||
def get_interval_period(self, obj: PeriodicTask) -> str | None:
|
||||
return obj.interval.period if obj.interval_id else None
|
||||
|
||||
def get_crontab_minute(self, obj: PeriodicTask) -> str | None:
|
||||
return obj.crontab.minute if obj.crontab_id else None
|
||||
@staticmethod
|
||||
def _coerce_crontab_number(value: str | None) -> int | None:
|
||||
if value is None:
|
||||
return None
|
||||
return int(value) if str(value).isdigit() else None
|
||||
|
||||
def get_crontab_hour(self, obj: PeriodicTask) -> str | None:
|
||||
return obj.crontab.hour if obj.crontab_id else None
|
||||
def get_crontab_minute(self, obj: PeriodicTask) -> int | None:
|
||||
return self._coerce_crontab_number(obj.crontab.minute) if obj.crontab_id else None
|
||||
|
||||
def get_crontab_day_of_week(self, obj: PeriodicTask) -> str | None:
|
||||
return obj.crontab.day_of_week if obj.crontab_id else None
|
||||
|
||||
def get_crontab_day_of_month(self, obj: PeriodicTask) -> str | None:
|
||||
return obj.crontab.day_of_month if obj.crontab_id else None
|
||||
|
||||
def get_crontab_month_of_year(self, obj: PeriodicTask) -> str | None:
|
||||
return obj.crontab.month_of_year if obj.crontab_id else None
|
||||
def get_crontab_hour(self, obj: PeriodicTask) -> int | None:
|
||||
return self._coerce_crontab_number(obj.crontab.hour) if obj.crontab_id else None
|
||||
|
||||
def get_notify_on_error(self, obj: PeriodicTask) -> bool:
|
||||
return bool(get_periodic_task_payload(obj).get("notify_on_error", False))
|
||||
|
||||
def to_representation(self, instance):
|
||||
data = super().to_representation(instance)
|
||||
return {key: value for key, value in data.items() if value is not None}
|
||||
|
||||
|
||||
class ExchangePeriodicTaskListResponseSerializer(serializers.Serializer):
|
||||
"""Frontend-friendly wrapper for periodic tasks list."""
|
||||
|
||||
results = ExchangePeriodicTaskSerializer(many=True, read_only=True)
|
||||
|
||||
|
||||
class ExchangePeriodicTaskUpsertSerializer(serializers.Serializer):
|
||||
"""Входные данные для создания и изменения периодической задачи обмена."""
|
||||
|
||||
name = serializers.CharField(max_length=200, required=False)
|
||||
description = serializers.CharField(required=False, allow_blank=True)
|
||||
enabled = serializers.BooleanField(required=False)
|
||||
|
||||
schedule_type = serializers.ChoiceField(
|
||||
choices=["interval", "crontab"],
|
||||
required=False,
|
||||
)
|
||||
schedule_type = serializers.CharField(required=False)
|
||||
interval_every = serializers.IntegerField(min_value=1, required=False)
|
||||
interval_period = serializers.ChoiceField(
|
||||
choices=[choice[0] for choice in IntervalSchedule.PERIOD_CHOICES],
|
||||
required=False,
|
||||
)
|
||||
crontab_minute = serializers.CharField(max_length=64, required=False)
|
||||
crontab_hour = serializers.CharField(max_length=64, required=False)
|
||||
crontab_day_of_week = serializers.CharField(max_length=64, required=False)
|
||||
crontab_day_of_month = serializers.CharField(max_length=64, required=False)
|
||||
crontab_month_of_year = serializers.CharField(max_length=64, required=False)
|
||||
|
||||
mode = serializers.ChoiceField(
|
||||
choices=["all", "single", "selected"],
|
||||
required=False,
|
||||
)
|
||||
table = serializers.CharField(required=False)
|
||||
tables = serializers.ListField(
|
||||
child=serializers.CharField(),
|
||||
required=False,
|
||||
allow_empty=False,
|
||||
)
|
||||
truncate_before_copy = serializers.BooleanField(required=False)
|
||||
crontab_minute = serializers.IntegerField(min_value=0, max_value=59, required=False)
|
||||
crontab_hour = serializers.IntegerField(min_value=0, max_value=23, required=False)
|
||||
notify_on_error = serializers.BooleanField(required=False)
|
||||
|
||||
def validate(self, attrs: dict[str, Any]) -> dict[str, Any]:
|
||||
if not self.instance and "name" not in attrs:
|
||||
raise serializers.ValidationError({"name": "Обязательное поле."})
|
||||
|
||||
schedule_type = self._resolve_schedule_type(attrs)
|
||||
if not schedule_type:
|
||||
raise serializers.ValidationError(
|
||||
@@ -213,27 +206,41 @@ class ExchangePeriodicTaskUpsertSerializer(serializers.Serializer):
|
||||
payload = self._build_payload(attrs)
|
||||
schedule = self._build_schedule(attrs, schedule_type)
|
||||
|
||||
attrs["name"] = self._build_name()
|
||||
attrs["payload"] = validate_exchange_copy_payload(payload)
|
||||
attrs["schedule"] = schedule
|
||||
return attrs
|
||||
|
||||
def _resolve_schedule_type(self, attrs: dict[str, Any]) -> str | None:
|
||||
if "schedule_type" in attrs:
|
||||
return attrs["schedule_type"]
|
||||
schedule_type = str(attrs["schedule_type"]).strip().lower()
|
||||
if schedule_type not in {"interval", "daily"}:
|
||||
raise serializers.ValidationError(
|
||||
{
|
||||
"schedule_type": (
|
||||
"Допустимые значения: interval или daily."
|
||||
)
|
||||
}
|
||||
)
|
||||
return schedule_type
|
||||
|
||||
if self.instance and self.instance.interval_id:
|
||||
return "interval"
|
||||
|
||||
if self.instance and self.instance.crontab_id:
|
||||
return "crontab"
|
||||
return "daily"
|
||||
|
||||
return None
|
||||
|
||||
def _build_name(self) -> str:
|
||||
if self.instance:
|
||||
return self.instance.name
|
||||
return f"exchange-periodic-{uuid.uuid4()}"
|
||||
|
||||
def _build_payload(self, attrs: dict[str, Any]) -> dict[str, Any]:
|
||||
current_payload = (
|
||||
get_periodic_task_payload(self.instance) if self.instance else {}
|
||||
)
|
||||
mode = attrs.get("mode", current_payload.get("mode", "all"))
|
||||
truncate_before_copy = attrs.get(
|
||||
"truncate_before_copy",
|
||||
current_payload.get("truncate_before_copy", True),
|
||||
@@ -243,20 +250,10 @@ class ExchangePeriodicTaskUpsertSerializer(serializers.Serializer):
|
||||
current_payload.get("notify_on_error", False),
|
||||
)
|
||||
|
||||
if "mode" in attrs and attrs["mode"] != "single" and "table" not in attrs:
|
||||
table = None
|
||||
else:
|
||||
table = attrs.get("table", current_payload.get("table"))
|
||||
|
||||
if "mode" in attrs and attrs["mode"] != "selected" and "tables" not in attrs:
|
||||
tables = None
|
||||
else:
|
||||
tables = attrs.get("tables", current_payload.get("tables"))
|
||||
|
||||
return {
|
||||
"mode": mode,
|
||||
"table": table,
|
||||
"tables": tables,
|
||||
"mode": current_payload.get("mode", "all"),
|
||||
"table": current_payload.get("table"),
|
||||
"tables": current_payload.get("tables"),
|
||||
"truncate_before_copy": truncate_before_copy,
|
||||
"notify_on_error": notify_on_error,
|
||||
}
|
||||
@@ -268,7 +265,7 @@ class ExchangePeriodicTaskUpsertSerializer(serializers.Serializer):
|
||||
) -> dict[str, Any]:
|
||||
if schedule_type == "interval":
|
||||
return self._build_interval_schedule(attrs)
|
||||
return self._build_crontab_schedule(attrs)
|
||||
return self._build_daily_schedule(attrs)
|
||||
|
||||
def _build_interval_schedule(self, attrs: dict[str, Any]) -> dict[str, Any]:
|
||||
current_schedule = self.instance.interval if self.instance else None
|
||||
@@ -295,40 +292,30 @@ class ExchangePeriodicTaskUpsertSerializer(serializers.Serializer):
|
||||
"period": interval_period,
|
||||
}
|
||||
|
||||
def _build_crontab_schedule(self, attrs: dict[str, Any]) -> dict[str, Any]:
|
||||
def _build_daily_schedule(self, attrs: dict[str, Any]) -> dict[str, Any]:
|
||||
current_schedule = self.instance.crontab if self.instance else None
|
||||
fields = {
|
||||
"minute": attrs.get(
|
||||
"crontab_minute",
|
||||
current_schedule.minute if current_schedule else None,
|
||||
),
|
||||
"hour": attrs.get(
|
||||
"crontab_hour",
|
||||
current_schedule.hour if current_schedule else None,
|
||||
),
|
||||
"day_of_week": attrs.get(
|
||||
"crontab_day_of_week",
|
||||
current_schedule.day_of_week if current_schedule else None,
|
||||
),
|
||||
"day_of_month": attrs.get(
|
||||
"crontab_day_of_month",
|
||||
current_schedule.day_of_month if current_schedule else None,
|
||||
),
|
||||
"month_of_year": attrs.get(
|
||||
"crontab_month_of_year",
|
||||
current_schedule.month_of_year if current_schedule else None,
|
||||
),
|
||||
}
|
||||
minute = attrs.get(
|
||||
"crontab_minute",
|
||||
int(current_schedule.minute) if current_schedule else None,
|
||||
)
|
||||
hour = attrs.get(
|
||||
"crontab_hour",
|
||||
int(current_schedule.hour) if current_schedule else None,
|
||||
)
|
||||
|
||||
errors = {
|
||||
f"crontab_{field_name}": "Обязательное поле для crontab."
|
||||
for field_name, value in fields.items()
|
||||
if value is None
|
||||
}
|
||||
errors = {}
|
||||
if minute is None:
|
||||
errors["crontab_minute"] = "Обязательное поле для daily."
|
||||
if hour is None:
|
||||
errors["crontab_hour"] = "Обязательное поле для daily."
|
||||
if errors:
|
||||
raise serializers.ValidationError(errors)
|
||||
|
||||
return {
|
||||
"type": "crontab",
|
||||
**fields,
|
||||
"minute": str(minute),
|
||||
"hour": str(hour),
|
||||
"day_of_week": "*",
|
||||
"day_of_month": "*",
|
||||
"month_of_year": "*",
|
||||
}
|
||||
|
||||
@@ -8,8 +8,11 @@ from apps.core.services import BackgroundJobService
|
||||
from apps.exchange.models import ExchangeConnection
|
||||
from apps.exchange.serializers import (
|
||||
ExchangeConnectionCreateSerializer,
|
||||
ExchangeConnectionListResponseSerializer,
|
||||
ExchangeConnectionSerializer,
|
||||
ExchangeConnectionTestResponseSerializer,
|
||||
ExchangeCopyRequestSerializer,
|
||||
ExchangePeriodicTaskListResponseSerializer,
|
||||
ExchangePeriodicTaskSerializer,
|
||||
ExchangePeriodicTaskUpsertSerializer,
|
||||
)
|
||||
@@ -45,7 +48,7 @@ class ExchangeConnectionListCreateView(APIView):
|
||||
"Пароль в ответ не возвращается."
|
||||
),
|
||||
responses={
|
||||
200: ExchangeConnectionSerializer(many=True),
|
||||
200: ExchangeConnectionListResponseSerializer,
|
||||
**ErrorResponses.ADMIN,
|
||||
},
|
||||
)
|
||||
@@ -100,16 +103,7 @@ class ExchangeConnectionTestView(APIView):
|
||||
),
|
||||
request_body=ExchangeConnectionCreateSerializer,
|
||||
responses={
|
||||
200: openapi.Response(
|
||||
description="Проверка успешна",
|
||||
schema=openapi.Schema(
|
||||
type=openapi.TYPE_OBJECT,
|
||||
properties={
|
||||
"status": openapi.Schema(type=openapi.TYPE_STRING),
|
||||
"message": openapi.Schema(type=openapi.TYPE_STRING),
|
||||
},
|
||||
),
|
||||
),
|
||||
200: ExchangeConnectionTestResponseSerializer,
|
||||
400: CommonResponses.BAD_REQUEST,
|
||||
**ErrorResponses.ADMIN,
|
||||
},
|
||||
@@ -232,7 +226,7 @@ class ExchangePeriodicTaskListCreateView(APIView):
|
||||
"django_celery_beat."
|
||||
),
|
||||
responses={
|
||||
200: ExchangePeriodicTaskSerializer(many=True),
|
||||
200: ExchangePeriodicTaskListResponseSerializer,
|
||||
**ErrorResponses.ADMIN,
|
||||
},
|
||||
)
|
||||
@@ -263,8 +257,8 @@ class ExchangePeriodicTaskListCreateView(APIView):
|
||||
try:
|
||||
task = ExchangePeriodicTaskService.create_periodic_task(
|
||||
name=serializer.validated_data["name"],
|
||||
description=serializer.validated_data.get("description", ""),
|
||||
enabled=serializer.validated_data.get("enabled", True),
|
||||
description="",
|
||||
enabled=True,
|
||||
payload=serializer.validated_data["payload"],
|
||||
schedule=serializer.validated_data["schedule"],
|
||||
)
|
||||
@@ -327,9 +321,6 @@ class ExchangePeriodicTaskDetailView(APIView):
|
||||
try:
|
||||
task = ExchangePeriodicTaskService.update_periodic_task(
|
||||
task=task,
|
||||
name=serializer.validated_data.get("name"),
|
||||
description=serializer.validated_data.get("description"),
|
||||
enabled=serializer.validated_data.get("enabled"),
|
||||
payload=serializer.validated_data["payload"],
|
||||
schedule=serializer.validated_data["schedule"],
|
||||
)
|
||||
|
||||
@@ -314,6 +314,13 @@ class FNSZipUploadSerializer(serializers.Serializer):
|
||||
return value
|
||||
|
||||
|
||||
class FNSFileUploadSuccessSerializer(serializers.Serializer):
|
||||
"""Ответ одиночной загрузки FNS в формате frontend."""
|
||||
|
||||
success = serializers.BooleanField(read_only=True)
|
||||
message = serializers.CharField(read_only=True)
|
||||
|
||||
|
||||
class ParsingSettingsSerializer(serializers.ModelSerializer):
|
||||
"""Настройки периодичности обновления источников парсинга."""
|
||||
|
||||
@@ -429,6 +436,15 @@ class ParserLoadLogListSerializer(serializers.Serializer):
|
||||
updated_at = serializers.DateTimeField(read_only=True)
|
||||
|
||||
|
||||
class ParserLoadLogPageSerializer(serializers.Serializer):
|
||||
"""Пагинированный ответ списка логов для frontend."""
|
||||
|
||||
count = serializers.IntegerField(read_only=True)
|
||||
next = serializers.CharField(read_only=True, allow_null=True)
|
||||
previous = serializers.CharField(read_only=True, allow_null=True)
|
||||
results = ParserLoadLogListSerializer(many=True, read_only=True)
|
||||
|
||||
|
||||
class SourceCardRefreshParamSerializer(serializers.Serializer):
|
||||
"""Описание параметра ручного обновления карточки источника."""
|
||||
|
||||
@@ -548,3 +564,36 @@ class SourceCardRefreshResponseSerializer(serializers.Serializer):
|
||||
status = serializers.CharField(read_only=True)
|
||||
requested_at = serializers.DateTimeField(read_only=True)
|
||||
tasks = SourceCardRefreshTaskSerializer(many=True, read_only=True)
|
||||
|
||||
|
||||
class FrontendApiResponseSerializer(serializers.Serializer):
|
||||
"""Общая форма success/data/errors/meta для frontend sources API."""
|
||||
|
||||
success = serializers.BooleanField(read_only=True)
|
||||
errors = serializers.JSONField(read_only=True, allow_null=True)
|
||||
meta = serializers.JSONField(read_only=True, allow_null=True)
|
||||
|
||||
|
||||
class SourceCardListResponseSerializer(FrontendApiResponseSerializer):
|
||||
"""Envelope для списка карточек источников."""
|
||||
|
||||
data = SourceCardSerializer(many=True, read_only=True)
|
||||
|
||||
|
||||
class SourceTaskStatusListResponseSerializer(FrontendApiResponseSerializer):
|
||||
"""Envelope для списка статусов источников."""
|
||||
|
||||
data = SourceTaskStatusSerializer(many=True, read_only=True)
|
||||
|
||||
|
||||
class SourceCardDetailResponseSerializer(FrontendApiResponseSerializer):
|
||||
"""Envelope для детальной карточки источника."""
|
||||
|
||||
data = SourceCardDetailSerializer(read_only=True)
|
||||
|
||||
|
||||
class SourceCardRefreshFrontendResponseSerializer(serializers.Serializer):
|
||||
"""Минимальный ответ запуска обновления карточки по md."""
|
||||
|
||||
task_id = serializers.CharField(read_only=True, allow_null=True)
|
||||
status = serializers.CharField(read_only=True)
|
||||
|
||||
@@ -24,18 +24,24 @@ from apps.parsers.serializers import (
|
||||
FinancialReportDetailSerializer,
|
||||
FinancialReportSerializer,
|
||||
FNSFileUploadSerializer,
|
||||
FNSFileUploadSuccessSerializer,
|
||||
IndustrialCertificateSerializer,
|
||||
IndustrialProductSerializer,
|
||||
InspectionSerializer,
|
||||
ManufacturerSerializer,
|
||||
ParserLoadLogListSerializer,
|
||||
ParserLoadLogPageSerializer,
|
||||
ParserLoadLogSerializer,
|
||||
ParsingSettingsSerializer,
|
||||
ProcurementSerializer,
|
||||
SourceCardDetailResponseSerializer,
|
||||
SourceCardDetailSerializer,
|
||||
SourceCardListResponseSerializer,
|
||||
SourceCardRefreshFrontendResponseSerializer,
|
||||
SourceCardRefreshRequestSerializer,
|
||||
SourceCardRefreshResponseSerializer,
|
||||
SourceCardSerializer,
|
||||
SourceTaskStatusListResponseSerializer,
|
||||
SourceTaskStatusSerializer,
|
||||
)
|
||||
from apps.parsers.source_cards import SourceCardService
|
||||
@@ -683,15 +689,16 @@ class FNSReportUploadView(APIView):
|
||||
),
|
||||
manual_parameters=[
|
||||
openapi.Parameter(
|
||||
name="files",
|
||||
name="file",
|
||||
in_=openapi.IN_FORM,
|
||||
type=openapi.TYPE_FILE,
|
||||
required=True,
|
||||
description="Файл(ы) для загрузки (fin_*.xlsx)",
|
||||
description="Файл для загрузки (fin_*.xlsx)",
|
||||
),
|
||||
],
|
||||
consumes=["multipart/form-data"],
|
||||
responses={
|
||||
200: FNSFileUploadSuccessSerializer,
|
||||
202: openapi.Response(
|
||||
description="Файлы приняты в обработку",
|
||||
schema=openapi.Schema(
|
||||
@@ -809,7 +816,7 @@ class SourceCardListView(APIView):
|
||||
"для фронтенда."
|
||||
),
|
||||
responses={
|
||||
200: SourceCardSerializer(many=True),
|
||||
200: SourceCardListResponseSerializer,
|
||||
**ErrorResponses.AUTHENTICATED,
|
||||
},
|
||||
)
|
||||
@@ -832,7 +839,7 @@ class SourceTaskStatusListView(APIView):
|
||||
"мониторинга парсинга."
|
||||
),
|
||||
responses={
|
||||
200: SourceTaskStatusSerializer(many=True),
|
||||
200: SourceTaskStatusListResponseSerializer,
|
||||
**ErrorResponses.AUTHENTICATED,
|
||||
},
|
||||
)
|
||||
@@ -855,7 +862,7 @@ class SourceCardDetailView(APIView):
|
||||
"включая подисточники, последние загрузки и активные задачи."
|
||||
),
|
||||
responses={
|
||||
200: SourceCardDetailSerializer,
|
||||
200: SourceCardDetailResponseSerializer,
|
||||
**ErrorResponses.AUTHENTICATED_NOT_FOUND,
|
||||
},
|
||||
)
|
||||
@@ -878,7 +885,7 @@ class SourceCardRefreshView(APIView):
|
||||
),
|
||||
request_body=SourceCardRefreshRequestSerializer,
|
||||
responses={
|
||||
202: SourceCardRefreshResponseSerializer,
|
||||
202: SourceCardRefreshFrontendResponseSerializer,
|
||||
400: CommonResponses.BAD_REQUEST,
|
||||
**ErrorResponses.ADMIN_NOT_FOUND,
|
||||
},
|
||||
@@ -892,17 +899,9 @@ class SourceCardRefreshView(APIView):
|
||||
requested_by_id=request.user.id if request.user.is_authenticated else None,
|
||||
params=serializer.validated_data.get("params", {}),
|
||||
)
|
||||
output = SourceCardRefreshResponseSerializer(payload)
|
||||
serialized_payload = output.data
|
||||
tasks = serialized_payload.get("tasks", [])
|
||||
task_id = tasks[0]["task_id"] if tasks else None
|
||||
response_payload = {
|
||||
"task_id": task_id,
|
||||
"status": "accepted",
|
||||
}
|
||||
if len(tasks) > 1:
|
||||
response_payload["tasks"] = tasks
|
||||
response_payload["source_card"] = serialized_payload.get("source_card")
|
||||
output = SourceCardRefreshResponseSerializer(payload).data
|
||||
tasks = output.get("tasks", [])
|
||||
response_payload = {"task_id": tasks[0]["task_id"] if tasks else None, "status": "accepted"}
|
||||
return Response(
|
||||
response_payload,
|
||||
status=status.HTTP_202_ACCEPTED,
|
||||
@@ -937,7 +936,7 @@ class ParserLoadLogViewSet(ReadOnlyModelViewSet):
|
||||
"Возвращает историю загрузок данных парсерами.\n"
|
||||
"Доступно только администраторам.\n"
|
||||
"Поддерживает фильтрацию по: source, status, batch_id.\n"
|
||||
"Поддерживает search по source, status, batch_id и error_message.\n"
|
||||
"Поддерживает search по всем полям строки frontend-лога.\n"
|
||||
"Поддерживает ordering по: id, batch_id, source, status, records_count, "
|
||||
"created_at, updated_at."
|
||||
),
|
||||
@@ -947,7 +946,7 @@ class ParserLoadLogViewSet(ReadOnlyModelViewSet):
|
||||
in_=openapi.IN_QUERY,
|
||||
type=openapi.TYPE_STRING,
|
||||
required=False,
|
||||
description="Поиск по source, status, batch_id и error_message",
|
||||
description="Поиск по всем полям строки frontend-лога",
|
||||
),
|
||||
openapi.Parameter(
|
||||
name="ordering",
|
||||
@@ -961,7 +960,7 @@ class ParserLoadLogViewSet(ReadOnlyModelViewSet):
|
||||
),
|
||||
],
|
||||
responses={
|
||||
200: ParserLoadLogSerializer(many=True),
|
||||
200: ParserLoadLogPageSerializer,
|
||||
**ErrorResponses.ADMIN,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -13,10 +13,23 @@ class RegisterSerializer(serializers.ModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = Register
|
||||
fields = ["id", "name", "created_at", "updated_at"]
|
||||
fields = ["id", "name"]
|
||||
read_only_fields = fields
|
||||
|
||||
|
||||
class RegisterListResponseSerializer(serializers.Serializer):
|
||||
"""Frontend-friendly wrapper for registries list."""
|
||||
|
||||
results = RegisterSerializer(many=True, read_only=True)
|
||||
|
||||
|
||||
class RegisterUploadSuccessSerializer(serializers.Serializer):
|
||||
"""Минимальный ответ успешной загрузки реестра."""
|
||||
|
||||
success = serializers.BooleanField(read_only=True)
|
||||
message = serializers.CharField(read_only=True)
|
||||
|
||||
|
||||
class RegistryMembershipPeriodSerializer(serializers.ModelSerializer):
|
||||
"""Сериализатор периода участия организации в реестре."""
|
||||
|
||||
|
||||
@@ -12,7 +12,9 @@ from apps.registers.serializers import (
|
||||
OrganizationListQuerySerializer,
|
||||
OrganizationSerializer,
|
||||
RegisterFileUploadSerializer,
|
||||
RegisterListResponseSerializer,
|
||||
RegisterSerializer,
|
||||
RegisterUploadSuccessSerializer,
|
||||
RegistryOrganizationListQuerySerializer,
|
||||
)
|
||||
from apps.registers.services import RegisterImportError, RegisterImportService
|
||||
@@ -44,7 +46,7 @@ class RegisterViewSet(ReadOnlyModelViewSet):
|
||||
operation_summary="Список реестров",
|
||||
operation_description="Возвращает список доступных реестров.",
|
||||
responses={
|
||||
200: RegisterSerializer(many=True),
|
||||
200: RegisterListResponseSerializer,
|
||||
**ErrorResponses.AUTHENTICATED,
|
||||
},
|
||||
)
|
||||
@@ -327,31 +329,7 @@ class RegisterUploadView(APIView):
|
||||
],
|
||||
consumes=["multipart/form-data"],
|
||||
responses={
|
||||
201: openapi.Response(
|
||||
description="Список организаций успешно загружен",
|
||||
schema=openapi.Schema(
|
||||
type=openapi.TYPE_OBJECT,
|
||||
properties={
|
||||
"upload_id": openapi.Schema(type=openapi.TYPE_INTEGER),
|
||||
"registry_id": openapi.Schema(type=openapi.TYPE_STRING),
|
||||
"registry_name": openapi.Schema(type=openapi.TYPE_STRING),
|
||||
"actual_date": openapi.Schema(
|
||||
type=openapi.TYPE_STRING,
|
||||
format=openapi.FORMAT_DATE,
|
||||
),
|
||||
"rows_in_file": openapi.Schema(type=openapi.TYPE_INTEGER),
|
||||
"organizations_created": openapi.Schema(
|
||||
type=openapi.TYPE_INTEGER
|
||||
),
|
||||
"organizations_updated": openapi.Schema(
|
||||
type=openapi.TYPE_INTEGER
|
||||
),
|
||||
"opened_periods": openapi.Schema(type=openapi.TYPE_INTEGER),
|
||||
"closed_periods": openapi.Schema(type=openapi.TYPE_INTEGER),
|
||||
"active_periods": openapi.Schema(type=openapi.TYPE_INTEGER),
|
||||
},
|
||||
),
|
||||
),
|
||||
201: RegisterUploadSuccessSerializer,
|
||||
400: CommonResponses.BAD_REQUEST,
|
||||
**ErrorResponses.ADMIN,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user