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.contrib import admin
from django.urls import path
from foldbank.api import AccountsAPI, TransactionsAPI, UpcomingRecurringPaymentAPI
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:
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_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")),
"to_account": Account.objects.get(user=admin, holders_name="Swiggy"),
"amount": "35",
"amount": "249",
"tag": Tag.objects.get(title="Food & Drinks", sub_category="Swiggy"),
"defaults": {
"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")),
"to_account": Account.objects.get(user=admin, holders_name="Netflix"),
"amount": "35",
"amount": "199",
"tag": Tag.objects.get(title="Subscription", sub_category="Netflix"),
"defaults": {
"created_at": make_aware(datetime.datetime(year=2023, month=1, day=23, hour=23, minute=45)),
}
},
]
# Transaction.objects.all().delete()
for t in transactions:
Transaction.objects.get_or_create(**t)

View file

@ -31,7 +31,6 @@ class CustomAccountManager(BaseUserManager):
return user
# Create your models here.
class User(AbstractBaseUser, PermissionsMixin):
# 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}"
class RecurringPayment(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
from_account = models.ForeignKey(
@ -170,7 +168,6 @@ class RecurringPayment(models.Model):
return f"{self.from_account.holders_name} - {self.to_account.holders_name}"
class RecurringPaymentTransactionLink(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
recurring_payment = models.ForeignKey("RecurringPayment", on_delete=models.PROTECT)