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

updates #37

Merged
merged 1 commit into from
Jan 26, 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
4 changes: 2 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ jobs:
-p 5003:8765 \
${{ env.IMAGE }}-final:latest
- name: Install requirements
run: docker exec fastapi-tdd pip install black==23.1.0 flake8==6.0.0 isort==5.12.0 pytest==7.2.2
run: docker exec fastapi-tdd pip install black==23.12.1 flake8==7.0.0 isort==5.13.2 pytest==7.4.4
- name: Pytest
run: docker exec fastapi-tdd python -m pytest .
- name: Flake8
Expand All @@ -98,7 +98,7 @@ jobs:
runs-on: ubuntu-latest
needs: [build, test]
env:
HEROKU_APP_NAME: radiant-everglades-49858
HEROKU_APP_NAME: quiet-citadel-80656
HEROKU_REGISTRY_IMAGE: registry.heroku.com/${HEROKU_APP_NAME}/summarizer
steps:
- name: Checkout
Expand Down
4 changes: 2 additions & 2 deletions project/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# pull official base image
FROM python:3.11.2-slim-buster
FROM python:3.12.1-slim-bookworm

# set working directory
WORKDIR /usr/src/app
Expand All @@ -10,7 +10,7 @@ ENV PYTHONUNBUFFERED 1

# install system dependencies
RUN apt-get update \
&& apt-get -y install netcat gcc postgresql \
&& apt-get -y install netcat-traditional gcc postgresql \
&& apt-get clean

# install python dependencies
Expand Down
16 changes: 8 additions & 8 deletions project/Dockerfile.prod
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
###########

# pull official base image
FROM python:3.11.2-slim-buster as builder
FROM python:3.12.1-slim-bookworm as builder

# install system dependencies
RUN apt-get update \
&& apt-get -y install gcc postgresql \
&& apt-get -y install netcat-traditional gcc postgresql \
&& apt-get clean

# set work directory
Expand All @@ -24,18 +24,18 @@ RUN pip wheel --no-cache-dir --no-deps --wheel-dir /usr/src/app/wheels -r requir

# lint
COPY . /usr/src/app/
RUN pip install black==23.1.0 flake8==6.0.0 isort==5.12.0
RUN pip install black==23.12.1 flake8==7.0.0 isort==5.13.2
RUN flake8 .
RUN black --exclude=migrations .
RUN isort .
RUN black --exclude=migrations . --check
RUN isort . --check-only


#########
# FINAL #
#########

# pull official base image
FROM python:3.11.2-slim-buster
FROM python:3.12.1-slim-bookworm

# create directory for the app user
RUN mkdir -p /home/app
Expand All @@ -57,15 +57,15 @@ ENV TESTING 0

# install system dependencies
RUN apt-get update \
&& apt-get -y install netcat gcc postgresql \
&& apt-get -y install netcat-traditional gcc postgresql \
&& apt-get clean

