feat: v1 APIs done

Signed-off-by: Ameya Shenoy <shenoy.ameya@gmail.com>
This commit is contained in:
Ameya Shenoy 2023-03-20 19:20:20 +05:30
parent d59993286c
commit d7295d9f31
Signed by: codingcoffee
GPG key ID: EEC8EA855D61CEEC
4 changed files with 271 additions and 7 deletions

View file

@ -17,13 +17,16 @@ from django.conf import settings
from django.conf.urls.static import static from django.conf.urls.static import static
from django.contrib import admin from django.contrib import admin
from django.urls import path from django.urls import path
from foldbank.api import AccountsAPI, TransactionsAPI, UpcomingRecurringPaymentAPI
urlpatterns = [ urlpatterns = [
path('admin/', admin.site.urls), path("admin/", admin.site.urls),
path("api/v1/transactions/", TransactionsAPI.as_view()),
path("api/v1/accounts/", AccountsAPI.as_view()),
path("api/v1/recurringPayments/upcoming/", UpcomingRecurringPaymentAPI.as_view()),
] ]
if settings.DEBUG: if settings.DEBUG:
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

263
backend/foldbank/api.py Normal file
View file

@ -0,0 +1,263 @@
import logging
from collections import OrderedDict
from random import randrange
from django.conf import settings
from django.contrib.auth import get_user_model
from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema
from foldbank.models import Account, RecurringPayment, Transaction, User
from rest_framework import generics, serializers
from rest_framework.pagination import LimitOffsetPagination as _LimitOffsetPagination
from rest_framework.response import Response
UserModel = get_user_model()
logger = logging.getLogger(__name__)
def create_serializer_class(name, fields):
return type(name, (serializers.Serializer,), fields)
def inline_serializer(*, fields, data=None, **kwargs):
serializer_class = create_serializer_class(name="", fields=fields)
if data is not None:
return serializer_class(data=data, **kwargs)
return serializer_class(**kwargs)
def get_paginated_response(
*,
pagination_class,
serializer_class,
queryset,
request,
view,
mod_func=None,
):
paginator = pagination_class()
page = paginator.paginate_queryset(queryset, request, view=view)
if page is not None:
if mod_func:
page = mod_func(page)
serializer = serializer_class(page, many=True)
return paginator.get_paginated_response(serializer.data)
serializer = serializer_class(queryset, many=True)
return Response(serializer.data)
class LimitOffsetPagination(_LimitOffsetPagination):
default_limit = 10
max_limit = 50
def get_paginated_data(self, data):
return OrderedDict(
[
("limit", self.limit),
("offset", self.offset),
("count", self.count),
("next", self.get_next_link()),
("previous", self.get_previous_link()),
("results", data),
]
)
def get_paginated_response(self, data):
"""
We redefine this method in order to return `limit` and `offset`.
This is used by the frontend to construct the pagination itself.
"""
return Response(
OrderedDict(
[
("limit", self.limit),
("offset", self.offset),
("count", self.count),
("next", self.get_next_link()),
("previous", self.get_previous_link()),
("results", data),
]
),
)
def get_transactions(user):
return Transaction.objects.filter(from_account__user=user).order_by("created_at")
class TransactionsAPI(generics.ListAPIView):
authentication_classes = []
permission_classes = []
class Pagination(LimitOffsetPagination):
pass
class TransactionsInputSerializer(serializers.Serializer):
bank_id = serializers.UUIDField(default=None)
class TransactionsOutputSerializer(serializers.Serializer):
id = serializers.UUIDField()
created_at = serializers.DateTimeField()
amount = serializers.IntegerField()
tag = inline_serializer(
required=True,
fields={
"title": serializers.CharField(required=True),
"icon_type": serializers.CharField(required=True),
},
)
to_account = inline_serializer(
required=True,
fields={
"holders_name": serializers.CharField(required=True),
},
)
success_response = openapi.Response(
"Success Response", TransactionsOutputSerializer
)
@swagger_auto_schema(
query_serializer=TransactionsInputSerializer,
operation_summary="Transactions List",
responses={
200: success_response,
},
tags=["Transactions"],
)
def get(self, request):
qs = []
serializer = self.TransactionsInputSerializer(data=request.query_params)
serializer.is_valid(raise_exception=True)
# TODO: hardcoding nishant to mitigate user login
user = User.objects.get(username="nishant")
qs = get_transactions(user=user)
def modify_qs(qs):
return qs
return get_paginated_response(
pagination_class=self.Pagination,
serializer_class=self.TransactionsOutputSerializer,
queryset=qs,
request=request,
view=self,
mod_func=modify_qs,
)
def get_bank_accounts(user):
return Account.objects.filter(user=user)
class AccountsAPI(generics.ListAPIView):
authentication_classes = []
permission_classes = []
class Pagination(LimitOffsetPagination):
pass
class AccountsOutputSerializer(serializers.Serializer):
id = serializers.UUIDField()
created_at = serializers.DateTimeField()
balance = serializers.IntegerField()
bank = inline_serializer(
required=True,
fields={
"name": serializers.CharField(required=True),
"logo": serializers.CharField(required=True),
},
)
account_number = serializers.CharField()
ifsc_code = serializers.CharField()
swift_bic = serializers.CharField()
holders_name = serializers.CharField()
success_response = openapi.Response("Success Response", AccountsOutputSerializer)
@swagger_auto_schema(
operation_summary="Accounts List",
responses={
200: success_response,
},
tags=["Accounts"],
)
def get(self, request):
qs = []
# TODO: hardcoding nishant to mitigate user login
user = User.objects.get(username="nishant")
qs = get_bank_accounts(user=user)
def modify_qs(qs):
return qs
return get_paginated_response(
pagination_class=self.Pagination,
serializer_class=self.AccountsOutputSerializer,
queryset=qs,
request=request,
view=self,
mod_func=modify_qs,
)
def get_upcoming_recurring_payments(user):
return RecurringPayment.objects.filter(from_account__user=user)
class UpcomingRecurringPaymentAPI(generics.ListAPIView):
authentication_classes = []
permission_classes = []
class Pagination(LimitOffsetPagination):
pass
class UpcomingRecurringPaymentOutputSerializer(serializers.Serializer):
id = serializers.UUIDField()
amount = serializers.IntegerField()
due_on = serializers.DateTimeField()
frequency = serializers.DurationField()
to_account = inline_serializer(
required=True,
fields={
"holders_name": serializers.CharField(required=True),
},
)
success_response = openapi.Response("Success Response", UpcomingRecurringPaymentOutputSerializer)
@swagger_auto_schema(
operation_summary="Recurring Payments List",
responses={
200: success_response,
},
tags=["Recurring Payments"],
)
def get(self, request):
qs = []
# TODO: hardcoding nishant to mitigate user login
user = User.objects.get(username="nishant")
qs = get_upcoming_recurring_payments(user=user)
def modify_qs(qs):
return qs
return get_paginated_response(
pagination_class=self.Pagination,
serializer_class=self.UpcomingRecurringPaymentOutputSerializer,
queryset=qs,
request=request,
view=self,
mod_func=modify_qs,
)

