feat(workbench): add runtime GIS and sample integrations
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
"""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()
|
||||
Reference in New Issue
Block a user