1use anyhow::{anyhow, Result};
4use reqwest::{
5 header::{HeaderMap, HeaderValue, AUTHORIZATION},
6 Method, Response, StatusCode,
7};
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10use std::time::Duration;
11
12#[doc(hidden)]
15pub use reqwest::Proxy;
16
17#[cfg(all(feature = "v1", feature = "v1_20_x"))]
18mod structs_1_20_x;
19#[cfg(all(feature = "v1", feature = "v1_20_x"))]
20pub use structs_1_20_x::*;
21
22#[derive(Clone)]
23pub struct Config {
24 pub token: String,
25 pub address: String,
26}
27
28impl Config {
29 pub fn from_env() -> Self {
30 Self {
31 token: read_env_or_default("CONSUL_TOKEN", ""),
32 address: read_env_or_default("CONSUL_ADDRESS", "http://127.0.0.1:8500"),
33 }
34 }
35}
36
37impl Default for Config {
38 fn default() -> Self {
39 Self::from_env()
40 }
41}
42
43pub struct ClientBuilder {
44 cfg: Config,
45 proxies: Vec<Proxy>,
46 timeout: Option<Duration>,
47}
48
49impl ClientBuilder {
50 pub fn new(cfg: Config) -> Self {
51 Self {
52 cfg,
53 proxies: vec![],
54 timeout: None,
55 }
56 }
57
58 pub fn with_proxy(mut self, proxy: Proxy) -> Self {
59 self.proxies.push(proxy);
60 self
61 }
62
63 pub fn with_timeout(mut self, timeout: Duration) -> Self {
64 self.timeout = Some(timeout);
65 self
66 }
67
68 pub fn build(self) -> Result<Client> {
69 let mut headers = HeaderMap::new();
70 if !self.cfg.token.is_empty() {
71 headers.insert(
72 AUTHORIZATION,
73 HeaderValue::from_str(&format!("Bearer {}", self.cfg.token)).unwrap(),
74 );
75 }
76
77 let mut builder = reqwest::ClientBuilder::new();
78 builder = builder.default_headers(headers);
79
80 for proxy in self.proxies {
81 builder = builder.proxy(proxy)
83 }
84
85 if let Some(v) = self.timeout {
86 builder = builder.timeout(v);
87 }
88
89 Ok(Client {
90 cfg: self.cfg,
91 http: builder.build()?,
92 #[cfg(feature = "v1")]
93 prefix: "/v1".to_string(),
94 })
95 }
96}
97
98#[derive(Debug, Default, Serialize, Deserialize)]
99pub struct FilterRequestQuery {
100 pub filter: Option<String>,
101
102 #[cfg(feature = "enterprise")]
103 #[serde(skip_serializing_if = "Option::is_none")]
104 pub ns: Option<String>,
105}
106
107#[derive(Debug, Default, Serialize, Deserialize)]
108pub struct DeregisterCheckRequestQuery {
109 #[serde(skip_serializing)]
110 pub check_id: String,
111
112 #[cfg(feature = "enterprise")]
113 #[serde(skip_serializing_if = "Option::is_none")]
114 pub ns: Option<String>,
115}
116
117#[derive(Debug, Default, Serialize, Deserialize)]
118pub struct AgentTTLCheckRequestQuery {
119 #[serde(skip_serializing)]
120 pub check_id: String,
121
122 pub note: Option<String>,
123
124 #[cfg(feature = "enterprise")]
125 #[serde(skip_serializing_if = "Option::is_none")]
126 pub ns: Option<String>,
127}
128
129#[derive(Debug, Default, Serialize, Deserialize)]
130pub struct AgentTTLCheckUpdateRequestQuery {
131 #[serde(skip_serializing)]
132 pub check_id: String,
133
134 #[cfg(feature = "enterprise")]
135 #[serde(skip_serializing_if = "Option::is_none")]
136 pub ns: Option<String>,
137}
138
139#[derive(Debug, Default, Serialize, Deserialize)]
140pub struct AgentTTLCheckUpdateRequestBody {
141 #[serde(rename = "Status")]
142 pub status: Option<String>,
143
144 #[serde(rename = "Output")]
145 pub output: Option<String>,
146}
147
148#[derive(Debug, Default, Serialize, Deserialize)]
149pub struct ServiceConfigurationRequestQuery {
150 #[serde(skip_serializing)]
151 pub service_id: String,
152
153 #[cfg(feature = "enterprise")]
154 #[serde(skip_serializing_if = "Option::is_none")]
155 pub ns: Option<String>,
156}
157
158#[derive(Debug, Default, Serialize, Deserialize)]
159pub struct LocalServiceHealthByNameRequestQuery {
160 #[cfg(feature = "enterprise")]
161 #[serde(skip_serializing_if = "Option::is_none")]
162 pub ns: Option<String>,
163}
164
165#[derive(Debug, Default, Serialize, Deserialize)]
166pub struct LocalServiceHealthByIDRequestQuery {
167 #[cfg(feature = "enterprise")]
168 #[serde(skip_serializing_if = "Option::is_none")]
169 pub ns: Option<String>,
170}
171
172#[derive(Debug, Default, Serialize, Deserialize)]
173pub struct RegisterServiceRequestQuery {
174 #[serde(rename = "replace-existing-checks")]
178 #[serde(skip_serializing_if = "Option::is_none")]
179 pub replace_existing_checks: Option<String>,
180
181 #[cfg(feature = "enterprise")]
182 #[serde(skip_serializing_if = "Option::is_none")]
183 pub ns: Option<String>,
184}
185
186#[derive(Debug, Default, Serialize, Deserialize)]
187pub struct DeregisterServiceRequestQuery {
188 #[cfg(feature = "enterprise")]
189 #[serde(skip_serializing_if = "Option::is_none")]
190 pub ns: Option<String>,
191
192 #[cfg(feature = "enterprise")]
193 #[serde(skip_serializing_if = "Option::is_none")]
194 pub partition: Option<String>,
195}
196
197#[derive(Debug, Default, Serialize, Deserialize)]
198pub struct EnableMaintenanceModeRequestQuery {
199 #[serde(skip_serializing)]
200 pub service_id: String,
201
202 pub enable: bool,
205
206 #[serde(skip_serializing_if = "Option::is_none")]
211 pub reason: Option<String>,
212
213 #[cfg(feature = "enterprise")]
214 #[serde(skip_serializing_if = "Option::is_none")]
215 pub ns: Option<String>,
216}
217
218#[derive(Debug, Default, Serialize, Deserialize)]
219pub struct ConnectAuthorizeRequestQuery {
220 #[cfg(feature = "enterprise")]
221 #[serde(skip_serializing_if = "Option::is_none")]
222 pub ns: Option<String>,
223}
224
225#[derive(Debug, Default, Serialize, Deserialize)]
226pub struct ConnectAuthorizeRequestReply {
227 #[serde(rename = "Authorized")]
229 pub authorized: bool,
230
231 #[serde(rename = "Reason")]
233 pub reason: String,
234}
235
236#[derive(Debug, Default, Serialize, Deserialize)]
237pub struct KVReadKeyQuery {
238 pub dc: Option<String>,
241
242 pub recurse: Option<bool>,
245
246 pub raw: Option<bool>,
249
250 pub keys: Option<bool>,
253
254 pub separator: Option<String>,
259
260 #[cfg(feature = "enterprise")]
261 #[serde(skip_serializing_if = "Option::is_none")]
262 pub ns: Option<String>,
263
264 #[cfg(feature = "enterprise")]
268 #[serde(skip_serializing_if = "Option::is_none")]
269 pub partition: Option<String>,
270}
271
272#[derive(Debug, Default, Serialize, Deserialize)]
273pub struct KVCreateOrUpdateKeyQuery {
274 pub dc: Option<String>,
277
278 pub flags: Option<u64>,
282
283 pub cas: Option<u64>,
289
290 pub acquire: Option<String>,
302
303 pub release: Option<String>,
308
309 #[cfg(feature = "enterprise")]
310 #[serde(skip_serializing_if = "Option::is_none")]
311 pub ns: Option<String>,
312
313 #[cfg(feature = "enterprise")]
314 #[serde(skip_serializing_if = "Option::is_none")]
315 pub partition: Option<String>,
316}
317
318#[derive(Debug, Default, Serialize, Deserialize)]
319pub struct KVDeleteKeyQuery {
320 pub dc: Option<String>,
324
325 pub recurse: Option<bool>,
328
329 pub cas: Option<u64>,
335
336 #[cfg(feature = "enterprise")]
337 #[serde(skip_serializing_if = "Option::is_none")]
338 pub ns: Option<String>,
339
340 #[cfg(feature = "enterprise")]
341 #[serde(skip_serializing_if = "Option::is_none")]
342 pub partition: Option<String>,
343}
344
345#[derive(Debug, Default, Serialize, Deserialize)]
346pub struct CatalogRegisterEntityQuery {
347 #[cfg(feature = "enterprise")]
348 #[serde(skip_serializing_if = "Option::is_none")]
349 pub ns: Option<String>,
350}
351
352#[derive(Debug, Default, Serialize, Deserialize)]
353pub struct CatalogDeregisterEntityQuery {
354 #[cfg(feature = "enterprise")]
355 #[serde(skip_serializing_if = "Option::is_none")]
356 pub ns: Option<String>,
357}
358
359#[derive(Debug, Default, Serialize, Deserialize)]
360pub struct CatalogListServicesQuery {
361 #[serde(skip_serializing_if = "Option::is_none")]
362 pub dc: Option<String>,
363
364 #[serde(rename = "node-meta")]
365 #[serde(skip_serializing_if = "Option::is_none")]
366 pub node_meta: Option<String>,
367
368 #[serde(skip_serializing_if = "Option::is_none")]
369 pub filter: Option<String>,
370
371 #[cfg(feature = "enterprise")]
372 #[serde(skip_serializing_if = "Option::is_none")]
373 pub ns: Option<String>,
374
375 #[cfg(feature = "enterprise")]
376 #[serde(skip_serializing_if = "Option::is_none")]
377 pub partition: Option<String>,
378}
379
380#[derive(Debug, Default, Serialize, Deserialize)]
381pub struct CatalogListNodesForServiceQuery {
382 #[serde(skip_serializing_if = "Option::is_none")]
383 pub dc: Option<String>,
384
385 #[serde(skip_serializing_if = "Option::is_none")]
386 pub tag: Option<String>,
387
388 #[serde(skip_serializing_if = "Option::is_none")]
389 pub near: Option<String>,
390
391 #[serde(rename = "node-meta")]
392 #[serde(skip_serializing_if = "Option::is_none")]
393 pub node_meta: Option<String>,
394
395 #[serde(skip_serializing_if = "Option::is_none")]
396 pub filter: Option<String>,
397
398 #[cfg(feature = "enterprise")]
399 #[serde(skip_serializing_if = "Option::is_none")]
400 pub ns: Option<String>,
401}
402
403#[derive(Debug, Default, Serialize, Deserialize)]
404pub struct CatalogNodeServicesQuery {
405 #[serde(skip_serializing_if = "Option::is_none")]
406 pub dc: Option<String>,
407
408 #[serde(skip_serializing_if = "Option::is_none")]
409 pub filter: Option<String>,
410
411 #[cfg(feature = "enterprise")]
412 #[serde(skip_serializing_if = "Option::is_none")]
413 pub ns: Option<String>,
414}
415
416#[derive(Debug, Default, Serialize, Deserialize)]
417pub struct CatalogGatewayServicesQuery {
418 #[serde(skip_serializing_if = "Option::is_none")]
419 pub dc: Option<String>,
420
421 #[cfg(feature = "enterprise")]
422 #[serde(skip_serializing_if = "Option::is_none")]
423 pub ns: Option<String>,
424}
425
426#[derive(Debug, Default, Serialize, Deserialize)]
427pub struct EventFireQuery {
428 #[serde(skip_serializing_if = "Option::is_none")]
429 pub dc: Option<String>,
430
431 #[serde(skip_serializing_if = "Option::is_none")]
432 pub node: Option<String>,
433
434 #[serde(skip_serializing_if = "Option::is_none")]
435 pub service: Option<String>,
436
437 #[serde(skip_serializing_if = "Option::is_none")]
438 pub tag: Option<String>,
439}
440
441#[derive(Debug, Default, Serialize, Deserialize)]
442pub struct EventListQuery {
443 #[serde(skip_serializing_if = "Option::is_none")]
444 pub name: Option<String>,
445
446 #[serde(skip_serializing_if = "Option::is_none")]
447 pub node: Option<String>,
448
449 #[serde(skip_serializing_if = "Option::is_none")]
450 pub service: Option<String>,
451
452 #[serde(skip_serializing_if = "Option::is_none")]
453 pub tag: Option<String>,
454}
455
456#[derive(Debug, Default, Serialize, Deserialize)]
457pub struct HealthListNodesQuery {
458 #[serde(skip_serializing_if = "Option::is_none")]
459 pub dc: Option<String>,
460
461 #[serde(skip_serializing_if = "Option::is_none")]
462 pub filter: Option<String>,
463
464 #[cfg(feature = "enterprise")]
465 #[serde(skip_serializing_if = "Option::is_none")]
466 pub ns: Option<String>,
467}
468
469#[derive(Debug, Default, Serialize, Deserialize)]
470pub struct HealthListServicesQuery {
471 #[serde(skip_serializing_if = "Option::is_none")]
472 pub dc: Option<String>,
473
474 #[serde(skip_serializing_if = "Option::is_none")]
475 pub near: Option<String>,
476
477 #[serde(rename = "node-meta")]
478 #[serde(skip_serializing_if = "Option::is_none")]
479 pub node_meta: Option<String>,
480
481 #[serde(skip_serializing_if = "Option::is_none")]
482 pub filter: Option<String>,
483
484 #[cfg(feature = "enterprise")]
485 #[serde(skip_serializing_if = "Option::is_none")]
486 pub ns: Option<String>,
487}
488
489#[derive(Debug, Default, Serialize, Deserialize)]
490pub struct HealthListServiceInstancesQuery {
491 #[serde(skip_serializing_if = "Option::is_none")]
492 pub dc: Option<String>,
493
494 #[serde(skip_serializing_if = "Option::is_none")]
495 pub near: Option<String>,
496
497 #[serde(skip_serializing_if = "Option::is_none")]
498 pub tag: Option<String>,
499
500 #[serde(rename = "node-meta")]
501 #[serde(skip_serializing_if = "Option::is_none")]
502 pub node_meta: Option<String>,
503
504 #[serde(skip_serializing_if = "Option::is_none")]
505 pub passing: Option<bool>,
506
507 #[serde(skip_serializing_if = "Option::is_none")]
508 pub filter: Option<String>,
509
510 #[serde(skip_serializing_if = "Option::is_none")]
511 pub peer: Option<String>,
512
513 #[serde(skip_serializing_if = "Option::is_none")]
514 pub index: Option<u64>,
515
516 #[cfg(feature = "enterprise")]
517 #[serde(skip_serializing_if = "Option::is_none")]
518 pub ns: Option<String>,
519
520 #[cfg(feature = "enterprise")]
521 #[serde(skip_serializing_if = "Option::is_none")]
522 pub sg: Option<String>,
523}
524
525#[derive(Debug, Default, Serialize, Deserialize)]
526pub struct HealthListStateQuery {
527 #[serde(skip_serializing_if = "Option::is_none")]
528 pub dc: Option<String>,
529
530 #[serde(skip_serializing_if = "Option::is_none")]
531 pub near: Option<String>,
532
533 #[serde(rename = "node-meta")]
534 #[serde(skip_serializing_if = "Option::is_none")]
535 pub node_meta: Option<String>,
536
537 #[serde(skip_serializing_if = "Option::is_none")]
538 pub filter: Option<String>,
539
540 #[cfg(feature = "enterprise")]
541 #[serde(skip_serializing_if = "Option::is_none")]
542 pub ns: Option<String>,
543}
544
545#[cfg(feature = "enterprise")]
546#[derive(Debug, Default, Serialize, Deserialize)]
547pub struct NamespaceCreateBody {
548 #[serde(rename = "Name")]
553 pub name: String,
554
555 #[serde(rename = "Description")]
557 #[serde(skip_serializing_if = "Option::is_none")]
558 pub description: Option<String>,
559
560 #[serde(rename = "ACLs")]
561 #[serde(skip_serializing_if = "Option::is_none")]
562 pub acls: Option<NamespaceACLConfig>,
563
564 #[serde(rename = "Meta")]
565 #[serde(skip_serializing_if = "Option::is_none")]
566 pub meta: Option<::std::collections::HashMap<String, String>>,
567
568 #[serde(rename = "Partition")]
569 #[serde(skip_serializing_if = "Option::is_none")]
570 pub partition: Option<String>,
571}
572
573#[cfg(feature = "enterprise")]
574#[derive(Debug, Default, Serialize, Deserialize)]
575pub struct NamespaceDetail {
576 #[serde(rename = "Name")]
578 pub name: String,
579
580 #[serde(rename = "Description")]
582 #[serde(skip_serializing_if = "Option::is_none")]
583 pub description: Option<String>,
584
585 #[serde(rename = "ACLs")]
586 #[serde(skip_serializing_if = "Option::is_none")]
587 pub acls: Option<NamespaceACLConfig>,
588
589 #[serde(rename = "Meta")]
590 #[serde(skip_serializing_if = "Option::is_none")]
591 pub meta: Option<::std::collections::HashMap<String, String>>,
592
593 #[serde(rename = "CreateIndex")]
594 pub create_index: u64,
595
596 #[serde(rename = "ModifyIndex")]
597 pub modify_index: u64,
598}
599
600#[cfg(feature = "enterprise")]
601#[derive(Debug, Default, Serialize, Deserialize)]
602pub struct NamespaceReadQuery {
603 #[serde(skip_serializing_if = "Option::is_none")]
604 pub partition: Option<String>,
605}
606
607#[cfg(feature = "enterprise")]
608#[derive(Debug, Default, Serialize, Deserialize)]
609pub struct NamespaceUpdateBody {
610 #[serde(rename = "Name")]
613 #[serde(skip_serializing_if = "Option::is_none")]
614 pub name: Option<String>,
615
616 #[serde(rename = "Description")]
618 #[serde(skip_serializing_if = "Option::is_none")]
619 pub description: Option<String>,
620
621 #[serde(rename = "ACLs")]
622 #[serde(skip_serializing_if = "Option::is_none")]
623 pub acls: Option<NamespaceACLConfig>,
624
625 #[serde(rename = "Meta")]
626 #[serde(skip_serializing_if = "Option::is_none")]
627 pub meta: Option<::std::collections::HashMap<String, String>>,
628
629 #[serde(rename = "Partition")]
630 #[serde(skip_serializing_if = "Option::is_none")]
631 pub partition: Option<String>,
632}
633
634#[derive(Debug, Default, Serialize, Deserialize)]
635pub struct StatusQuery {
636 #[serde(skip_serializing_if = "Option::is_none")]
637 pub dc: Option<String>,
638}
639
640#[derive(Clone)]
642pub struct Client {
643 cfg: Config,
644 http: reqwest::Client,
645 prefix: String,
646}
647
648impl Client {
649 pub fn new() -> Self {
651 ClientBuilder::new(Config::default()).build().unwrap()
652 }
653
654 pub async fn agent_checks(
659 &self,
660 q: &FilterRequestQuery,
661 ) -> Result<HashMap<String, HealthCheck>> {
662 let resp = self
663 .execute_request(Method::GET, "/agent/checks", q, None, &())
664 .await?;
665
666 resp.json().await.map_err(|e| anyhow!(e))
667 }
668
669 pub async fn agent_check_register(&self, b: &CheckDefinition) -> Result<bool> {
674 let resp = self
675 .execute_request(Method::PUT, "/agent/check/register", &(), None, b)
676 .await?;
677 Ok(resp.status() == StatusCode::OK)
678 }
679
680 pub async fn agent_check_deregister(&self, q: &DeregisterCheckRequestQuery) -> Result<bool> {
685 let path = format!("/agent/check/deregister/{}", q.check_id);
686 let resp = self
687 .execute_request(Method::PUT, &path, q, None, &())
688 .await?;
689 Ok(resp.status() == StatusCode::OK)
690 }
691
692 pub async fn agent_check_pass(&self, q: &AgentTTLCheckRequestQuery) -> Result<bool> {
696 let path = format!("/agent/check/pass/{}", q.check_id);
697 let resp = self
698 .execute_request(Method::PUT, &path, q, None, &())
699 .await?;
700
701 Ok(resp.status() == StatusCode::OK)
702 }
703
704 pub async fn agent_check_warn(&self, q: &AgentTTLCheckRequestQuery) -> Result<()> {
708 let path = format!("/agent/check/warn/{}", q.check_id);
709 let resp = self
710 .execute_request(Method::PUT, &path, q, None, &())
711 .await?;
712 resp.json().await.map_err(|e| anyhow!(e))
713 }
714
715 pub async fn agent_check_fail(&self, q: &AgentTTLCheckRequestQuery) -> Result<bool> {
719 let path = format!("/agent/check/fail/{}", q.check_id);
720 let resp = self
721 .execute_request(Method::PUT, &path, q, None, &())
722 .await?;
723 Ok(resp.status() == StatusCode::OK)
724 }
725
726 pub async fn agent_check_update(
730 &self,
731 q: &AgentTTLCheckUpdateRequestQuery,
732 b: &AgentTTLCheckUpdateRequestBody,
733 ) -> Result<bool> {
734 let path = format!("/agent/check/update/{}", q.check_id);
735 let resp = self.execute_request(Method::PUT, &path, q, None, b).await?;
736 Ok(resp.status() == StatusCode::OK)
737 }
738
739 pub async fn agent_services(
744 &self,
745 q: &FilterRequestQuery,
746 ) -> Result<HashMap<String, AgentService>> {
747 let resp = self
748 .execute_request(Method::GET, "/agent/services", q, None, &())
749 .await?;
750 resp.json().await.map_err(|e| anyhow!(e))
751 }
752
753 pub async fn agent_service_configuration(
754 &self,
755 q: &ServiceConfigurationRequestQuery,
756 ) -> Result<Option<AgentService>> {
757 let path = format!("/agent/service/{}", q.service_id);
758 let resp = self
759 .execute_request(Method::GET, &path, q, None, &())
760 .await?;
761
762 if resp.status() == StatusCode::NOT_FOUND {
763 return Ok(None);
764 }
765
766 Ok(Some(resp.json().await.map_err(|e| anyhow!(e))?))
767 }
768
769 pub async fn agent_get_service_health_by_name<S: Into<String>>(
777 &self,
778 service_name: S,
779 q: &LocalServiceHealthByNameRequestQuery,
780 ) -> Result<Vec<AgentServiceChecksInfo>> {
781 let path = format!("/agent/health/service/name/{}", service_name.into());
782 let resp = self
783 .execute_request(Method::GET, &path, q, None, &())
784 .await?;
785 resp.json().await.map_err(|e| anyhow!(e))
786 }
787
788 pub async fn agent_get_service_health_by_id<S: Into<String>>(
792 &self,
793 service_id: S,
794 q: &LocalServiceHealthByIDRequestQuery,
795 ) -> Result<Option<AgentServiceChecksInfo>> {
796 let path = format!("/agent/health/service/id/{}", service_id.into());
797 let resp = self
798 .execute_request(Method::GET, &path, q, None, &())
799 .await?;
800
801 if resp.status() == StatusCode::NOT_FOUND {
802 return Ok(None);
803 }
804
805 Ok(Some(resp.json().await.map_err(|e| anyhow!(e))?))
806 }
807
808 pub async fn agent_register_service(
816 &self,
817 q: &RegisterServiceRequestQuery,
818 b: &ServiceDefinition,
819 ) -> Result<bool> {
820 let resp = self
821 .execute_request(Method::PUT, "/agent/service/register", q, None, b)
822 .await?;
823 Ok(resp.status() == StatusCode::OK)
824 }
825
826 pub async fn agent_deregister_service<S: Into<String>>(
833 &self,
834 service_id: S,
835 q: &DeregisterServiceRequestQuery,
836 ) -> Result<bool> {
837 let path = format!("/agent/service/deregister/{}", service_id.into());
838 let resp = self
839 .execute_request(Method::PUT, &path, q, None, &())
840 .await?;
841 Ok(resp.status() == StatusCode::OK)
842 }
843
844 pub async fn agent_enable_maintenance_mode(
852 &self,
853 q: &EnableMaintenanceModeRequestQuery,
854 ) -> Result<bool> {
855 let path = format!("/agent/service/maintenance/{}", q.service_id);
856 let resp = self
857 .execute_request(Method::PUT, &path, q, None, &())
858 .await?;
859 Ok(resp.status() == StatusCode::OK)
860 }
861
862 pub async fn agent_connect_authorize(
863 &self,
864 q: &ConnectAuthorizeRequestQuery,
865 b: &ConnectAuthorizeRequest,
866 ) -> Result<ConnectAuthorizeRequestReply> {
867 let resp = self
868 .execute_request(Method::POST, "/agent/connect/authorize", q, None, b)
869 .await?;
870 resp.json().await.map_err(|e| anyhow!(e))
871 }
872
873 pub async fn catalog_register_entity(
879 &self,
880 q: &CatalogRegisterEntityQuery,
881 b: &RegisterRequest,
882 ) -> Result<bool> {
883 let resp = self
884 .execute_request(Method::PUT, "/catalog/register", q, None, b)
885 .await?;
886
887 Ok(resp.status() == StatusCode::OK)
888 }
889
890 pub async fn catalog_deregister_entity(
896 &self,
897 q: &CatalogDeregisterEntityQuery,
898 b: &DeregisterRequest,
899 ) -> Result<bool> {
900 let resp = self
901 .execute_request(Method::PUT, "/catalog/deregister", q, None, b)
902 .await?;
903
904 Ok(resp.status() == StatusCode::OK)
905 }
906
907 pub async fn catalog_list_datacenters(&self) -> Result<Vec<String>> {
913 let resp = self
914 .execute_request(Method::GET, "/catalog/datacenters", &(), None, &())
915 .await?;
916
917 resp.json().await.map_err(|e| anyhow!(e))
918 }
919
920 pub async fn catalog_list_nodes(&self) -> Result<Vec<Node>> {
923 let resp = self
924 .execute_request(Method::GET, "/catalog/nodes", &(), None, &())
925 .await?;
926
927 resp.json().await.map_err(|e| anyhow!(e))
928 }
929
930 pub async fn catalog_list_services(
933 &self,
934 q: &CatalogListServicesQuery,
935 ) -> Result<::std::collections::HashMap<String, Vec<String>>> {
936 let resp = self
937 .execute_request(Method::GET, "/catalog/services", q, None, &())
938 .await?;
939
940 resp.json().await.map_err(|e| anyhow!(e))
941 }
942
943 pub async fn catalog_list_nodes_for_service<S: Into<String>>(
947 &self,
948 service_name: S,
949 q: &CatalogListNodesForServiceQuery,
950 ) -> Result<Vec<ServiceNode>> {
951 let p = format!("/catalog/service/{}", service_name.into());
952
953 let resp = self.execute_request(Method::GET, &p, q, None, &()).await?;
954
955 resp.json().await.map_err(|e| anyhow!(e))
956 }
957
958 pub async fn catalog_list_nodes_for_mesh_capable_service<S: Into<String>>(
965 &self,
966 service: S,
967 q: &CatalogListNodesForServiceQuery,
968 ) -> Result<Vec<ServiceNode>> {
969 let p = format!("/catalog/connect/{}", service.into());
970
971 let resp = self.execute_request(Method::GET, &p, q, None, &()).await?;
972
973 resp.json().await.map_err(|e| anyhow!(e))
974 }
975
976 pub async fn catalog_node_services<S: Into<String>>(
979 &self,
980 node_name: S,
981 q: &CatalogNodeServicesQuery,
982 ) -> Result<Option<NodeServices>> {
983 let p = format!("/catalog/node/{}", node_name.into());
984
985 let resp = self.execute_request(Method::GET, &p, q, None, &()).await?;
986
987 if resp.status() == StatusCode::NOT_FOUND {
988 return Ok(None);
989 }
990
991 resp.json().await.map_err(|e| anyhow!(e))
992 }
993
994 pub async fn catalog_gateway_services<S: Into<String>>(
998 &self,
999 gateway: S,
1000 q: &CatalogGatewayServicesQuery,
1001 ) -> Result<Vec<GatewayService>> {
1002 let p = format!("/catalog/gateway-services/{}", gateway.into());
1003
1004 let resp = self.execute_request(Method::GET, &p, q, None, &()).await?;
1005
1006 resp.json().await.map_err(|e| anyhow!(e))
1007 }
1008
1009 pub async fn event_fire<S: Into<String>>(
1012 &self,
1013 name: S,
1014 body: Option<Vec<u8>>,
1015 q: &EventFireQuery,
1016 ) -> Result<bool> {
1017 let p = format!("/event/fire/{}", name.into());
1018
1019 let resp = self.execute_request(Method::PUT, &p, q, body, &()).await?;
1020
1021 Ok(resp.status() == StatusCode::OK)
1022 }
1023
1024 pub async fn event_list(&self, q: &EventListQuery) -> Result<Vec<UserEvent>> {
1031 let resp = self
1032 .execute_request(Method::GET, "/event/list", q, None, &())
1033 .await?;
1034
1035 let mut list: Vec<UserEvent> = resp.json().await.map_err(|e| anyhow!(e))?;
1036
1037 for item in list.iter_mut() {
1038 item.payload = item.payload.clone().map_or(None, |v| {
1039 if v.0 == "bnVsbA==" {
1041 None
1042 } else {
1043 Some(v)
1044 }
1045 })
1046 }
1047
1048 Ok(list)
1049 }
1050
1051 pub async fn health_list_nodes<S: Into<String>>(
1055 &self,
1056 node: S,
1057 q: &HealthListNodesQuery,
1058 ) -> Result<Vec<HealthCheck>> {
1059 let p = format!("/health/node/{}", node.into());
1060
1061 let resp = self.execute_request(Method::GET, &p, q, None, &()).await?;
1062
1063 resp.json().await.map_err(|e| anyhow!(e))
1064 }
1065
1066 pub async fn health_list_services<S: Into<String>>(
1070 &self,
1071 service: S,
1072 q: &HealthListServicesQuery,
1073 ) -> Result<Vec<HealthCheck>> {
1074 let p = format!("/health/checks/{}", service.into());
1075
1076 let resp = self.execute_request(Method::GET, &p, q, None, &()).await?;
1077
1078 resp.json().await.map_err(|e| anyhow!(e))
1079 }
1080
1081 pub async fn health_list_service_instances<S: Into<String>>(
1086 &self,
1087 service: S,
1088 q: &HealthListServiceInstancesQuery,
1089 ) -> Result<Vec<CheckServiceNode>> {
1090 let p = format!("/health/service/{}", service.into());
1091
1092 let resp = self.execute_request(Method::GET, &p, q, None, &()).await?;
1093
1094 resp.json().await.map_err(|e| anyhow!(e))
1095 }
1096
1097 pub async fn health_list_service_instances_for_mesh_capable<S: Into<String>>(
1106 &self,
1107 service: S,
1108 q: &HealthListServiceInstancesQuery,
1109 ) -> Result<Vec<CheckServiceNode>> {
1110 let p = format!("/health/connect/{}", service.into());
1111
1112 let resp = self.execute_request(Method::GET, &p, q, None, &()).await?;
1113
1114 resp.json().await.map_err(|e| anyhow!(e))
1115 }
1116
1117 pub async fn health_list_service_instances_for_ingress_gateways<S: Into<String>>(
1125 &self,
1126 service: S,
1127 q: &HealthListServiceInstancesQuery,
1128 ) -> Result<Vec<CheckServiceNode>> {
1129 let p = format!("/health/ingress/{}", service.into());
1130
1131 let resp = self.execute_request(Method::GET, &p, q, None, &()).await?;
1132
1133 resp.json().await.map_err(|e| anyhow!(e))
1134 }
1135
1136 pub async fn health_list_state(
1141 &self,
1142 state: Health,
1143 q: &HealthListStateQuery,
1144 ) -> Result<Vec<HealthCheck>> {
1145 let p = format!("/health/state/{}", state);
1146
1147 let resp = self.execute_request(Method::GET, &p, q, None, &()).await?;
1148
1149 resp.json().await.map_err(|e| anyhow!(e))
1150 }
1151
1152 pub async fn kv_read_key<S: Into<String>>(
1156 &self,
1157 key: S,
1158 q: &KVReadKeyQuery,
1159 ) -> Result<Option<Vec<u8>>> {
1160 let path = format!("/kv/{}", key.into());
1161 let resp = self
1162 .execute_request(Method::GET, &path, q, None, &())
1163 .await?;
1164
1165 if resp.status() == StatusCode::NOT_FOUND {
1166 return Ok(None);
1167 }
1168
1169 let full = resp.bytes().await?;
1170
1171 if full.is_empty() {
1172 return Ok(Some(vec![]));
1173 }
1174
1175 Ok(Some(full.to_vec()))
1176 }
1177
1178 pub async fn kv_create_or_update_key<S: Into<String>>(
1182 &self,
1183 key: S,
1184 b: Vec<u8>,
1185 q: &KVCreateOrUpdateKeyQuery,
1186 ) -> Result<bool> {
1187 let path = format!("/kv/{}", key.into());
1188 let resp = self
1189 .execute_request(Method::PUT, &path, q, Some(b), &())
1190 .await?;
1191 resp.json().await.map_err(|e| anyhow!(e))
1192 }
1193
1194 pub async fn kv_delete_key<S: Into<String>>(
1197 &self,
1198 key: S,
1199 q: &KVDeleteKeyQuery,
1200 ) -> Result<bool> {
1201 let path = format!("/kv/{}", key.into());
1202 let resp = self
1203 .execute_request(Method::DELETE, &path, q, None, &())
1204 .await?;
1205 resp.json().await.map_err(|e| anyhow!(e))
1206 }
1207
1208 #[cfg(feature = "enterprise")]
1215 pub async fn namespace_create(&self, b: &NamespaceCreateBody) -> Result<NamespaceDetail> {
1216 let resp = self
1217 .execute_request(Method::PUT, "/namespace", &(), None, b)
1218 .await?;
1219 resp.json().await.map_err(|e| anyhow!(e))
1220 }
1221
1222 #[cfg(feature = "enterprise")]
1229 pub async fn namespace_read<S: Into<String>>(
1230 &self,
1231 name: S,
1232 q: &NamespaceReadQuery,
1233 ) -> Result<Option<NamespaceDetail>> {
1234 let p = format!("/namespace/{}", name.into());
1235
1236 let resp = self.execute_request(Method::GET, &p, &q, None, &()).await?;
1237
1238 if resp.status() == StatusCode::NOT_FOUND {
1239 return Ok(None);
1240 }
1241
1242 Ok(Some(resp.json().await.map_err(|e| anyhow!(e))?))
1243 }
1244
1245 #[cfg(feature = "enterprise")]
1252 pub async fn namespace_update<S: Into<String>>(
1253 &self,
1254 name: S,
1255 b: &NamespaceUpdateBody,
1256 ) -> Result<NamespaceDetail> {
1257 let p = format!("/namespace/{}", name.into());
1258
1259 let resp = self.execute_request(Method::PUT, &p, &(), None, &b).await?;
1260
1261 resp.json().await.map_err(|e| anyhow!(e))
1262 }
1263
1264 pub async fn status_leader(&self, q: &StatusQuery) -> Result<String> {
1270 let resp = self
1271 .execute_request(Method::GET, "/status/leader", q, None, &())
1272 .await?;
1273
1274 resp.text_with_charset("utf-8")
1275 .await
1276 .map_err(|e| anyhow!(e))
1277 }
1278
1279 pub async fn status_peers(&self, q: &StatusQuery) -> Result<Vec<String>> {
1287 let resp = self
1288 .execute_request(Method::GET, "/status/peers", q, None, &())
1289 .await?;
1290
1291 resp.json().await.map_err(|e| anyhow!(e))
1292 }
1293
1294 async fn execute_request<Q, B>(
1295 &self,
1296 method: Method,
1297 path: &str,
1298 query: &Q,
1299 raw_body: Option<Vec<u8>>,
1300 json_body: &B,
1301 ) -> Result<Response>
1302 where
1303 Q: Serialize,
1304 B: Serialize,
1305 {
1306 let path = format!("{}{}{}", self.cfg.address, self.prefix, path);
1307 let mut b = self.http.request(method.clone(), &path);
1308
1309 b = b.query(query);
1310
1311 if method == Method::PUT || method == Method::POST {
1312 if let Some(body) = raw_body {
1313 b = b.body(body)
1314 } else {
1315 b = b.json(json_body);
1316 }
1317 }
1318
1319 let resp = b.send().await?;
1320 Ok(resp)
1321 }
1322}
1323
1324#[inline]
1325fn read_env_or_default(key: &str, default: &str) -> String {
1326 std::env::var(key).unwrap_or_else(|_| default.to_string())
1327}
1328
1329#[cfg(test)]
1330mod tests {
1331 use super::*;
1332
1333 #[test]
1334 fn create_client() {
1335 let _ = Client::new();
1336 }
1337}