Skip to main content

clickhouse_cloud_api/
client.rs

1//! HTTP client for the ClickHouse Cloud API.
2//!
3//! Auto-generated from the OpenAPI specification.
4
5use crate::error::Error;
6use crate::models::*;
7
8/// Authentication mode for the API client.
9#[derive(Debug, Clone)]
10enum Auth {
11    Basic { key_id: String, key_secret: String },
12    Bearer { token: String },
13}
14
15/// Credentials for a single Query API request. Basic carries a per-service
16/// Query API key; Bearer carries the user's OAuth token.
17enum QueryAuth<'a> {
18    Basic {
19        key_id: &'a str,
20        key_secret: &'a str,
21    },
22    Bearer {
23        token: &'a str,
24    },
25}
26
27/// ClickHouse Cloud API client.
28///
29/// Supports both HTTP Basic Auth (API key/secret) and Bearer token (OAuth) authentication.
30#[derive(Debug, Clone)]
31pub struct Client {
32    http: reqwest::Client,
33    base_url: String,
34    auth: Auth,
35    /// Explicit Query API host override; see [`Client::with_query_host`].
36    query_host: Option<String>,
37}
38
39/// Derive the Query API host from a management API base URL by swapping the
40/// `api.` host prefix for `queries.`, so each environment talks to its own
41/// query host. Staging and dev serve the management API under an extra
42/// `control-plane.` label that the query host doesn't have, so it is
43/// dropped too:
44///
45/// - `https://api.clickhouse.cloud` → `https://queries.clickhouse.cloud`
46/// - `https://api.control-plane.clickhouse-staging.com` →
47///   `https://queries.clickhouse-staging.com`
48///
49/// Returns `None` when the base URL isn't of that shape (e.g. a localhost
50/// test server).
51fn derive_query_host(base_url: &str) -> Option<String> {
52    let parsed = url::Url::parse(base_url).ok()?;
53    let rest = parsed.host_str()?.strip_prefix("api.")?;
54    let rest = rest.strip_prefix("control-plane.").unwrap_or(rest);
55    let port = parsed
56        .port()
57        .map(|p| format!(":{p}"))
58        .unwrap_or_default();
59    Some(format!("{}://queries.{}{}", parsed.scheme(), rest, port))
60}
61
62impl Client {
63    /// Create a new client with the default base URL (`https://api.clickhouse.cloud`).
64    pub fn new(key_id: impl Into<String>, key_secret: impl Into<String>) -> Self {
65        Self::with_base_url("https://api.clickhouse.cloud", key_id, key_secret)
66    }
67
68    /// Create a new client with a custom base URL.
69    pub fn with_base_url(
70        base_url: impl Into<String>,
71        key_id: impl Into<String>,
72        key_secret: impl Into<String>,
73    ) -> Self {
74        Self {
75            http: reqwest::Client::new(),
76            base_url: base_url.into().trim_end_matches('/').to_string(),
77            auth: Auth::Basic {
78                key_id: key_id.into(),
79                key_secret: key_secret.into(),
80            },
81            query_host: None,
82        }
83    }
84
85    /// Create a new client with Bearer token authentication and a custom base URL.
86    pub fn with_bearer_token(
87        base_url: impl Into<String>,
88        token: impl Into<String>,
89    ) -> Self {
90        Self {
91            http: reqwest::Client::new(),
92            base_url: base_url.into().trim_end_matches('/').to_string(),
93            auth: Auth::Bearer {
94                token: token.into(),
95            },
96            query_host: None,
97        }
98    }
99
100    /// Create a new client with a pre-built HTTP client and Basic auth.
101    ///
102    /// Use this when you need to customize the underlying `reqwest::Client`
103    /// (e.g. to set a custom user-agent or timeout).
104    pub fn with_http_client(
105        http: reqwest::Client,
106        base_url: impl Into<String>,
107        key_id: impl Into<String>,
108        key_secret: impl Into<String>,
109    ) -> Self {
110        Self {
111            http,
112            base_url: base_url.into().trim_end_matches('/').to_string(),
113            auth: Auth::Basic {
114                key_id: key_id.into(),
115                key_secret: key_secret.into(),
116            },
117            query_host: None,
118        }
119    }
120
121    /// Create a new client with a pre-built HTTP client and Bearer auth.
122    ///
123    /// Use this when you need to customize the underlying `reqwest::Client`
124    /// (e.g. to set a custom user-agent or timeout).
125    pub fn with_http_client_bearer(
126        http: reqwest::Client,
127        base_url: impl Into<String>,
128        token: impl Into<String>,
129    ) -> Self {
130        Self {
131            http,
132            base_url: base_url.into().trim_end_matches('/').to_string(),
133            auth: Auth::Bearer {
134                token: token.into(),
135            },
136            query_host: None,
137        }
138    }
139
140    /// Replace the Bearer token without rebuilding the client.
141    ///
142    /// Useful for refreshing an expired OAuth token.
143    /// Returns an error if the client is using Basic auth.
144    pub fn set_bearer_token(&mut self, token: impl Into<String>) -> Result<(), Error> {
145        match &mut self.auth {
146            Auth::Bearer { token: t } => {
147                *t = token.into();
148                Ok(())
149            }
150            Auth::Basic { .. } => Err(Error::AuthMismatch(
151                "set_bearer_token called on a Basic-auth client".into(),
152            )),
153        }
154    }
155
156    /// Override the Query API host used by [`Client::run_query`] and
157    /// [`Client::run_query_bearer`].
158    ///
159    /// When not set, the host is taken from the `CLICKHOUSE_CLOUD_QUERY_HOST`
160    /// env var if present, otherwise derived from the client's base URL
161    /// (`api.<domain>` → `queries.<domain>`), falling back to the production
162    /// host `https://queries.clickhouse.cloud`.
163    pub fn with_query_host(mut self, host: impl Into<String>) -> Self {
164        self.query_host = Some(host.into().trim_end_matches('/').to_string());
165        self
166    }
167
168    /// Resolve the Query API host: explicit override, then env var, then
169    /// derivation from the base URL, then the production default.
170    fn resolved_query_host(&self) -> String {
171        if let Some(host) = &self.query_host {
172            return host.clone();
173        }
174        if let Ok(host) = std::env::var("CLICKHOUSE_CLOUD_QUERY_HOST") {
175            return host;
176        }
177        derive_query_host(&self.base_url)
178            .unwrap_or_else(|| "https://queries.clickhouse.cloud".to_string())
179    }
180
181    fn request(&self, method: reqwest::Method, path: &str) -> reqwest::RequestBuilder {
182        let builder = self
183            .http
184            .request(method, format!("{}{}", self.base_url, path));
185        match &self.auth {
186            Auth::Basic { key_id, key_secret } => builder.basic_auth(key_id, Some(key_secret)),
187            Auth::Bearer { token } => builder.bearer_auth(token),
188        }
189    }
190
191    /// Run a SQL statement against a service's Query API endpoint.
192    ///
193    /// Hits the environment's query host (see [`Client::with_query_host`]
194    /// for resolution order) using Basic auth with the provided
195    /// `key_id`/`key_secret` — a per-service key bound to a query endpoint
196    /// with role `sql_console_read_only` (or `sql_console_admin`). This
197    /// bypasses the client's primary auth because Query API keys are scoped
198    /// to a single service.
199    ///
200    /// `wake_service` resends the wake confirmation the query host asks for
201    /// when the target service is idled — see [`Error::ServiceIdle`].
202    ///
203    /// Returns the streaming response so the caller can forward it to
204    /// stdout or buffer it into memory.
205    #[allow(clippy::too_many_arguments)]
206    pub async fn run_query(
207        &self,
208        service_id: &str,
209        key_id: &str,
210        key_secret: &str,
211        sql: &str,
212        database: Option<&str>,
213        format: &str,
214        wake_service: bool,
215    ) -> Result<reqwest::Response, Error> {
216        self.run_query_with(
217            QueryAuth::Basic { key_id, key_secret },
218            service_id,
219            sql,
220            database,
221            format,
222            wake_service,
223        )
224        .await
225    }
226
227    /// Run a SQL statement against a service's Query API endpoint using the
228    /// client's own OAuth Bearer token.
229    ///
230    /// Unlike [`Client::run_query`], no per-service Query API key and no
231    /// query-endpoint configuration are needed: the Query API authenticates
232    /// the user's identity directly (SQL-console style), and SQL permissions
233    /// follow the user's console role.
234    ///
235    /// `wake_service` resends the wake confirmation the query host asks for
236    /// when the target service is idled — see [`Error::ServiceIdle`].
237    ///
238    /// Returns an error if the client is using Basic auth.
239    pub async fn run_query_bearer(
240        &self,
241        service_id: &str,
242        sql: &str,
243        database: Option<&str>,
244        format: &str,
245        wake_service: bool,
246    ) -> Result<reqwest::Response, Error> {
247        let token = match &self.auth {
248            Auth::Bearer { token } => token,
249            Auth::Basic { .. } => {
250                return Err(Error::AuthMismatch(
251                    "run_query_bearer called on a Basic-auth client".into(),
252                ));
253            }
254        };
255        self.run_query_with(
256            QueryAuth::Bearer { token },
257            service_id,
258            sql,
259            database,
260            format,
261            wake_service,
262        )
263        .await
264    }
265
266    async fn run_query_with(
267        &self,
268        auth: QueryAuth<'_>,
269        service_id: &str,
270        sql: &str,
271        database: Option<&str>,
272        format: &str,
273        wake_service: bool,
274    ) -> Result<reqwest::Response, Error> {
275        #[derive(serde::Serialize)]
276        #[serde(rename_all = "camelCase")]
277        struct RunQueryBody<'a> {
278            run_id: String,
279            sql: &'a str,
280            #[serde(skip_serializing_if = "Option::is_none")]
281            database: Option<&'a str>,
282        }
283
284        let url = format!(
285            "{}/service/{}/run",
286            self.resolved_query_host().trim_end_matches('/'),
287            service_id,
288        );
289
290        let body = RunQueryBody {
291            run_id: uuid::Uuid::new_v4().to_string(),
292            sql,
293            database,
294        };
295
296        let request = self
297            .http
298            .post(url)
299            .query(&[("format", format)])
300            .header("content-type", "text/plain;charset=UTF-8")
301            .header("x-service-type", "clickhouse");
302        // `wake-service: true` is the wake confirmation the query host asks
303        // for via a 206 `Confirm wake service` response (the SQL console
304        // sends it after prompting the user).
305        let request = if wake_service {
306            request.header("wake-service", "true")
307        } else {
308            request
309        };
310        // `auth-provider: custom` tells the query host the credentials are a
311        // custom (user-provisioned) Query API key. Bearer tokens carry their
312        // own provider information, so the header is omitted for them.
313        let request = match auth {
314            QueryAuth::Basic { key_id, key_secret } => request
315                .basic_auth(key_id, Some(key_secret))
316                .header("auth-provider", "custom"),
317            QueryAuth::Bearer { token } => request.bearer_auth(token),
318        };
319
320        let response = request.json(&body).send().await?;
321
322        let status = response.status();
323        // 206 means the service can't take the query in its current state:
324        // `Confirm wake service` for an idled service (resend with the
325        // wake confirmation to wake it and run the query), `Service is
326        // stopped` for one that must be started explicitly.
327        if status.as_u16() == 206 {
328            let body_text = response.text().await.unwrap_or_default();
329            #[derive(serde::Deserialize)]
330            struct StateBody {
331                data: Option<String>,
332            }
333            let data = serde_json::from_str::<StateBody>(&body_text)
334                .ok()
335                .and_then(|b| b.data);
336            return Err(match data.as_deref() {
337                Some("Confirm wake service") => Error::ServiceIdle,
338                Some("Service is stopped") => Error::ServiceStopped,
339                _ => Error::Api {
340                    status: 206,
341                    message: body_text,
342                },
343            });
344        }
345        if !status.is_success() {
346            let body_text = response.text().await.unwrap_or_default();
347            return Err(Error::Api {
348                status: status.as_u16(),
349                message: if body_text.is_empty() {
350                    format!("Query API returned {status}")
351                } else {
352                    body_text
353                },
354            });
355        }
356
357        Ok(response)
358    }
359
360    /// Get list of available organizations
361    pub async fn organization_get_list(
362        &self,
363    ) -> Result<ApiResponse<Vec<Organization>>, Error> {
364        let path = "/v1/organizations".to_string();
365        let req = self.request(reqwest::Method::GET, &path);
366        let resp = req.send().await?;
367        let status = resp.status();
368        let body_text = resp.text().await?;
369        if !status.is_success() {
370            return Err(Error::Api {
371                status: status.as_u16(),
372                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
373                    .ok()
374                    .and_then(|r| r.error)
375                    .unwrap_or(body_text.clone()),
376            });
377        }
378        Ok(serde_json::from_str(&body_text)?)
379    }
380
381    /// Get organization details
382    pub async fn organization_get(
383        &self,
384        organization_id: &str,
385    ) -> Result<ApiResponse<Organization>, Error> {
386        let path = format!("/v1/organizations/{organization_id}");
387        let req = self.request(reqwest::Method::GET, &path);
388        let resp = req.send().await?;
389        let status = resp.status();
390        let body_text = resp.text().await?;
391        if !status.is_success() {
392            return Err(Error::Api {
393                status: status.as_u16(),
394                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
395                    .ok()
396                    .and_then(|r| r.error)
397                    .unwrap_or(body_text.clone()),
398            });
399        }
400        Ok(serde_json::from_str(&body_text)?)
401    }
402
403    /// Update organization details
404    pub async fn organization_update(
405        &self,
406        organization_id: &str,
407        body: &OrganizationPatchRequest,
408    ) -> Result<ApiResponse<Organization>, Error> {
409        let path = format!("/v1/organizations/{organization_id}");
410        let mut req = self.request(reqwest::Method::PATCH, &path);
411        req = req.json(body);
412        let resp = req.send().await?;
413        let status = resp.status();
414        let body_text = resp.text().await?;
415        if !status.is_success() {
416            return Err(Error::Api {
417                status: status.as_u16(),
418                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
419                    .ok()
420                    .and_then(|r| r.error)
421                    .unwrap_or(body_text.clone()),
422            });
423        }
424        Ok(serde_json::from_str(&body_text)?)
425    }
426
427    /// List of organization activities
428    pub async fn activity_get_list(
429        &self,
430        organization_id: &str,
431        from_date: Option<&str>,
432        to_date: Option<&str>,
433    ) -> Result<ApiResponse<Vec<Activity>>, Error> {
434        let path = format!("/v1/organizations/{organization_id}/activities");
435        let mut req = self.request(reqwest::Method::GET, &path);
436        if let Some(v) = from_date {
437            req = req.query(&[("from_date", v)]);
438        }
439        if let Some(v) = to_date {
440            req = req.query(&[("to_date", v)]);
441        }
442        let resp = req.send().await?;
443        let status = resp.status();
444        let body_text = resp.text().await?;
445        if !status.is_success() {
446            return Err(Error::Api {
447                status: status.as_u16(),
448                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
449                    .ok()
450                    .and_then(|r| r.error)
451                    .unwrap_or(body_text.clone()),
452            });
453        }
454        Ok(serde_json::from_str(&body_text)?)
455    }
456
457    /// Organization activity
458    pub async fn activity_get(
459        &self,
460        organization_id: &str,
461        activity_id: &str,
462    ) -> Result<ApiResponse<Activity>, Error> {
463        let path = format!("/v1/organizations/{organization_id}/activities/{activity_id}");
464        let req = self.request(reqwest::Method::GET, &path);
465        let resp = req.send().await?;
466        let status = resp.status();
467        let body_text = resp.text().await?;
468        if !status.is_success() {
469            return Err(Error::Api {
470                status: status.as_u16(),
471                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
472                    .ok()
473                    .and_then(|r| r.error)
474                    .unwrap_or(body_text.clone()),
475            });
476        }
477        Ok(serde_json::from_str(&body_text)?)
478    }
479
480    /// Create BYOC Infrastructure
481    pub async fn organization_byoc_infrastructure_create(
482        &self,
483        organization_id: &str,
484        body: &ByocInfrastructurePostRequest,
485    ) -> Result<ApiResponse<ByocConfig>, Error> {
486        let path = format!("/v1/organizations/{organization_id}/byocInfrastructure");
487        let mut req = self.request(reqwest::Method::POST, &path);
488        req = req.json(body);
489        let resp = req.send().await?;
490        let status = resp.status();
491        let body_text = resp.text().await?;
492        if !status.is_success() {
493            return Err(Error::Api {
494                status: status.as_u16(),
495                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
496                    .ok()
497                    .and_then(|r| r.error)
498                    .unwrap_or(body_text.clone()),
499            });
500        }
501        Ok(serde_json::from_str(&body_text)?)
502    }
503
504    /// Remove a BYOC infrastructure
505    pub async fn organization_byoc_infrastructure_delete(
506        &self,
507        organization_id: &str,
508        byoc_infrastructure_id: &str,
509    ) -> Result<ApiResponse<serde_json::Value>, Error> {
510        let path = format!("/v1/organizations/{organization_id}/byocInfrastructure/{byoc_infrastructure_id}");
511        let req = self.request(reqwest::Method::DELETE, &path);
512        let resp = req.send().await?;
513        let status = resp.status();
514        let body_text = resp.text().await?;
515        if !status.is_success() {
516            return Err(Error::Api {
517                status: status.as_u16(),
518                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
519                    .ok()
520                    .and_then(|r| r.error)
521                    .unwrap_or(body_text.clone()),
522            });
523        }
524        Ok(serde_json::from_str(&body_text)?)
525    }
526
527    /// Update BYOC Infrastructure
528    pub async fn organization_byoc_infrastructure_update(
529        &self,
530        organization_id: &str,
531        byoc_infrastructure_id: &str,
532        body: &ByocInfrastructurePatchRequest,
533    ) -> Result<ApiResponse<ByocConfig>, Error> {
534        let path = format!("/v1/organizations/{organization_id}/byocInfrastructure/{byoc_infrastructure_id}");
535        let mut req = self.request(reqwest::Method::PATCH, &path);
536        req = req.json(body);
537        let resp = req.send().await?;
538        let status = resp.status();
539        let body_text = resp.text().await?;
540        if !status.is_success() {
541            return Err(Error::Api {
542                status: status.as_u16(),
543                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
544                    .ok()
545                    .and_then(|r| r.error)
546                    .unwrap_or(body_text.clone()),
547            });
548        }
549        Ok(serde_json::from_str(&body_text)?)
550    }
551
552    /// List all invitations
553    pub async fn invitation_get_list(
554        &self,
555        organization_id: &str,
556    ) -> Result<ApiResponse<Vec<Invitation>>, Error> {
557        let path = format!("/v1/organizations/{organization_id}/invitations");
558        let req = self.request(reqwest::Method::GET, &path);
559        let resp = req.send().await?;
560        let status = resp.status();
561        let body_text = resp.text().await?;
562        if !status.is_success() {
563            return Err(Error::Api {
564                status: status.as_u16(),
565                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
566                    .ok()
567                    .and_then(|r| r.error)
568                    .unwrap_or(body_text.clone()),
569            });
570        }
571        Ok(serde_json::from_str(&body_text)?)
572    }
573
574    /// Create an invitation
575    pub async fn invitation_create(
576        &self,
577        organization_id: &str,
578        body: &InvitationPostRequest,
579    ) -> Result<ApiResponse<Invitation>, Error> {
580        let path = format!("/v1/organizations/{organization_id}/invitations");
581        let mut req = self.request(reqwest::Method::POST, &path);
582        req = req.json(body);
583        let resp = req.send().await?;
584        let status = resp.status();
585        let body_text = resp.text().await?;
586        if !status.is_success() {
587            return Err(Error::Api {
588                status: status.as_u16(),
589                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
590                    .ok()
591                    .and_then(|r| r.error)
592                    .unwrap_or(body_text.clone()),
593            });
594        }
595        Ok(serde_json::from_str(&body_text)?)
596    }
597
598    /// Get invitation details
599    pub async fn invitation_get(
600        &self,
601        organization_id: &str,
602        invitation_id: &str,
603    ) -> Result<ApiResponse<Invitation>, Error> {
604        let path = format!("/v1/organizations/{organization_id}/invitations/{invitation_id}");
605        let req = self.request(reqwest::Method::GET, &path);
606        let resp = req.send().await?;
607        let status = resp.status();
608        let body_text = resp.text().await?;
609        if !status.is_success() {
610            return Err(Error::Api {
611                status: status.as_u16(),
612                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
613                    .ok()
614                    .and_then(|r| r.error)
615                    .unwrap_or(body_text.clone()),
616            });
617        }
618        Ok(serde_json::from_str(&body_text)?)
619    }
620
621    /// Delete organization invitation
622    pub async fn invitation_delete(
623        &self,
624        organization_id: &str,
625        invitation_id: &str,
626    ) -> Result<ApiResponse<serde_json::Value>, Error> {
627        let path = format!("/v1/organizations/{organization_id}/invitations/{invitation_id}");
628        let req = self.request(reqwest::Method::DELETE, &path);
629        let resp = req.send().await?;
630        let status = resp.status();
631        let body_text = resp.text().await?;
632        if !status.is_success() {
633            return Err(Error::Api {
634                status: status.as_u16(),
635                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
636                    .ok()
637                    .and_then(|r| r.error)
638                    .unwrap_or(body_text.clone()),
639            });
640        }
641        Ok(serde_json::from_str(&body_text)?)
642    }
643
644    /// Get list of all keys
645    pub async fn openapi_key_get_list(
646        &self,
647        organization_id: &str,
648    ) -> Result<ApiResponse<Vec<ApiKey>>, Error> {
649        let path = format!("/v1/organizations/{organization_id}/keys");
650        let req = self.request(reqwest::Method::GET, &path);
651        let resp = req.send().await?;
652        let status = resp.status();
653        let body_text = resp.text().await?;
654        if !status.is_success() {
655            return Err(Error::Api {
656                status: status.as_u16(),
657                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
658                    .ok()
659                    .and_then(|r| r.error)
660                    .unwrap_or(body_text.clone()),
661            });
662        }
663        Ok(serde_json::from_str(&body_text)?)
664    }
665
666    /// Create key
667    pub async fn openapi_key_create(
668        &self,
669        organization_id: &str,
670        body: &ApiKeyPostRequest,
671    ) -> Result<ApiResponse<ApiKeyPostResponse>, Error> {
672        let path = format!("/v1/organizations/{organization_id}/keys");
673        let mut req = self.request(reqwest::Method::POST, &path);
674        req = req.json(body);
675        let resp = req.send().await?;
676        let status = resp.status();
677        let body_text = resp.text().await?;
678        if !status.is_success() {
679            return Err(Error::Api {
680                status: status.as_u16(),
681                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
682                    .ok()
683                    .and_then(|r| r.error)
684                    .unwrap_or(body_text.clone()),
685            });
686        }
687        Ok(serde_json::from_str(&body_text)?)
688    }
689
690    /// Get key details
691    pub async fn openapi_key_get(
692        &self,
693        organization_id: &str,
694        key_id: &str,
695    ) -> Result<ApiResponse<ApiKey>, Error> {
696        let path = format!("/v1/organizations/{organization_id}/keys/{key_id}");
697        let req = self.request(reqwest::Method::GET, &path);
698        let resp = req.send().await?;
699        let status = resp.status();
700        let body_text = resp.text().await?;
701        if !status.is_success() {
702            return Err(Error::Api {
703                status: status.as_u16(),
704                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
705                    .ok()
706                    .and_then(|r| r.error)
707                    .unwrap_or(body_text.clone()),
708            });
709        }
710        Ok(serde_json::from_str(&body_text)?)
711    }
712
713    /// Update key
714    pub async fn openapi_key_update(
715        &self,
716        organization_id: &str,
717        key_id: &str,
718        body: &ApiKeyPatchRequest,
719    ) -> Result<ApiResponse<ApiKey>, Error> {
720        let path = format!("/v1/organizations/{organization_id}/keys/{key_id}");
721        let mut req = self.request(reqwest::Method::PATCH, &path);
722        req = req.json(body);
723        let resp = req.send().await?;
724        let status = resp.status();
725        let body_text = resp.text().await?;
726        if !status.is_success() {
727            return Err(Error::Api {
728                status: status.as_u16(),
729                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
730                    .ok()
731                    .and_then(|r| r.error)
732                    .unwrap_or(body_text.clone()),
733            });
734        }
735        Ok(serde_json::from_str(&body_text)?)
736    }
737
738    /// Delete key
739    pub async fn openapi_key_delete(
740        &self,
741        organization_id: &str,
742        key_id: &str,
743    ) -> Result<ApiResponse<serde_json::Value>, Error> {
744        let path = format!("/v1/organizations/{organization_id}/keys/{key_id}");
745        let req = self.request(reqwest::Method::DELETE, &path);
746        let resp = req.send().await?;
747        let status = resp.status();
748        let body_text = resp.text().await?;
749        if !status.is_success() {
750            return Err(Error::Api {
751                status: status.as_u16(),
752                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
753                    .ok()
754                    .and_then(|r| r.error)
755                    .unwrap_or(body_text.clone()),
756            });
757        }
758        Ok(serde_json::from_str(&body_text)?)
759    }
760
761    /// List organization members
762    pub async fn member_get_list(
763        &self,
764        organization_id: &str,
765    ) -> Result<ApiResponse<Vec<Member>>, Error> {
766        let path = format!("/v1/organizations/{organization_id}/members");
767        let req = self.request(reqwest::Method::GET, &path);
768        let resp = req.send().await?;
769        let status = resp.status();
770        let body_text = resp.text().await?;
771        if !status.is_success() {
772            return Err(Error::Api {
773                status: status.as_u16(),
774                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
775                    .ok()
776                    .and_then(|r| r.error)
777                    .unwrap_or(body_text.clone()),
778            });
779        }
780        Ok(serde_json::from_str(&body_text)?)
781    }
782
783    /// Get member details
784    pub async fn member_get(
785        &self,
786        organization_id: &str,
787        user_id: &str,
788    ) -> Result<ApiResponse<Member>, Error> {
789        let path = format!("/v1/organizations/{organization_id}/members/{user_id}");
790        let req = self.request(reqwest::Method::GET, &path);
791        let resp = req.send().await?;
792        let status = resp.status();
793        let body_text = resp.text().await?;
794        if !status.is_success() {
795            return Err(Error::Api {
796                status: status.as_u16(),
797                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
798                    .ok()
799                    .and_then(|r| r.error)
800                    .unwrap_or(body_text.clone()),
801            });
802        }
803        Ok(serde_json::from_str(&body_text)?)
804    }
805
806    /// Update organization member
807    pub async fn member_update(
808        &self,
809        organization_id: &str,
810        user_id: &str,
811        body: &MemberPatchRequest,
812    ) -> Result<ApiResponse<Member>, Error> {
813        let path = format!("/v1/organizations/{organization_id}/members/{user_id}");
814        let mut req = self.request(reqwest::Method::PATCH, &path);
815        req = req.json(body);
816        let resp = req.send().await?;
817        let status = resp.status();
818        let body_text = resp.text().await?;
819        if !status.is_success() {
820            return Err(Error::Api {
821                status: status.as_u16(),
822                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
823                    .ok()
824                    .and_then(|r| r.error)
825                    .unwrap_or(body_text.clone()),
826            });
827        }
828        Ok(serde_json::from_str(&body_text)?)
829    }
830
831    /// Remove an organization member
832    pub async fn member_delete(
833        &self,
834        organization_id: &str,
835        user_id: &str,
836    ) -> Result<ApiResponse<serde_json::Value>, Error> {
837        let path = format!("/v1/organizations/{organization_id}/members/{user_id}");
838        let req = self.request(reqwest::Method::DELETE, &path);
839        let resp = req.send().await?;
840        let status = resp.status();
841        let body_text = resp.text().await?;
842        if !status.is_success() {
843            return Err(Error::Api {
844                status: status.as_u16(),
845                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
846                    .ok()
847                    .and_then(|r| r.error)
848                    .unwrap_or(body_text.clone()),
849            });
850        }
851        Ok(serde_json::from_str(&body_text)?)
852    }
853
854    /// List all available roles for an organization
855    pub async fn organization_roles_get_list(
856        &self,
857        organization_id: &str,
858    ) -> Result<ApiResponse<Vec<RBACRole>>, Error> {
859        let path = format!("/v1/organizations/{organization_id}/roles");
860        let req = self.request(reqwest::Method::GET, &path);
861        let resp = req.send().await?;
862        let status = resp.status();
863        let body_text = resp.text().await?;
864        if !status.is_success() {
865            return Err(Error::Api {
866                status: status.as_u16(),
867                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
868                    .ok()
869                    .and_then(|r| r.error)
870                    .unwrap_or(body_text.clone()),
871            });
872        }
873        Ok(serde_json::from_str(&body_text)?)
874    }
875
876    /// Create a new role
877    pub async fn organization_role_post(
878        &self,
879        organization_id: &str,
880        body: &RoleCreateRequest,
881    ) -> Result<ApiResponse<RBACRole>, Error> {
882        let path = format!("/v1/organizations/{organization_id}/roles");
883        let mut req = self.request(reqwest::Method::POST, &path);
884        req = req.json(body);
885        let resp = req.send().await?;
886        let status = resp.status();
887        let body_text = resp.text().await?;
888        if !status.is_success() {
889            return Err(Error::Api {
890                status: status.as_u16(),
891                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
892                    .ok()
893                    .and_then(|r| r.error)
894                    .unwrap_or(body_text.clone()),
895            });
896        }
897        Ok(serde_json::from_str(&body_text)?)
898    }
899
900    /// Get role details
901    pub async fn organization_role_get(
902        &self,
903        organization_id: &str,
904        role_id: &str,
905    ) -> Result<ApiResponse<RBACRole>, Error> {
906        let path = format!("/v1/organizations/{organization_id}/roles/{role_id}");
907        let req = self.request(reqwest::Method::GET, &path);
908        let resp = req.send().await?;
909        let status = resp.status();
910        let body_text = resp.text().await?;
911        if !status.is_success() {
912            return Err(Error::Api {
913                status: status.as_u16(),
914                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
915                    .ok()
916                    .and_then(|r| r.error)
917                    .unwrap_or(body_text.clone()),
918            });
919        }
920        Ok(serde_json::from_str(&body_text)?)
921    }
922
923    /// Update a role
924    pub async fn organization_role_patch(
925        &self,
926        organization_id: &str,
927        role_id: &str,
928        body: &RoleUpdateRequest,
929    ) -> Result<ApiResponse<RBACRole>, Error> {
930        let path = format!("/v1/organizations/{organization_id}/roles/{role_id}");
931        let mut req = self.request(reqwest::Method::PATCH, &path);
932        req = req.json(body);
933        let resp = req.send().await?;
934        let status = resp.status();
935        let body_text = resp.text().await?;
936        if !status.is_success() {
937            return Err(Error::Api {
938                status: status.as_u16(),
939                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
940                    .ok()
941                    .and_then(|r| r.error)
942                    .unwrap_or(body_text.clone()),
943            });
944        }
945        Ok(serde_json::from_str(&body_text)?)
946    }
947
948    /// Delete a role
949    pub async fn organization_role_delete(
950        &self,
951        organization_id: &str,
952        role_id: &str,
953    ) -> Result<ApiResponse<serde_json::Value>, Error> {
954        let path = format!("/v1/organizations/{organization_id}/roles/{role_id}");
955        let req = self.request(reqwest::Method::DELETE, &path);
956        let resp = req.send().await?;
957        let status = resp.status();
958        let body_text = resp.text().await?;
959        if !status.is_success() {
960            return Err(Error::Api {
961                status: status.as_u16(),
962                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
963                    .ok()
964                    .and_then(|r| r.error)
965                    .unwrap_or(body_text.clone()),
966            });
967        }
968        Ok(serde_json::from_str(&body_text)?)
969    }
970
971    /// Create new Postgres service
972    pub async fn postgres_service_create(
973        &self,
974        organization_id: &str,
975        body: &PostgresServicePostRequest,
976    ) -> Result<ApiResponse<PostgresService>, Error> {
977        let path = format!("/v1/organizations/{organization_id}/postgres");
978        let mut req = self.request(reqwest::Method::POST, &path);
979        req = req.json(body);
980        let resp = req.send().await?;
981        let status = resp.status();
982        let body_text = resp.text().await?;
983        if !status.is_success() {
984            return Err(Error::Api {
985                status: status.as_u16(),
986                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
987                    .ok()
988                    .and_then(|r| r.error)
989                    .unwrap_or(body_text.clone()),
990            });
991        }
992        Ok(serde_json::from_str(&body_text)?)
993    }
994
995    /// List of organization Postgres services
996    pub async fn postgres_service_get_list(
997        &self,
998        organization_id: &str,
999    ) -> Result<ApiResponse<Vec<PostgresServiceListItem>>, Error> {
1000        let path = format!("/v1/organizations/{organization_id}/postgres");
1001        let req = self.request(reqwest::Method::GET, &path);
1002        let resp = req.send().await?;
1003        let status = resp.status();
1004        let body_text = resp.text().await?;
1005        if !status.is_success() {
1006            return Err(Error::Api {
1007                status: status.as_u16(),
1008                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
1009                    .ok()
1010                    .and_then(|r| r.error)
1011                    .unwrap_or(body_text.clone()),
1012            });
1013        }
1014        Ok(serde_json::from_str(&body_text)?)
1015    }
1016
1017    /// Get PostgreSQL service details
1018    pub async fn postgres_service_get(
1019        &self,
1020        organization_id: &str,
1021        postgres_id: &str,
1022    ) -> Result<ApiResponse<PostgresService>, Error> {
1023        let path = format!("/v1/organizations/{organization_id}/postgres/{postgres_id}");
1024        let req = self.request(reqwest::Method::GET, &path);
1025        let resp = req.send().await?;
1026        let status = resp.status();
1027        let body_text = resp.text().await?;
1028        if !status.is_success() {
1029            return Err(Error::Api {
1030                status: status.as_u16(),
1031                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
1032                    .ok()
1033                    .and_then(|r| r.error)
1034                    .unwrap_or(body_text.clone()),
1035            });
1036        }
1037        Ok(serde_json::from_str(&body_text)?)
1038    }
1039
1040    /// Delete a PostgreSQL service
1041    pub async fn postgres_service_delete(
1042        &self,
1043        organization_id: &str,
1044        postgres_id: &str,
1045    ) -> Result<ApiResponse<serde_json::Value>, Error> {
1046        let path = format!("/v1/organizations/{organization_id}/postgres/{postgres_id}");
1047        let req = self.request(reqwest::Method::DELETE, &path);
1048        let resp = req.send().await?;
1049        let status = resp.status();
1050        let body_text = resp.text().await?;
1051        if !status.is_success() {
1052            return Err(Error::Api {
1053                status: status.as_u16(),
1054                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
1055                    .ok()
1056                    .and_then(|r| r.error)
1057                    .unwrap_or(body_text.clone()),
1058            });
1059        }
1060        Ok(serde_json::from_str(&body_text)?)
1061    }
1062
1063    /// Update a PostgreSQL service
1064    pub async fn postgres_service_patch(
1065        &self,
1066        organization_id: &str,
1067        postgres_id: &str,
1068        body: &PostgresServicePatchRequest,
1069    ) -> Result<ApiResponse<PostgresService>, Error> {
1070        let path = format!("/v1/organizations/{organization_id}/postgres/{postgres_id}");
1071        let mut req = self.request(reqwest::Method::PATCH, &path);
1072        req = req.json(body);
1073        let resp = req.send().await?;
1074        let status = resp.status();
1075        let body_text = resp.text().await?;
1076        if !status.is_success() {
1077            return Err(Error::Api {
1078                status: status.as_u16(),
1079                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
1080                    .ok()
1081                    .and_then(|r| r.error)
1082                    .unwrap_or(body_text.clone()),
1083            });
1084        }
1085        Ok(serde_json::from_str(&body_text)?)
1086    }
1087
1088    /// Get Postgres CA certs
1089    pub async fn postgres_service_certs_get(
1090        &self,
1091        organization_id: &str,
1092        postgres_id: &str,
1093    ) -> Result<String, Error> {
1094        let path = format!("/v1/organizations/{organization_id}/postgres/{postgres_id}/caCertificates");
1095        let req = self.request(reqwest::Method::GET, &path);
1096        let resp = req.send().await?;
1097        let status = resp.status();
1098        let body_text = resp.text().await?;
1099        if !status.is_success() {
1100            return Err(Error::Api {
1101                status: status.as_u16(),
1102                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
1103                    .ok()
1104                    .and_then(|r| r.error)
1105                    .unwrap_or(body_text.clone()),
1106            });
1107        }
1108        Ok(body_text)
1109    }
1110
1111    /// Get PostgreSQL service configuration
1112    pub async fn postgres_instance_config_get(
1113        &self,
1114        organization_id: &str,
1115        postgres_id: &str,
1116    ) -> Result<ApiResponse<PostgresInstanceConfig>, Error> {
1117        let path = format!("/v1/organizations/{organization_id}/postgres/{postgres_id}/config");
1118        let req = self.request(reqwest::Method::GET, &path);
1119        let resp = req.send().await?;
1120        let status = resp.status();
1121        let body_text = resp.text().await?;
1122        if !status.is_success() {
1123            return Err(Error::Api {
1124                status: status.as_u16(),
1125                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
1126                    .ok()
1127                    .and_then(|r| r.error)
1128                    .unwrap_or(body_text.clone()),
1129            });
1130        }
1131        Ok(serde_json::from_str(&body_text)?)
1132    }
1133
1134    /// Replace Postgres service configuration
1135    pub async fn postgres_instance_config_post(
1136        &self,
1137        organization_id: &str,
1138        postgres_id: &str,
1139        body: &PostgresInstanceConfig,
1140    ) -> Result<ApiResponse<PostgresInstanceUpdateConfigResponse>, Error> {
1141        let path = format!("/v1/organizations/{organization_id}/postgres/{postgres_id}/config");
1142        let mut req = self.request(reqwest::Method::POST, &path);
1143        req = req.json(body);
1144        let resp = req.send().await?;
1145        let status = resp.status();
1146        let body_text = resp.text().await?;
1147        if !status.is_success() {
1148            return Err(Error::Api {
1149                status: status.as_u16(),
1150                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
1151                    .ok()
1152                    .and_then(|r| r.error)
1153                    .unwrap_or(body_text.clone()),
1154            });
1155        }
1156        Ok(serde_json::from_str(&body_text)?)
1157    }
1158
1159    /// Update Postgres service configuration
1160    pub async fn postgres_instance_config_patch(
1161        &self,
1162        organization_id: &str,
1163        postgres_id: &str,
1164        body: &PostgresInstanceConfig,
1165    ) -> Result<ApiResponse<PostgresInstanceUpdateConfigResponse>, Error> {
1166        let path = format!("/v1/organizations/{organization_id}/postgres/{postgres_id}/config");
1167        let mut req = self.request(reqwest::Method::PATCH, &path);
1168        req = req.json(body);
1169        let resp = req.send().await?;
1170        let status = resp.status();
1171        let body_text = resp.text().await?;
1172        if !status.is_success() {
1173            return Err(Error::Api {
1174                status: status.as_u16(),
1175                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
1176                    .ok()
1177                    .and_then(|r| r.error)
1178                    .unwrap_or(body_text.clone()),
1179            });
1180        }
1181        Ok(serde_json::from_str(&body_text)?)
1182    }
1183
1184    /// Update Postgres superuser password
1185    pub async fn postgres_service_set_password(
1186        &self,
1187        organization_id: &str,
1188        postgres_id: &str,
1189        body: &PostgresServiceSetPassword,
1190    ) -> Result<ApiResponse<PostgresServicePasswordResource>, Error> {
1191        let path = format!("/v1/organizations/{organization_id}/postgres/{postgres_id}/password");
1192        let mut req = self.request(reqwest::Method::PATCH, &path);
1193        req = req.json(body);
1194        let resp = req.send().await?;
1195        let status = resp.status();
1196        let body_text = resp.text().await?;
1197        if !status.is_success() {
1198            return Err(Error::Api {
1199                status: status.as_u16(),
1200                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
1201                    .ok()
1202                    .and_then(|r| r.error)
1203                    .unwrap_or(body_text.clone()),
1204            });
1205        }
1206        Ok(serde_json::from_str(&body_text)?)
1207    }
1208
1209    /// Create a read replica for a Postgres service
1210    pub async fn postgres_instance_create_read_replica(
1211        &self,
1212        organization_id: &str,
1213        postgres_id: &str,
1214        body: &PostgresServiceReadReplicaRequest,
1215    ) -> Result<ApiResponse<PostgresService>, Error> {
1216        let path = format!("/v1/organizations/{organization_id}/postgres/{postgres_id}/readReplica");
1217        let mut req = self.request(reqwest::Method::POST, &path);
1218        req = req.json(body);
1219        let resp = req.send().await?;
1220        let status = resp.status();
1221        let body_text = resp.text().await?;
1222        if !status.is_success() {
1223            return Err(Error::Api {
1224                status: status.as_u16(),
1225                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
1226                    .ok()
1227                    .and_then(|r| r.error)
1228                    .unwrap_or(body_text.clone()),
1229            });
1230        }
1231        Ok(serde_json::from_str(&body_text)?)
1232    }
1233
1234    /// Get PostgreSQL service metrics
1235    pub async fn postgres_instance_prometheus_get(
1236        &self,
1237        organization_id: &str,
1238        postgres_id: &str,
1239    ) -> Result<String, Error> {
1240        let path =
1241            format!("/v1/organizations/{organization_id}/postgres/{postgres_id}/prometheus");
1242        let req = self.request(reqwest::Method::GET, &path);
1243        let resp = req.send().await?;
1244        let status = resp.status();
1245        if !status.is_success() {
1246            let body_text = resp.text().await?;
1247            return Err(Error::Api {
1248                status: status.as_u16(),
1249                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
1250                    .ok()
1251                    .and_then(|r| r.error)
1252                    .unwrap_or(body_text),
1253            });
1254        }
1255        Ok(resp.text().await?)
1256    }
1257
1258    /// Get organization PostgreSQL metrics
1259    pub async fn postgres_org_prometheus_get(
1260        &self,
1261        organization_id: &str,
1262    ) -> Result<String, Error> {
1263        let path = format!("/v1/organizations/{organization_id}/postgres/prometheus");
1264        let req = self.request(reqwest::Method::GET, &path);
1265        let resp = req.send().await?;
1266        let status = resp.status();
1267        if !status.is_success() {
1268            let body_text = resp.text().await?;
1269            return Err(Error::Api {
1270                status: status.as_u16(),
1271                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
1272                    .ok()
1273                    .and_then(|r| r.error)
1274                    .unwrap_or(body_text),
1275            });
1276        }
1277        Ok(resp.text().await?)
1278    }
1279
1280    /// Restore a Postgres service
1281    pub async fn postgres_instance_restore(
1282        &self,
1283        organization_id: &str,
1284        postgres_id: &str,
1285        body: &PostgresServiceRestoreRequest,
1286    ) -> Result<ApiResponse<PostgresService>, Error> {
1287        let path = format!("/v1/organizations/{organization_id}/postgres/{postgres_id}/restoredService");
1288        let mut req = self.request(reqwest::Method::POST, &path);
1289        req = req.json(body);
1290        let resp = req.send().await?;
1291        let status = resp.status();
1292        let body_text = resp.text().await?;
1293        if !status.is_success() {
1294            return Err(Error::Api {
1295                status: status.as_u16(),
1296                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
1297                    .ok()
1298                    .and_then(|r| r.error)
1299                    .unwrap_or(body_text.clone()),
1300            });
1301        }
1302        Ok(serde_json::from_str(&body_text)?)
1303    }
1304
1305    /// Update Postgres service state
1306    pub async fn postgres_service_patch_state(
1307        &self,
1308        organization_id: &str,
1309        postgres_id: &str,
1310        body: &PostgresServiceSetState,
1311    ) -> Result<ApiResponse<PostgresService>, Error> {
1312        let path = format!("/v1/organizations/{organization_id}/postgres/{postgres_id}/state");
1313        let mut req = self.request(reqwest::Method::PATCH, &path);
1314        req = req.json(body);
1315        let resp = req.send().await?;
1316        let status = resp.status();
1317        let body_text = resp.text().await?;
1318        if !status.is_success() {
1319            return Err(Error::Api {
1320                status: status.as_u16(),
1321                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
1322                    .ok()
1323                    .and_then(|r| r.error)
1324                    .unwrap_or(body_text.clone()),
1325            });
1326        }
1327        Ok(serde_json::from_str(&body_text)?)
1328    }
1329
1330    /// Get private endpoint configuration for region within cloud provider for an organization
1331    #[deprecated]
1332    #[allow(deprecated)]
1333    pub async fn organization_private_endpoint_config_get_list(
1334        &self,
1335        organization_id: &str,
1336        cloud_provider: &str,
1337        region_id: &str,
1338    ) -> Result<ApiResponse<OrganizationCloudRegionPrivateEndpointConfig>, Error> {
1339        let path = format!("/v1/organizations/{organization_id}/privateEndpointConfig");
1340        let mut req = self.request(reqwest::Method::GET, &path);
1341        req = req.query(&[("cloud_provider", cloud_provider)]);
1342        req = req.query(&[("region_id", region_id)]);
1343        let resp = req.send().await?;
1344        let status = resp.status();
1345        let body_text = resp.text().await?;
1346        if !status.is_success() {
1347            return Err(Error::Api {
1348                status: status.as_u16(),
1349                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
1350                    .ok()
1351                    .and_then(|r| r.error)
1352                    .unwrap_or(body_text.clone()),
1353            });
1354        }
1355        Ok(serde_json::from_str(&body_text)?)
1356    }
1357
1358    /// Get organization metrics
1359    pub async fn organization_prometheus_get(
1360        &self,
1361        organization_id: &str,
1362        filtered_metrics: Option<&str>,
1363    ) -> Result<String, Error> {
1364        let path = format!("/v1/organizations/{organization_id}/prometheus");
1365        let mut req = self.request(reqwest::Method::GET, &path);
1366        if let Some(v) = filtered_metrics {
1367            req = req.query(&[("filtered_metrics", v)]);
1368        }
1369        let resp = req.send().await?;
1370        let status = resp.status();
1371        if !status.is_success() {
1372            let body_text = resp.text().await?;
1373            return Err(Error::Api {
1374                status: status.as_u16(),
1375                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
1376                    .ok()
1377                    .and_then(|r| r.error)
1378                    .unwrap_or(body_text),
1379            });
1380        }
1381        Ok(resp.text().await?)
1382    }
1383
1384    /// List of organization services
1385    pub async fn instance_get_list(
1386        &self,
1387        organization_id: &str,
1388        filters: &[&str],
1389    ) -> Result<ApiResponse<Vec<Service>>, Error> {
1390        let path = format!("/v1/organizations/{organization_id}/services");
1391        let mut req = self.request(reqwest::Method::GET, &path);
1392        for f in filters {
1393            req = req.query(&[("filter", f)]);
1394        }
1395        let resp = req.send().await?;
1396        let status = resp.status();
1397        let body_text = resp.text().await?;
1398        if !status.is_success() {
1399            return Err(Error::Api {
1400                status: status.as_u16(),
1401                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
1402                    .ok()
1403                    .and_then(|r| r.error)
1404                    .unwrap_or(body_text.clone()),
1405            });
1406        }
1407        Ok(serde_json::from_str(&body_text)?)
1408    }
1409
1410    /// Create new service
1411    pub async fn instance_create(
1412        &self,
1413        organization_id: &str,
1414        body: &ServicePostRequest,
1415    ) -> Result<ApiResponse<ServicePostResponse>, Error> {
1416        let path = format!("/v1/organizations/{organization_id}/services");
1417        let mut req = self.request(reqwest::Method::POST, &path);
1418        req = req.json(body);
1419        let resp = req.send().await?;
1420        let status = resp.status();
1421        let body_text = resp.text().await?;
1422        if !status.is_success() {
1423            return Err(Error::Api {
1424                status: status.as_u16(),
1425                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
1426                    .ok()
1427                    .and_then(|r| r.error)
1428                    .unwrap_or(body_text.clone()),
1429            });
1430        }
1431        Ok(serde_json::from_str(&body_text)?)
1432    }
1433
1434    /// Get service details
1435    pub async fn instance_get(
1436        &self,
1437        organization_id: &str,
1438        service_id: &str,
1439    ) -> Result<ApiResponse<Service>, Error> {
1440        let path = format!("/v1/organizations/{organization_id}/services/{service_id}");
1441        let req = self.request(reqwest::Method::GET, &path);
1442        let resp = req.send().await?;
1443        let status = resp.status();
1444        let body_text = resp.text().await?;
1445        if !status.is_success() {
1446            return Err(Error::Api {
1447                status: status.as_u16(),
1448                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
1449                    .ok()
1450                    .and_then(|r| r.error)
1451                    .unwrap_or(body_text.clone()),
1452            });
1453        }
1454        Ok(serde_json::from_str(&body_text)?)
1455    }
1456
1457    /// Update service basic details
1458    pub async fn instance_update(
1459        &self,
1460        organization_id: &str,
1461        service_id: &str,
1462        body: &ServicePatchRequest,
1463    ) -> Result<ApiResponse<Service>, Error> {
1464        let path = format!("/v1/organizations/{organization_id}/services/{service_id}");
1465        let mut req = self.request(reqwest::Method::PATCH, &path);
1466        req = req.json(body);
1467        let resp = req.send().await?;
1468        let status = resp.status();
1469        let body_text = resp.text().await?;
1470        if !status.is_success() {
1471            return Err(Error::Api {
1472                status: status.as_u16(),
1473                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
1474                    .ok()
1475                    .and_then(|r| r.error)
1476                    .unwrap_or(body_text.clone()),
1477            });
1478        }
1479        Ok(serde_json::from_str(&body_text)?)
1480    }
1481
1482    /// Delete service
1483    pub async fn instance_delete(
1484        &self,
1485        organization_id: &str,
1486        service_id: &str,
1487    ) -> Result<ApiResponse<serde_json::Value>, Error> {
1488        let path = format!("/v1/organizations/{organization_id}/services/{service_id}");
1489        let req = self.request(reqwest::Method::DELETE, &path);
1490        let resp = req.send().await?;
1491        let status = resp.status();
1492        let body_text = resp.text().await?;
1493        if !status.is_success() {
1494            return Err(Error::Api {
1495                status: status.as_u16(),
1496                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
1497                    .ok()
1498                    .and_then(|r| r.error)
1499                    .unwrap_or(body_text.clone()),
1500            });
1501        }
1502        Ok(serde_json::from_str(&body_text)?)
1503    }
1504
1505    /// Get service backup bucket
1506    pub async fn backup_bucket_get(
1507        &self,
1508        organization_id: &str,
1509        service_id: &str,
1510    ) -> Result<ApiResponse<BackupBucket>, Error> {
1511        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/backupBucket");
1512        let req = self.request(reqwest::Method::GET, &path);
1513        let resp = req.send().await?;
1514        let status = resp.status();
1515        let body_text = resp.text().await?;
1516        if !status.is_success() {
1517            return Err(Error::Api {
1518                status: status.as_u16(),
1519                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
1520                    .ok()
1521                    .and_then(|r| r.error)
1522                    .unwrap_or(body_text.clone()),
1523            });
1524        }
1525        Ok(serde_json::from_str(&body_text)?)
1526    }
1527
1528    /// Create service backup bucket
1529    pub async fn backup_bucket_create(
1530        &self,
1531        organization_id: &str,
1532        service_id: &str,
1533        body: &BackupBucketPostRequest,
1534    ) -> Result<ApiResponse<BackupBucket>, Error> {
1535        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/backupBucket");
1536        let mut req = self.request(reqwest::Method::POST, &path);
1537        req = req.json(body);
1538        let resp = req.send().await?;
1539        let status = resp.status();
1540        let body_text = resp.text().await?;
1541        if !status.is_success() {
1542            return Err(Error::Api {
1543                status: status.as_u16(),
1544                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
1545                    .ok()
1546                    .and_then(|r| r.error)
1547                    .unwrap_or(body_text.clone()),
1548            });
1549        }
1550        Ok(serde_json::from_str(&body_text)?)
1551    }
1552
1553    /// Update service backup bucket
1554    pub async fn backup_bucket_update(
1555        &self,
1556        organization_id: &str,
1557        service_id: &str,
1558        body: &BackupBucketPatchRequest,
1559    ) -> Result<ApiResponse<BackupBucket>, Error> {
1560        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/backupBucket");
1561        let mut req = self.request(reqwest::Method::PATCH, &path);
1562        req = req.json(body);
1563        let resp = req.send().await?;
1564        let status = resp.status();
1565        let body_text = resp.text().await?;
1566        if !status.is_success() {
1567            return Err(Error::Api {
1568                status: status.as_u16(),
1569                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
1570                    .ok()
1571                    .and_then(|r| r.error)
1572                    .unwrap_or(body_text.clone()),
1573            });
1574        }
1575        Ok(serde_json::from_str(&body_text)?)
1576    }
1577
1578    /// Delete service backup bucket
1579    pub async fn backup_bucket_delete(
1580        &self,
1581        organization_id: &str,
1582        service_id: &str,
1583    ) -> Result<ApiResponse<serde_json::Value>, Error> {
1584        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/backupBucket");
1585        let req = self.request(reqwest::Method::DELETE, &path);
1586        let resp = req.send().await?;
1587        let status = resp.status();
1588        let body_text = resp.text().await?;
1589        if !status.is_success() {
1590            return Err(Error::Api {
1591                status: status.as_u16(),
1592                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
1593                    .ok()
1594                    .and_then(|r| r.error)
1595                    .unwrap_or(body_text.clone()),
1596            });
1597        }
1598        Ok(serde_json::from_str(&body_text)?)
1599    }
1600
1601    /// Get service backup configuration
1602    pub async fn backup_configuration_get(
1603        &self,
1604        organization_id: &str,
1605        service_id: &str,
1606    ) -> Result<ApiResponse<BackupConfiguration>, Error> {
1607        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/backupConfiguration");
1608        let req = self.request(reqwest::Method::GET, &path);
1609        let resp = req.send().await?;
1610        let status = resp.status();
1611        let body_text = resp.text().await?;
1612        if !status.is_success() {
1613            return Err(Error::Api {
1614                status: status.as_u16(),
1615                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
1616                    .ok()
1617                    .and_then(|r| r.error)
1618                    .unwrap_or(body_text.clone()),
1619            });
1620        }
1621        Ok(serde_json::from_str(&body_text)?)
1622    }
1623
1624    /// Update service backup configuration
1625    pub async fn backup_configuration_update(
1626        &self,
1627        organization_id: &str,
1628        service_id: &str,
1629        body: &BackupConfigurationPatchRequest,
1630    ) -> Result<ApiResponse<BackupConfiguration>, Error> {
1631        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/backupConfiguration");
1632        let mut req = self.request(reqwest::Method::PATCH, &path);
1633        req = req.json(body);
1634        let resp = req.send().await?;
1635        let status = resp.status();
1636        let body_text = resp.text().await?;
1637        if !status.is_success() {
1638            return Err(Error::Api {
1639                status: status.as_u16(),
1640                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
1641                    .ok()
1642                    .and_then(|r| r.error)
1643                    .unwrap_or(body_text.clone()),
1644            });
1645        }
1646        Ok(serde_json::from_str(&body_text)?)
1647    }
1648
1649    /// List of service backups
1650    pub async fn backup_get_list(
1651        &self,
1652        organization_id: &str,
1653        service_id: &str,
1654    ) -> Result<ApiResponse<Vec<Backup>>, Error> {
1655        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/backups");
1656        let req = self.request(reqwest::Method::GET, &path);
1657        let resp = req.send().await?;
1658        let status = resp.status();
1659        let body_text = resp.text().await?;
1660        if !status.is_success() {
1661            return Err(Error::Api {
1662                status: status.as_u16(),
1663                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
1664                    .ok()
1665                    .and_then(|r| r.error)
1666                    .unwrap_or(body_text.clone()),
1667            });
1668        }
1669        Ok(serde_json::from_str(&body_text)?)
1670    }
1671
1672    /// Get backup details
1673    pub async fn backup_get(
1674        &self,
1675        organization_id: &str,
1676        service_id: &str,
1677        backup_id: &str,
1678    ) -> Result<ApiResponse<Backup>, Error> {
1679        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/backups/{backup_id}");
1680        let req = self.request(reqwest::Method::GET, &path);
1681        let resp = req.send().await?;
1682        let status = resp.status();
1683        let body_text = resp.text().await?;
1684        if !status.is_success() {
1685            return Err(Error::Api {
1686                status: status.as_u16(),
1687                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
1688                    .ok()
1689                    .and_then(|r| r.error)
1690                    .unwrap_or(body_text.clone()),
1691            });
1692        }
1693        Ok(serde_json::from_str(&body_text)?)
1694    }
1695
1696    /// List ClickPipes
1697    pub async fn click_pipe_get_list(
1698        &self,
1699        organization_id: &str,
1700        service_id: &str,
1701    ) -> Result<ApiResponse<Vec<ClickPipe>>, Error> {
1702        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/clickpipes");
1703        let req = self.request(reqwest::Method::GET, &path);
1704        let resp = req.send().await?;
1705        let status = resp.status();
1706        let body_text = resp.text().await?;
1707        if !status.is_success() {
1708            return Err(Error::Api {
1709                status: status.as_u16(),
1710                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
1711                    .ok()
1712                    .and_then(|r| r.error)
1713                    .unwrap_or(body_text.clone()),
1714            });
1715        }
1716        Ok(serde_json::from_str(&body_text)?)
1717    }
1718
1719    /// Create ClickPipe
1720    pub async fn click_pipe_create(
1721        &self,
1722        organization_id: &str,
1723        service_id: &str,
1724        body: &ClickPipePostRequest,
1725    ) -> Result<ApiResponse<ClickPipe>, Error> {
1726        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/clickpipes");
1727        let mut req = self.request(reqwest::Method::POST, &path);
1728        req = req.json(body);
1729        let resp = req.send().await?;
1730        let status = resp.status();
1731        let body_text = resp.text().await?;
1732        if !status.is_success() {
1733            return Err(Error::Api {
1734                status: status.as_u16(),
1735                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
1736                    .ok()
1737                    .and_then(|r| r.error)
1738                    .unwrap_or(body_text.clone()),
1739            });
1740        }
1741        Ok(serde_json::from_str(&body_text)?)
1742    }
1743
1744    /// Get ClickPipe
1745    pub async fn click_pipe_get(
1746        &self,
1747        organization_id: &str,
1748        service_id: &str,
1749        click_pipe_id: &str,
1750    ) -> Result<ApiResponse<ClickPipe>, Error> {
1751        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/clickpipes/{click_pipe_id}");
1752        let req = self.request(reqwest::Method::GET, &path);
1753        let resp = req.send().await?;
1754        let status = resp.status();
1755        let body_text = resp.text().await?;
1756        if !status.is_success() {
1757            return Err(Error::Api {
1758                status: status.as_u16(),
1759                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
1760                    .ok()
1761                    .and_then(|r| r.error)
1762                    .unwrap_or(body_text.clone()),
1763            });
1764        }
1765        Ok(serde_json::from_str(&body_text)?)
1766    }
1767
1768    /// Update ClickPipe
1769    pub async fn click_pipe_update(
1770        &self,
1771        organization_id: &str,
1772        service_id: &str,
1773        click_pipe_id: &str,
1774        body: &ClickPipePatchRequest,
1775    ) -> Result<ApiResponse<ClickPipe>, Error> {
1776        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/clickpipes/{click_pipe_id}");
1777        let mut req = self.request(reqwest::Method::PATCH, &path);
1778        req = req.json(body);
1779        let resp = req.send().await?;
1780        let status = resp.status();
1781        let body_text = resp.text().await?;
1782        if !status.is_success() {
1783            return Err(Error::Api {
1784                status: status.as_u16(),
1785                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
1786                    .ok()
1787                    .and_then(|r| r.error)
1788                    .unwrap_or(body_text.clone()),
1789            });
1790        }
1791        Ok(serde_json::from_str(&body_text)?)
1792    }
1793
1794    /// Delete ClickPipe
1795    pub async fn click_pipe_delete(
1796        &self,
1797        organization_id: &str,
1798        service_id: &str,
1799        click_pipe_id: &str,
1800    ) -> Result<ApiResponse<serde_json::Value>, Error> {
1801        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/clickpipes/{click_pipe_id}");
1802        let req = self.request(reqwest::Method::DELETE, &path);
1803        let resp = req.send().await?;
1804        let status = resp.status();
1805        let body_text = resp.text().await?;
1806        if !status.is_success() {
1807            return Err(Error::Api {
1808                status: status.as_u16(),
1809                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
1810                    .ok()
1811                    .and_then(|r| r.error)
1812                    .unwrap_or(body_text.clone()),
1813            });
1814        }
1815        Ok(serde_json::from_str(&body_text)?)
1816    }
1817
1818    /// Update ClickPipe scaling
1819    pub async fn click_pipe_scaling_update(
1820        &self,
1821        organization_id: &str,
1822        service_id: &str,
1823        click_pipe_id: &str,
1824        body: &ClickPipeScalingPatchRequest,
1825    ) -> Result<ApiResponse<ClickPipe>, Error> {
1826        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/clickpipes/{click_pipe_id}/scaling");
1827        let mut req = self.request(reqwest::Method::PATCH, &path);
1828        req = req.json(body);
1829        let resp = req.send().await?;
1830        let status = resp.status();
1831        let body_text = resp.text().await?;
1832        if !status.is_success() {
1833            return Err(Error::Api {
1834                status: status.as_u16(),
1835                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
1836                    .ok()
1837                    .and_then(|r| r.error)
1838                    .unwrap_or(body_text.clone()),
1839            });
1840        }
1841        Ok(serde_json::from_str(&body_text)?)
1842    }
1843
1844    /// Get ClickPipe settings
1845    pub async fn click_pipe_settings_get(
1846        &self,
1847        organization_id: &str,
1848        service_id: &str,
1849        click_pipe_id: &str,
1850    ) -> Result<ApiResponse<ClickPipeSettings>, Error> {
1851        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/clickpipes/{click_pipe_id}/settings");
1852        let req = self.request(reqwest::Method::GET, &path);
1853        let resp = req.send().await?;
1854        let status = resp.status();
1855        let body_text = resp.text().await?;
1856        if !status.is_success() {
1857            return Err(Error::Api {
1858                status: status.as_u16(),
1859                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
1860                    .ok()
1861                    .and_then(|r| r.error)
1862                    .unwrap_or(body_text.clone()),
1863            });
1864        }
1865        Ok(serde_json::from_str(&body_text)?)
1866    }
1867
1868    /// Update ClickPipe settings
1869    pub async fn click_pipe_settings_update(
1870        &self,
1871        organization_id: &str,
1872        service_id: &str,
1873        click_pipe_id: &str,
1874        body: &ClickPipeSettingsPutRequest,
1875    ) -> Result<ApiResponse<ClickPipeSettings>, Error> {
1876        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/clickpipes/{click_pipe_id}/settings");
1877        let mut req = self.request(reqwest::Method::PUT, &path);
1878        req = req.json(body);
1879        let resp = req.send().await?;
1880        let status = resp.status();
1881        let body_text = resp.text().await?;
1882        if !status.is_success() {
1883            return Err(Error::Api {
1884                status: status.as_u16(),
1885                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
1886                    .ok()
1887                    .and_then(|r| r.error)
1888                    .unwrap_or(body_text.clone()),
1889            });
1890        }
1891        Ok(serde_json::from_str(&body_text)?)
1892    }
1893
1894    /// Update ClickPipe state
1895    pub async fn click_pipe_state_update(
1896        &self,
1897        organization_id: &str,
1898        service_id: &str,
1899        click_pipe_id: &str,
1900        body: &ClickPipeStatePatchRequest,
1901    ) -> Result<ApiResponse<ClickPipe>, Error> {
1902        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/clickpipes/{click_pipe_id}/state");
1903        let mut req = self.request(reqwest::Method::PATCH, &path);
1904        req = req.json(body);
1905        let resp = req.send().await?;
1906        let status = resp.status();
1907        let body_text = resp.text().await?;
1908        if !status.is_success() {
1909            return Err(Error::Api {
1910                status: status.as_u16(),
1911                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
1912                    .ok()
1913                    .and_then(|r| r.error)
1914                    .unwrap_or(body_text.clone()),
1915            });
1916        }
1917        Ok(serde_json::from_str(&body_text)?)
1918    }
1919
1920    /// Get CDC ClickPipes scaling
1921    pub async fn click_pipe_cdc_scaling_get(
1922        &self,
1923        organization_id: &str,
1924        service_id: &str,
1925    ) -> Result<ApiResponse<ClickPipesCdcScaling>, Error> {
1926        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/clickpipesCdcScaling");
1927        let req = self.request(reqwest::Method::GET, &path);
1928        let resp = req.send().await?;
1929        let status = resp.status();
1930        let body_text = resp.text().await?;
1931        if !status.is_success() {
1932            return Err(Error::Api {
1933                status: status.as_u16(),
1934                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
1935                    .ok()
1936                    .and_then(|r| r.error)
1937                    .unwrap_or(body_text.clone()),
1938            });
1939        }
1940        Ok(serde_json::from_str(&body_text)?)
1941    }
1942
1943    /// Update CDC ClickPipes scaling
1944    pub async fn click_pipe_cdc_scaling_update(
1945        &self,
1946        organization_id: &str,
1947        service_id: &str,
1948        body: &ClickPipesCdcScalingPatchRequest,
1949    ) -> Result<ApiResponse<ClickPipesCdcScaling>, Error> {
1950        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/clickpipesCdcScaling");
1951        let mut req = self.request(reqwest::Method::PATCH, &path);
1952        req = req.json(body);
1953        let resp = req.send().await?;
1954        let status = resp.status();
1955        let body_text = resp.text().await?;
1956        if !status.is_success() {
1957            return Err(Error::Api {
1958                status: status.as_u16(),
1959                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
1960                    .ok()
1961                    .and_then(|r| r.error)
1962                    .unwrap_or(body_text.clone()),
1963            });
1964        }
1965        Ok(serde_json::from_str(&body_text)?)
1966    }
1967
1968    /// List reverse private endpoints
1969    pub async fn click_pipe_reverse_private_endpoint_get_list(
1970        &self,
1971        organization_id: &str,
1972        service_id: &str,
1973    ) -> Result<ApiResponse<Vec<ReversePrivateEndpoint>>, Error> {
1974        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/clickpipesReversePrivateEndpoints");
1975        let req = self.request(reqwest::Method::GET, &path);
1976        let resp = req.send().await?;
1977        let status = resp.status();
1978        let body_text = resp.text().await?;
1979        if !status.is_success() {
1980            return Err(Error::Api {
1981                status: status.as_u16(),
1982                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
1983                    .ok()
1984                    .and_then(|r| r.error)
1985                    .unwrap_or(body_text.clone()),
1986            });
1987        }
1988        Ok(serde_json::from_str(&body_text)?)
1989    }
1990
1991    /// Create reverse private endpoint
1992    pub async fn click_pipe_reverse_private_endpoint_create(
1993        &self,
1994        organization_id: &str,
1995        service_id: &str,
1996        body: &CreateReversePrivateEndpoint,
1997    ) -> Result<ApiResponse<ReversePrivateEndpoint>, Error> {
1998        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/clickpipesReversePrivateEndpoints");
1999        let mut req = self.request(reqwest::Method::POST, &path);
2000        req = req.json(body);
2001        let resp = req.send().await?;
2002        let status = resp.status();
2003        let body_text = resp.text().await?;
2004        if !status.is_success() {
2005            return Err(Error::Api {
2006                status: status.as_u16(),
2007                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
2008                    .ok()
2009                    .and_then(|r| r.error)
2010                    .unwrap_or(body_text.clone()),
2011            });
2012        }
2013        Ok(serde_json::from_str(&body_text)?)
2014    }
2015
2016    /// Get reverse private endpoint
2017    pub async fn click_pipe_reverse_private_endpoint_get(
2018        &self,
2019        organization_id: &str,
2020        service_id: &str,
2021        reverse_private_endpoint_id: &str,
2022    ) -> Result<ApiResponse<ReversePrivateEndpoint>, Error> {
2023        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/clickpipesReversePrivateEndpoints/{reverse_private_endpoint_id}");
2024        let req = self.request(reqwest::Method::GET, &path);
2025        let resp = req.send().await?;
2026        let status = resp.status();
2027        let body_text = resp.text().await?;
2028        if !status.is_success() {
2029            return Err(Error::Api {
2030                status: status.as_u16(),
2031                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
2032                    .ok()
2033                    .and_then(|r| r.error)
2034                    .unwrap_or(body_text.clone()),
2035            });
2036        }
2037        Ok(serde_json::from_str(&body_text)?)
2038    }
2039
2040    /// Delete reverse private endpoint
2041    pub async fn click_pipe_reverse_private_endpoint_delete(
2042        &self,
2043        organization_id: &str,
2044        service_id: &str,
2045        reverse_private_endpoint_id: &str,
2046    ) -> Result<ApiResponse<serde_json::Value>, Error> {
2047        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/clickpipesReversePrivateEndpoints/{reverse_private_endpoint_id}");
2048        let req = self.request(reqwest::Method::DELETE, &path);
2049        let resp = req.send().await?;
2050        let status = resp.status();
2051        let body_text = resp.text().await?;
2052        if !status.is_success() {
2053            return Err(Error::Api {
2054                status: status.as_u16(),
2055                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
2056                    .ok()
2057                    .and_then(|r| r.error)
2058                    .unwrap_or(body_text.clone()),
2059            });
2060        }
2061        Ok(serde_json::from_str(&body_text)?)
2062    }
2063
2064    /// ClickStack: List Alerts
2065    pub async fn click_stack_list_alerts(
2066        &self,
2067        organization_id: &str,
2068        service_id: &str,
2069    ) -> Result<ApiResponse<Vec<ClickStackAlertResponse>>, Error> {
2070        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/clickstack/alerts");
2071        let req = self.request(reqwest::Method::GET, &path);
2072        let resp = req.send().await?;
2073        let status = resp.status();
2074        let body_text = resp.text().await?;
2075        if !status.is_success() {
2076            return Err(Error::Api {
2077                status: status.as_u16(),
2078                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
2079                    .ok()
2080                    .and_then(|r| r.error)
2081                    .unwrap_or(body_text.clone()),
2082            });
2083        }
2084        Ok(serde_json::from_str(&body_text)?)
2085    }
2086
2087    /// ClickStack: Create Alert
2088    pub async fn click_stack_create_alert(
2089        &self,
2090        organization_id: &str,
2091        service_id: &str,
2092        body: &ClickStackCreateAlertRequest,
2093    ) -> Result<ApiResponse<ClickStackAlertResponse>, Error> {
2094        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/clickstack/alerts");
2095        let mut req = self.request(reqwest::Method::POST, &path);
2096        req = req.json(body);
2097        let resp = req.send().await?;
2098        let status = resp.status();
2099        let body_text = resp.text().await?;
2100        if !status.is_success() {
2101            return Err(Error::Api {
2102                status: status.as_u16(),
2103                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
2104                    .ok()
2105                    .and_then(|r| r.error)
2106                    .unwrap_or(body_text.clone()),
2107            });
2108        }
2109        Ok(serde_json::from_str(&body_text)?)
2110    }
2111
2112    /// ClickStack: Get Alert
2113    pub async fn click_stack_get_alert(
2114        &self,
2115        organization_id: &str,
2116        service_id: &str,
2117        click_stack_alert_id: &str,
2118    ) -> Result<ApiResponse<ClickStackAlertResponse>, Error> {
2119        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/clickstack/alerts/{click_stack_alert_id}");
2120        let req = self.request(reqwest::Method::GET, &path);
2121        let resp = req.send().await?;
2122        let status = resp.status();
2123        let body_text = resp.text().await?;
2124        if !status.is_success() {
2125            return Err(Error::Api {
2126                status: status.as_u16(),
2127                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
2128                    .ok()
2129                    .and_then(|r| r.error)
2130                    .unwrap_or(body_text.clone()),
2131            });
2132        }
2133        Ok(serde_json::from_str(&body_text)?)
2134    }
2135
2136    /// ClickStack: Update Alert
2137    pub async fn click_stack_update_alert(
2138        &self,
2139        organization_id: &str,
2140        service_id: &str,
2141        click_stack_alert_id: &str,
2142        body: &ClickStackUpdateAlertRequest,
2143    ) -> Result<ApiResponse<ClickStackAlertResponse>, Error> {
2144        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/clickstack/alerts/{click_stack_alert_id}");
2145        let mut req = self.request(reqwest::Method::PUT, &path);
2146        req = req.json(body);
2147        let resp = req.send().await?;
2148        let status = resp.status();
2149        let body_text = resp.text().await?;
2150        if !status.is_success() {
2151            return Err(Error::Api {
2152                status: status.as_u16(),
2153                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
2154                    .ok()
2155                    .and_then(|r| r.error)
2156                    .unwrap_or(body_text.clone()),
2157            });
2158        }
2159        Ok(serde_json::from_str(&body_text)?)
2160    }
2161
2162    /// ClickStack: Delete Alert
2163    pub async fn click_stack_delete_alert(
2164        &self,
2165        organization_id: &str,
2166        service_id: &str,
2167        click_stack_alert_id: &str,
2168    ) -> Result<ApiResponse<serde_json::Value>, Error> {
2169        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/clickstack/alerts/{click_stack_alert_id}");
2170        let req = self.request(reqwest::Method::DELETE, &path);
2171        let resp = req.send().await?;
2172        let status = resp.status();
2173        let body_text = resp.text().await?;
2174        if !status.is_success() {
2175            return Err(Error::Api {
2176                status: status.as_u16(),
2177                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
2178                    .ok()
2179                    .and_then(|r| r.error)
2180                    .unwrap_or(body_text.clone()),
2181            });
2182        }
2183        Ok(serde_json::from_str(&body_text)?)
2184    }
2185
2186    /// ClickStack: List Dashboards
2187    pub async fn click_stack_list_dashboards(
2188        &self,
2189        organization_id: &str,
2190        service_id: &str,
2191    ) -> Result<ApiResponse<Vec<ClickStackDashboardResponse>>, Error> {
2192        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/clickstack/dashboards");
2193        let req = self.request(reqwest::Method::GET, &path);
2194        let resp = req.send().await?;
2195        let status = resp.status();
2196        let body_text = resp.text().await?;
2197        if !status.is_success() {
2198            return Err(Error::Api {
2199                status: status.as_u16(),
2200                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
2201                    .ok()
2202                    .and_then(|r| r.error)
2203                    .unwrap_or(body_text.clone()),
2204            });
2205        }
2206        Ok(serde_json::from_str(&body_text)?)
2207    }
2208
2209    /// ClickStack: Create Dashboard
2210    pub async fn click_stack_create_dashboard(
2211        &self,
2212        organization_id: &str,
2213        service_id: &str,
2214        body: &ClickStackCreateDashboardRequest,
2215    ) -> Result<ApiResponse<ClickStackDashboardResponse>, Error> {
2216        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/clickstack/dashboards");
2217        let mut req = self.request(reqwest::Method::POST, &path);
2218        req = req.json(body);
2219        let resp = req.send().await?;
2220        let status = resp.status();
2221        let body_text = resp.text().await?;
2222        if !status.is_success() {
2223            return Err(Error::Api {
2224                status: status.as_u16(),
2225                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
2226                    .ok()
2227                    .and_then(|r| r.error)
2228                    .unwrap_or(body_text.clone()),
2229            });
2230        }
2231        Ok(serde_json::from_str(&body_text)?)
2232    }
2233
2234    /// ClickStack: Get Dashboard
2235    pub async fn click_stack_get_dashboard(
2236        &self,
2237        organization_id: &str,
2238        service_id: &str,
2239        click_stack_dashboard_id: &str,
2240    ) -> Result<ApiResponse<ClickStackDashboardResponse>, Error> {
2241        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/clickstack/dashboards/{click_stack_dashboard_id}");
2242        let req = self.request(reqwest::Method::GET, &path);
2243        let resp = req.send().await?;
2244        let status = resp.status();
2245        let body_text = resp.text().await?;
2246        if !status.is_success() {
2247            return Err(Error::Api {
2248                status: status.as_u16(),
2249                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
2250                    .ok()
2251                    .and_then(|r| r.error)
2252                    .unwrap_or(body_text.clone()),
2253            });
2254        }
2255        Ok(serde_json::from_str(&body_text)?)
2256    }
2257
2258    /// ClickStack: Update Dashboard
2259    pub async fn click_stack_update_dashboard(
2260        &self,
2261        organization_id: &str,
2262        service_id: &str,
2263        click_stack_dashboard_id: &str,
2264        body: &ClickStackUpdateDashboardRequest,
2265    ) -> Result<ApiResponse<ClickStackDashboardResponse>, Error> {
2266        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/clickstack/dashboards/{click_stack_dashboard_id}");
2267        let mut req = self.request(reqwest::Method::PUT, &path);
2268        req = req.json(body);
2269        let resp = req.send().await?;
2270        let status = resp.status();
2271        let body_text = resp.text().await?;
2272        if !status.is_success() {
2273            return Err(Error::Api {
2274                status: status.as_u16(),
2275                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
2276                    .ok()
2277                    .and_then(|r| r.error)
2278                    .unwrap_or(body_text.clone()),
2279            });
2280        }
2281        Ok(serde_json::from_str(&body_text)?)
2282    }
2283
2284    /// ClickStack: Delete Dashboard
2285    pub async fn click_stack_delete_dashboard(
2286        &self,
2287        organization_id: &str,
2288        service_id: &str,
2289        click_stack_dashboard_id: &str,
2290    ) -> Result<ApiResponse<serde_json::Value>, Error> {
2291        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/clickstack/dashboards/{click_stack_dashboard_id}");
2292        let req = self.request(reqwest::Method::DELETE, &path);
2293        let resp = req.send().await?;
2294        let status = resp.status();
2295        let body_text = resp.text().await?;
2296        if !status.is_success() {
2297            return Err(Error::Api {
2298                status: status.as_u16(),
2299                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
2300                    .ok()
2301                    .and_then(|r| r.error)
2302                    .unwrap_or(body_text.clone()),
2303            });
2304        }
2305        Ok(serde_json::from_str(&body_text)?)
2306    }
2307
2308    /// ClickStack: List Sources
2309    pub async fn click_stack_list_sources(
2310        &self,
2311        organization_id: &str,
2312        service_id: &str,
2313    ) -> Result<ApiResponse<Vec<ClickStackSource>>, Error> {
2314        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/clickstack/sources");
2315        let req = self.request(reqwest::Method::GET, &path);
2316        let resp = req.send().await?;
2317        let status = resp.status();
2318        let body_text = resp.text().await?;
2319        if !status.is_success() {
2320            return Err(Error::Api {
2321                status: status.as_u16(),
2322                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
2323                    .ok()
2324                    .and_then(|r| r.error)
2325                    .unwrap_or(body_text.clone()),
2326            });
2327        }
2328        Ok(serde_json::from_str(&body_text)?)
2329    }
2330
2331    /// ClickStack: List Webhooks
2332    pub async fn click_stack_list_webhooks(
2333        &self,
2334        organization_id: &str,
2335        service_id: &str,
2336    ) -> Result<ApiResponse<Vec<ClickStackWebhook>>, Error> {
2337        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/clickstack/webhooks");
2338        let req = self.request(reqwest::Method::GET, &path);
2339        let resp = req.send().await?;
2340        let status = resp.status();
2341        let body_text = resp.text().await?;
2342        if !status.is_success() {
2343            return Err(Error::Api {
2344                status: status.as_u16(),
2345                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
2346                    .ok()
2347                    .and_then(|r| r.error)
2348                    .unwrap_or(body_text.clone()),
2349            });
2350        }
2351        Ok(serde_json::from_str(&body_text)?)
2352    }
2353
2354    /// Update service password
2355    pub async fn instance_password_update(
2356        &self,
2357        organization_id: &str,
2358        service_id: &str,
2359        body: &ServicePasswordPatchRequest,
2360    ) -> Result<ApiResponse<ServicePasswordPatchResponse>, Error> {
2361        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/password");
2362        let mut req = self.request(reqwest::Method::PATCH, &path);
2363        req = req.json(body);
2364        let resp = req.send().await?;
2365        let status = resp.status();
2366        let body_text = resp.text().await?;
2367        if !status.is_success() {
2368            return Err(Error::Api {
2369                status: status.as_u16(),
2370                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
2371                    .ok()
2372                    .and_then(|r| r.error)
2373                    .unwrap_or(body_text.clone()),
2374            });
2375        }
2376        Ok(serde_json::from_str(&body_text)?)
2377    }
2378
2379    /// Create a private endpoint
2380    pub async fn instance_private_endpoint_create(
2381        &self,
2382        organization_id: &str,
2383        service_id: &str,
2384        body: &ServicPrivateEndpointePostRequest,
2385    ) -> Result<ApiResponse<InstancePrivateEndpoint>, Error> {
2386        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/privateEndpoint");
2387        let mut req = self.request(reqwest::Method::POST, &path);
2388        req = req.json(body);
2389        let resp = req.send().await?;
2390        let status = resp.status();
2391        let body_text = resp.text().await?;
2392        if !status.is_success() {
2393            return Err(Error::Api {
2394                status: status.as_u16(),
2395                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
2396                    .ok()
2397                    .and_then(|r| r.error)
2398                    .unwrap_or(body_text.clone()),
2399            });
2400        }
2401        Ok(serde_json::from_str(&body_text)?)
2402    }
2403
2404    /// Get private endpoint configuration
2405    pub async fn instance_private_endpoint_config_get(
2406        &self,
2407        organization_id: &str,
2408        service_id: &str,
2409    ) -> Result<ApiResponse<PrivateEndpointConfig>, Error> {
2410        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/privateEndpointConfig");
2411        let req = self.request(reqwest::Method::GET, &path);
2412        let resp = req.send().await?;
2413        let status = resp.status();
2414        let body_text = resp.text().await?;
2415        if !status.is_success() {
2416            return Err(Error::Api {
2417                status: status.as_u16(),
2418                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
2419                    .ok()
2420                    .and_then(|r| r.error)
2421                    .unwrap_or(body_text.clone()),
2422            });
2423        }
2424        Ok(serde_json::from_str(&body_text)?)
2425    }
2426
2427    /// Get service metrics
2428    pub async fn instance_prometheus_get(
2429        &self,
2430        organization_id: &str,
2431        service_id: &str,
2432        filtered_metrics: Option<&str>,
2433    ) -> Result<String, Error> {
2434        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/prometheus");
2435        let mut req = self.request(reqwest::Method::GET, &path);
2436        if let Some(v) = filtered_metrics {
2437            req = req.query(&[("filtered_metrics", v)]);
2438        }
2439        let resp = req.send().await?;
2440        let status = resp.status();
2441        if !status.is_success() {
2442            let body_text = resp.text().await?;
2443            return Err(Error::Api {
2444                status: status.as_u16(),
2445                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
2446                    .ok()
2447                    .and_then(|r| r.error)
2448                    .unwrap_or(body_text),
2449            });
2450        }
2451        Ok(resp.text().await?)
2452    }
2453
2454    /// Update service auto scaling settings
2455    pub async fn instance_replica_scaling_update(
2456        &self,
2457        organization_id: &str,
2458        service_id: &str,
2459        body: &ServiceReplicaScalingPatchRequest,
2460    ) -> Result<ApiResponse<ServiceScalingPatchResponse>, Error> {
2461        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/replicaScaling");
2462        let mut req = self.request(reqwest::Method::PATCH, &path);
2463        req = req.json(body);
2464        let resp = req.send().await?;
2465        let status = resp.status();
2466        let body_text = resp.text().await?;
2467        if !status.is_success() {
2468            return Err(Error::Api {
2469                status: status.as_u16(),
2470                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
2471                    .ok()
2472                    .and_then(|r| r.error)
2473                    .unwrap_or(body_text.clone()),
2474            });
2475        }
2476        Ok(serde_json::from_str(&body_text)?)
2477    }
2478
2479    /// Update service auto scaling settings
2480    #[deprecated]
2481    #[allow(deprecated)]
2482    pub async fn instance_scaling_update(
2483        &self,
2484        organization_id: &str,
2485        service_id: &str,
2486        body: &ServiceScalingPatchRequest,
2487    ) -> Result<ApiResponse<Service>, Error> {
2488        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/scaling");
2489        let mut req = self.request(reqwest::Method::PATCH, &path);
2490        req = req.json(body);
2491        let resp = req.send().await?;
2492        let status = resp.status();
2493        let body_text = resp.text().await?;
2494        if !status.is_success() {
2495            return Err(Error::Api {
2496                status: status.as_u16(),
2497                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
2498                    .ok()
2499                    .and_then(|r| r.error)
2500                    .unwrap_or(body_text.clone()),
2501            });
2502        }
2503        Ok(serde_json::from_str(&body_text)?)
2504    }
2505
2506    /// Get service autoscaling schedule
2507    pub async fn scaling_schedule_get(
2508        &self,
2509        organization_id: &str,
2510        service_id: &str,
2511    ) -> Result<ApiResponse<ScalingSchedule>, Error> {
2512        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/scalingSchedule");
2513        let req = self.request(reqwest::Method::GET, &path);
2514        let resp = req.send().await?;
2515        let status = resp.status();
2516        let body_text = resp.text().await?;
2517        if !status.is_success() {
2518            return Err(Error::Api {
2519                status: status.as_u16(),
2520                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
2521                    .ok()
2522                    .and_then(|r| r.error)
2523                    .unwrap_or(body_text.clone()),
2524            });
2525        }
2526        Ok(serde_json::from_str(&body_text)?)
2527    }
2528
2529    /// Create or replace service autoscaling schedule
2530    pub async fn scaling_schedule_upsert(
2531        &self,
2532        organization_id: &str,
2533        service_id: &str,
2534        body: &ScalingSchedulePostRequest,
2535    ) -> Result<ApiResponse<ScalingSchedule>, Error> {
2536        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/scalingSchedule");
2537        let mut req = self.request(reqwest::Method::POST, &path);
2538        req = req.json(body);
2539        let resp = req.send().await?;
2540        let status = resp.status();
2541        let body_text = resp.text().await?;
2542        if !status.is_success() {
2543            return Err(Error::Api {
2544                status: status.as_u16(),
2545                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
2546                    .ok()
2547                    .and_then(|r| r.error)
2548                    .unwrap_or(body_text.clone()),
2549            });
2550        }
2551        Ok(serde_json::from_str(&body_text)?)
2552    }
2553
2554    /// Delete service scheduled scaling
2555    pub async fn scaling_schedule_delete(
2556        &self,
2557        organization_id: &str,
2558        service_id: &str,
2559    ) -> Result<ApiResponse<serde_json::Value>, Error> {
2560        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/scalingSchedule");
2561        let req = self.request(reqwest::Method::DELETE, &path);
2562        let resp = req.send().await?;
2563        let status = resp.status();
2564        let body_text = resp.text().await?;
2565        if !status.is_success() {
2566            return Err(Error::Api {
2567                status: status.as_u16(),
2568                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
2569                    .ok()
2570                    .and_then(|r| r.error)
2571                    .unwrap_or(body_text.clone()),
2572            });
2573        }
2574        Ok(serde_json::from_str(&body_text)?)
2575    }
2576
2577    /// Get service upgrade window
2578    pub async fn upgrade_window_get(
2579        &self,
2580        organization_id: &str,
2581        service_id: &str,
2582    ) -> Result<ApiResponse<UpgradeWindow>, Error> {
2583        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/upgradeWindow");
2584        let req = self.request(reqwest::Method::GET, &path);
2585        let resp = req.send().await?;
2586        let status = resp.status();
2587        let body_text = resp.text().await?;
2588        if !status.is_success() {
2589            return Err(Error::Api {
2590                status: status.as_u16(),
2591                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
2592                    .ok()
2593                    .and_then(|r| r.error)
2594                    .unwrap_or(body_text.clone()),
2595            });
2596        }
2597        Ok(serde_json::from_str(&body_text)?)
2598    }
2599
2600    /// Set service upgrade window
2601    pub async fn upgrade_window_update(
2602        &self,
2603        organization_id: &str,
2604        service_id: &str,
2605        body: &UpgradeWindowPutRequest,
2606    ) -> Result<ApiResponse<UpgradeWindow>, Error> {
2607        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/upgradeWindow");
2608        let mut req = self.request(reqwest::Method::PUT, &path);
2609        req = req.json(body);
2610        let resp = req.send().await?;
2611        let status = resp.status();
2612        let body_text = resp.text().await?;
2613        if !status.is_success() {
2614            return Err(Error::Api {
2615                status: status.as_u16(),
2616                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
2617                    .ok()
2618                    .and_then(|r| r.error)
2619                    .unwrap_or(body_text.clone()),
2620            });
2621        }
2622        Ok(serde_json::from_str(&body_text)?)
2623    }
2624
2625    /// Delete service upgrade window
2626    pub async fn upgrade_window_delete(
2627        &self,
2628        organization_id: &str,
2629        service_id: &str,
2630    ) -> Result<ApiResponse<serde_json::Value>, Error> {
2631        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/upgradeWindow");
2632        let req = self.request(reqwest::Method::DELETE, &path);
2633        let resp = req.send().await?;
2634        let status = resp.status();
2635        let body_text = resp.text().await?;
2636        if !status.is_success() {
2637            return Err(Error::Api {
2638                status: status.as_u16(),
2639                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
2640                    .ok()
2641                    .and_then(|r| r.error)
2642                    .unwrap_or(body_text.clone()),
2643            });
2644        }
2645        Ok(serde_json::from_str(&body_text)?)
2646    }
2647
2648    /// Get the service query endpoint for a given instance
2649    pub async fn instance_query_endpoint_get(
2650        &self,
2651        organization_id: &str,
2652        service_id: &str,
2653    ) -> Result<ApiResponse<ServiceQueryAPIEndpoint>, Error> {
2654        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/serviceQueryEndpoint");
2655        let req = self.request(reqwest::Method::GET, &path);
2656        let resp = req.send().await?;
2657        let status = resp.status();
2658        let body_text = resp.text().await?;
2659        if !status.is_success() {
2660            return Err(Error::Api {
2661                status: status.as_u16(),
2662                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
2663                    .ok()
2664                    .and_then(|r| r.error)
2665                    .unwrap_or(body_text.clone()),
2666            });
2667        }
2668        Ok(serde_json::from_str(&body_text)?)
2669    }
2670
2671    /// Delete the service query endpoint for a given instance
2672    pub async fn instance_query_endpoint_delete(
2673        &self,
2674        organization_id: &str,
2675        service_id: &str,
2676    ) -> Result<ApiResponse<serde_json::Value>, Error> {
2677        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/serviceQueryEndpoint");
2678        let req = self.request(reqwest::Method::DELETE, &path);
2679        let resp = req.send().await?;
2680        let status = resp.status();
2681        let body_text = resp.text().await?;
2682        if !status.is_success() {
2683            return Err(Error::Api {
2684                status: status.as_u16(),
2685                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
2686                    .ok()
2687                    .and_then(|r| r.error)
2688                    .unwrap_or(body_text.clone()),
2689            });
2690        }
2691        Ok(serde_json::from_str(&body_text)?)
2692    }
2693
2694    /// Upsert the service query endpoint for a given instance
2695    pub async fn instance_query_endpoint_upsert(
2696        &self,
2697        organization_id: &str,
2698        service_id: &str,
2699        body: &InstanceServiceQueryApiEndpointsPostRequest,
2700    ) -> Result<ApiResponse<ServiceQueryAPIEndpoint>, Error> {
2701        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/serviceQueryEndpoint");
2702        let mut req = self.request(reqwest::Method::POST, &path);
2703        req = req.json(body);
2704        let resp = req.send().await?;
2705        let status = resp.status();
2706        let body_text = resp.text().await?;
2707        if !status.is_success() {
2708            return Err(Error::Api {
2709                status: status.as_u16(),
2710                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
2711                    .ok()
2712                    .and_then(|r| r.error)
2713                    .unwrap_or(body_text.clone()),
2714            });
2715        }
2716        Ok(serde_json::from_str(&body_text)?)
2717    }
2718
2719    /// Update service state
2720    pub async fn instance_state_update(
2721        &self,
2722        organization_id: &str,
2723        service_id: &str,
2724        body: &ServiceStatePatchRequest,
2725    ) -> Result<ApiResponse<Service>, Error> {
2726        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/state");
2727        let mut req = self.request(reqwest::Method::PATCH, &path);
2728        req = req.json(body);
2729        let resp = req.send().await?;
2730        let status = resp.status();
2731        let body_text = resp.text().await?;
2732        if !status.is_success() {
2733            return Err(Error::Api {
2734                status: status.as_u16(),
2735                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
2736                    .ok()
2737                    .and_then(|r| r.error)
2738                    .unwrap_or(body_text.clone()),
2739            });
2740        }
2741        Ok(serde_json::from_str(&body_text)?)
2742    }
2743
2744    /// Get organization usage costs
2745    pub async fn usage_cost_get(
2746        &self,
2747        organization_id: &str,
2748        from_date: &str,
2749        to_date: &str,
2750        filters: &[&str],
2751    ) -> Result<ApiResponse<UsageCost>, Error> {
2752        let path = format!("/v1/organizations/{organization_id}/usageCost");
2753        let mut req = self.request(reqwest::Method::GET, &path);
2754        req = req.query(&[("from_date", from_date)]);
2755        req = req.query(&[("to_date", to_date)]);
2756        for f in filters {
2757            req = req.query(&[("filter", f)]);
2758        }
2759        let resp = req.send().await?;
2760        let status = resp.status();
2761        let body_text = resp.text().await?;
2762        if !status.is_success() {
2763            return Err(Error::Api {
2764                status: status.as_u16(),
2765                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
2766                    .ok()
2767                    .and_then(|r| r.error)
2768                    .unwrap_or(body_text.clone()),
2769            });
2770        }
2771        Ok(serde_json::from_str(&body_text)?)
2772    }
2773
2774    /// List ClickHouse settings
2775    pub async fn service_clickhouse_settings_list_get(
2776        &self,
2777        organization_id: &str,
2778        service_id: &str,
2779    ) -> Result<ApiResponse<ServiceClickhouseSettingsList>, Error> {
2780        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/clickhouseSettings");
2781        let req = self.request(reqwest::Method::GET, &path);
2782        let resp = req.send().await?;
2783        let status = resp.status();
2784        let body_text = resp.text().await?;
2785        if !status.is_success() {
2786            return Err(Error::Api {
2787                status: status.as_u16(),
2788                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
2789                    .ok()
2790                    .and_then(|r| r.error)
2791                    .unwrap_or(body_text.clone()),
2792            });
2793        }
2794        Ok(serde_json::from_str(&body_text)?)
2795    }
2796
2797    /// Update ClickHouse settings
2798    pub async fn service_clickhouse_settings_update(
2799        &self,
2800        organization_id: &str,
2801        service_id: &str,
2802        body: &ServiceClickhouseSettingsPatchRequest,
2803    ) -> Result<ApiResponse<ServiceClickhouseSettingsPatchResponse>, Error> {
2804        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/clickhouseSettings");
2805        let mut req = self.request(reqwest::Method::PATCH, &path);
2806        req = req.json(body);
2807        let resp = req.send().await?;
2808        let status = resp.status();
2809        let body_text = resp.text().await?;
2810        if !status.is_success() {
2811            return Err(Error::Api {
2812                status: status.as_u16(),
2813                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
2814                    .ok()
2815                    .and_then(|r| r.error)
2816                    .unwrap_or(body_text.clone()),
2817            });
2818        }
2819        Ok(serde_json::from_str(&body_text)?)
2820    }
2821
2822    /// Get ClickHouse settings schema
2823    pub async fn service_clickhouse_settings_schema_get(
2824        &self,
2825        organization_id: &str,
2826        service_id: &str,
2827    ) -> Result<ApiResponse<ServiceClickhouseSettingsSchema>, Error> {
2828        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/clickhouseSettings/schema");
2829        let req = self.request(reqwest::Method::GET, &path);
2830        let resp = req.send().await?;
2831        let status = resp.status();
2832        let body_text = resp.text().await?;
2833        if !status.is_success() {
2834            return Err(Error::Api {
2835                status: status.as_u16(),
2836                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
2837                    .ok()
2838                    .and_then(|r| r.error)
2839                    .unwrap_or(body_text.clone()),
2840            });
2841        }
2842        Ok(serde_json::from_str(&body_text)?)
2843    }
2844
2845    /// Get ClickHouse setting
2846    pub async fn service_clickhouse_setting_get(
2847        &self,
2848        organization_id: &str,
2849        service_id: &str,
2850        setting_name: &str,
2851    ) -> Result<ApiResponse<ServiceClickhouseSetting>, Error> {
2852        let path = format!("/v1/organizations/{organization_id}/services/{service_id}/clickhouseSettings/{setting_name}");
2853        let req = self.request(reqwest::Method::GET, &path);
2854        let resp = req.send().await?;
2855        let status = resp.status();
2856        let body_text = resp.text().await?;
2857        if !status.is_success() {
2858            return Err(Error::Api {
2859                status: status.as_u16(),
2860                message: serde_json::from_str::<ApiResponse<serde_json::Value>>(&body_text)
2861                    .ok()
2862                    .and_then(|r| r.error)
2863                    .unwrap_or(body_text.clone()),
2864            });
2865        }
2866        Ok(serde_json::from_str(&body_text)?)
2867    }
2868
2869}
2870
2871#[cfg(test)]
2872mod tests {
2873    use super::derive_query_host;
2874
2875    #[test]
2876    fn derive_query_host_prod() {
2877        assert_eq!(
2878            derive_query_host("https://api.clickhouse.cloud").as_deref(),
2879            Some("https://queries.clickhouse.cloud")
2880        );
2881    }
2882
2883    #[test]
2884    fn derive_query_host_staging() {
2885        assert_eq!(
2886            derive_query_host("https://api.control-plane.clickhouse-staging.com").as_deref(),
2887            Some("https://queries.clickhouse-staging.com")
2888        );
2889    }
2890
2891    #[test]
2892    fn derive_query_host_dev() {
2893        assert_eq!(
2894            derive_query_host("https://api.control-plane.clickhouse-dev.com").as_deref(),
2895            Some("https://queries.clickhouse-dev.com")
2896        );
2897    }
2898
2899    #[test]
2900    fn derive_query_host_plain_api_prefix_without_control_plane() {
2901        assert_eq!(
2902            derive_query_host("https://api.clickhouse-staging.com").as_deref(),
2903            Some("https://queries.clickhouse-staging.com")
2904        );
2905    }
2906
2907    #[test]
2908    fn derive_query_host_non_api_host_is_none() {
2909        assert_eq!(derive_query_host("http://127.0.0.1:8123"), None);
2910        assert_eq!(derive_query_host("https://example.com"), None);
2911    }
2912
2913    #[test]
2914    fn derive_query_host_invalid_url_is_none() {
2915        assert_eq!(derive_query_host("not a url"), None);
2916    }
2917
2918    #[test]
2919    fn derive_query_host_preserves_non_default_port() {
2920        assert_eq!(
2921            derive_query_host("https://api.mycorp.example.com:8443").as_deref(),
2922            Some("https://queries.mycorp.example.com:8443")
2923        );
2924        // Default ports are normalized away by the URL parser and stay off
2925        // the derived host.
2926        assert_eq!(
2927            derive_query_host("https://api.clickhouse.cloud:443").as_deref(),
2928            Some("https://queries.clickhouse.cloud")
2929        );
2930    }
2931}