feat: v1 APIs done
Signed-off-by: Ameya Shenoy <shenoy.ameya@gmail.com>
This commit is contained in:
parent
d59993286c
commit
d7295d9f31
4 changed files with 271 additions and 7 deletions
|
|
@ -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
263
backend/foldbank/api.py
Normal 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,
|
||||
)
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in a new issue