mirror of
https://github.com/dancojocaru2000/foxbank.git
synced 2025-02-22 23:39:36 +02:00
Implemented initial /transactions endpoint support
This commit is contained in:
parent
18fe6e9355
commit
5da66e520b
7 changed files with 126 additions and 12 deletions
3
server/.vscode/launch.json
vendored
3
server/.vscode/launch.json
vendored
|
@ -11,7 +11,8 @@
|
|||
"module": "flask",
|
||||
"env": {
|
||||
"FLASK_APP": "server.py",
|
||||
"FLASK_ENV": "development"
|
||||
"FLASK_ENV": "development",
|
||||
"FLASK_RUN_PORT": "5001"
|
||||
},
|
||||
"args": [
|
||||
"run",
|
||||
|
|
|
@ -3,6 +3,7 @@ from flask_smorest import Api
|
|||
|
||||
from .accounts import bp as acc_bp
|
||||
from .login import bp as login_bp
|
||||
from .transactions import bp as transactions_bp
|
||||
|
||||
class ApiWithErr(Api):
|
||||
def handle_http_exception(self, error):
|
||||
|
@ -27,3 +28,4 @@ def init_apis(app: Flask):
|
|||
})
|
||||
api.register_blueprint(login_bp, url_prefix='/login')
|
||||
api.register_blueprint(acc_bp, url_prefix='/accounts')
|
||||
api.register_blueprint(transactions_bp, url_prefix='/transactions')
|
||||
|
|
|
@ -13,12 +13,10 @@ bp = Blueprint('accounts', __name__, description='Bank Accounts operations')
|
|||
VALID_CURRENCIES = ['RON', 'EUR', 'USD']
|
||||
ACCOUNT_TYPES = ['Checking', 'Savings']
|
||||
|
||||
class MetaCurrenciesSchema(Schema):
|
||||
status = fields.Constant('success')
|
||||
class MetaCurrenciesSchema(returns.SuccessSchema):
|
||||
currencies = fields.List(fields.Str())
|
||||
|
||||
class MetaAccountTypesSchema(Schema):
|
||||
status = fields.Constant('success')
|
||||
class MetaAccountTypesSchema(returns.SuccessSchema):
|
||||
account_types = fields.List(fields.Str(), data_key='accountTypes')
|
||||
|
||||
@bp.get('/meta/currencies')
|
||||
|
@ -36,7 +34,7 @@ def get_valid_account_types():
|
|||
|
||||
|
||||
class AccountResponseSchema(returns.SuccessSchema):
|
||||
account = fields.Nested(Account.Schema)
|
||||
account = fields.Nested(Account.AccountSchema)
|
||||
|
||||
|
||||
@bp.get('/<int:account_id>')
|
||||
|
@ -79,7 +77,7 @@ class AccountsList(MethodView):
|
|||
custom_name = fields.String(data_key='customName')
|
||||
|
||||
class CreateAccountResponseSchema(returns.SuccessSchema):
|
||||
account = fields.Nested(Account.Schema)
|
||||
account = fields.Nested(Account.AccountSchema)
|
||||
|
||||
@ensure_logged_in
|
||||
@bp.response(401, returns.ErrorSchema, description='Login failure')
|
||||
|
@ -99,7 +97,7 @@ class AccountsList(MethodView):
|
|||
return returns.success(account=account.to_json())
|
||||
|
||||
class AccountsResponseSchema(returns.SuccessSchema):
|
||||
accounts = fields.List(fields.Nested(Account.Schema))
|
||||
accounts = fields.List(fields.Nested(Account.AccountSchema))
|
||||
|
||||
@ensure_logged_in
|
||||
@bp.response(401, returns.ErrorSchema, description='Login failure')
|
||||
|
|
46
server/foxbank_server/apis/transactions.py
Normal file
46
server/foxbank_server/apis/transactions.py
Normal file
|
@ -0,0 +1,46 @@
|
|||
from flask.views import MethodView
|
||||
from flask_smorest import Blueprint
|
||||
from marshmallow import Schema, fields
|
||||
|
||||
from ..decorators import ensure_logged_in
|
||||
from ..models import Transaction
|
||||
from ..db_utils import get_transactions
|
||||
from .. import returns
|
||||
|
||||
bp = Blueprint('transactions', __name__, description='Bank transfers and other transactions')
|
||||
|
||||
@bp.route('/')
|
||||
class TransactionsList(MethodView):
|
||||
class TransactionsParams(Schema):
|
||||
account_id = fields.Int(min=1)
|
||||
|
||||
class TransactionsGetResponse(returns.SuccessSchema):
|
||||
transactions = fields.List(fields.Nested(Transaction.TransactionSchema))
|
||||
|
||||
@ensure_logged_in
|
||||
@bp.response(401, returns.ErrorSchema, description='Login failure')
|
||||
@bp.doc(security=[{'Token': []}])
|
||||
@bp.arguments(TransactionsParams, as_kwargs=True, location='query')
|
||||
@bp.response(200, TransactionsGetResponse)
|
||||
def get(self, account_id: int):
|
||||
"""Get transactions for a certain account"""
|
||||
return returns.success(
|
||||
transactions=[t.to_json() for t in get_transactions(account_id)]
|
||||
)
|
||||
|
||||
class TransactionsCreateParams(Schema):
|
||||
account_id = fields.Int(min=1)
|
||||
destination_iban = fields.Str()
|
||||
amount = fields.Int(min=1)
|
||||
|
||||
class TransactionsCreateResponse(returns.SuccessSchema):
|
||||
transaction = fields.Nested(Transaction.TransactionSchema)
|
||||
|
||||
@ensure_logged_in
|
||||
@bp.response(401, returns.ErrorSchema, description='Login failure')
|
||||
@bp.doc(security=[{'Token': []}])
|
||||
@bp.arguments(TransactionsCreateParams)
|
||||
@bp.response(200, TransactionsCreateResponse)
|
||||
def post(self, account_id: int, destination_iban: str, amount: int):
|
||||
"""Create a send_transfer transaction"""
|
||||
raise NotImplementedError()
|
|
@ -158,4 +158,26 @@ class Module(ModuleType):
|
|||
|
||||
self.db.commit()
|
||||
|
||||
@get_db
|
||||
def get_transactions(self, account_id: int) -> list[models.Transaction]:
|
||||
cur = self.db.cursor()
|
||||
cur.execute(
|
||||
'select transaction_id from accounts_transactions where account_id = ?',
|
||||
(account_id,),
|
||||
)
|
||||
|
||||
transactions = []
|
||||
for tid in (row['transaction_id'] for row in cur.fetchall()):
|
||||
cur.execute(
|
||||
'select * from transactions where id = ?',
|
||||
(tid,),
|
||||
)
|
||||
|
||||
db_res = cur.fetchone()
|
||||
if db_res is None:
|
||||
continue
|
||||
transactions.append(models.Transaction.from_query(db_res))
|
||||
|
||||
return transactions
|
||||
|
||||
sys.modules[__name__] = Module(__name__)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from dataclasses import dataclass
|
||||
from marshmallow import Schema, fields
|
||||
from datetime import datetime
|
||||
|
||||
@dataclass
|
||||
class User:
|
||||
|
@ -51,7 +52,7 @@ class Account:
|
|||
account_type: str
|
||||
custom_name: str
|
||||
|
||||
class Schema(Schema):
|
||||
class AccountSchema(Schema):
|
||||
id = fields.Int(required=False)
|
||||
iban = fields.Str()
|
||||
currency = fields.Str()
|
||||
|
@ -82,3 +83,47 @@ class Account:
|
|||
@classmethod
|
||||
def from_query(cls, query_result):
|
||||
return cls(*query_result)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Transaction:
|
||||
id: int
|
||||
date_time: datetime
|
||||
other_party: str
|
||||
status: str
|
||||
transaction_type: str
|
||||
extra: str
|
||||
|
||||
class TransactionSchema(Schema):
|
||||
id = fields.Int(required=False)
|
||||
date_time = fields.DateTime(data_key='datetime')
|
||||
other_party = fields.Str(data_key='otherParty')
|
||||
transaction_type = fields.Str(data_key='transactionType')
|
||||
extra = fields.Str()
|
||||
|
||||
@staticmethod
|
||||
def new_transaction(date_time: datetime, other_party: str, status: str, transaction_type: str, extra: str = '') -> 'Account':
|
||||
return Transaction(
|
||||
id=-1,
|
||||
date_time=date_time,
|
||||
other_party=other_party,
|
||||
status=status,
|
||||
transaction_type=transaction_type,
|
||||
extra=extra,
|
||||
)
|
||||
|
||||
def to_json(self, include_id=True):
|
||||
result = {
|
||||
'datetime': self.date_time.isoformat(),
|
||||
'otherParty': self.other_party,
|
||||
'status': self.status,
|
||||
'transactionType': self.transaction_type,
|
||||
'extra': self.extra,
|
||||
}
|
||||
if include_id:
|
||||
result['id'] = self.id
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def from_query(cls, query_result):
|
||||
return cls(*query_result)
|
||||
|
|
|
@ -74,15 +74,15 @@ def success(http_status: Any = _HTTPStatus.OK, /, **kargs):
|
|||
|
||||
# Schemas
|
||||
|
||||
from marshmallow import Schema, fields
|
||||
from marshmallow import Schema, fields, validate
|
||||
|
||||
class ErrorSchema(Schema):
|
||||
status = fields.Constant('error')
|
||||
status = fields.Str(default='error', validate=validate.Equal('error'))
|
||||
code = fields.Str()
|
||||
message = fields.Str(required=False)
|
||||
|
||||
class SuccessSchema(Schema):
|
||||
status = fields.Constant('success')
|
||||
status = fields.Str(default='success', validate=validate.Equal('success'))
|
||||
|
||||
# smorest
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue