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

6 years ago
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
)