View file

@ -303,7 +303,7 @@ class Command(BaseCommand):
{ {
"from_account": Account.objects.get(user=nishant, bank=Bank.objects.get(name="Axis Bank")), "from_account": Account.objects.get(user=nishant, bank=Bank.objects.get(name="Axis Bank")),
"to_account": Account.objects.get(user=admin, holders_name="Swiggy"), "to_account": Account.objects.get(user=admin, holders_name="Swiggy"),
"amount": "35", "amount": "249",
"tag": Tag.objects.get(title="Food & Drinks", sub_category="Swiggy"), "tag": Tag.objects.get(title="Food & Drinks", sub_category="Swiggy"),
"defaults": { "defaults": {
"created_at": make_aware(datetime.datetime(year=2023, month=1, day=1, hour=11, minute=17)), "created_at": make_aware(datetime.datetime(year=2023, month=1, day=1, hour=11, minute=17)),
@ -312,13 +312,14 @@ class Command(BaseCommand):
{ {
"from_account": Account.objects.get(user=nishant, bank=Bank.objects.get(name="Axis Bank")), "from_account": Account.objects.get(user=nishant, bank=Bank.objects.get(name="Axis Bank")),
"to_account": Account.objects.get(user=admin, holders_name="Netflix"), "to_account": Account.objects.get(user=admin, holders_name="Netflix"),
"amount": "35", "amount": "199",
"tag": Tag.objects.get(title="Subscription", sub_category="Netflix"), "tag": Tag.objects.get(title="Subscription", sub_category="Netflix"),
"defaults": { "defaults": {
"created_at": make_aware(datetime.datetime(year=2023, month=1, day=23, hour=23, minute=45)), "created_at": make_aware(datetime.datetime(year=2023, month=1, day=23, hour=23, minute=45)),
} }
}, },
] ]
# Transaction.objects.all().delete()
for t in transactions: for t in transactions:
Transaction.objects.get_or_create(**t) Transaction.objects.get_or_create(**t)

View file

@ -31,7 +31,6 @@ class CustomAccountManager(BaseUserManager):
return user return user
# Create your models here. # Create your models here.
class User(AbstractBaseUser, PermissionsMixin): class User(AbstractBaseUser, PermissionsMixin):
# TODO: make username case insensitive yet retain case sensitivity # TODO: make username case insensitive yet retain case sensitivity
@ -151,7 +150,6 @@ class Transaction(models.Model):
return f"{self.from_account.holders_name} - {self.to_account.holders_name}" return f"{self.from_account.holders_name} - {self.to_account.holders_name}"
class RecurringPayment(models.Model): class RecurringPayment(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
from_account = models.ForeignKey( from_account = models.ForeignKey(
@ -170,7 +168,6 @@ class RecurringPayment(models.Model):
return f"{self.from_account.holders_name} - {self.to_account.holders_name}" return f"{self.from_account.holders_name} - {self.to_account.holders_name}"
class RecurringPaymentTransactionLink(models.Model): class RecurringPaymentTransactionLink(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
recurring_payment = models.ForeignKey("RecurringPayment", on_delete=models.PROTECT) recurring_payment = models.ForeignKey("RecurringPayment", on_delete=models.PROTECT)