You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
231 lines
6.5 KiB
Python
231 lines
6.5 KiB
Python
import os
|
|
from typing import List, Tuple, Any, Dict, Optional
|
|
from molten import (
|
|
annotate,
|
|
App,
|
|
Request,
|
|
QueryParam,
|
|
Route,
|
|
Settings,
|
|
SettingsComponent,
|
|
ResponseRendererMiddleware,
|
|
HTTP_201,
|
|
HTTP_202,
|
|
HTTP_404,
|
|
HTTPError,
|
|
)
|
|
from molten.openapi import Metadata, OpenAPIHandler, OpenAPIUIHandler
|
|
from molten.contrib.sqlalchemy import (
|
|
SQLAlchemyEngineComponent,
|
|
SQLAlchemySessionComponent,
|
|
SQLAlchemyMiddleware,
|
|
)
|
|
|
|
from .errors import EntityNotFound
|
|
|
|
from .manager import (
|
|
AlertManager,
|
|
StoreManager,
|
|
GeographyManager,
|
|
AlertManagerComponent,
|
|
StoreManagerComponent,
|
|
GeographyManagerComponent,
|
|
)
|
|
from .schema import Alert, Store, Geography, APIResponse
|
|
from .util import ExtJSONRender
|
|
|
|
|
|
class MarketLook(App):
|
|
def handle_404(self, request: Request) -> Tuple[str, APIResponse]:
|
|
return (
|
|
HTTP_404,
|
|
APIResponse(
|
|
status=404,
|
|
message=f"The resource you are looking for {request.scheme}://{request.host}{request.path} doesn't exist",
|
|
),
|
|
)
|
|
|
|
|
|
# We are passing explicitly the engine param for
|
|
# establishing the utc as the timezone for our connection.
|
|
settings = Settings(
|
|
{
|
|
"database_engine_dsn": os.getenv(
|
|
"DATABASE_DSN", "postgres://molten:local@localhost/market_look"
|
|
),
|
|
"database_engine_params": {
|
|
"echo": True,
|
|
"connect_args": {"options": "-c timezone=utc"},
|
|
},
|
|
}
|
|
)
|
|
|
|
get_schema = OpenAPIHandler(
|
|
metadata=Metadata(
|
|
title="Market Look API",
|
|
description="An API for managing promotion compliance at store.",
|
|
version="0.1.0",
|
|
)
|
|
)
|
|
|
|
get_docs = OpenAPIUIHandler()
|
|
setattr(get_docs, "openapi_tags", ["API Management"])
|
|
|
|
|
|
def name(name: Optional[QueryParam]) -> APIResponse:
|
|
_name = name or "Molten"
|
|
return APIResponse(
|
|
status=200, message=f"Hello, {_name}! Glad you are programming with us"
|
|
)
|
|
|
|
|
|
def ping() -> APIResponse:
|
|
return APIResponse(200, "Pong")
|
|
|
|
|
|
@annotate(openapi_tags=["Stores"])
|
|
def get_stores(store_manager: StoreManager) -> List[Store]:
|
|
"""Returns a collection of Stores"""
|
|
stores = store_manager.get_stores()
|
|
return stores
|
|
|
|
|
|
@annotate(openapi_tags=["Stores"])
|
|
def create_store(
|
|
store: Store, store_manager: StoreManager
|
|
) -> Tuple[Any, Store, Dict[str, str]]:
|
|
"""Creates a new store resource and returns its respresentation"""
|
|
try:
|
|
_store = store_manager.create_store(store)
|
|
except HTTPError as err:
|
|
raise err
|
|
headers = {"Location": _store.href}
|
|
return HTTP_201, _store, headers
|
|
|
|
|
|
@annotate(openapi_tags=["Stores"])
|
|
def get_store_by_id(id: int, store_manager: StoreManager) -> Store:
|
|
_store = store_manager.get_store_by_id(id)
|
|
|
|
if _store is None:
|
|
raise HTTPError(
|
|
HTTP_404,
|
|
{
|
|
"status": 404,
|
|
"message": f"The resource you are looking for /stores/{id} does not exist",
|
|
},
|
|
)
|
|
|
|
return _store
|
|
|
|
|
|
@annotate(openapi_tags=["Stores"])
|
|
def update_store(store: Store, store_manager: StoreManager) -> Store:
|
|
_updated_store = store_manager.update_store(store)
|
|
return _updated_store
|
|
|
|
|
|
@annotate(openapi_tags=["Stores"])
|
|
def delete_store_by_id(id: int, store_manager: StoreManager) -> Tuple[Any, APIResponse]:
|
|
store_manager.delete_store_by_id(id)
|
|
return (
|
|
HTTP_202,
|
|
APIResponse(status=202, message=f"Delete request for store: {id} accepted"),
|
|
)
|
|
|
|
|
|
@annotate(openapi_tags=["Geographies"])
|
|
def get_geographies(geo_manager: GeographyManager) -> List[Geography]:
|
|
_geographies = geo_manager.get_geographies()
|
|
return _geographies
|
|
|
|
|
|
@annotate(openapi_tags=["Geographies"])
|
|
def get_geo_by_id(id: int, geo_manager: GeographyManager) -> Geography:
|
|
try:
|
|
_geo = geo_manager.get_geo_by_id(id=id)
|
|
except HTTPError as err:
|
|
raise err
|
|
|
|
return _geo
|
|
|
|
|
|
@annotate(openapi_tags=["Geographies"])
|
|
def get_geo_stores(id: int, store_manager: StoreManager) -> List[Store]:
|
|
stores = store_manager.get_stores_by_geo(geo_id=id)
|
|
return stores
|
|
|
|
|
|
@annotate(openapi_tags=["Alerts"])
|
|
def get_alerts(alert_manager: AlertManager) -> List[Alert]:
|
|
alerts = alert_manager.get_alerts()
|
|
return alerts
|
|
|
|
|
|
@annotate(openapi_tags=["Alerts"])
|
|
def get_alert_by_id(id: int, alert_manager: AlertManager) -> Alert:
|
|
try:
|
|
alert = alert_manager.get_alert_by_id(alert_id=id)
|
|
except EntityNotFound as err:
|
|
raise HTTPError(HTTP_404, APIResponse(status=404, message=err.message))
|
|
return alert
|
|
|
|
|
|
@annotate(openapi_tags=["Alerts"])
|
|
def get_store_alerts(id: int, alert_manager: AlertManager) -> List[Alert]:
|
|
try:
|
|
alerts = alert_manager.get_alerts_at_store(store_id=id)
|
|
except EntityNotFound as err:
|
|
raise HTTPError(HTTP_404, APIResponse(status=404, message=err.message))
|
|
return alerts
|
|
|
|
|
|
@annotate(openapi_tags=["Alerts"])
|
|
def get_geo_alerts(id: int, alert_manager: AlertManager) -> List[Alert]:
|
|
try:
|
|
alerts = alert_manager.get_alerts_at_geo(geo_id=id)
|
|
except EntityNotFound as err:
|
|
raise HTTPError(HTTP_404, APIResponse(status=404, message=err.message))
|
|
return alerts
|
|
|
|
|
|
routes = [
|
|
Route("/", method="GET", handler=name),
|
|
Route("/ping", method="GET", handler=ping),
|
|
# Stores
|
|
Route("/stores", method="GET", handler=get_stores),
|
|
Route("/stores", method="POST", handler=create_store),
|
|
Route("/stores/{id}", method="GET", handler=get_store_by_id),
|
|
Route("/stores/{id}", method="PUT", handler=update_store),
|
|
Route("/stores/{id}", method="DELETE", handler=delete_store_by_id),
|
|
Route("/geographies/{id}/stores", method="GET", handler=get_geo_stores),
|
|
# Geographies
|
|
Route("/geographies", method="GET", handler=get_geographies),
|
|
Route("/geographies/{id}", method="GET", handler=get_geo_by_id),
|
|
# Alerts
|
|
Route("/alerts", method="GET", handler=get_alerts),
|
|
Route("/alerts/{id}", method="GET", handler=get_alert_by_id),
|
|
Route("/stores/{id}/alerts", method="GET", handler=get_store_alerts),
|
|
Route("/geographies/{id}/alerts", method="GET", handler=get_geo_alerts),
|
|
# OpenAPI
|
|
Route("/_schema", get_schema),
|
|
Route("/docs", get_docs),
|
|
]
|
|
|
|
components = [
|
|
SettingsComponent(settings),
|
|
SQLAlchemyEngineComponent(),
|
|
SQLAlchemySessionComponent(),
|
|
StoreManagerComponent(),
|
|
GeographyManagerComponent(),
|
|
AlertManagerComponent()
|
|
]
|
|
|
|
middleware = [ResponseRendererMiddleware(), SQLAlchemyMiddleware()]
|
|
|
|
renderers = [ExtJSONRender()]
|
|
|
|
app = MarketLook(
|
|
routes=routes, components=components, middleware=middleware, renderers=renderers
|
|
)
|