Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bankapi migrations | fix bugs #58

Merged
merged 2 commits into from
Mar 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ jobs:
pip install -e .[ci-tests]
- name: Test with pytest
run: |
pytest tests --cov
pytest tests --cov="../src"
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v4.0.1
with:
Expand Down
4 changes: 3 additions & 1 deletion src/costy/adapters/bankapi/bankapi.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from datetime import datetime

from adaptix import Retort, name_mapping
from httpx import AsyncClient
Expand Down Expand Up @@ -86,4 +87,5 @@ async def update_bankapis(self, bankapis: list[BankAPI]) -> None:

async def read_bank_operations(self, bankapi: BankAPI) -> list[BankOperationDTO]:
bank_gateway = self._bank_gateways[bankapi.name]
return await bank_gateway.fetch_operations(bankapi.access_data, bankapi.user_id)
from_time = datetime.fromtimestamp(bankapi.updated_at) if bankapi.updated_at else None
return await bank_gateway.fetch_operations(bankapi.access_data, bankapi.user_id, from_time)
3 changes: 2 additions & 1 deletion src/costy/adapters/bankapi/monobank.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def __init__(
retort: Retort
):
self._web_session = web_session
self._bank_conf = bank_conf
self._bank_conf = bank_conf["monobank"]
self._retort = retort.extend(recipe=[loader(P[Operation].id, lambda _: None)])

async def fetch_operations(
Expand Down Expand Up @@ -47,6 +47,7 @@ async def fetch_operations(

for operation in total_operations:
operation["user_id"] = user_id
operation["bank_name"] = "monobank"

loaded_operations = self._retort.load(total_operations, list[Operation])
return [
Expand Down
6 changes: 3 additions & 3 deletions src/costy/application/common/operation/dto.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ class ListOperationDTO:

@dataclass(kw_only=True)
class UpdateOperationData:
amount: int | type[Sentinel] = Sentinel
amount: int | None = None
description: str | None | type[Sentinel] = Sentinel
time: int | type[Sentinel] = Sentinel
category_id: CategoryId | type[Sentinel] = Sentinel
time: int | None = None
category_id: CategoryId | None | type[Sentinel] = Sentinel


@dataclass
Expand Down
18 changes: 12 additions & 6 deletions src/costy/domain/services/operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,21 @@ def create(
def update(
self,
operation: Operation,
amount: int | type[Sentinel] = Sentinel,
amount: int | None = None,
description: str | None | type[Sentinel] = Sentinel,
time: int | type[Sentinel] = Sentinel,
category_id: CategoryId | type[Sentinel] = Sentinel,
time: int | None = None,
category_id: CategoryId | None | type[Sentinel] = Sentinel,
):
exclude_params = ['self', 'operation', 'exclude_params']
exclude_params = ('self', 'operation', 'exclude_params', 'sentinel_params')
sentinel_params = ('description', 'category_id')
params = {
name: value for name, value in locals().items()
if value is not Sentinel and name not in exclude_params
name: value for name, value in locals().items() if
(name not in exclude_params) and
(
(name in sentinel_params and value is not Sentinel) or
(name not in sentinel_params and value is not None)
)

}
for name, value in params.items():
setattr(operation, name, value)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
"""bankapi

Revision ID: 8f0d001e35c6
Revises: 14d9cdbdf029
Create Date: 2024-03-14 19:20:38.568784

"""
from typing import Sequence, Union

import sqlalchemy as sa
from alembic import op

# revision identifiers, used by Alembic.
revision: str = '8f0d001e35c6'
down_revision: Union[str, None] = '14d9cdbdf029'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('bankapis',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(), nullable=True),
sa.Column('access_data', sa.JSON(), nullable=True),
sa.Column('updated_at', sa.Integer(), nullable=True),
sa.Column('user_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.add_column('categories', sa.Column('mcc', sa.Integer(), nullable=True))
op.add_column('operations', sa.Column('bank_name', sa.String(), nullable=True))
# ### end Alembic commands ###


def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('operations', 'bank_name')
op.drop_column('categories', 'mcc')
op.drop_table('bankapis')
# ### end Alembic commands ###
3 changes: 2 additions & 1 deletion src/costy/main/web.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,5 +78,6 @@ async def finalization():
on_shutdown=[finalization],
exception_handlers={
BaseError: base_error_handler
}
},
debug=True
)
1 change: 1 addition & 0 deletions src/costy/presentation/api/routers/authenticate.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

class AuthenticationController(Controller):
path = "/auth"
tags = ("Authentication",)

@post(status_code=200)
async def login(self, ioc: InteractorFactory, data: LoginInputDTO) -> Response[dict[str, str]]:
Expand Down
1 change: 1 addition & 0 deletions src/costy/presentation/api/routers/bankapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

class BankAPIController(Controller):
path = "bankapi"
tags = ("Banks integration",)

@get()
async def get_bankapi_list(
Expand Down
1 change: 1 addition & 0 deletions src/costy/presentation/api/routers/category.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

class CategoryController(Controller):
path = '/categories'
tags = ("Categories",)

@get()
async def get_list_categories(
Expand Down
22 changes: 21 additions & 1 deletion src/costy/presentation/api/routers/operation.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from dataclasses import dataclass

from litestar import Controller, delete, get, post, put

from costy.application.common.id_provider import IdProvider
Expand All @@ -7,12 +9,24 @@
UpdateOperationData,
UpdateOperationDTO,
)
from costy.domain.models.category import CategoryId
from costy.domain.models.operation import Operation, OperationId
from costy.domain.sentinel import Sentinel
from costy.presentation.interactor_factory import InteractorFactory


@dataclass(kw_only=True)
class UpdateOperationPureData:
"""Dataclass without user defined types for OpenAPI"""
amount: int | None = None
description: str | None = "" # Sentinel value
time: int | None = None
category_id: CategoryId | None = None


class OperationController(Controller):
path = '/operations'
tags = ("Operations",)

@get()
async def get_list_operations(
Expand Down Expand Up @@ -52,8 +66,14 @@ async def update_operation(
operation_id: int,
ioc: InteractorFactory,
id_provider: IdProvider,
data: UpdateOperationData,
pure_data: UpdateOperationPureData,
) -> None:
async with ioc.update_operation(id_provider) as update_operation:
data = UpdateOperationData(
amount=pure_data.amount,
description=pure_data.description if pure_data.description != "" else Sentinel,
time=pure_data.time,
category_id=pure_data.category_id if pure_data.category_id is not None else Sentinel
)
request_data = UpdateOperationDTO(OperationId(operation_id), data)
await update_operation(request_data)
1 change: 1 addition & 0 deletions src/costy/presentation/api/routers/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

class UserController(Controller):
path = "/users"
tags = ("Users",)

@post()
async def register(self, ioc: InteractorFactory, data: NewUserDTO) -> dict[str, UserId]:
Expand Down
2 changes: 1 addition & 1 deletion tests/common/adapters.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ async def monobank_adapter(web_session, retort) -> MonobankGateway:
with open(str(resources.files("costy.adapters.bankapi") / "_banks.json"), "r") as f:
banks = json.load(f)

return MonobankGateway(web_session, banks["monobank"], retort)
return MonobankGateway(web_session, banks, retort)


@fixture
Expand Down
Loading