# install python dependencies
COPY --from=builder /usr/src/app/wheels /wheels
COPY --from=builder /usr/src/app/requirements.txt .
RUN pip install --upgrade pip
RUN pip install --no-cache /wheels/*
RUN pip install "uvicorn[standard]==0.21.1"
RUN pip install "uvicorn[standard]==0.26.0"

# add app
COPY . .
Expand Down
10 changes: 5 additions & 5 deletions project/app/api/crud.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,18 @@
from app.models.tortoise import TextSummary


async def get_all() -> List:
summaries = await TextSummary.all().values()
return summaries


async def get(id: int) -> Union[dict, None]:
summary = await TextSummary.filter(id=id).first().values()
if summary:
return summary
return None


async def get_all() -> List:
summaries = await TextSummary.all().values()
return summaries


async def post(payload: SummaryPayloadSchema) -> int:
summary = TextSummary(url=payload.url, summary="")
await summary.save()
Expand Down
12 changes: 6 additions & 6 deletions project/app/api/summaries.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,6 @@
router = APIRouter()


@router.get("/", response_model=List[SummarySchema])
async def read_all_summaries() -> List[SummarySchema]:
return await crud.get_all()


@router.get("/{id}/", response_model=SummarySchema)
async def read_summary(id: int = Path(..., gt=0)) -> SummarySchema:
summary = await crud.get(id)
Expand All @@ -29,13 +24,18 @@ async def read_summary(id: int = Path(..., gt=0)) -> SummarySchema:
return summary


@router.get("/", response_model=List[SummarySchema])
async def read_all_summaries() -> List[SummarySchema]:
return await crud.get_all()


@router.post("/", response_model=SummaryResponseSchema, status_code=201)
async def create_summary(
payload: SummaryPayloadSchema, background_tasks: BackgroundTasks
) -> SummaryResponseSchema:
summary_id = await crud.post(payload)

background_tasks.add_task(generate_summary, summary_id, payload.url)
background_tasks.add_task(generate_summary, summary_id, str(payload.url))

response_object = {"id": summary_id, "url": payload.url}
return response_object
Expand Down
3 changes: 2 additions & 1 deletion project/app/config.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import logging
from functools import lru_cache

from pydantic import AnyUrl, BaseSettings
from pydantic import AnyUrl
from pydantic_settings import BaseSettings

log = logging.getLogger("uvicorn")

Expand Down
2 changes: 1 addition & 1 deletion project/db/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# pull official base image
FROM postgres:15
FROM postgres:16

# run create.sql on init
ADD create.sql /docker-entrypoint-initdb.d
12 changes: 6 additions & 6 deletions project/requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
black==23.1.0
flake8==6.0.0
isort==5.12.0
pytest==7.2.2
pytest-cov==4.0.0
pytest-xdist==3.2.1
black==23.12.1
flake8==7.0.0
isort==5.13.2
pytest==7.4.4
pytest-cov==4.1.0
pytest-xdist==3.5.0

-r requirements.txt
15 changes: 8 additions & 7 deletions project/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
aerich==0.7.1
asyncpg==0.27.0
fastapi==0.94.1
gunicorn==20.1.0
httpx==0.23.3
aerich==0.7.2
asyncpg==0.29.0
fastapi==0.109.0
gunicorn==21.0.1
httpx==0.26.0
newspaper3k==0.2.8
tortoise-orm==0.19.3
uvicorn==0.21.1
pydantic-settings==2.1.0
tortoise-orm==0.20.0
uvicorn==0.26.0
65 changes: 42 additions & 23 deletions project/tests/test_summaries.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def mock_generate_summary(summary_id, url):
)

assert response.status_code == 201
assert response.json()["url"] == "https://foo.bar"
assert response.json()["url"] == "https://foo.bar/"


def test_create_summaries_invalid_json(test_app):
Expand All @@ -25,16 +25,20 @@ def test_create_summaries_invalid_json(test_app):
assert response.json() == {
"detail": [
{
"input": {},
"loc": ["body", "url"],
"msg": "field required",
"type": "value_error.missing",
"msg": "Field required",
"type": "missing",
"url": "https://errors.pydantic.dev/2.5/v/missing",
}
]
}

response = test_app.post("/summaries/", data=json.dumps({"url": "invalid://url"}))
assert response.status_code == 422
assert response.json()["detail"][0]["msg"] == "URL scheme not permitted"
assert (
response.json()["detail"][0]["msg"] == "URL scheme should be 'http' or 'https'"
)


def test_read_summary(test_app_with_db, monkeypatch):
Expand All @@ -53,7 +57,7 @@ def mock_generate_summary(summary_id, url):

response_dict = response.json()
assert response_dict["id"] == summary_id
assert response_dict["url"] == "https://foo.bar"
assert response_dict["url"] == "https://foo.bar/"
assert response_dict["created_at"]


Expand All @@ -67,10 +71,12 @@ def test_read_summary_incorrect_id(test_app_with_db):
assert response.json() == {
"detail": [
{
"ctx": {"gt": 0},
"input": "0",
"loc": ["path", "id"],
"msg": "ensure this value is greater than 0",
"type": "value_error.number.not_gt",
"ctx": {"limit_value": 0},
"msg": "Input should be greater than 0",
"type": "greater_than",
"url": "https://errors.pydantic.dev/2.5/v/greater_than",
}
]
}
Expand Down Expand Up @@ -107,7 +113,7 @@ def mock_generate_summary(summary_id, url):

response = test_app_with_db.delete(f"/summaries/{summary_id}/")
assert response.status_code == 200
assert response.json() == {"id": summary_id, "url": "https://foo.bar"}
assert response.json() == {"id": summary_id, "url": "https://foo.bar/"}


def test_remove_summary_incorrect_id(test_app_with_db):
Expand All @@ -120,10 +126,12 @@ def test_remove_summary_incorrect_id(test_app_with_db):
assert response.json() == {
"detail": [
{
"ctx": {"gt": 0},
"input": "0",
"loc": ["path", "id"],
"msg": "ensure this value is greater than 0",
"type": "value_error.number.not_gt",
"ctx": {"limit_value": 0},
"msg": "Input should be greater than 0",
"type": "greater_than",
"url": "https://errors.pydantic.dev/2.5/v/greater_than",
}
]
}
Expand All @@ -148,7 +156,7 @@ def mock_generate_summary(summary_id, url):

response_dict = response.json()
assert response_dict["id"] == summary_id
assert response_dict["url"] == "https://foo.bar"
assert response_dict["url"] == "https://foo.bar/"
assert response_dict["summary"] == "updated!"
assert response_dict["created_at"]

Expand All @@ -168,10 +176,12 @@ def mock_generate_summary(summary_id, url):
422,
[
{
"type": "greater_than",
"loc": ["path", "id"],
"msg": "ensure this value is greater than 0",
"type": "value_error.number.not_gt",
"ctx": {"limit_value": 0},
"msg": "Input should be greater than 0",
"input": "0",
"ctx": {"gt": 0},
"url": "https://errors.pydantic.dev/2.5/v/greater_than",
}
],
],
Expand All @@ -181,14 +191,18 @@ def mock_generate_summary(summary_id, url):
422,
[
{
"type": "missing",
"loc": ["body", "url"],
"msg": "field required",
"type": "value_error.missing",
"msg": "Field required",
"input": {},
"url": "https://errors.pydantic.dev/2.5/v/missing",
},
{
"type": "missing",
"loc": ["body", "summary"],
"msg": "field required",
"type": "value_error.missing",
"msg": "Field required",
"input": {},
"url": "https://errors.pydantic.dev/2.5/v/missing",
},
],
],
Expand All @@ -198,9 +212,11 @@ def mock_generate_summary(summary_id, url):
422,
[
{
"type": "missing",
"loc": ["body", "summary"],
"msg": "field required",
"type": "value_error.missing",
"msg": "Field required",
"input": {"url": "https://foo.bar"},
"url": "https://errors.pydantic.dev/2.5/v/missing",
}
],
],
Expand All @@ -213,6 +229,7 @@ def test_update_summary_invalid(
f"/summaries/{summary_id}/", data=json.dumps(payload)
)
assert response.status_code == status_code
print(response.json()["detail"])
assert response.json()["detail"] == detail


Expand All @@ -222,4 +239,6 @@ def test_update_summary_invalid_url(test_app):
data=json.dumps({"url": "invalid://url", "summary": "updated!"}),
)
assert response.status_code == 422
assert response.json()["detail"][0]["msg"] == "URL scheme not permitted"
assert (
response.json()["detail"][0]["msg"] == "URL scheme should be 'http' or 'https'"
)
Loading
Loading