Account creation works

This commit is contained in:
Thastertyn 2025-02-05 23:04:12 +01:00
parent 35a9e64a10
commit e28e1027d0
11 changed files with 155 additions and 63 deletions

46
poetry.lock generated
View File

@ -135,6 +135,7 @@ files = [
[package.dependencies] [package.dependencies]
mypy_extensions = ">=1.0.0" mypy_extensions = ">=1.0.0"
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
typing_extensions = ">=4.6.0" typing_extensions = ">=4.6.0"
[package.extras] [package.extras]
@ -265,6 +266,47 @@ postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"]
pymysql = ["pymysql"] pymysql = ["pymysql"]
sqlcipher = ["sqlcipher3_binary"] sqlcipher = ["sqlcipher3_binary"]
[[package]]
name = "tomli"
version = "2.2.1"
description = "A lil' TOML parser"
optional = false
python-versions = ">=3.8"
files = [
{file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"},
{file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"},
{file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"},
{file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"},
{file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"},
{file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"},
{file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"},
{file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"},
{file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"},
{file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"},
{file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"},
{file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"},
{file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"},
{file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"},
{file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"},
{file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"},
{file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"},
{file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"},
{file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"},
{file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"},
{file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"},
{file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"},
{file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"},
{file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"},
{file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"},
{file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"},
{file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"},
{file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"},
{file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"},
{file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"},
{file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"},
{file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"},
]
[[package]] [[package]]
name = "typing-extensions" name = "typing-extensions"
version = "4.12.2" version = "4.12.2"
@ -278,5 +320,5 @@ files = [
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = "^3.12" python-versions = "^3.8"
content-hash = "c924c9033c266a86318eb310c7ef433b346d0ad4ff1574e44f7c8c664653106f" content-hash = "822be0591477b901b0d6f8a34048f570227733ebf8eda1cae6a079ab2f5a1415"

View File

@ -6,7 +6,7 @@ authors = ["Thastertyn <thastertyn@thastertyn.xyz>"]
readme = "README.md" readme = "README.md"
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.6" python = "^3.8"
sqlalchemy = "^2.0.37" sqlalchemy = "^2.0.37"
python-dotenv = "^1.0.1" python-dotenv = "^1.0.1"

View File

@ -75,7 +75,7 @@ class BankNode():
client_socket, address = socket_server.accept() client_socket, address = socket_server.accept()
self.logger.info("%s connected", address[0]) self.logger.info("%s connected", address[0])
process = BankWorker(client_socket, address, self.config.to_dict()) process = BankWorker(client_socket, address, self.config)
process.start() process.start()
def exit_with_error(self): def exit_with_error(self):

View File

@ -3,6 +3,7 @@ from typing import Dict, Callable
from core import Request, Response from core import Request, Response
from core.exceptions import BankNodeError from core.exceptions import BankNodeError
from database.exceptions import DatabaseError
from bank_protocol.commands import (account_balance, from bank_protocol.commands import (account_balance,
account_create, account_create,
@ -36,7 +37,8 @@ class CommandHandler:
raise BankNodeError(f"Unknown command {request.command_code}") raise BankNodeError(f"Unknown command {request.command_code}")
command = self.registered_commands[request.command_code] command = self.registered_commands[request.command_code]
try:
response = command(request, self.config) response = command(request, self.config)
return f"{request.command_code} {response}" return f"{request.command_code} {response}"
except DatabaseError as e:
return f"ER {e.message}"

View File

@ -1,7 +1,16 @@
from core.request import Request from core import Request, Response, BankNodeConfig
from bank_protocol.exceptions import InvalidRequest
from services.account_service import create_account
def account_create(request: Request, config: BankNodeConfig) -> Response:
if request.body is not None:
raise InvalidRequest("Incorrect usage")
account_number = create_account()
return f"{account_number}/{config.ip}"
def account_create(request: Request):
pass
__all__ = ["account_create"] __all__ = ["account_create"]

View File

@ -1,7 +1,8 @@
from core import Request from core import Request, BankNodeConfig
from bank_protocol.exceptions import InvalidRequest from bank_protocol.exceptions import InvalidRequest
def account_deposit(request: Request):
def account_deposit(request: Request, config: BankNodeConfig):
split_body = request.body.split("/") split_body = request.body.split("/")
split_ip = split_body[1].split(" ") split_ip = split_body[1].split(" ")

View File

@ -53,7 +53,7 @@ class DatabaseManager():
@classmethod @classmethod
@contextmanager @contextmanager
def get_session(cls) -> Generator[Session]: def get_session(cls) -> Generator[Session, None, None]:
session = cls._instance.Session() session = cls._instance.Session()
try: try:
yield session yield session

View File

@ -37,4 +37,16 @@ class OutOfAccountSpaceError(DatabaseError):
self.message = message self.message = message
__all__ = ["DatabaseError", "DatabaseConnectionError", "DuplicateEntryError"] class InsufficientBalance(DatabaseError):
def __init__(self, message: str):
super().__init__(message)
self.message = message
class InvalidOperation(DatabaseError):
def __init__(self, message: str):
super().__init__(message)
self.message = message
__all__ = ["DatabaseError", "DatabaseConnectionError", "DuplicateEntryError", "InsufficientBalance", "OutOfAccountSpaceError", "NonexistentAccountError", "EmptyDatabaseConfigError", "InvalidOperation"]

View File

@ -5,7 +5,7 @@ from .base_model import Base
class Account(Base): class Account(Base):
__tablename__ = 'account' __tablename__ = 'account'
__table_args__ = (CheckConstraint('account_number > 10000 and account_number <= 99999'),) __table_args__ = (CheckConstraint('account_number >= 10000 and account_number <= 99999'),)
account_number = Column(Integer, nullable=False, primary_key=True) account_number = Column(Integer, nullable=False, primary_key=True)
balance = Column(Integer) balance = Column(Integer)

View File

@ -1,47 +0,0 @@
from sqlalchemy import func
from models import Account
from database import DatabaseManager
from database.exceptions import OutOfAccountSpaceError, NonexistentAccountError
from utils.constants import MIN_ACCOUNT_NUMBER, MAX_ACCOUNT_NUMBER
def get_next_id() -> int:
with DatabaseManager.get_session() as session:
new_id = session.query(func.max(Account.account_number)).scalar()
new_id = new_id if new_id is not None else MIN_ACCOUNT_NUMBER
if new_id > MAX_ACCOUNT_NUMBER:
raise OutOfAccountSpaceError("Too many users already exist, cannot open new account")
return new_id
def create_account() -> int:
new_id = get_next_id()
with DatabaseManager.get_session() as session:
new_account = Account(account_number=new_id, balance=0)
session.add(new_account)
session.commit()
return new_id
def get_account_balance(account_number: int) -> int:
with DatabaseManager.get_session() as session:
account: Account = session.query(Account).where(Account.account_number == account_number).one_or_none()
if account is NotImplemented:
raise NonexistentAccountError(f"Account with number {account_number} doesn't exist")
return account.balance
def withdraw_from_account():
pass
def deposit_into_account():
pass
def delete_account():
pass

View File

@ -0,0 +1,73 @@
from sqlalchemy import func
from models import Account
from database import DatabaseManager
from database.exceptions import OutOfAccountSpaceError, NonexistentAccountError, InsufficientBalance, InvalidOperation
from utils.constants import MIN_ACCOUNT_NUMBER, MAX_ACCOUNT_NUMBER
def get_next_id() -> int:
with DatabaseManager.get_session() as session:
current_max_id = session.query(func.max(Account.account_number)).scalar()
current_max_id = current_max_id + 1 if current_max_id is not None else MIN_ACCOUNT_NUMBER
if current_max_id > MAX_ACCOUNT_NUMBER:
raise OutOfAccountSpaceError("Too many users already exist, cannot open new account")
return current_max_id
def create_account() -> int:
new_id = get_next_id()
with DatabaseManager.get_session() as session:
new_account = Account(account_number=new_id, balance=0)
session.add(new_account)
session.commit()
return new_id
def get_account_balance(account_number: int) -> int:
with DatabaseManager.get_session() as session:
account: Account = session.query(Account).where(Account.account_number == account_number).one_or_none()
if account is None:
raise NonexistentAccountError(f"Account with number {account_number} doesn't exist")
return account.balance
def withdraw_from_account(account_number: int, amount: int):
modify_balance(account_number, amount, True)
def deposit_into_account(account_number: int, amount: int):
modify_balance(account_number, amount, False)
def modify_balance(account_number: int, amount: int, subtract: bool):
with DatabaseManager.get_session() as session:
account: Account = session.query(Account).where(Account.account_number == account_number).one_or_none()
if account is None:
raise NonexistentAccountError(f"Account with number {account_number} doesn't exist")
if subtract:
account.balance += amount
else:
if account.balance - amount < 0:
raise InsufficientBalance("Not enough funds on account to withdraw this much")
account.balance -= amount
session.commit()
def delete_account(account_number: int):
with DatabaseManager.get_session() as session:
account: Account = session.query(Account).where(Account.account_number == account_number).one_or_none()
if account is None:
raise NonexistentAccountError(f"Account with number {account_number} doesn't exist")
if account.balance > 0:
raise InvalidOperation("Cannot delete an account with leftover funds")
session.delete(account)
session.commit()