"""Application.""" from typing import Any, cast import sentry_sdk from audience_common.logger.asgi.middleware import RequestLoggerMiddleware from fastapi import FastAPI, HTTPException, status from fastapi.datastructures import Default from fastapi.middleware import Middleware from fastapi.requests import Request from fastapi.responses import JSONResponse, RedirectResponse from jwtauth.asgi.middleware import JWTAuthenticationMiddleware from jwtauth.utils import jwt_auth_enabled_for_env, jwt_auth_from_environment from owscontext.context.asgi.middleware import ( CorrelationIdMiddleware, RequestContextMiddleware, ) from starlette.middleware import _MiddlewareFactory from product_staging import config from product_staging.allowed_value_sets.api.routers import allowed_value_sets from product_staging.allowed_value_sets.exceptions import HydrateMetadatException from product_staging.api import error_handlers from product_staging.api.datasources import datasources_lifespan from product_staging.api.dependencies import BulkSessionSlugRedirect from product_staging.api.routers import ( bulk_session, bulk_session_assets, bulk_session_ingestion, bulk_session_metadata, infra, lookup, tasks, ) from product_staging.exceptions import OwsProductStagingException from product_staging.logic.metadata_json.exceptions import ParsingError app = FastAPI( debug=config.APP_DEBUG, title=config.SERVICE_NAME, version=config.SERVICE_VERSION, default_response_class=Default(JSONResponse), description="Orchard web service to serve as staging area for content before ingest.", lifespan=datasources_lifespan, middleware=[ Middleware(cast(_MiddlewareFactory[Any], CorrelationIdMiddleware)), Middleware(cast(_MiddlewareFactory[Any], RequestContextMiddleware)), Middleware( cast(_MiddlewareFactory[Any], RequestLoggerMiddleware), exclude_paths=["/hello/", "/redoc", "/docs", "/openapi.json"], ), Middleware( cast(_MiddlewareFactory[Any], JWTAuthenticationMiddleware), environment=config.ENVIRONMENT, enabled=jwt_auth_enabled_for_env(config.ENVIRONMENT), exclude_paths=["/hello/$", "/redoc", "/docs", "/openapi.json"], auth=jwt_auth_from_environment(config.ENVIRONMENT), ), ], ) app.include_router(bulk_session.router) app.include_router(bulk_session_assets.router) app.include_router(bulk_session_ingestion.router) app.include_router(bulk_session_metadata.router) app.include_router(infra.router) app.include_router(lookup.router) app.include_router(allowed_value_sets.router) app.include_router(tasks.router) # Register exception handlers app.add_exception_handler(Exception, error_handlers.default_error_handler) app.add_exception_handler( OwsProductStagingException, error_handlers.default_error_handler ) app.add_exception_handler( HTTPException, error_handlers.http_error_handler, ) @app.exception_handler(BulkSessionSlugRedirect) async def bulk_session_slug_redirect_handler( request: Request, exc: BulkSessionSlugRedirect ) -> RedirectResponse: return RedirectResponse(url=exc.location, status_code=307) @app.exception_handler(ParsingError) async def parsing_error_handler(request: Request, exc: ParsingError): return JSONResponse( content={ "code": "bad_request", "message": exc.code, "details": exc.message, }, status_code=status.HTTP_400_BAD_REQUEST, ) @app.exception_handler(HydrateMetadatException) async def hydration_error_handler(request: Request, exc: HydrateMetadatException): return JSONResponse( content={ "code": "bad_request", "message": exc.code, "details": exc.message, }, status_code=status.HTTP_400_BAD_REQUEST, ) if config.ENVIRONMENT in [config.PROD_ENVIRONMENT, config.QA_ENVIRONMENT]: if not config.SENTRY_DSN: raise Exception(f"Sentry is not configured in {config.ENVIRONMENT}") sentry_sdk.init(dsn=config.SENTRY_DSN)