Skip to content

Commit

Permalink
chore: upgrade dependencies
Browse files Browse the repository at this point in the history
  • Loading branch information
tjmaynes committed Feb 29, 2024
1 parent e62f069 commit 370150b
Show file tree
Hide file tree
Showing 23 changed files with 115 additions and 111 deletions.
1 change: 1 addition & 0 deletions .tool-versions
@@ -1 +1,2 @@
python 3.11.3
kubectl 1.26.2
10 changes: 5 additions & 5 deletions Makefile
Expand Up @@ -15,18 +15,18 @@ test: migrate
. .venv/bin/activate; python3 -m pytest

lint:
. .venv/bin/activate; mypy api/
. .venv/bin/activate; mypy app/

start: migrate
. .venv/bin/activate; uvicorn --host 0.0.0.0 --port $(PORT) api.main:app
. .venv/bin/activate; uvicorn --host 0.0.0.0 --port $(PORT) app.main:api

seed: migrate
. .venv/bin/activate; python3 -m api.seed
. .venv/bin/activate; python3 -m app.seed

run_local_db:
start_local_db:
kubectl apply -f ./k8s/shopping-cart-common/secret.yml
kubectl apply -f ./k8s/shopping-cart-db/deployment.yml
kubectl apply -f ./k8s/shopping-cart-db/persistence.local.yml
(mkdir -p /tmp/shopping-cart/data || true) && kubectl apply -f ./k8s/shopping-cart-db/persistence.local.yml

connect_localhost_to_remote_db:
kubectl port-forward svc/shopping-cart-db 5432:5432
Expand Down
4 changes: 2 additions & 2 deletions README.md
Expand Up @@ -78,7 +78,7 @@ Next, let's make sure the test suite for the application is running as expected.

2. Since our test suite talks to a database, let's make sure that PostgreSQL is running locally via:
```bash
make run_local_db
make start_local_db
```

3. Finally, let's run our tests via:
Expand All @@ -101,7 +101,7 @@ make test

To make sure the database is running, run the following command:
```bash
make run_local_db
make start_local_db
```

To start the app and database locally, run the following command:
Expand Down
45 changes: 0 additions & 45 deletions api/cart/domain.py

This file was deleted.

File renamed without changes.
File renamed without changes.
36 changes: 36 additions & 0 deletions app/cart/domain.py
@@ -0,0 +1,36 @@
from typing import List, Dict, Callable, Any, Tuple
from dataclasses import dataclass, field
from result import Ok, Err, Result
from pydantic import BaseModel
from datetime import datetime
from app.core import InvalidItemException, CustomException

class CartItemIn(BaseModel):
name: str
price: int
manufacturer: str

class CartItem(BaseModel):
id: str
name: str
price: int
manufacturer: str
created_at: datetime
last_modified_at: datetime

def validate_cart_item(item: CartItemIn) -> Result[CartItemIn, CustomException]:
errors = []

if not item.name or len(item.name) == 0:
errors.append("'name' must not be empty")

if item.price is None or int(item.price) < 99:
errors.append("'price' must be a non-empty number greater than 99")

if not item.manufacturer or len(item.manufacturer) == 0:
errors.append("'manufacturer' must not be empty")

