mirror of
https://github.com/koala73/worldmonitor.git
synced 2026-04-25 17:14:57 +02:00
feat(supply-chain): Sprint D — GetSectorDependency RPC + vendor route-intelligence API + webhooks (#2905)
* feat(supply-chain): Sprint D — GetSectorDependency RPC + vendor route-intelligence API + webhooks * fix(supply-chain): move bypass-corridors + chokepoint-registry to server/_shared to fix api/ boundary violations * fix(supply-chain): webhooks — persist secret, fix sub-resource routing, add ownership check * fix(supply-chain): address PR #2905 review findings - Use SHA-256(apiKey) for ownerTag instead of last-12-chars (unambiguous ownership) - Implement GET /api/v2/shipping/webhooks list route via per-owner Redis Set index - Tighten SSRF: https-only, expanded metadata hostname blocklist, document DNS rebinding edge-runtime limitation - Fix get-sector-dependency.ts stale src/config/ imports → server/_shared/ (Greptile P1) * fix(supply-chain): getSectorDependency returns blank primaryChokepointId for landlocked countries computeExposures() previously mapped over all of CHOKEPOINT_REGISTRY even when nearestRouteIds was empty, producing a full array of score-0 entries in registry insertion order. The caller's exposures[0] then picked the first registry entry (Suez) as the "primary" chokepoint despite primaryChokepointExposure = 0. LI, AD, SM, BT and other landlocked countries were all silently assigned a fake chokepoint. Fix: guard at the top of computeExposures() -- return [] when input is empty so primaryChokepointId stays '' and primaryChokepointExposure stays 0.
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
syntax = "proto3";
|
||||
package worldmonitor.supply_chain.v1;
|
||||
|
||||
import "buf/validate/validate.proto";
|
||||
import "sebuf/http/annotations.proto";
|
||||
|
||||
// DependencyFlag classifies how a country+sector dependency can fail.
|
||||
enum DependencyFlag {
|
||||
DEPENDENCY_FLAG_UNSPECIFIED = 0;
|
||||
// >80% of imports come from a single exporter country.
|
||||
DEPENDENCY_FLAG_SINGLE_SOURCE_CRITICAL = 1;
|
||||
// >80% of imports transit a single chokepoint with no viable bypass route.
|
||||
DEPENDENCY_FLAG_SINGLE_CORRIDOR_CRITICAL = 2;
|
||||
// Both SINGLE_SOURCE_CRITICAL and SINGLE_CORRIDOR_CRITICAL apply.
|
||||
DEPENDENCY_FLAG_COMPOUND_RISK = 3;
|
||||
// Viable bypass exists AND multiple exporters are available.
|
||||
DEPENDENCY_FLAG_DIVERSIFIABLE = 4;
|
||||
}
|
||||
|
||||
message GetSectorDependencyRequest {
|
||||
string iso2 = 1 [
|
||||
(buf.validate.field).required = true,
|
||||
(buf.validate.field).string.len = 2,
|
||||
(buf.validate.field).string.pattern = "^[A-Z]{2}$",
|
||||
(sebuf.http.query) = {name: "iso2"}
|
||||
];
|
||||
// HS2 chapter code, e.g. "27" (mineral fuels), "85" (electronics)
|
||||
string hs2 = 2 [
|
||||
(buf.validate.field).required = true,
|
||||
(sebuf.http.query) = {name: "hs2"}
|
||||
];
|
||||
}
|
||||
|
||||
message GetSectorDependencyResponse {
|
||||
string iso2 = 1;
|
||||
string hs2 = 2;
|
||||
// Human-readable HS2 chapter name.
|
||||
string hs2_label = 3;
|
||||
// One or more dependency flags; empty means no critical dependency identified.
|
||||
repeated DependencyFlag flags = 4;
|
||||
// ISO2 of the country supplying the largest share of this sector's imports.
|
||||
string primary_exporter_iso2 = 5;
|
||||
// Share of imports from the primary exporter (0–1). 0 = no Comtrade data available.
|
||||
double primary_exporter_share = 6;
|
||||
// Chokepoint ID with the highest exposure score for this country+sector.
|
||||
string primary_chokepoint_id = 7;
|
||||
// Exposure score for the primary chokepoint (0–100).
|
||||
double primary_chokepoint_exposure = 8;
|
||||
// Whether at least one viable bypass corridor exists for the primary chokepoint.
|
||||
bool has_viable_bypass = 9;
|
||||
string fetched_at = 10;
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import "worldmonitor/supply_chain/v1/get_shipping_stress.proto";
|
||||
import "worldmonitor/supply_chain/v1/get_country_chokepoint_index.proto";
|
||||
import "worldmonitor/supply_chain/v1/get_bypass_options.proto";
|
||||
import "worldmonitor/supply_chain/v1/get_country_cost_shock.proto";
|
||||
import "worldmonitor/supply_chain/v1/get_sector_dependency.proto";
|
||||
|
||||
service SupplyChainService {
|
||||
option (sebuf.http.service_config) = {base_path: "/api/supply-chain/v1"};
|
||||
@@ -45,4 +46,9 @@ service SupplyChainService {
|
||||
rpc GetCountryCostShock(GetCountryCostShockRequest) returns (GetCountryCostShockResponse) {
|
||||
option (sebuf.http.config) = {path: "/get-country-cost-shock", method: HTTP_METHOD_GET};
|
||||
}
|
||||
|
||||
// GetSectorDependency returns dependency flags and risk profile for a country+HS2 sector. PRO-gated.
|
||||
rpc GetSectorDependency(GetSectorDependencyRequest) returns (GetSectorDependencyResponse) {
|
||||
option (sebuf.http.config) = {path: "/get-sector-dependency", method: HTTP_METHOD_GET};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user