feat: polish & windviz & deploy
This commit is contained in:
parent
81b8e763bd
commit
465ad00f7b
78 changed files with 20622 additions and 2154 deletions
|
|
@ -1,84 +1,37 @@
|
|||
openapi: 3.0.4
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: Predictor API
|
||||
version: 0.0.1
|
||||
title: stratoflights-predictor API
|
||||
version: "1.0.0"
|
||||
description: |
|
||||
Balloon trajectory prediction and wind-dataset management.
|
||||
|
||||
Three prediction surfaces are exposed:
|
||||
|
||||
* **`GET /api/v1/prediction`** — Tawhiri-compatible, drop-in for the
|
||||
Cambridge University Spaceflight predictor.
|
||||
* **`POST /api/v2/prediction`** — profile-driven synchronous prediction
|
||||
(arbitrary chains of propagators with constraints).
|
||||
* **`POST /api/v1/predictions`** — the same profile API run asynchronously
|
||||
via a worker pool, polled by job id.
|
||||
|
||||
Dataset management (download, list, delete, job status) lives under
|
||||
`/api/v1/admin/`, and wind-field visualization data (leaflet-velocity /
|
||||
wind-layer format) under `/api/v1/wind/`.
|
||||
|
||||
servers:
|
||||
- url: /
|
||||
description: This server.
|
||||
|
||||
tags:
|
||||
- name: Prediction
|
||||
- name: Datasets
|
||||
- name: Wind
|
||||
- name: Health
|
||||
|
||||
paths:
|
||||
/api/v1/prediction:
|
||||
get:
|
||||
tags:
|
||||
- Prediction
|
||||
summary: Perform prediction
|
||||
operationId: performPrediction
|
||||
parameters:
|
||||
- in: query
|
||||
name: launch_latitude
|
||||
required: true
|
||||
schema:
|
||||
type: number
|
||||
- in: query
|
||||
name: launch_longitude
|
||||
required: true
|
||||
schema:
|
||||
type: number
|
||||
- in: query
|
||||
name: launch_datetime
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: date-time
|
||||
- in: query
|
||||
name: launch_altitude
|
||||
schema:
|
||||
type: number
|
||||
- in: query
|
||||
name: profile
|
||||
schema:
|
||||
type: string
|
||||
enum: [standard_profile, float_profile]
|
||||
default: standard_profile
|
||||
- in: query
|
||||
name: ascent_rate
|
||||
schema:
|
||||
type: number
|
||||
- in: query
|
||||
name: burst_altitude
|
||||
schema:
|
||||
type: number
|
||||
- in: query
|
||||
name: descent_rate
|
||||
schema:
|
||||
type: number
|
||||
- in: query
|
||||
name: float_altitude
|
||||
schema:
|
||||
type: number
|
||||
- in: query
|
||||
name: stop_datetime
|
||||
schema:
|
||||
type: string
|
||||
format: date-time
|
||||
- in: query
|
||||
name: dataset
|
||||
schema:
|
||||
type: string
|
||||
format: date-time
|
||||
responses:
|
||||
"200":
|
||||
description: Prediction response
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/PredictionResponse'
|
||||
default:
|
||||
description: Error
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
/ready:
|
||||
get:
|
||||
tags:
|
||||
- Health
|
||||
tags: [Health]
|
||||
summary: Readiness check
|
||||
operationId: readinessCheck
|
||||
responses:
|
||||
|
|
@ -87,113 +40,652 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ReadinessResponse'
|
||||
$ref: "#/components/schemas/ReadinessResponse"
|
||||
default:
|
||||
description: Error
|
||||
$ref: "#/components/responses/DefaultError"
|
||||
|
||||
/api/v1/prediction:
|
||||
get:
|
||||
tags: [Prediction]
|
||||
summary: Tawhiri-compatible prediction
|
||||
operationId: performPrediction
|
||||
parameters:
|
||||
- { in: query, name: launch_latitude, required: true, schema: { type: number } }
|
||||
- { in: query, name: launch_longitude, required: true, schema: { type: number } }
|
||||
- { in: query, name: launch_datetime, required: true, schema: { type: string, format: date-time } }
|
||||
- { in: query, name: launch_altitude, schema: { type: number } }
|
||||
- { in: query, name: profile, schema: { type: string, enum: [standard_profile, float_profile], default: standard_profile } }
|
||||
- { in: query, name: ascent_rate, schema: { type: number } }
|
||||
- { in: query, name: burst_altitude, schema: { type: number } }
|
||||
- { in: query, name: descent_rate, schema: { type: number } }
|
||||
- { in: query, name: float_altitude, schema: { type: number } }
|
||||
- { in: query, name: stop_datetime, schema: { type: string, format: date-time } }
|
||||
- { in: query, name: dataset, schema: { type: string, format: date-time } }
|
||||
responses:
|
||||
"200":
|
||||
description: Prediction response
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
$ref: "#/components/schemas/PredictionResponse"
|
||||
default:
|
||||
$ref: "#/components/responses/DefaultError"
|
||||
|
||||
/api/v2/prediction:
|
||||
post:
|
||||
tags: [Prediction]
|
||||
summary: Profile-driven prediction (synchronous)
|
||||
operationId: performPredictionV2
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/PredictionV2Request"
|
||||
responses:
|
||||
"200":
|
||||
description: Prediction result
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/PredictionV2Response"
|
||||
default:
|
||||
$ref: "#/components/responses/DefaultError"
|
||||
|
||||
/api/v1/predictions:
|
||||
post:
|
||||
tags: [Prediction]
|
||||
summary: Enqueue an asynchronous prediction
|
||||
operationId: createPredictionJob
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/PredictionV2Request"
|
||||
responses:
|
||||
"202":
|
||||
description: Job accepted
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/PredictionJob"
|
||||
default:
|
||||
$ref: "#/components/responses/DefaultError"
|
||||
|
||||
/api/v1/predictions/{id}:
|
||||
get:
|
||||
tags: [Prediction]
|
||||
summary: Poll an asynchronous prediction job
|
||||
operationId: getPredictionJob
|
||||
parameters:
|
||||
- { in: path, name: id, required: true, schema: { type: string } }
|
||||
responses:
|
||||
"200":
|
||||
description: Job status (with result when complete)
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/PredictionJob"
|
||||
default:
|
||||
$ref: "#/components/responses/DefaultError"
|
||||
delete:
|
||||
tags: [Prediction]
|
||||
summary: Cancel a queued prediction job
|
||||
operationId: cancelPredictionJob
|
||||
parameters:
|
||||
- { in: path, name: id, required: true, schema: { type: string } }
|
||||
responses:
|
||||
"204":
|
||||
description: Cancelled
|
||||
default:
|
||||
$ref: "#/components/responses/DefaultError"
|
||||
|
||||
/api/v1/admin/datasets:
|
||||
get:
|
||||
tags: [Datasets]
|
||||
summary: List stored datasets
|
||||
operationId: listDatasets
|
||||
responses:
|
||||
"200":
|
||||
description: Stored datasets
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/DatasetList"
|
||||
default:
|
||||
$ref: "#/components/responses/DefaultError"
|
||||
post:
|
||||
tags: [Datasets]
|
||||
summary: Trigger a dataset download
|
||||
operationId: triggerDatasetDownload
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/DownloadRequest"
|
||||
responses:
|
||||
"202":
|
||||
description: Download accepted
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/DownloadAccepted"
|
||||
default:
|
||||
$ref: "#/components/responses/DefaultError"
|
||||
|
||||
/api/v1/admin/datasets/{name}:
|
||||
delete:
|
||||
tags: [Datasets]
|
||||
summary: Delete a stored dataset by filename
|
||||
operationId: deleteDataset
|
||||
parameters:
|
||||
- { in: path, name: name, required: true, schema: { type: string } }
|
||||
responses:
|
||||
"204":
|
||||
description: Deleted
|
||||
default:
|
||||
$ref: "#/components/responses/DefaultError"
|
||||
|
||||
/api/v1/admin/jobs:
|
||||
get:
|
||||
tags: [Datasets]
|
||||
summary: List dataset download jobs
|
||||
operationId: listDatasetJobs
|
||||
responses:
|
||||
"200":
|
||||
description: Download jobs
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/DownloadJob"
|
||||
default:
|
||||
$ref: "#/components/responses/DefaultError"
|
||||
|
||||
/api/v1/admin/jobs/{id}:
|
||||
get:
|
||||
tags: [Datasets]
|
||||
summary: Get a dataset download job
|
||||
operationId: getDatasetJob
|
||||
parameters:
|
||||
- { in: path, name: id, required: true, schema: { type: string } }
|
||||
responses:
|
||||
"200":
|
||||
description: Download job
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/DownloadJob"
|
||||
default:
|
||||
$ref: "#/components/responses/DefaultError"
|
||||
delete:
|
||||
tags: [Datasets]
|
||||
summary: Cancel a running download job
|
||||
operationId: cancelDatasetJob
|
||||
parameters:
|
||||
- { in: path, name: id, required: true, schema: { type: string } }
|
||||
responses:
|
||||
"204":
|
||||
description: Cancelled
|
||||
default:
|
||||
$ref: "#/components/responses/DefaultError"
|
||||
|
||||
/api/v1/admin/status:
|
||||
get:
|
||||
tags: [Datasets]
|
||||
summary: Service status summary
|
||||
operationId: getServiceStatus
|
||||
responses:
|
||||
"200":
|
||||
description: Status
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/StatusResponse"
|
||||
default:
|
||||
$ref: "#/components/responses/DefaultError"
|
||||
|
||||
/api/v1/wind/meta:
|
||||
get:
|
||||
tags: [Wind]
|
||||
summary: Wind-field visualization metadata
|
||||
operationId: getWindMeta
|
||||
responses:
|
||||
"200":
|
||||
description: Metadata describing the active dataset for visualization
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/WindMeta"
|
||||
default:
|
||||
$ref: "#/components/responses/DefaultError"
|
||||
|
||||
/api/v1/wind/field:
|
||||
get:
|
||||
tags: [Wind]
|
||||
summary: Wind-field velocity grid (leaflet-velocity / wind-layer format)
|
||||
operationId: getWindField
|
||||
parameters:
|
||||
- { in: query, name: time, schema: { type: string, format: date-time } }
|
||||
- { in: query, name: altitude, schema: { type: number } }
|
||||
- { in: query, name: min_lat, schema: { type: number } }
|
||||
- { in: query, name: max_lat, schema: { type: number } }
|
||||
- { in: query, name: min_lng, schema: { type: number } }
|
||||
- { in: query, name: max_lng, schema: { type: number } }
|
||||
- { in: query, name: step, schema: { type: number } }
|
||||
responses:
|
||||
"200":
|
||||
description: Two-component (U, V) velocity grid
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/WindComponent"
|
||||
default:
|
||||
$ref: "#/components/responses/DefaultError"
|
||||
|
||||
components:
|
||||
responses:
|
||||
DefaultError:
|
||||
description: Error
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Error"
|
||||
|
||||
schemas:
|
||||
Error:
|
||||
type: object
|
||||
required:
|
||||
- error
|
||||
required: [error]
|
||||
properties:
|
||||
error:
|
||||
type: object
|
||||
required:
|
||||
- type
|
||||
- description
|
||||
required: [type, description]
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
type: { type: string }
|
||||
description: { type: string }
|
||||
|
||||
ReadinessResponse:
|
||||
type: object
|
||||
required: [status]
|
||||
properties:
|
||||
status: { type: string, enum: [ok, not_ready, error] }
|
||||
dataset_time: { type: string, format: date-time }
|
||||
error_message: { type: string }
|
||||
|
||||
# --- Tawhiri v1 ---------------------------------------------------------
|
||||
PredictionResponse:
|
||||
type: object
|
||||
required:
|
||||
- prediction
|
||||
- metadata
|
||||
required: [prediction, metadata]
|
||||
properties:
|
||||
request:
|
||||
type: object
|
||||
properties:
|
||||
dataset:
|
||||
type: string
|
||||
launch_latitude:
|
||||
type: number
|
||||
launch_longitude:
|
||||
type: number
|
||||
launch_datetime:
|
||||
type: string
|
||||
launch_altitude:
|
||||
type: number
|
||||
profile:
|
||||
type: string
|
||||
ascent_rate:
|
||||
type: number
|
||||
burst_altitude:
|
||||
type: number
|
||||
descent_rate:
|
||||
type: number
|
||||
dataset: { type: string }
|
||||
launch_latitude: { type: number }
|
||||
launch_longitude: { type: number }
|
||||
launch_datetime: { type: string }
|
||||
launch_altitude: { type: number }
|
||||
profile: { type: string }
|
||||
ascent_rate: { type: number }
|
||||
burst_altitude: { type: number }
|
||||
descent_rate: { type: number }
|
||||
prediction:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required:
|
||||
- stage
|
||||
- trajectory
|
||||
required: [stage, trajectory]
|
||||
properties:
|
||||
stage:
|
||||
type: string
|
||||
enum: ["ascent", "descent", "float"]
|
||||
stage: { type: string, enum: [ascent, descent, float] }
|
||||
trajectory:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required:
|
||||
- datetime
|
||||
- latitude
|
||||
- longitude
|
||||
- altitude
|
||||
properties:
|
||||
datetime:
|
||||
type: string
|
||||
format: date-time
|
||||
latitude:
|
||||
type: number
|
||||
longitude:
|
||||
type: number
|
||||
altitude:
|
||||
type: number
|
||||
$ref: "#/components/schemas/TawhiriPoint"
|
||||
metadata:
|
||||
type: object
|
||||
required:
|
||||
- start_datetime
|
||||
- complete_datetime
|
||||
required: [start_datetime, complete_datetime]
|
||||
properties:
|
||||
start_datetime:
|
||||
type: string
|
||||
format: date-time
|
||||
complete_datetime:
|
||||
type: string
|
||||
format: date-time
|
||||
start_datetime: { type: string, format: date-time }
|
||||
complete_datetime: { type: string, format: date-time }
|
||||
warnings:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
ReadinessResponse:
|
||||
|
||||
TawhiriPoint:
|
||||
type: object
|
||||
required:
|
||||
- status
|
||||
required: [datetime, latitude, longitude, altitude]
|
||||
properties:
|
||||
status:
|
||||
type: string
|
||||
enum: [ok, not_ready, error]
|
||||
dataset_time:
|
||||
type: string
|
||||
format: date-time
|
||||
error_message:
|
||||
datetime: { type: string, format: date-time }
|
||||
latitude: { type: number }
|
||||
longitude: { type: number }
|
||||
altitude: { type: number }
|
||||
|
||||
# --- v2 profile-driven --------------------------------------------------
|
||||
PredictionV2Request:
|
||||
type: object
|
||||
required: [launch, profile]
|
||||
description: |
|
||||
A profile-driven prediction. `profile` is an ordered chain of
|
||||
propagators; each integrates from where the previous ended. A stage's
|
||||
`constraints` decide when it ends and what happens next: stop the
|
||||
profile, hand off to `fallback_index`, or clip to the boundary.
|
||||
properties:
|
||||
launch: { $ref: "#/components/schemas/Launch" }
|
||||
direction:
|
||||
type: string
|
||||
enum: [forward, reverse]
|
||||
default: forward
|
||||
description: forward integrates launch→landing; reverse integrates backward in time.
|
||||
profile:
|
||||
type: array
|
||||
items: { $ref: "#/components/schemas/StageSpec" }
|
||||
globals:
|
||||
type: array
|
||||
description: constraints evaluated on every stage in addition to its own.
|
||||
items: { $ref: "#/components/schemas/ConstraintSpec" }
|
||||
options: { $ref: "#/components/schemas/Options" }
|
||||
example:
|
||||
launch: { time: "2026-03-28T12:00:00Z", latitude: 52.2, longitude: 0.1, altitude: 0 }
|
||||
profile:
|
||||
- name: ascent
|
||||
model: { type: constant_rate, rate: 5, include_wind: true }
|
||||
constraints: [{ type: altitude, op: ">=", limit: 30000 }]
|
||||
- name: descent
|
||||
model: { type: parachute_descent, sea_level_rate: 5, include_wind: true }
|
||||
constraints: [{ type: terrain_contact }]
|
||||
|
||||
Launch:
|
||||
type: object
|
||||
required: [time, latitude, longitude]
|
||||
properties:
|
||||
time: { type: string, format: date-time }
|
||||
latitude: { type: number }
|
||||
longitude: { type: number }
|
||||
altitude: { type: number }
|
||||
|
||||
StageSpec:
|
||||
type: object
|
||||
required: [name, model]
|
||||
properties:
|
||||
name: { type: string }
|
||||
model: { $ref: "#/components/schemas/ModelSpec" }
|
||||
constraints:
|
||||
type: array
|
||||
items: { $ref: "#/components/schemas/ConstraintSpec" }
|
||||
fallback_index: { type: integer }
|
||||
|
||||
ModelSpec:
|
||||
type: object
|
||||
required: [type]
|
||||
properties:
|
||||
type: { type: string, enum: [constant_rate, parachute_descent, piecewise, wind] }
|
||||
rate: { type: number }
|
||||
sea_level_rate: { type: number }
|
||||
include_wind: { type: boolean }
|
||||
segments:
|
||||
type: array
|
||||
items: { $ref: "#/components/schemas/PiecewiseSegment" }
|
||||
|
||||
PiecewiseSegment:
|
||||
type: object
|
||||
required: [until, rate]
|
||||
properties:
|
||||
until: { type: number }
|
||||
rate: { type: number }
|
||||
reference: { type: string, enum: [absolute, profile_start, propagator_start], default: absolute }
|
||||
|
||||
ConstraintSpec:
|
||||
type: object
|
||||
required: [type]
|
||||
properties:
|
||||
type: { type: string, enum: [altitude, time, terrain_contact, polygon] }
|
||||
op: { type: string, enum: ["<", "<=", ">", ">=", "=="] }
|
||||
limit: { type: number }
|
||||
action: { type: string, enum: [stop, fallback, clip], default: stop }
|
||||
mode: { type: string, enum: [inside, outside] }
|
||||
label: { type: string }
|
||||
vertices:
|
||||
type: array
|
||||
items: { $ref: "#/components/schemas/PolygonVertex" }
|
||||
|
||||
PolygonVertex:
|
||||
type: object
|
||||
required: [lat, lng]
|
||||
properties:
|
||||
lat: { type: number }
|
||||
lng: { type: number }
|
||||
|
||||
Options:
|
||||
type: object
|
||||
properties:
|
||||
step_seconds: { type: number }
|
||||
tolerance: { type: number }
|
||||
|
||||
PredictionV2Response:
|
||||
type: object
|
||||
required: [stages, dataset, started_at, completed_at]
|
||||
properties:
|
||||
stages:
|
||||
type: array
|
||||
items: { $ref: "#/components/schemas/StageResult" }
|
||||
events:
|
||||
type: array
|
||||
items: { $ref: "#/components/schemas/EventSummary" }
|
||||
dataset: { $ref: "#/components/schemas/DatasetInfo" }
|
||||
started_at: { type: string, format: date-time }
|
||||
completed_at: { type: string, format: date-time }
|
||||
|
||||
StageResult:
|
||||
type: object
|
||||
required: [name, outcome, trajectory]
|
||||
properties:
|
||||
name: { type: string }
|
||||
outcome: { type: string, enum: [stopped, fallback, continued] }
|
||||
constraint: { type: string }
|
||||
termination: { $ref: "#/components/schemas/TerminationInfo" }
|
||||
events:
|
||||
type: array
|
||||
items: { $ref: "#/components/schemas/EventSummary" }
|
||||
trajectory:
|
||||
type: array
|
||||
items: { $ref: "#/components/schemas/TrajectoryPoint" }
|
||||
|
||||
TrajectoryPoint:
|
||||
type: object
|
||||
required: [time, latitude, longitude, altitude]
|
||||
properties:
|
||||
time: { type: string, format: date-time }
|
||||
latitude: { type: number }
|
||||
longitude: { type: number }
|
||||
altitude: { type: number }
|
||||
|
||||
GeoState:
|
||||
type: object
|
||||
required: [lat, lng, altitude]
|
||||
properties:
|
||||
lat: { type: number }
|
||||
lng: { type: number }
|
||||
altitude: { type: number }
|
||||
|
||||
TerminationInfo:
|
||||
type: object
|
||||
required: [violation_time, violation_state, refined_time, refined_state]
|
||||
properties:
|
||||
violation_time: { type: string, format: date-time }
|
||||
violation_state: { $ref: "#/components/schemas/GeoState" }
|
||||
refined_time: { type: string, format: date-time }
|
||||
refined_state: { $ref: "#/components/schemas/GeoState" }
|
||||
|
||||
EventSummary:
|
||||
type: object
|
||||
required: [type, count]
|
||||
properties:
|
||||
type: { type: string }
|
||||
count: { type: integer, format: int64 }
|
||||
first_time: { type: number }
|
||||
last_time: { type: number }
|
||||
first_state: { $ref: "#/components/schemas/GeoState" }
|
||||
last_state: { $ref: "#/components/schemas/GeoState" }
|
||||
message: { type: string }
|
||||
|
||||
DatasetInfo:
|
||||
type: object
|
||||
required: [source, epoch]
|
||||
properties:
|
||||
source: { type: string }
|
||||
epoch: { type: string, format: date-time }
|
||||
|
||||
# --- async jobs ---------------------------------------------------------
|
||||
PredictionJob:
|
||||
type: object
|
||||
required: [id, status, created_at]
|
||||
properties:
|
||||
id: { type: string }
|
||||
status: { type: string, enum: [pending, running, complete, failed, cancelled] }
|
||||
created_at: { type: string, format: date-time }
|
||||
started_at: { type: string, format: date-time }
|
||||
completed_at: { type: string, format: date-time }
|
||||
error: { type: string }
|
||||
result: { $ref: "#/components/schemas/PredictionV2Response" }
|
||||
|
||||
# --- dataset admin ------------------------------------------------------
|
||||
Region:
|
||||
type: object
|
||||
required: [min_lat, max_lat, min_lng, max_lng]
|
||||
properties:
|
||||
min_lat: { type: number }
|
||||
max_lat: { type: number }
|
||||
min_lng: { type: number }
|
||||
max_lng: { type: number }
|
||||
|
||||
HourRange:
|
||||
type: object
|
||||
required: [min_hour, max_hour]
|
||||
properties:
|
||||
min_hour: { type: integer }
|
||||
max_hour: { type: integer }
|
||||
|
||||
SubsetSpec:
|
||||
type: object
|
||||
properties:
|
||||
region: { $ref: "#/components/schemas/Region" }
|
||||
hour_range: { $ref: "#/components/schemas/HourRange" }
|
||||
members:
|
||||
type: array
|
||||
items: { type: integer }
|
||||
|
||||
Coverage:
|
||||
type: object
|
||||
required: [region, start_time, end_time]
|
||||
properties:
|
||||
region: { $ref: "#/components/schemas/Region" }
|
||||
start_time: { type: string, format: date-time }
|
||||
end_time: { type: string, format: date-time }
|
||||
|
||||
DownloadRequest:
|
||||
type: object
|
||||
properties:
|
||||
epoch: { type: string, format: date-time }
|
||||
latest: { type: boolean }
|
||||
subset: { $ref: "#/components/schemas/SubsetSpec" }
|
||||
|
||||
DownloadAccepted:
|
||||
type: object
|
||||
required: [job_id]
|
||||
properties:
|
||||
job_id: { type: string }
|
||||
|
||||
DatasetEntry:
|
||||
type: object
|
||||
required: [filename, epoch, loaded]
|
||||
properties:
|
||||
filename: { type: string }
|
||||
epoch: { type: string, format: date-time }
|
||||
subset: { $ref: "#/components/schemas/SubsetSpec" }
|
||||
coverage: { $ref: "#/components/schemas/Coverage" }
|
||||
loaded: { type: boolean }
|
||||
|
||||
DatasetList:
|
||||
type: object
|
||||
required: [source, datasets]
|
||||
properties:
|
||||
source: { type: string }
|
||||
datasets:
|
||||
type: array
|
||||
items: { $ref: "#/components/schemas/DatasetEntry" }
|
||||
|
||||
DownloadJob:
|
||||
type: object
|
||||
required: [id, source, dataset, epoch, status, started_at, total_units, done_units, bytes]
|
||||
properties:
|
||||
id: { type: string }
|
||||
source: { type: string }
|
||||
dataset: { type: string }
|
||||
epoch: { type: string, format: date-time }
|
||||
status: { type: string, enum: [pending, running, complete, failed, cancelled] }
|
||||
started_at: { type: string, format: date-time }
|
||||
ended_at: { type: string, format: date-time }
|
||||
error: { type: string }
|
||||
total_units: { type: integer }
|
||||
done_units: { type: integer }
|
||||
bytes: { type: integer, format: int64 }
|
||||
|
||||
StatusResponse:
|
||||
type: object
|
||||
required: [source, uptime, goroutines, memory_mb, jobs_by_status, stored_datasets, loaded_datasets]
|
||||
properties:
|
||||
source: { type: string }
|
||||
uptime: { type: string }
|
||||
goroutines: { type: integer }
|
||||
memory_mb: { type: integer, format: int64 }
|
||||
jobs_by_status:
|
||||
type: object
|
||||
additionalProperties: { type: integer }
|
||||
stored_datasets: { type: integer }
|
||||
loaded_datasets: { type: integer }
|
||||
|
||||
# --- wind visualization -------------------------------------------------
|
||||
WindMeta:
|
||||
type: object
|
||||
required: [source, epoch, default_step, min_step, suggested_altitudes, bbox]
|
||||
properties:
|
||||
source: { type: string }
|
||||
epoch: { type: string, format: date-time }
|
||||
default_step: { type: number }
|
||||
min_step: { type: number }
|
||||
suggested_altitudes:
|
||||
type: array
|
||||
items: { type: integer }
|
||||
bbox: { $ref: "#/components/schemas/Region" }
|
||||
|
||||
WindComponent:
|
||||
type: object
|
||||
required: [header, data]
|
||||
properties:
|
||||
header: { $ref: "#/components/schemas/WindHeader" }
|
||||
data:
|
||||
type: array
|
||||
items: { type: number }
|
||||
|
||||
WindHeader:
|
||||
type: object
|
||||
required: [parameterCategory, parameterNumber, nx, ny, lo1, la1, lo2, la2, dx, dy, refTime, forecastTime]
|
||||
properties:
|
||||
parameterCategory: { type: integer }
|
||||
parameterNumber: { type: integer }
|
||||
parameterNumberName: { type: string }
|
||||
parameterUnit: { type: string }
|
||||
nx: { type: integer }
|
||||
ny: { type: integer }
|
||||
lo1: { type: number }
|
||||
la1: { type: number }
|
||||
lo2: { type: number }
|
||||
la2: { type: number }
|
||||
dx: { type: number }
|
||||
dy: { type: number }
|
||||
refTime: { type: string }
|
||||
forecastTime: { type: integer }
|
||||
|
|
|
|||
13
api/spec.go
Normal file
13
api/spec.go
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
// Package apispec embeds the OpenAPI specification so it can be served at
|
||||
// runtime (for the ReDoc documentation page and /openapi.yaml) without
|
||||
// shipping a separate file alongside the binary.
|
||||
//
|
||||
// The spec at rest/predictor.swagger.yml is the single source of truth: it
|
||||
// is both the ogen code-generation input (see the Makefile generate-ogen
|
||||
// target) and the document served by the API's docs handler.
|
||||
package apispec
|
||||
|
||||
import _ "embed"
|
||||
|
||||
//go:embed rest/predictor.swagger.yml
|
||||
var Spec []byte
|
||||
Loading…
Add table
Add a link
Reference in a new issue