feat: added clients endpoints
This commit is contained in:
@@ -0,0 +1,14 @@
|
|||||||
|
import typing
|
||||||
|
from uuid import UUID
|
||||||
|
|
||||||
|
from ninja import ModelSchema
|
||||||
|
|
||||||
|
from apps.client.models import Client
|
||||||
|
|
||||||
|
|
||||||
|
class Client(ModelSchema):
|
||||||
|
client_id: UUID
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Client
|
||||||
|
exclude: typing.ClassVar[tuple[str]] = (Client.id.field.name,)
|
||||||
@@ -0,0 +1,156 @@
|
|||||||
|
from http import HTTPStatus as status
|
||||||
|
from django.test import TestCase
|
||||||
|
from django.urls import reverse
|
||||||
|
import json
|
||||||
|
from uuid import uuid4
|
||||||
|
from apps.client.models import Client
|
||||||
|
|
||||||
|
|
||||||
|
class ClientTests(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.client_1 = Client.objects.create(
|
||||||
|
login="testuser1", age=25, location="City1", gender="MALE"
|
||||||
|
)
|
||||||
|
self.client_2 = Client.objects.create(
|
||||||
|
login="testuser2", age=30, location="City2", gender="FEMALE"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.bulk_url = "/clients/bulk"
|
||||||
|
self.get_url = "/clients"
|
||||||
|
|
||||||
|
def test_bulk_create_or_update(self):
|
||||||
|
client_3_id = str(uuid4())
|
||||||
|
client_data = [
|
||||||
|
{
|
||||||
|
"client_id": client_3_id,
|
||||||
|
"login": "newusers",
|
||||||
|
"age": 21,
|
||||||
|
"location": "City1",
|
||||||
|
"gender": "FEMALE",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"client_id": str(self.client_1.id),
|
||||||
|
"login": "updateduser",
|
||||||
|
"age": 26,
|
||||||
|
"location": "City1",
|
||||||
|
"gender": "MALE",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"client_id": client_3_id,
|
||||||
|
"login": "newusersa",
|
||||||
|
"age": 25,
|
||||||
|
"location": "City1",
|
||||||
|
"gender": "FEMALE",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"client_id": client_3_id,
|
||||||
|
"login": "newuser",
|
||||||
|
"age": 22,
|
||||||
|
"location": "City3",
|
||||||
|
"gender": "MALE",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
response = self.client.post(
|
||||||
|
self.bulk_url,
|
||||||
|
data=json.dumps(client_data),
|
||||||
|
content_type="application/json",
|
||||||
|
)
|
||||||
|
client_3 = Client.objects.get(id=client_3_id)
|
||||||
|
self.client_1.refresh_from_db()
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, status.CREATED)
|
||||||
|
self.assertEqual(Client.objects.count(), 3)
|
||||||
|
self.assertEqual(self.client_1.login, "updateduser")
|
||||||
|
self.assertEqual(client_3.location, "City3")
|
||||||
|
self.assertEqual(client_3.login, "newuser")
|
||||||
|
self.assertEqual(len(response.json()), 2)
|
||||||
|
self.assertEqual(response.json()[1]["client_id"], str(client_3.id))
|
||||||
|
self.assertEqual(response.json()[1]["login"], client_3.login)
|
||||||
|
|
||||||
|
def test_bulk_create_invalid_data(self):
|
||||||
|
client_data = [
|
||||||
|
{
|
||||||
|
"client_id": "invalid_uuid",
|
||||||
|
"login": "baduser",
|
||||||
|
"age": 150,
|
||||||
|
"location": "City4",
|
||||||
|
"gender": "UNKNOWN",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
response = self.client.post(
|
||||||
|
self.bulk_url,
|
||||||
|
data=json.dumps(client_data),
|
||||||
|
content_type="application/json",
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, status.BAD_REQUEST)
|
||||||
|
|
||||||
|
def test_duplicate_advertiser_ids(self):
|
||||||
|
adv_id = uuid4()
|
||||||
|
data = [
|
||||||
|
{
|
||||||
|
"client_id": str(adv_id),
|
||||||
|
"login": "baduser",
|
||||||
|
"age": 10,
|
||||||
|
"location": "City4",
|
||||||
|
"gender": "FEMALE",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"client_id": str(adv_id),
|
||||||
|
"login": "Last",
|
||||||
|
"age": 14,
|
||||||
|
"location": "City4",
|
||||||
|
"gender": "MALE",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
response = self.client.post(
|
||||||
|
self.bulk_url, json.dumps(data), content_type="application/json"
|
||||||
|
)
|
||||||
|
client = Client.objects.get(id=adv_id)
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, status.CREATED)
|
||||||
|
self.assertEqual(client.login, "Last")
|
||||||
|
self.assertEqual(client.age, 14)
|
||||||
|
self.assertEqual(client.gender, "MALE")
|
||||||
|
|
||||||
|
def test_invalid_client_id_format(self):
|
||||||
|
client_data = [
|
||||||
|
{
|
||||||
|
"client_id": "invalid_uuid",
|
||||||
|
"login": "baduser",
|
||||||
|
"age": 150,
|
||||||
|
"location": "City4",
|
||||||
|
"gender": "UNKNOWN",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
response = self.client.post(
|
||||||
|
self.bulk_url,
|
||||||
|
json.dumps(client_data),
|
||||||
|
content_type="application/json",
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, status.BAD_REQUEST)
|
||||||
|
|
||||||
|
def test_empty_bulk_request(self):
|
||||||
|
response = self.client.post(
|
||||||
|
self.bulk_url, json.dumps([]), content_type="application/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, status.CREATED)
|
||||||
|
self.assertEqual(len(response.json()), 0)
|
||||||
|
|
||||||
|
def test_get_client_success(self):
|
||||||
|
response = self.client.get(f"{self.get_url}/{self.client_1.id}")
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, status.OK)
|
||||||
|
self.assertEqual(response.json()["login"], self.client_1.login)
|
||||||
|
|
||||||
|
def test_get_client_not_found(self):
|
||||||
|
response = self.client.get(f"{self.get_url}/{uuid4()}")
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, status.NOT_FOUND)
|
||||||
|
|
||||||
|
def test_get_client_invalid_uuid(self):
|
||||||
|
response = self.client.get(f"{self.get_url}/invalid_uuid")
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, status.BAD_REQUEST)
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
from collections import defaultdict
|
||||||
|
from http import HTTPStatus as status
|
||||||
|
from uuid import UUID
|
||||||
|
|
||||||
|
from django.db import transaction
|
||||||
|
from django.http import HttpRequest
|
||||||
|
from django.shortcuts import get_object_or_404
|
||||||
|
from ninja import Router
|
||||||
|
|
||||||
|
from api.v1 import schemas as global_schemas
|
||||||
|
from api.v1.clients import schemas
|
||||||
|
from apps.client.models import Client
|
||||||
|
|
||||||
|
router = Router(tags=["clients"])
|
||||||
|
|
||||||
|
|
||||||
|
@router.post(
|
||||||
|
"/bulk",
|
||||||
|
response={
|
||||||
|
status.CREATED: list[schemas.Client],
|
||||||
|
status.BAD_REQUEST: global_schemas.BadRequestError,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
def bulk_create_or_update(
|
||||||
|
request: HttpRequest, data: list[schemas.Client]
|
||||||
|
) -> tuple[status, list[Client]]:
|
||||||
|
latest_clients = defaultdict(lambda: None)
|
||||||
|
|
||||||
|
for item in reversed(data):
|
||||||
|
Client(id=item.client_id, **item.dict(exclude={"client_id"})).validate(
|
||||||
|
validate_unique=False,
|
||||||
|
validate_constraints=False,
|
||||||
|
)
|
||||||
|
if latest_clients[item.client_id] is None:
|
||||||
|
latest_clients[item.client_id] = item
|
||||||
|
|
||||||
|
unique_clients = reversed(list(latest_clients.values()))
|
||||||
|
|
||||||
|
result = []
|
||||||
|
|
||||||
|
with transaction.atomic():
|
||||||
|
for client in unique_clients:
|
||||||
|
client_instance, _ = Client.objects.update_or_create(
|
||||||
|
id=client.client_id,
|
||||||
|
defaults={**dict(client)},
|
||||||
|
)
|
||||||
|
result.append(client_instance)
|
||||||
|
|
||||||
|
return status.CREATED, result
|
||||||
|
|
||||||
|
|
||||||
|
@router.get(
|
||||||
|
"/{client_id}",
|
||||||
|
response={
|
||||||
|
status.OK: schemas.Client,
|
||||||
|
status.BAD_REQUEST: global_schemas.BadRequestError,
|
||||||
|
status.NOT_FOUND: global_schemas.NotFoundError,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
def get_client(
|
||||||
|
request: HttpRequest, client_id: UUID
|
||||||
|
) -> tuple[status, schemas.Client]:
|
||||||
|
return status.OK, get_object_or_404(Client, id=client_id)
|
||||||
Reference in New Issue
Block a user