if errors:
return Err(InvalidItemException(errors))
else:
return Ok(item)
16 changes: 8 additions & 8 deletions api/cart/repository.py → app/cart/repository.py
@@ -1,5 +1,5 @@
from typing import List, TypeVar, Tuple
from api.core import Repository, CustomException, UnknownException, NotFoundException, Connection
from app.core import Repository, CustomException, UnknownException, NotFoundException, Connection
from .domain import CartItem, CartItemIn
from result import Ok, Err, Result
import datetime
Expand All @@ -19,7 +19,7 @@ def get_all_items(self, page_number: int = 0, page_size: int = 10) -> Result[Lis
return Ok([])

except Exception as inst:
return Err(UnknownException(list(inst.args)))
return Err(UnknownException(inst))


def get_item_by_id(self, id: str) -> Result[CartItem, CustomException]:
Expand All @@ -32,7 +32,7 @@ def get_item_by_id(self, id: str) -> Result[CartItem, CustomException]:
return Err(NotFoundException())

except Exception as inst:
return Err(UnknownException(list(inst.args)))
return Err(UnknownException(inst))


def add_item(self, item: CartItemIn) -> Result[CartItem, CustomException]:
Expand All @@ -46,11 +46,11 @@ def add_item(self, item: CartItemIn) -> Result[CartItem, CustomException]:

if result:
return Ok(self.__from_tuple(result))
return Err(UnknownException(["Unable to add item at this time!"]))
return Err(UnknownException("Unable to add item at this time!"))

except Exception as inst:
self.__db_conn.rollback()
return Err(UnknownException(list(inst.args)))
return Err(UnknownException(inst))


def update_item(self, id: str, item: CartItemIn) -> Result[CartItem, CustomException]:
Expand All @@ -67,7 +67,7 @@ def update_item(self, id: str, item: CartItemIn) -> Result[CartItem, CustomExcep

except Exception as inst:
self.__db_conn.rollback()
return Err(UnknownException(list(inst.args)))
return Err(UnknownException(inst))


def remove_item_by_id(self, id: str) -> Result[str, CustomException]:
Expand All @@ -83,12 +83,12 @@ def remove_item_by_id(self, id: str) -> Result[str, CustomException]:

except Exception as inst:
self.__db_conn.rollback()
return Err(UnknownException(list(inst.args)))
return Err(UnknownException(inst))


def __from_tuple(self, tuple: Tuple) -> CartItem:
return CartItem(
id=tuple[0],
id="{}".format(tuple[0]),
name=tuple[1],
price=tuple[2],
manufacturer=tuple[3],
Expand Down
2 changes: 1 addition & 1 deletion api/cart/service.py → app/cart/service.py
@@ -1,5 +1,5 @@
from typing import Callable, List
from api.core import Service, Repository
from app.core import Service, Repository
from .domain import CartItem, CartItemIn, validate_cart_item

def CartService(repository: Repository) -> Service[CartItemIn, CartItem]:
Expand Down
8 changes: 4 additions & 4 deletions api/core/__init__.py → app/core/__init__.py
@@ -1,11 +1,11 @@
from api.core.repository import Repository
from api.core.service import Service
from api.core.exceptions import (\
from app.core.repository import Repository
from app.core.service import Service
from app.core.exceptions import (\
CustomException, \
DBConnectionFailedException, \
NotFoundException, \
CustomException, \
InvalidItemException, \
BadRequestException, \
UnknownException)
from api.core.types import Connection
from app.core.types import Connection
12 changes: 11 additions & 1 deletion api/core/exceptions.py → app/core/exceptions.py
@@ -1,17 +1,27 @@
class CustomException(Exception):
"""Basic exception for errors raised"""
def __init__(self, message):
self.message = message
def __str__(self):
return self.message
pass

class DBConnectionFailedException(CustomException):
"""Unable to connect to repository"""
pass

class NotFoundException(CustomException):
"""Unable to find item in repository"""
pass

class InvalidItemException(CustomException):
"""Given an invalid item"""
pass

class BadRequestException(CustomException):
"""Invalid item given"""
pass

class UnknownException(CustomException):
"""Unknown"""
"""Unknown"""
pass
File renamed without changes.
4 changes: 2 additions & 2 deletions api/core/service.py → app/core/service.py
Expand Up @@ -23,15 +23,15 @@ def get_item_by_id(self, id: str) -> Result[T, CustomException]:
def add_item(self, item: S) -> Result[T, CustomException]:
validation_result = self.__validate_item(item)
if isinstance(validation_result, Ok):
return self.__repository.add_item(validation_result.value)
return self.__repository.add_item(validation_result.ok_value)
else:
return validation_result


def update_item(self, id: str, item: S) -> Result[T, CustomException]:
validation_result = self.__validate_item(item)
if isinstance(validation_result, Ok):
return self.__repository.update_item(id, validation_result.value)
return self.__repository.update_item(id, validation_result.ok_value)
else:
return validation_result

Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion api/health/service.py → app/health/service.py
@@ -1,5 +1,5 @@
from psycopg2 import Error as Psycopg2Error
from api.core import Connection
from app.core import Connection
from .domain import Health

class HealthService:
Expand Down
46 changes: 23 additions & 23 deletions api/main.py → app/main.py
Expand Up @@ -6,13 +6,13 @@
from typing import List, Dict
import json
from os import getenv
from api.core import CustomException, InvalidItemException, NotFoundException
from api.cart import CartService, CartRepository, CartItem, CartItemIn
from api.health import HealthService, Health
from app.core import CustomException, InvalidItemException, NotFoundException
from app.cart import CartService, CartRepository, CartItem, CartItemIn
from app.health import HealthService, Health

def build_app() -> FastAPI:
def build_api() -> FastAPI:
db_conn = connect(getenv("DATABASE_URL"))

health_service = HealthService(db_conn = db_conn)
cart_repository = CartRepository(db_conn = db_conn)
cart_service = CartService(repository = cart_repository)
Expand All @@ -27,55 +27,55 @@ async def health_endpoint() -> JSONResponse:
else:
return JSONResponse(status_code=500, content=jsonable_encoder(health))

@app.get("/cart/", response_model=List[CartItem])
@app.get("/cart", response_model=List[CartItem])
async def fetch_cart_items(page_number: int = 0, page_size: int = 10) -> JSONResponse:
items_result = cart_service.get_items(page_number, page_size)
if isinstance(items_result, Ok):
return JSONResponse(status_code=200, content=jsonable_encoder(items_result.value))
return JSONResponse(status_code=200, content=jsonable_encoder(items_result.ok_value))
else:
return JSONResponse(status_code=500, content=jsonable_encoder(items_result.value))
return JSONResponse(status_code=500, content=jsonable_encoder(items_result.err_value))

@app.get("/cart/{id}", response_model=CartItem)
async def fetch_item_by_id(id: str) -> JSONResponse:
item_result = cart_service.get_item_by_id(id)
if isinstance(item_result, Ok):
return JSONResponse(status_code=200, content=jsonable_encoder(item_result.value))
return JSONResponse(status_code=200, content=jsonable_encoder(item_result.ok_value))
else:
return JSONResponse(status_code=500, content=jsonable_encoder(item_result.value))
return JSONResponse(status_code=500, content=jsonable_encoder(item_result.err_value))

@app.post("/cart/", response_model=CartItem)
async def create_cart_item(item: CartItemIn) -> JSONResponse:
add_item_result = cart_service.add_item(item)
if isinstance(add_item_result, Ok):
return JSONResponse(status_code=201, content=jsonable_encoder(add_item_result.value))
return JSONResponse(status_code=201, content=jsonable_encoder(add_item_result.ok_value))
else:
if isinstance(add_item_result.value, InvalidItemException):
return JSONResponse(status_code=422, content=str(add_item_result.value))
if isinstance(add_item_result.err_value, InvalidItemException):
return JSONResponse(status_code=422, content=str(add_item_result.err_value))
else:
return JSONResponse(status_code=500, content=jsonable_encoder(add_item_result.value))
return JSONResponse(status_code=500, content=jsonable_encoder(add_item_result.err_value))

@app.put("/cart/{id}", response_model=CartItem)
async def update_cart_item(id: str, item: CartItemIn) -> JSONResponse:
update_item_result = cart_service.update_item(id, item)
if isinstance(update_item_result, Ok):
return JSONResponse(status_code=200, content=jsonable_encoder(update_item_result.value))
return JSONResponse(status_code=200, content=jsonable_encoder(update_item_result.ok_value))
else:
if isinstance(update_item_result.value, InvalidItemException):
return JSONResponse(status_code=422, content=str(update_item_result.value))
if isinstance(update_item_result.err_value, InvalidItemException):
return JSONResponse(status_code=422, content=str(update_item_result.err_value))
else:
return JSONResponse(status_code=500, content=jsonable_encoder(update_item_result.value))
return JSONResponse(status_code=500, content=jsonable_encoder(update_item_result.err_value))

@app.delete("/cart/{id}", response_model=Dict)
async def delete_cart_item(id: str) -> JSONResponse:
delete_item_result = cart_service.remove_item_by_id(id)
if isinstance(delete_item_result, Ok):
return JSONResponse(status_code=200, content=jsonable_encoder({ "id": delete_item_result.value }))
return JSONResponse(status_code=200, content=jsonable_encoder({ "id": delete_item_result.ok_value }))
else:
if isinstance(delete_item_result.value, NotFoundException):
return JSONResponse(status_code=404, content=str(delete_item_result.value))
if isinstance(delete_item_result.err_value, NotFoundException):
return JSONResponse(status_code=404, content=str(delete_item_result.err_value))
else:
return JSONResponse(status_code=500, content=jsonable_encoder(delete_item_result.value))
return JSONResponse(status_code=500, content=jsonable_encoder(delete_item_result.err_value))

return app

app = build_app()
api = build_api()

0 comments on commit 370150b

Please sign in to comment.