Skip to content

Commit

Permalink
add types and tests for primary ports
Browse files Browse the repository at this point in the history
Co-authored-by: Luis Alejandro Bordo García <bgluiszz@gmail.com>
  • Loading branch information
maany and alebg committed Sep 19, 2023
1 parent c9d0b45 commit 4526be5
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 18 deletions.
20 changes: 14 additions & 6 deletions lib/primary_ports.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,33 @@
from abc import abstractmethod
from typing import Generic
from lib.bac import BaseAbstractClass
from lib.usecase_models import BaseErrorResponseModel, BaseRequestModel, BaseResponseModel
from lib.usecase_models import (
BaseErrorResponseModel,
BaseRequestModel,
BaseResponseModel,
TBaseErrorResponseModel,
TBaseRequestModel,
TBaseResponseModel,
)


class BaseInputPort(BaseAbstractClass):
class BaseInputPort(BaseAbstractClass, Generic[TBaseRequestModel]):
def __init__(self) -> None:
super().__init__()

@abstractmethod
def execute(self, requestModel: BaseRequestModel) -> None:
def execute(self, requestModel: TBaseRequestModel) -> None:
pass


class BaseOutputPort(BaseAbstractClass):
class BaseOutputPort(BaseAbstractClass, Generic[TBaseResponseModel, TBaseErrorResponseModel]):
def __init__(self) -> None:
super().__init__()

@abstractmethod
def presentSuccess(self, responseModel: BaseResponseModel) -> None:
def presentSuccess(self, responseModel: TBaseResponseModel) -> None:
raise NotImplementedError

@abstractmethod
def presentError(self, errorModel: BaseErrorResponseModel) -> None:
def presentError(self, errorModel: TBaseErrorResponseModel) -> None:
raise NotImplementedError
7 changes: 6 additions & 1 deletion lib/usecase_models.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Literal
from typing import Literal, TypeVar
from pydantic import BaseModel


Expand All @@ -19,3 +19,8 @@ class BaseErrorResponseModel(BaseModel):
status: Literal[False] = False
code: int
message: str


TBaseRequestModel = TypeVar("TBaseRequestModel", bound=BaseRequestModel)
TBaseResponseModel = TypeVar("TBaseResponseModel", bound=BaseResponseModel)
TBaseErrorResponseModel = TypeVar("TBaseErrorResponseModel", bound=BaseErrorResponseModel)
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ build-backend = "poetry.core.masonry.api"

[tool.mypy]
python_version = "3.10"
script = true
strict = true
warn_return_any = true
warn_unused_configs = true
ignore_missing_imports = true
plugins = ["pydantic.mypy"]
explicit_package_bases = true
check_untyped_defs = true
exclude = [
"tests",
"tests/types",
]

[tool.black]
Expand Down
23 changes: 14 additions & 9 deletions tests/test_usecase_model.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from typing import Any
from lib.primary_ports import BaseInputPort, BaseOutputPort
from lib.usecase_models import BaseErrorResponseModel, BaseRequestModel, BaseResponseModel

Expand All @@ -11,31 +12,35 @@ class RequestModel(BaseRequestModel):
type: str


class UseCase(BaseInputPort):
def __init__(self, presenter: BaseOutputPort):
class ErrorResponseModel(BaseErrorResponseModel):
error: str


class UseCase(BaseInputPort[RequestModel]):
def __init__(self, presenter: BaseOutputPort[ResponseModel, ErrorResponseModel]) -> None:
super().__init__()
self.presenter = presenter

def execute(self, requestModel: RequestModel):
def execute(self, requestModel: RequestModel) -> None:
self.logger.info(f"Executing {self} with {requestModel}")
responseModel = ResponseModel(name=requestModel.name)
self.logger.info(f"Returning {responseModel}")
self.presenter.presentSuccess(responseModel)


class Presenter(BaseOutputPort):
def __init__(self):
class Presenter(BaseOutputPort[ResponseModel, ErrorResponseModel]):
def __init__(self) -> None:
super().__init__()

def presentSuccess(self, responseModel: BaseErrorResponseModel):
def presentSuccess(self, responseModel: ResponseModel) -> None:
print(f"Success: {responseModel}")

def presentError(self, errorModel: BaseErrorResponseModel):
def presentError(self, errorModel: ErrorResponseModel) -> None:
print(f"Error: {errorModel}")


def test_usecase_models(caplog, capfd):
usecase: BaseInputPort = UseCase(presenter=Presenter())
def test_usecase_models(caplog: Any, capfd: Any) -> None:
usecase: BaseInputPort[RequestModel] = UseCase(presenter=Presenter())
requestModel = RequestModel(name="Test", type="Test")
usecase.execute(requestModel=requestModel)
# capture log output
Expand Down

0 comments on commit 4526be5

Please sign in to comment.