mirror of
https://github.com/koala73/worldmonitor.git
synced 2026-04-25 17:14:57 +02:00
* feat(supply-chain): energy flow estimates per chokepoint (mb/d card row) - Add FlowEstimate proto message + ChokepointInfo field 15; regenerate stubs - Add baselineId mapping to _chokepoint-ids.ts (7 of 13 chokepoints) - Add relayId to seed-chokepoint-baselines.mjs CHOKEPOINTS entries - New seed-chokepoint-flows.mjs: reads portwatch + baselines, computes 7d tanker avg vs 90d baseline, outputs flow_ratio and current_mbd; prefers DWT (capTanker) when available; flags disruption if last 3 days each below 0.85 threshold; writes energy:chokepoint-flows:v1 (TTL 3d) - get-chokepoint-status.ts: parallel-reads flows key, attaches flowEstimate - SupplyChainPanel: compact card gains mb/d row (red <85%, amber <95%) - 19 new unit tests for flow computation and seeder contract * fix(chokepoint-flows): base useDwt on 90d baseline window, not recent 7 days Zero recent capTanker is the disruption signal, not a reason to fall back to vessel counts. Switching metrics during peak disruption caused the seeder to report a higher (less accurate) flow estimate exactly when oil-flow collapse is most acute. useDwt is now locked to whether the baseline window has DWT data -- stable across disruption events. Adds regression test covering DWT-collapse scenario. * fix(chokepoint-flows): require majority DWT coverage in baseline before activating DWT mode capBaselineSum > 0 would activate DWT on a single non-zero day during partial data roll-out, pulling down the baseline average via zero-filled gaps. Now requires >= ceil(prev90.length / 2) days with DWT data. ArcGIS data is all-or-nothing per chokepoint in practice, so this guard catches edge cases without affecting normal operation.
446 lines
16 KiB
YAML
446 lines
16 KiB
YAML
openapi: 3.1.0
|
||
info:
|
||
title: SupplyChainService API
|
||
version: 1.0.0
|
||
paths:
|
||
/api/supply-chain/v1/get-shipping-rates:
|
||
get:
|
||
tags:
|
||
- SupplyChainService
|
||
summary: GetShippingRates
|
||
operationId: GetShippingRates
|
||
responses:
|
||
"200":
|
||
description: Successful response
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/GetShippingRatesResponse'
|
||
"400":
|
||
description: Validation error
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/ValidationError'
|
||
default:
|
||
description: Error response
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/Error'
|
||
/api/supply-chain/v1/get-chokepoint-status:
|
||
get:
|
||
tags:
|
||
- SupplyChainService
|
||
summary: GetChokepointStatus
|
||
operationId: GetChokepointStatus
|
||
responses:
|
||
"200":
|
||
description: Successful response
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/GetChokepointStatusResponse'
|
||
"400":
|
||
description: Validation error
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/ValidationError'
|
||
default:
|
||
description: Error response
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/Error'
|
||
/api/supply-chain/v1/get-critical-minerals:
|
||
get:
|
||
tags:
|
||
- SupplyChainService
|
||
summary: GetCriticalMinerals
|
||
operationId: GetCriticalMinerals
|
||
responses:
|
||
"200":
|
||
description: Successful response
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/GetCriticalMineralsResponse'
|
||
"400":
|
||
description: Validation error
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/ValidationError'
|
||
default:
|
||
description: Error response
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/Error'
|
||
/api/supply-chain/v1/get-shipping-stress:
|
||
get:
|
||
tags:
|
||
- SupplyChainService
|
||
summary: GetShippingStress
|
||
description: GetShippingStress returns carrier market data and a composite stress index.
|
||
operationId: GetShippingStress
|
||
responses:
|
||
"200":
|
||
description: Successful response
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/GetShippingStressResponse'
|
||
"400":
|
||
description: Validation error
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/ValidationError'
|
||
default:
|
||
description: Error response
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/Error'
|
||
components:
|
||
schemas:
|
||
Error:
|
||
type: object
|
||
properties:
|
||
message:
|
||
type: string
|
||
description: Error message (e.g., 'user not found', 'database connection failed')
|
||
description: Error is returned when a handler encounters an error. It contains a simple error message that the developer can customize.
|
||
FieldViolation:
|
||
type: object
|
||
properties:
|
||
field:
|
||
type: string
|
||
description: The field path that failed validation (e.g., 'user.email' for nested fields). For header validation, this will be the header name (e.g., 'X-API-Key')
|
||
description:
|
||
type: string
|
||
description: Human-readable description of the validation violation (e.g., 'must be a valid email address', 'required field missing')
|
||
required:
|
||
- field
|
||
- description
|
||
description: FieldViolation describes a single validation error for a specific field.
|
||
ValidationError:
|
||
type: object
|
||
properties:
|
||
violations:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/FieldViolation'
|
||
description: List of validation violations
|
||
required:
|
||
- violations
|
||
description: ValidationError is returned when request validation fails. It contains a list of field violations describing what went wrong.
|
||
GetShippingRatesRequest:
|
||
type: object
|
||
GetShippingRatesResponse:
|
||
type: object
|
||
properties:
|
||
indices:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/ShippingIndex'
|
||
fetchedAt:
|
||
type: string
|
||
upstreamUnavailable:
|
||
type: boolean
|
||
ShippingIndex:
|
||
type: object
|
||
properties:
|
||
indexId:
|
||
type: string
|
||
name:
|
||
type: string
|
||
currentValue:
|
||
type: number
|
||
format: double
|
||
previousValue:
|
||
type: number
|
||
format: double
|
||
changePct:
|
||
type: number
|
||
format: double
|
||
unit:
|
||
type: string
|
||
history:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/ShippingRatePoint'
|
||
spikeAlert:
|
||
type: boolean
|
||
ShippingRatePoint:
|
||
type: object
|
||
properties:
|
||
date:
|
||
type: string
|
||
value:
|
||
type: number
|
||
format: double
|
||
GetChokepointStatusRequest:
|
||
type: object
|
||
GetChokepointStatusResponse:
|
||
type: object
|
||
properties:
|
||
chokepoints:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/ChokepointInfo'
|
||
fetchedAt:
|
||
type: string
|
||
upstreamUnavailable:
|
||
type: boolean
|
||
ChokepointInfo:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: string
|
||
name:
|
||
type: string
|
||
lat:
|
||
type: number
|
||
format: double
|
||
lon:
|
||
type: number
|
||
format: double
|
||
disruptionScore:
|
||
type: integer
|
||
format: int32
|
||
status:
|
||
type: string
|
||
activeWarnings:
|
||
type: integer
|
||
format: int32
|
||
congestionLevel:
|
||
type: string
|
||
affectedRoutes:
|
||
type: array
|
||
items:
|
||
type: string
|
||
description:
|
||
type: string
|
||
aisDisruptions:
|
||
type: integer
|
||
format: int32
|
||
directions:
|
||
type: array
|
||
items:
|
||
type: string
|
||
directionalDwt:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/DirectionalDwt'
|
||
transitSummary:
|
||
$ref: '#/components/schemas/TransitSummary'
|
||
flowEstimate:
|
||
$ref: '#/components/schemas/FlowEstimate'
|
||
DirectionalDwt:
|
||
type: object
|
||
properties:
|
||
direction:
|
||
type: string
|
||
dwtThousandTonnes:
|
||
type: number
|
||
format: double
|
||
wowChangePct:
|
||
type: number
|
||
format: double
|
||
TransitSummary:
|
||
type: object
|
||
properties:
|
||
todayTotal:
|
||
type: integer
|
||
format: int32
|
||
todayTanker:
|
||
type: integer
|
||
format: int32
|
||
todayCargo:
|
||
type: integer
|
||
format: int32
|
||
todayOther:
|
||
type: integer
|
||
format: int32
|
||
wowChangePct:
|
||
type: number
|
||
format: double
|
||
history:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/TransitDayCount'
|
||
riskLevel:
|
||
type: string
|
||
incidentCount7d:
|
||
type: integer
|
||
format: int32
|
||
disruptionPct:
|
||
type: number
|
||
format: double
|
||
riskSummary:
|
||
type: string
|
||
riskReportAction:
|
||
type: string
|
||
TransitDayCount:
|
||
type: object
|
||
properties:
|
||
date:
|
||
type: string
|
||
tanker:
|
||
type: integer
|
||
format: int32
|
||
cargo:
|
||
type: integer
|
||
format: int32
|
||
other:
|
||
type: integer
|
||
format: int32
|
||
total:
|
||
type: integer
|
||
format: int32
|
||
container:
|
||
type: integer
|
||
format: int32
|
||
dryBulk:
|
||
type: integer
|
||
format: int32
|
||
generalCargo:
|
||
type: integer
|
||
format: int32
|
||
roro:
|
||
type: integer
|
||
format: int32
|
||
capContainer:
|
||
type: number
|
||
format: double
|
||
capDryBulk:
|
||
type: number
|
||
format: double
|
||
capGeneralCargo:
|
||
type: number
|
||
format: double
|
||
capRoro:
|
||
type: number
|
||
format: double
|
||
capTanker:
|
||
type: number
|
||
format: double
|
||
FlowEstimate:
|
||
type: object
|
||
properties:
|
||
currentMbd:
|
||
type: number
|
||
format: double
|
||
baselineMbd:
|
||
type: number
|
||
format: double
|
||
flowRatio:
|
||
type: number
|
||
format: double
|
||
disrupted:
|
||
type: boolean
|
||
source:
|
||
type: string
|
||
hazardAlertLevel:
|
||
type: string
|
||
hazardAlertName:
|
||
type: string
|
||
GetCriticalMineralsRequest:
|
||
type: object
|
||
GetCriticalMineralsResponse:
|
||
type: object
|
||
properties:
|
||
minerals:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/CriticalMineral'
|
||
fetchedAt:
|
||
type: string
|
||
upstreamUnavailable:
|
||
type: boolean
|
||
CriticalMineral:
|
||
type: object
|
||
properties:
|
||
mineral:
|
||
type: string
|
||
topProducers:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/MineralProducer'
|
||
hhi:
|
||
type: number
|
||
format: double
|
||
riskRating:
|
||
type: string
|
||
globalProduction:
|
||
type: number
|
||
format: double
|
||
unit:
|
||
type: string
|
||
MineralProducer:
|
||
type: object
|
||
properties:
|
||
country:
|
||
type: string
|
||
countryCode:
|
||
type: string
|
||
productionTonnes:
|
||
type: number
|
||
format: double
|
||
sharePct:
|
||
type: number
|
||
format: double
|
||
GetShippingStressRequest:
|
||
type: object
|
||
GetShippingStressResponse:
|
||
type: object
|
||
properties:
|
||
carriers:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/ShippingStressCarrier'
|
||
stressScore:
|
||
type: number
|
||
format: double
|
||
description: Composite stress score 0–100 (higher = more disruption).
|
||
stressLevel:
|
||
type: string
|
||
description: '"low" | "moderate" | "elevated" | "critical".'
|
||
fetchedAt:
|
||
type: integer
|
||
format: int64
|
||
description: 'Warning: Values > 2^53 may lose precision in JavaScript'
|
||
upstreamUnavailable:
|
||
type: boolean
|
||
description: Set to true when upstream data source is unavailable and cached data is stale.
|
||
ShippingStressCarrier:
|
||
type: object
|
||
properties:
|
||
symbol:
|
||
type: string
|
||
description: Ticker or identifier (e.g., "BDRY", "ZIM").
|
||
name:
|
||
type: string
|
||
description: Human-readable name.
|
||
price:
|
||
type: number
|
||
format: double
|
||
description: Current price.
|
||
changePct:
|
||
type: number
|
||
format: double
|
||
description: Percentage change from previous close.
|
||
carrierType:
|
||
type: string
|
||
description: 'Carrier type: "etf" | "carrier" | "index".'
|
||
sparkline:
|
||
type: array
|
||
items:
|
||
type: number
|
||
format: double
|
||
description: 30-day price sparkline.
|
||
description: ShippingStressCarrier represents market stress data for a carrier or shipping index.
|