Add source_site_id to ActiveSite model and implement unique constraint. Update serializers and views for site registration and claiming. Enhance tests for site registration and claiming functionality.
This commit is contained in:
1 parent
521f36d22d
commit
61f954a3bd
12 files changed
+684
-172
No files matched your search
@@ -92,17 +92,22 @@ class VisibilityScopeTests(TestCase):
|
||||
self.assertEqual(response.data["results"], [])
|
||||
|
||||
def test_registering_active_site_attaches_matching_seed_site(self):
|
||||
ActiveSite.objects.create(
|
||||
code="orphan-seed",
|
||||
name="Orphan Seed",
|
||||
api_base_url="https://orphan.example/api/v1",
|
||||
api_key="k",
|
||||
managed_by=None,
|
||||
)
|
||||
self.client.force_authenticate(self.gm)
|
||||
response = self.client.post(
|
||||
"/api/v1/active-sites/",
|
||||
{
|
||||
"code": "orphan-seed",
|
||||
"name": "Orphan Seed",
|
||||
"is_active": True,
|
||||
},
|
||||
format="json",
|
||||
)
|
||||
self.assertEqual(response.status_code, 201)
|
||||
self.assertEqual(response.status_code, 200, response.data)
|
||||
self.orphan.refresh_from_db()
|
||||
self.assertEqual(self.orphan.active_site_id, response.data["id"])
|
||||
listed = self.client.get("/api/v1/sites/")
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
# Generated manually: ActiveSite.source_site_id from city Site.site_id
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("sync", "0005_rename_registered_by_to_managed_by"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="activesite",
|
||||
name="source_site_id",
|
||||
field=models.SlugField(
|
||||
blank=True,
|
||||
db_index=True,
|
||||
default="",
|
||||
help_text="Farm-location Site.site_id (slug) from the registering dashboard",
|
||||
max_length=64,
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,21 @@
|
||||
# Unique non-empty source_site_id so one farm site cannot own two ActiveSites.
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("sync", "0006_activesite_source_site_id"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddConstraint(
|
||||
model_name="activesite",
|
||||
constraint=models.UniqueConstraint(
|
||||
condition=~models.Q(source_site_id=""),
|
||||
fields=("source_site_id",),
|
||||
name="uniq_active_site_source_site_id",
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -15,6 +15,13 @@ class ActiveSite(models.Model):
|
||||
active_site_id = models.BigAutoField(primary_key=True)
|
||||
code = models.SlugField(max_length=64, unique=True, help_text="Stable id, e.g. sukawarna")
|
||||
name = models.CharField(max_length=120)
|
||||
source_site_id = models.SlugField(
|
||||
max_length=64,
|
||||
blank=True,
|
||||
default="",
|
||||
db_index=True,
|
||||
help_text="Farm-location Site.site_id (slug) from the registering dashboard",
|
||||
)
|
||||
api_base_url = models.URLField(
|
||||
max_length=255,
|
||||
blank=True,
|
||||
@@ -44,6 +51,13 @@ class ActiveSite(models.Model):
|
||||
class Meta:
|
||||
db_table = "active_sites"
|
||||
ordering = ["name"]
|
||||
constraints = [
|
||||
models.UniqueConstraint(
|
||||
fields=["source_site_id"],
|
||||
condition=~models.Q(source_site_id=""),
|
||||
name="uniq_active_site_source_site_id",
|
||||
),
|
||||
]
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"{self.name} ({self.code})"
|
||||
@@ -1,8 +1,16 @@
|
||||
from django.utils.text import slugify
|
||||
from rest_framework import serializers
|
||||
|
||||
from apps.sync.models import ActiveSite
|
||||
|
||||
|
||||
def _normalize_code(value: str) -> str:
|
||||
code = slugify(str(value or "").strip())[:64]
|
||||
if not code:
|
||||
raise serializers.ValidationError("Kode tidak valid.")
|
||||
return code
|
||||
|
||||
|
||||
class ActiveSiteSerializer(serializers.ModelSerializer):
|
||||
id = serializers.IntegerField(source="pk", read_only=True)
|
||||
# Never echo the outbound site API key back to the browser by default.
|
||||
@@ -17,6 +25,7 @@ class ActiveSiteSerializer(serializers.ModelSerializer):
|
||||
"id",
|
||||
"code",
|
||||
"name",
|
||||
"source_site_id",
|
||||
"api_base_url",
|
||||
"has_api_key",
|
||||
"managed_by",
|
||||
@@ -30,6 +39,7 @@ class ActiveSiteSerializer(serializers.ModelSerializer):
|
||||
]
|
||||
read_only_fields = [
|
||||
"id",
|
||||
"source_site_id",
|
||||
"has_api_key",
|
||||
"managed_by",
|
||||
"managed_by_name",
|
||||
@@ -58,3 +68,43 @@ class ActiveSiteWriteSerializer(serializers.ModelSerializer):
|
||||
"is_active",
|
||||
]
|
||||
extra_kwargs = {"api_key": {"write_only": True, "required": False}}
|
||||
|
||||
|
||||
class ActiveSiteClaimSerializer(serializers.Serializer):
|
||||
"""GM claims an ActiveSite that the site dashboard already registered."""
|
||||
|
||||
code = serializers.CharField(max_length=64)
|
||||
|
||||
def validate_code(self, value: str) -> str:
|
||||
return _normalize_code(value)
|
||||
|
||||
|
||||
class SiteRegisterSerializer(serializers.Serializer):
|
||||
"""Payload from a site dashboard registering into the ActiveSite registry."""
|
||||
|
||||
code = serializers.CharField(max_length=64)
|
||||
site_id = serializers.SlugField(max_length=64)
|
||||
name = serializers.CharField(max_length=120)
|
||||
api_base_url = serializers.URLField(max_length=255)
|
||||
api_key = serializers.CharField(max_length=255)
|
||||
|
||||
def validate_code(self, value: str) -> str:
|
||||
return _normalize_code(value)
|
||||
|
||||
def validate_site_id(self, value: str) -> str:
|
||||
site_id = str(value or "").strip()[:64]
|
||||
if not site_id:
|
||||
raise serializers.ValidationError("site_id wajib diisi.")
|
||||
return site_id
|
||||
|
||||
def validate_name(self, value: str) -> str:
|
||||
name = str(value or "").strip()
|
||||
if not name:
|
||||
raise serializers.ValidationError("Nama wajib diisi.")
|
||||
return name[:120]
|
||||
|
||||
def validate_api_key(self, value: str) -> str:
|
||||
key = str(value or "").strip()
|
||||
if not key:
|
||||
raise serializers.ValidationError("api_key wajib diisi.")
|
||||
return key
|
||||
@@ -0,0 +1,206 @@
|
||||
from django.test import TestCase
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from apps.accounts.models import User
|
||||
from apps.farms.models import Site
|
||||
from apps.sync.models import ActiveSite
|
||||
|
||||
|
||||
class RegisterFromSiteTests(TestCase):
|
||||
def setUp(self):
|
||||
self.client = APIClient()
|
||||
self.machine = User.objects.create_user(
|
||||
user_name="site-bot", password="x", status=User.STATUS_GM
|
||||
)
|
||||
self.gm = User.objects.create_user(
|
||||
user_name="gm1", password="x", status=User.STATUS_GM
|
||||
)
|
||||
self.gm_other = User.objects.create_user(
|
||||
user_name="gm2", password="x", status=User.STATUS_GM
|
||||
)
|
||||
|
||||
def test_register_creates_unclaimed_active_site(self):
|
||||
self.client.force_authenticate(self.machine)
|
||||
response = self.client.post(
|
||||
"/api/v1/active-sites/register-from-site/",
|
||||
{
|
||||
"code": "Sukawarna!",
|
||||
"site_id": "sukawarna-0001",
|
||||
"name": "Sukawarna",
|
||||
"api_base_url": "https://sukawarna.example/api/v1",
|
||||
"api_key": "site-secret",
|
||||
},
|
||||
format="json",
|
||||
)
|
||||
self.assertEqual(response.status_code, 201, response.data)
|
||||
self.assertEqual(response.data["code"], "sukawarna")
|
||||
self.assertEqual(response.data["name"], "Sukawarna")
|
||||
self.assertEqual(response.data["source_site_id"], "sukawarna-0001")
|
||||
self.assertEqual(response.data["api_base_url"], "https://sukawarna.example/api/v1")
|
||||
self.assertTrue(response.data["has_api_key"])
|
||||
self.assertIsNone(response.data["managed_by"])
|
||||
row = ActiveSite.objects.get(code="sukawarna")
|
||||
self.assertEqual(row.api_key, "site-secret")
|
||||
self.assertEqual(row.source_site_id, "sukawarna-0001")
|
||||
self.assertIsNone(row.managed_by_id)
|
||||
|
||||
def test_re_register_by_site_id_updates_even_if_code_changes(self):
|
||||
active = ActiveSite.objects.create(
|
||||
code="old-code",
|
||||
name="Old",
|
||||
source_site_id="sukawarna-0001",
|
||||
api_base_url="https://old.example/api/v1",
|
||||
api_key="old-key",
|
||||
managed_by=self.gm,
|
||||
)
|
||||
self.client.force_authenticate(self.machine)
|
||||
response = self.client.post(
|
||||
"/api/v1/active-sites/register-from-site/",
|
||||
{
|
||||
"code": "new-code",
|
||||
"site_id": "sukawarna-0001",
|
||||
"name": "Sukawarna",
|
||||
"api_base_url": "https://new.example/api/v1",
|
||||
"api_key": "new-key",
|
||||
},
|
||||
format="json",
|
||||
)
|
||||
self.assertEqual(response.status_code, 200, response.data)
|
||||
active.refresh_from_db()
|
||||
self.assertEqual(active.code, "new-code")
|
||||
self.assertEqual(active.name, "Sukawarna")
|
||||
self.assertEqual(active.source_site_id, "sukawarna-0001")
|
||||
self.assertEqual(active.api_base_url, "https://new.example/api/v1")
|
||||
self.assertEqual(active.api_key, "new-key")
|
||||
self.assertEqual(active.managed_by_id, self.gm.pk)
|
||||
self.assertEqual(ActiveSite.objects.count(), 1)
|
||||
|
||||
def test_second_register_same_site_id_never_creates_second_row(self):
|
||||
self.client.force_authenticate(self.machine)
|
||||
first = self.client.post(
|
||||
"/api/v1/active-sites/register-from-site/",
|
||||
{
|
||||
"code": "code-a",
|
||||
"site_id": "sukawarna-0001",
|
||||
"name": "Sukawarna",
|
||||
"api_base_url": "https://a.example/api/v1",
|
||||
"api_key": "key-a",
|
||||
},
|
||||
format="json",
|
||||
)
|
||||
self.assertEqual(first.status_code, 201, first.data)
|
||||
second = self.client.post(
|
||||
"/api/v1/active-sites/register-from-site/",
|
||||
{
|
||||
"code": "code-b",
|
||||
"site_id": "sukawarna-0001",
|
||||
"name": "Sukawarna",
|
||||
"api_base_url": "https://b.example/api/v1",
|
||||
"api_key": "key-b",
|
||||
},
|
||||
format="json",
|
||||
)
|
||||
self.assertEqual(second.status_code, 200, second.data)
|
||||
self.assertEqual(second.data["id"], first.data["id"])
|
||||
self.assertEqual(ActiveSite.objects.filter(source_site_id="sukawarna-0001").count(), 1)
|
||||
self.assertEqual(ActiveSite.objects.count(), 1)
|
||||
row = ActiveSite.objects.get(source_site_id="sukawarna-0001")
|
||||
self.assertEqual(row.code, "code-b")
|
||||
self.assertEqual(row.api_key, "key-b")
|
||||
|
||||
def test_re_register_updates_credentials_keeps_claim(self):
|
||||
active = ActiveSite.objects.create(
|
||||
code="sukawarna",
|
||||
name="Old",
|
||||
source_site_id="sukawarna-0001",
|
||||
api_base_url="https://old.example/api/v1",
|
||||
api_key="old-key",
|
||||
managed_by=self.gm,
|
||||
)
|
||||
self.client.force_authenticate(self.machine)
|
||||
response = self.client.post(
|
||||
"/api/v1/active-sites/register-from-site/",
|
||||
{
|
||||
"code": "sukawarna",
|
||||
"site_id": "sukawarna-0001",
|
||||
"name": "Sukawarna",
|
||||
"api_base_url": "https://new.example/api/v1",
|
||||
"api_key": "new-key",
|
||||
},
|
||||
format="json",
|
||||
)
|
||||
self.assertEqual(response.status_code, 200, response.data)
|
||||
active.refresh_from_db()
|
||||
self.assertEqual(active.name, "Sukawarna")
|
||||
self.assertEqual(active.api_base_url, "https://new.example/api/v1")
|
||||
self.assertEqual(active.api_key, "new-key")
|
||||
self.assertEqual(active.managed_by_id, self.gm.pk)
|
||||
|
||||
|
||||
class ClaimActiveSiteTests(TestCase):
|
||||
def setUp(self):
|
||||
self.client = APIClient()
|
||||
self.gm = User.objects.create_user(
|
||||
user_name="gm1", password="x", status=User.STATUS_GM
|
||||
)
|
||||
self.gm_other = User.objects.create_user(
|
||||
user_name="gm2", password="x", status=User.STATUS_GM
|
||||
)
|
||||
self.active = ActiveSite.objects.create(
|
||||
code="sukawarna",
|
||||
name="Sukawarna",
|
||||
api_base_url="https://sukawarna.example/api/v1",
|
||||
api_key="site-secret",
|
||||
managed_by=None,
|
||||
)
|
||||
|
||||
def test_gm_claims_registered_site_by_code(self):
|
||||
self.client.force_authenticate(self.gm)
|
||||
response = self.client.post(
|
||||
"/api/v1/active-sites/",
|
||||
{"code": "sukawarna"},
|
||||
format="json",
|
||||
)
|
||||
self.assertEqual(response.status_code, 200, response.data)
|
||||
self.active.refresh_from_db()
|
||||
self.assertEqual(self.active.managed_by_id, self.gm.pk)
|
||||
self.assertTrue(Site.objects.filter(active_site=self.active, user=self.gm).exists())
|
||||
|
||||
def test_claim_unknown_code_404(self):
|
||||
self.client.force_authenticate(self.gm)
|
||||
response = self.client.post(
|
||||
"/api/v1/active-sites/",
|
||||
{"code": "missing"},
|
||||
format="json",
|
||||
)
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
def test_claim_owned_by_other_gm_409(self):
|
||||
self.active.managed_by = self.gm_other
|
||||
self.active.save(update_fields=["managed_by"])
|
||||
self.client.force_authenticate(self.gm)
|
||||
response = self.client.post(
|
||||
"/api/v1/active-sites/",
|
||||
{"code": "sukawarna"},
|
||||
format="json",
|
||||
)
|
||||
self.assertEqual(response.status_code, 409)
|
||||
|
||||
def test_hapus_unclaims_without_deleting_active_site(self):
|
||||
self.active.managed_by = self.gm
|
||||
self.active.source_site_id = "sukawarna-0001"
|
||||
self.active.save(update_fields=["managed_by", "source_site_id"])
|
||||
hq_site = Site.objects.create(
|
||||
site_name="Sukawarna", user=self.gm, active_site=self.active
|
||||
)
|
||||
self.client.force_authenticate(self.gm)
|
||||
response = self.client.delete(f"/api/v1/active-sites/{self.active.pk}/")
|
||||
self.assertEqual(response.status_code, 204, response.data)
|
||||
self.assertTrue(ActiveSite.objects.filter(pk=self.active.pk).exists())
|
||||
self.active.refresh_from_db()
|
||||
self.assertIsNone(self.active.managed_by_id)
|
||||
self.assertEqual(self.active.api_key, "site-secret")
|
||||
self.assertEqual(self.active.source_site_id, "sukawarna-0001")
|
||||
hq_site.refresh_from_db()
|
||||
self.assertIsNone(hq_site.active_site_id)
|
||||
self.assertTrue(Site.objects.filter(pk=hq_site.pk).exists())
|
||||
+158
-9
@@ -1,49 +1,198 @@
|
||||
from rest_framework import status, viewsets
|
||||
from rest_framework import permissions, status, viewsets
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.response import Response
|
||||
from django.db import IntegrityError
|
||||
|
||||
from apps.accounts.permissions import CanRegisterActiveSite, IsExecutive, can_sync
|
||||
from apps.accounts.scoping import filter_active_sites_queryset
|
||||
from apps.farms.services.active_site_registry import ensure_hq_site_for_active_site
|
||||
from apps.sync.models import ActiveSite
|
||||
from apps.sync.serializers import ActiveSiteSerializer, ActiveSiteWriteSerializer
|
||||
from apps.sync.serializers import (
|
||||
ActiveSiteClaimSerializer,
|
||||
ActiveSiteSerializer,
|
||||
ActiveSiteWriteSerializer,
|
||||
SiteRegisterSerializer,
|
||||
)
|
||||
|
||||
|
||||
class ActiveSiteViewSet(viewsets.ModelViewSet):
|
||||
"""
|
||||
List/read: scoped (director all; BUH managed GMs; GM own).
|
||||
Create/update/delete: GM and GM Admin.
|
||||
Create (claim): GM and GM Admin claim a site-registered ActiveSite by code.
|
||||
Sync: GM and GM Admin only (stubs until mirror pull is implemented).
|
||||
|
||||
Creating an active site also attaches (or creates) an HQ Site so farm data
|
||||
appears in the executive view list.
|
||||
Site dashboards create/fill ActiveSite via register-from-site (API key auth).
|
||||
Claiming attaches managed_by and ensures an HQ Site for the view list.
|
||||
Destroy (Hapus) unclaims only — does not delete the ActiveSite row.
|
||||
"""
|
||||
|
||||
executive_sync_action = True
|
||||
http_method_names = ["get", "post", "put", "patch", "delete", "head", "options"]
|
||||
|
||||
def get_queryset(self):
|
||||
qs = ActiveSite.objects.select_related("managed_by").all()
|
||||
return filter_active_sites_queryset(qs, self.request.user)
|
||||
|
||||
def get_permissions(self):
|
||||
if self.action == "register_from_site":
|
||||
return [permissions.IsAuthenticated()]
|
||||
if self.action in {"create", "update", "partial_update", "destroy"}:
|
||||
return [CanRegisterActiveSite()]
|
||||
return [IsExecutive()]
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.action in {"create", "update", "partial_update"}:
|
||||
if self.action == "register_from_site":
|
||||
return SiteRegisterSerializer
|
||||
if self.action == "create":
|
||||
return ActiveSiteClaimSerializer
|
||||
if self.action in {"update", "partial_update"}:
|
||||
return ActiveSiteWriteSerializer
|
||||
return ActiveSiteSerializer
|
||||
|
||||
def perform_create(self, serializer):
|
||||
active_site = serializer.save(managed_by=self.request.user)
|
||||
def create(self, request, *args, **kwargs):
|
||||
"""GM claim-by-code: site must have registered first."""
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
code = serializer.validated_data["code"]
|
||||
|
||||
active_site = ActiveSite.objects.filter(code=code).first()
|
||||
if active_site is None:
|
||||
return Response(
|
||||
{
|
||||
"detail": (
|
||||
"Site belum mendaftar dari dasbor site. "
|
||||
"Minta staf site mendaftar dulu dengan kode ini."
|
||||
)
|
||||
},
|
||||
status=status.HTTP_404_NOT_FOUND,
|
||||
)
|
||||
|
||||
owner_id = active_site.managed_by_id
|
||||
if owner_id and owner_id != request.user.pk:
|
||||
return Response(
|
||||
{"detail": "Kode sudah diklaim oleh GM lain."},
|
||||
status=status.HTTP_409_CONFLICT,
|
||||
)
|
||||
|
||||
if owner_id != request.user.pk:
|
||||
active_site.managed_by = request.user
|
||||
active_site.save(update_fields=["managed_by", "updated_at"])
|
||||
|
||||
ensure_hq_site_for_active_site(active_site)
|
||||
return Response(
|
||||
ActiveSiteSerializer(active_site).data,
|
||||
status=status.HTTP_200_OK,
|
||||
)
|
||||
|
||||
def perform_update(self, serializer):
|
||||
active_site = serializer.save()
|
||||
if active_site.is_active:
|
||||
if active_site.is_active and active_site.managed_by_id:
|
||||
ensure_hq_site_for_active_site(active_site)
|
||||
|
||||
def destroy(self, request, *args, **kwargs):
|
||||
"""
|
||||
Hapus from daftar tampilan: unclaim only.
|
||||
|
||||
Keeps the ActiveSite registry row (credentials / source_site_id) so the
|
||||
site dashboard registration is not wiped. Clears managed_by and unlinks
|
||||
any HQ Site.active_site pointer.
|
||||
"""
|
||||
from apps.farms.models import Site
|
||||
|
||||
active_site = self.get_object()
|
||||
Site.objects.filter(active_site=active_site).update(active_site=None)
|
||||
active_site.managed_by = None
|
||||
active_site.save(update_fields=["managed_by", "updated_at"])
|
||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||
|
||||
@action(detail=False, methods=["post"], url_path="register-from-site")
|
||||
def register_from_site(self, request):
|
||||
"""
|
||||
Machine endpoint: site dashboard creates/updates ActiveSite credentials.
|
||||
Does not set managed_by (GM claim is separate).
|
||||
|
||||
One farm site_id maps to at most one ActiveSite. Re-register with a new
|
||||
code updates that row; it never inserts a second ActiveSite.
|
||||
"""
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
data = serializer.validated_data
|
||||
code = data["code"]
|
||||
source_site_id = data["site_id"]
|
||||
|
||||
active_site = ActiveSite.objects.filter(source_site_id=source_site_id).first()
|
||||
if active_site is None:
|
||||
by_code = ActiveSite.objects.filter(code=code).first()
|
||||
if by_code is not None:
|
||||
if by_code.source_site_id and by_code.source_site_id != source_site_id:
|
||||
return Response(
|
||||
{"detail": "Kode sudah dipakai site lain."},
|
||||
status=status.HTTP_409_CONFLICT,
|
||||
)
|
||||
# Legacy row missing source_site_id: attach this site to it.
|
||||
active_site = by_code
|
||||
|
||||
if active_site is None:
|
||||
try:
|
||||
active_site = ActiveSite.objects.create(
|
||||
code=code,
|
||||
name=data["name"],
|
||||
source_site_id=source_site_id,
|
||||
api_base_url=data["api_base_url"],
|
||||
api_key=data["api_key"],
|
||||
managed_by=None,
|
||||
is_active=True,
|
||||
)
|
||||
except IntegrityError:
|
||||
# Concurrent register for same source_site_id or code.
|
||||
active_site = ActiveSite.objects.filter(
|
||||
source_site_id=source_site_id
|
||||
).first() or ActiveSite.objects.filter(code=code).first()
|
||||
if active_site is None:
|
||||
return Response(
|
||||
{"detail": "Gagal mendaftarkan site — coba lagi."},
|
||||
status=status.HTTP_409_CONFLICT,
|
||||
)
|
||||
else:
|
||||
return Response(
|
||||
ActiveSiteSerializer(active_site).data,
|
||||
status=status.HTTP_201_CREATED,
|
||||
)
|
||||
|
||||
if (
|
||||
code != active_site.code
|
||||
and ActiveSite.objects.filter(code=code).exclude(pk=active_site.pk).exists()
|
||||
):
|
||||
return Response(
|
||||
{"detail": "Kode sudah dipakai site lain."},
|
||||
status=status.HTTP_409_CONFLICT,
|
||||
)
|
||||
|
||||
active_site.code = code
|
||||
active_site.name = data["name"]
|
||||
active_site.source_site_id = source_site_id
|
||||
active_site.api_base_url = data["api_base_url"]
|
||||
active_site.api_key = data["api_key"]
|
||||
active_site.is_active = True
|
||||
try:
|
||||
active_site.save(
|
||||
update_fields=[
|
||||
"code",
|
||||
"name",
|
||||
"source_site_id",
|
||||
"api_base_url",
|
||||
"api_key",
|
||||
"is_active",
|
||||
"updated_at",
|
||||
]
|
||||
)
|
||||
except IntegrityError:
|
||||
return Response(
|
||||
{"detail": "Site atau kode bentrok dengan pendaftaran lain."},
|
||||
status=status.HTTP_409_CONFLICT,
|
||||
)
|
||||
return Response(ActiveSiteSerializer(active_site).data, status=status.HTTP_200_OK)
|
||||
|
||||
@action(detail=True, methods=["post"], url_path="sync")
|
||||
def sync(self, request, pk=None):
|
||||
if not can_sync(request.user):
|
||||
|
||||
Reference in new issue
Block a user