Skip to main content

basilisk_rust_client/
gateway_api.rs

1use anyhow::Context;
2use reqwest::StatusCode;
3use serde::{Deserialize, Serialize};
4
5/// HTTP client for Basilisk gateway registry APIs.
6#[derive(Clone)]
7pub struct GatewayApiClient {
8    base_url: String,
9    http: reqwest::Client,
10}
11
12impl GatewayApiClient {
13    /// Creates a new gateway API client.
14    pub fn new(base_url: impl Into<String>) -> Self {
15        Self {
16            base_url: base_url.into().trim_end_matches('/').to_string(),
17            http: reqwest::Client::new(),
18        }
19    }
20
21    /// Registers a service instance after clearing `instance.instance_id` so the gateway can generate one.
22    pub async fn register_instance_auto(
23        &self,
24        request: &RegistrationRequest,
25    ) -> anyhow::Result<RegistrationResponse> {
26        let mut request = request.clone();
27        request.instance.instance_id.clear();
28        self.register_instance(&request).await
29    }
30
31    /// Registers a service instance at `/registry/register`.
32    pub async fn register_instance(
33        &self,
34        request: &RegistrationRequest,
35    ) -> anyhow::Result<RegistrationResponse> {
36        let url = format!("{}/registry/register", self.base_url);
37        let response = self
38            .http
39            .post(url)
40            .json(request)
41            .send()
42            .await
43            .context("failed to call /registry/register")?;
44
45        let status = response.status();
46        if status != StatusCode::OK {
47            let body = response
48                .text()
49                .await
50                .unwrap_or_else(|_| "<no body>".to_string());
51            anyhow::bail!("register failed with status {status}: {body}");
52        }
53
54        response
55            .json::<RegistrationResponse>()
56            .await
57            .context("failed to parse registry register response")
58    }
59
60    /// Deregisters an existing instance by service and instance id.
61    pub async fn deregister_instance(
62        &self,
63        service_id: &str,
64        instance_id: &str,
65    ) -> anyhow::Result<()> {
66        let url = format!(
67            "{}/registry/services/{}/instances/{}",
68            self.base_url, service_id, instance_id
69        );
70        let response = self
71            .http
72            .delete(url)
73            .send()
74            .await
75            .context("failed to call deregister endpoint")?;
76
77        let status = response.status();
78        if status != StatusCode::OK {
79            let body = response
80                .text()
81                .await
82                .unwrap_or_else(|_| "<no body>".to_string());
83            anyhow::bail!("deregister failed with status {status}: {body}");
84        }
85
86        Ok(())
87    }
88}
89
90/// Request body for gateway instance registration.
91#[derive(Debug, Clone, Serialize, Deserialize)]
92pub struct RegistrationRequest {
93    /// Logical service identifier.
94    #[serde(rename = "serviceId")]
95    pub service_id: String,
96    /// Service fingerprint/version marker.
97    pub fingerprint: String,
98    /// Path prefixes exposed by this instance.
99    #[serde(rename = "pathPrefixes")]
100    pub path_prefixes: Vec<String>,
101    /// Network/location metadata for the running instance.
102    pub instance: InstanceInfo,
103    /// Registration authentication metadata.
104    pub auth: AuthInfo,
105}
106
107/// Instance metadata sent to the gateway registry.
108#[derive(Debug, Clone, Serialize, Deserialize)]
109pub struct InstanceInfo {
110    /// Service instance id. Empty string allows server-side generation.
111    #[serde(rename = "instanceId")]
112    pub instance_id: String,
113    /// Upstream URL scheme.
114    pub scheme: String,
115    /// Upstream host.
116    pub host: String,
117    /// Upstream port.
118    pub port: u16,
119    /// Load-balancing weight.
120    pub weight: i32,
121}
122
123/// Registration auth metadata.
124#[derive(Debug, Clone, Serialize, Deserialize)]
125pub struct AuthInfo {
126    /// Auth mechanism type (for example `token`).
127    #[serde(rename = "type")]
128    pub auth_type: String,
129    /// Shared secret/token value.
130    pub token: String,
131}
132
133/// Successful response payload for instance registration.
134#[derive(Debug, Clone, Serialize, Deserialize)]
135pub struct RegistrationResponse {
136    /// Human-readable status message.
137    pub message: String,
138    /// Registered service id.
139    #[serde(rename = "serviceId")]
140    pub service_id: String,
141    /// Registered instance id.
142    #[serde(rename = "instanceId")]
143    pub instance_id: String,
144    /// Issued token for service-bus authentication.
145    pub token: String,
146}