Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion core/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from rest_framework import generics, serializers

from . import exceptions, models
from .models import Listing
from .service import CoreService


Expand Down Expand Up @@ -170,7 +171,7 @@ def validate(self, attrs):
# Principal and Repayment Validation
if attrs["principal"] > attrs["repayment_amount"]:
raise serializers.ValidationError(
{"detail": "Principal should be less tha Repayment amount"}
{"detail": "Principal should be less than Repayment amount"}
)

# validate offer token
Expand Down Expand Up @@ -234,3 +235,18 @@ def validate(self, attrs):
def save(self):
# delete the offer
CoreService.cancel_offer(self.validated_data["offer_id"])


class ListingSerializer(serializers.ModelSerializer):
class Meta:
model = Listing
fields = [
"id",
"nft_contract_address",
"nft_token_id",
"user",
"borrow_amount",
"duration",
"created_at",
"status",
]
26 changes: 26 additions & 0 deletions core/tests/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,29 @@ class Meta:
@factory.lazy_attribute
def contract_address(self):
return "0x" + "".join(random.choices("0123456789abcdef", k=60))

token_decimal = factory.Faker("pyint", min_value=6, max_value=18)


class UserFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.User

public_key = factory.LazyFunction(
lambda: "0x" + "".join(random.choices("0123456789abcdef", k=60))
)


class ListingFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.Listing

user = factory.SubFactory("core.factories.UserFactory")
nft_contract_address = factory.LazyFunction(
lambda: "0x" + "".join(random.choices("0123456789abcdef", k=60))
)
nft_token_id = factory.Sequence(lambda n: n)
borrow_amount = factory.Faker("pyint", min_value=100, max_value=10000)
repayment_amount = factory.Faker("pyint", min_value=110, max_value=11000)
duration = factory.Faker("pyint", min_value=1, max_value=365)
status = models.ListingStatus.OPEN
File renamed without changes.
87 changes: 87 additions & 0 deletions core/tests/test_listings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import pytest
from django.urls import reverse
from rest_framework.test import APIClient

from core.models import ListingStatus
from core.tests import factories


@pytest.mark.django_db
def test_list_active_listings():
client = APIClient()
url = reverse("listing-list")

user = factories.UserFactory()
active_listing = factories.ListingFactory(user=user, status=ListingStatus.OPEN)
factories.ListingFactory(user=user, status=ListingStatus.CLOSED)

response = client.get(url)
assert response.status_code == 200

data = response.json()
results = data["results"]
assert len(results) == 1

listing = results[0]
assert listing["id"] == str(active_listing.id)
assert listing["nft_contract_address"] == active_listing.nft_contract_address
assert listing["nft_token_id"] == active_listing.nft_token_id
assert listing["user"] == str(active_listing.user.id)
assert listing["borrow_amount"] == active_listing.borrow_amount
assert listing["duration"] == active_listing.duration
assert listing["status"] == active_listing.status


@pytest.mark.django_db
def test_listings_filter_by_collateral_contract():
client = APIClient()
url = reverse("listing-list")

user = factories.UserFactory()
factories.ListingFactory(
user=user, nft_contract_address="0xFilterThis", status=ListingStatus.OPEN
)

response = client.get(url, {"collateral_contract": "0xFilterThis"})
assert response.status_code == 200

data = response.json()
results = data["results"]
assert len(results) == 1
assert results[0]["nft_contract_address"] == "0xFilterThis"


@pytest.mark.django_db
def test_listings_filter_by_borrower_address():
client = APIClient()
url = reverse("listing-list")

user = factories.UserFactory(public_key="0xBorrowerFilter")

response = client.get(url, {"borrower_address": "0xBorrowerFilter"})
assert response.status_code == 200

data = response.json()
results = data["results"]
assert len(results) == 1
assert results[0]["user"] == str(user.id)


@pytest.mark.django_db
def test_listings_pagination_structure():
client = APIClient()
url = reverse("listing-list")

user = factories.UserFactory()
for _ in range(5):
factories.ListingFactory(user=user, status=ListingStatus.OPEN)

response = client.get(url)
assert response.status_code == 200

data = response.json()
assert "results" in data
assert isinstance(data["results"], list)
assert "count" in data
assert "next" in data
assert "previous" in data
5 changes: 5 additions & 0 deletions core/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@
views.SignInAPIView.as_view(),
name="signin",
),
path(
"listings/",
views.ListingListAPIView.as_view(),
name="listing-list",
),
path("offer/create/", views.OfferCreateAPIView.as_view(), name="create-offer"),
path("offer/cancel/", views.OfferCancelAPIView.as_view(), name="cancel-offer"),
]
32 changes: 31 additions & 1 deletion core/views.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
# Create your views here.
from rest_framework import status
from rest_framework import filters, status
from rest_framework.generics import GenericAPIView, ListAPIView
from rest_framework.pagination import PageNumberPagination
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response

from . import models, serializers
from .models import Listing, ListingStatus
from .serializers import ListingSerializer


class AcceptedNFTListAPIView(ListAPIView):
Expand Down Expand Up @@ -50,3 +53,30 @@ def post(self, request):
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(status=status.HTTP_204_NO_CONTENT)


class ListingPagination(PageNumberPagination):
page_size = 10
page_size_query_param = "page_size"
max_page_size = 100


class ListingListAPIView(ListAPIView):
serializer_class = ListingSerializer
pagination_class = ListingPagination
filter_backends = [filters.SearchFilter]
search_fields = ["nft_contract_address", "user__public_key"]

def get_queryset(self):
queryset = Listing.objects.filter(status=ListingStatus.OPEN).order_by(
"-created_at"
)
collateral_contract = self.request.query_params.get("collateral_contract")
borrower_address = self.request.query_params.get("borrower_address")

if collateral_contract:
queryset = queryset.filter(nft_contract_address__iexact=collateral_contract)
if borrower_address:
queryset = queryset.filter(user__public_key__iexact=borrower_address)

return queryset
Loading