Skip to content

Commit

Permalink
Merge pull request #15 from davidjnevin/add_fastapi_cleanchat_endpoint
Browse files Browse the repository at this point in the history
Add fastapi cleanchat endpoint
  • Loading branch information
davidjnevin authored Jan 30, 2024
2 parents a825681 + dd7daf3 commit fe6d9df
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 14 deletions.
1 change: 1 addition & 0 deletions src/chatcleaner/adapters/entrypoints/api/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ def start_application():
app_.container = container
include_router(app_)
configure_cors(app_)

# start orm mappers
try:
start_mappers()
Expand Down
30 changes: 26 additions & 4 deletions src/chatcleaner/adapters/entrypoints/api/v1/route_cleaning.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
import json
from typing import Any
from typing import Any, Union

from dependency_injector.wiring import Provide, inject
from fastapi import APIRouter, Depends, Response

from chatcleaner.adapters.entrypoints.api.v1.schemas_cleaning import (
AllCleaningsOut,
CleanedChatOut,
CleaningIn,
CleaningNotFound,
SingleCleaningOut,
)
from chatcleaner.domain.ports.use_cases.clean import CleanUseCaseInterface

router = APIRouter()


@router.get("/cleanings", response_model=None)
@router.get("/cleanings", response_model=AllCleaningsOut)
@inject
async def get_all_cleaning(
use_case: CleanUseCaseInterface = Depends(Provide["cleaning_use_case"]),
Expand All @@ -20,10 +27,25 @@ async def get_all_cleaning(
)


@router.get("/cleanings/{uuid}", response_model=None)
@router.get(
"/cleanings/{uuid}", response_model=Union[SingleCleaningOut, CleaningNotFound]
)
@inject
async def get_cleaning_by_uuid(
uuid: str,
use_case: CleanUseCaseInterface = Depends(Provide["cleaning_use_case"]),
) -> dict[str, Any]:
return use_case.get_by_uuid(uuid)
data = use_case.get_by_uuid(uuid)
return data


@router.post("/cleanings", response_model=Union[CleanedChatOut, None])
@inject
async def clean_chat(
chat: CleaningIn,
use_case: CleanUseCaseInterface = Depends(Provide["cleaning_use_case"]),
):
data = use_case.clean(chat.body)
return Response(
content=json.dumps(data), media_type="application/json", status_code=201
)
32 changes: 32 additions & 0 deletions src/chatcleaner/adapters/entrypoints/api/v1/schemas_cleaning.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import datetime

from pydantic import BaseModel


class CleaningIn(BaseModel):
body: str


class CleaningOut(BaseModel):
uuid: str
chat: str
cleaned_chat: str
created_at: datetime.datetime
updated_at: datetime.datetime


class AllCleaningsOut(BaseModel):
result: list[CleaningOut]


class CleaningNotFound(BaseModel):
result: str


class SingleCleaningOut(BaseModel):
result: CleaningOut


class CleanedChatOut(BaseModel):
uuid: str
cleaned_chat: str
1 change: 1 addition & 0 deletions src/chatcleaner/adapters/use_cases/clean.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ def _clean(self, chat: str):
model = cleaning_factory(**data_)
self.uow.cleaning.add(model)
self.uow.commit()
return {"uuid": model.uuid, "cleaned_chat": model.cleaned_chat}

def _get_all(self) -> list[dict[str, list[str]]]:
data_ = {"results": []}
Expand Down
5 changes: 5 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ def chat_text_with_times():
return "\n19:05:59 From David to Everyone:\nso far...\n19:35:48 From David to Everyone:\nOur highest priority is to satisfy the customer\nthrough early and continuous delivery\nof valuable software.\n19:36:59 From David to Everyone:\nthe highest, the lowest\n19:55:50 From David to Everyone:\nhttps://agilemanifesto.org/principles.html"


@pytest.fixture(scope="module")
def chat_text_without_times():
return "so far...\nOur highest priority is to satisfy the customer\nthrough early and continuous delivery\nof valuable software.\nthe highest, the lowest\nhttps://agilemanifesto.org/principles.html"


@pytest.fixture(scope="module")
def get_fake_repository():
return FakeCleaningRepository()
Expand Down
33 changes: 33 additions & 0 deletions tests/integrations/test_api.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import fastapi
import pytest
from fastapi.exceptions import HTTPException
from httpx import AsyncClient

from chatcleaner.adapters.entrypoints.api.app import app
Expand Down Expand Up @@ -59,3 +61,34 @@ async def test_get_cleaning_by_uuid_async_api_with_fake_uuid_return_Not_found(
assert response.status_code == 200
data = response.json()
assert data["result"] == "Not found"


@pytest.mark.anyio
@pytest.mark.integration
async def test_clean_chat_endpoint_returns_201(
get_fake_container,
async_client: AsyncClient,
chat_text_with_times: str,
chat_text_without_times: str,
):
use_case = get_fake_container.cleaning_use_case()
with app.container.cleaning_use_case.override(use_case):
response = await async_client.post(
"/clean/cleanings", json={"body": chat_text_with_times}
)
assert response.status_code == 201
assert response.json()["uuid"] is not None
assert response.json()["cleaned_chat"] == chat_text_without_times


@pytest.mark.anyio
@pytest.mark.integration
async def test_clean_chat_endpoint_returns_error_if_max_length_is_exceeded(
get_fake_container, async_client: AsyncClient
):
use_case = get_fake_container.cleaning_use_case()
with app.container.cleaning_use_case.override(use_case):
response = await async_client.post(
"/clean/cleanings", json={"chat": "a" * 2001}
)
assert response.status_code == 422
15 changes: 5 additions & 10 deletions tests/integrations/test_cleaning_use_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@
def test_clean_use_case_clean(get_fake_container, get_clean_use_case):
with Container.cleaning_uow.override(get_fake_container.cleaning_uow):
with Container.chat_service.override(get_fake_container.chat_service):
get_clean_use_case.clean("\n19:10:00 from David to Everyone:\ntest")
result = get_clean_use_case.clean(
"\n19:10:00 from David to Everyone:\ntest"
)
assert result["uuid"] is not None
assert result["cleaned_chat"] == "test"
uow_ = get_fake_container.cleaning_uow()
with uow_:
result = uow_.cleaning.get_all()
Expand All @@ -28,15 +32,6 @@ def test_clean_use_case_get_all(get_fake_container, get_clean_use_case):
result = get_clean_use_case.get_all()
# fixtures are scopes to module, so this should be 4
assert len(result["results"]) == 4
# breakpoint()
# assert result[0].chat == "\n19:10:00 from David to Everyone:\ntest"
# assert result[0].cleaned_chat == "test"
# assert result[1].chat == "\n19:10:00 from David to Everyone:\ntest 1"
# assert result[1].cleaned_chat == "test 1"
# assert result[2].chat == "\n19:10:00 from David to Everyone:\ntest 2"
# assert result[2].cleaned_chat == "test 2"
# assert result[3].chat == "\n19:10:00 from David to Everyone:\ntest 3"
# assert result[3].cleaned_chat == "test 3"


@pytest.mark.integration
Expand Down

0 comments on commit fe6d9df

Please sign in to comment.