Files
next-tjwater-server/samples/peripheral/api.py
T

138 lines
7.1 KiB
Python

"""Independent sample API for synthetic work orders and monitoring data."""
from typing import get_args
from uuid import UUID
from fastapi import Depends, FastAPI, Query, Request
from fastapi.exceptions import RequestValidationError
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from .auth import Identity, identity
from .config import SampleSettings
from .errors import SampleFailure
from .store import TRANSITIONS
from .schemas import (
AlertPage, Context, CreateTestDataRecord, CreateWorkOrder,
DeleteResource, DeletedResource, DemoDataset, Observations, PointPage, Receipt,
SampleError, Scenario, ScenarioCommand, ScenarioState, TestDataRecord,
TestDataRecordPage, UpdateTestDataRecord, UpdateWorkOrder, WorkOrder, WorkOrderPage,
)
def store(request: Request):
from .store import PostgresStore
return PostgresStore(request.app.state.settings)
def create_app(settings: SampleSettings | None = None) -> FastAPI:
app = FastAPI(title="TJWater 外围系统模拟接入", version="1.0.0",
description="测试监测与核查反馈样板,不是正式工单平台或设备控制服务。")
app.state.settings = settings or SampleSettings()
app.add_middleware(CORSMiddleware, allow_origins=app.state.settings.cors_origins,
allow_credentials=False, allow_methods=["GET", "POST", "PATCH", "DELETE"],
allow_headers=["Authorization", "Content-Type", "X-Project-Id"])
@app.exception_handler(SampleFailure)
async def failure(request, exc):
return JSONResponse(status_code=exc.status, content=exc.payload())
@app.exception_handler(RequestValidationError)
async def invalid(request, exc):
return JSONResponse(status_code=422, content=SampleFailure("SCHEMA_INVALID", "请求字段不符合样板契约").payload())
@app.get("/health")
def health():
return {"service": "peripheral-sample", "origin": "synthetic", "storage": "postgresql",
"status": "up", "storage_verified": False}
prefix = "/api/sample/v1"
errors = {code: {"model": SampleError} for code in (401, 403, 404, 409, 422, 502, 503)}
@app.get(prefix + "/context", response_model=Context, responses=errors)
def context(request: Request, who: Identity = Depends(identity), db=Depends(store)):
return {"protocol_version": "tjwater-peripheral-sample@1", "origin": "synthetic",
"project_id": who.project_id, "user_id": who.user_id, "can_write": who.can_write,
"scenarios": list(get_args(Scenario)) if request.app.state.settings.enable_faults else ["normal"],
"state_transitions": TRANSITIONS, "managed_resources": db.managed_resource_capabilities()}
@app.get(prefix + "/scenario", response_model=ScenarioState, responses=errors)
def scenario(who: Identity = Depends(identity), db=Depends(store)):
return db.scenario(who)
@app.post(prefix + "/scenario", response_model=ScenarioState, responses=errors)
def configure_scenario(command: ScenarioCommand, who: Identity = Depends(identity), db=Depends(store)):
return db.set_scenario(who, command)
@app.get(prefix + "/requests/{request_id}", response_model=Receipt, responses=errors)
def receipt(request_id: UUID, who: Identity = Depends(identity), db=Depends(store)):
return db.receipt(who, request_id)
@app.get(prefix + "/work-orders", response_model=WorkOrderPage, responses=errors)
def work_orders(limit: int = Query(20, ge=1, le=100), cursor: UUID | None = None,
who: Identity = Depends(identity), db=Depends(store)):
return db.list_work_orders(who, limit, cursor)
@app.post(prefix + "/work-orders", response_model=WorkOrder, status_code=201, responses=errors)
def create_work_order(command: CreateWorkOrder, who: Identity = Depends(identity), db=Depends(store)):
return db.create_work_order(who, command)
@app.get(prefix + "/work-orders/{work_order_id}", response_model=WorkOrder, responses=errors)
def work_order(work_order_id: UUID, who: Identity = Depends(identity), db=Depends(store)):
return db.get_work_order(who, work_order_id)
@app.patch(prefix + "/work-orders/{work_order_id}", response_model=WorkOrder, responses=errors)
def update_work_order(work_order_id: UUID, command: UpdateWorkOrder,
who: Identity = Depends(identity), db=Depends(store)):
return db.update_work_order(who, work_order_id, command)
@app.delete(prefix + "/work-orders/{work_order_id}", response_model=DeletedResource, responses=errors)
def delete_work_order(work_order_id: UUID, command: DeleteResource,
who: Identity = Depends(identity), db=Depends(store)):
return db.delete_work_order(who, work_order_id, command)
@app.get(prefix + "/test-data", response_model=TestDataRecordPage, responses=errors)
def test_data(limit: int = Query(20, ge=1, le=100), cursor: UUID | None = None,
who: Identity = Depends(identity), db=Depends(store)):
return db.list_test_data(who, limit, cursor)
@app.post(prefix + "/test-data", response_model=TestDataRecord, status_code=201, responses=errors)
def create_test_data(command: CreateTestDataRecord, who: Identity = Depends(identity), db=Depends(store)):
return db.create_test_data(who, command)
@app.get(prefix + "/test-data/{record_id}", response_model=TestDataRecord, responses=errors)
def test_data_record(record_id: UUID, who: Identity = Depends(identity), db=Depends(store)):
return db.get_test_data(who, record_id)
@app.patch(prefix + "/test-data/{record_id}", response_model=TestDataRecord, responses=errors)
def update_test_data(record_id: UUID, command: UpdateTestDataRecord,
who: Identity = Depends(identity), db=Depends(store)):
return db.update_test_data(who, record_id, command)
@app.delete(prefix + "/test-data/{record_id}", response_model=DeletedResource, responses=errors)
def delete_test_data(record_id: UUID, command: DeleteResource,
who: Identity = Depends(identity), db=Depends(store)):
return db.delete_test_data(who, record_id, command)
@app.get(prefix + "/monitoring/points", response_model=PointPage, responses=errors)
def points(limit: int = Query(100, ge=1, le=500), cursor: str | None = Query(None, max_length=128),
who: Identity = Depends(identity), db=Depends(store)):
return db.points(who, limit, cursor)
@app.get(prefix + "/monitoring/observations", response_model=Observations, responses=errors)
def observations(point_id: str, start: str, end: str, who: Identity = Depends(identity), db=Depends(store)):
return db.observations(who, point_id, start, end)
@app.get(prefix + "/monitoring/alerts", response_model=AlertPage, responses=errors)
def alerts(who: Identity = Depends(identity), db=Depends(store)):
return db.alerts(who)
@app.get(prefix + "/demo-data", response_model=DemoDataset, responses=errors)
def demo_data(who: Identity = Depends(identity), db=Depends(store)):
return db.demo_data(who)
return app
app = create_app()