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#[cfg(all(feature = "v1", feature = "v1_22_x"))]
23mod structs_1_22_x;
24#[cfg(all(feature = "v1", feature = "v1_22_x"))]
25pub use structs_1_22_x::*;
26
27#[derive(Clone, Debug)]
28pub struct Config {
29 pub token: String,
30 pub address: String,
31}
32
33impl Config {
34 pub fn from_env() -> Self {
35 Self {
36 token: read_env_or_default("CONSUL_TOKEN", ""),
37 address: read_env_or_default("CONSUL_ADDRESS", "http://127.0.0.1:8500"),
38 }
39 }
40}
41
42impl Default for Config {
43 fn default() -> Self {
44 Self::from_env()
45 }
46}
47
48pub struct ClientBuilder {
49 cfg: Config,
50 proxies: Vec<Proxy>,
51 timeout: Option<Duration>,
52}
53
54impl ClientBuilder {
55 pub fn new(cfg: Config) -> Self {
56 Self {
57 cfg,
58 proxies: vec![],
59 timeout: None,
60 }
61 }
62
63 pub fn with_proxy(mut self, proxy: Proxy) -> Self {
64 self.proxies.push(proxy);
65 self
66 }
67
68 pub fn with_timeout(mut self, timeout: Duration) -> Self {
69 self.timeout = Some(timeout);
70 self
71 }
72
73 pub fn build(self) -> Result<Client> {
74 let mut headers = HeaderMap::new();
75 if !self.cfg.token.is_empty() {
76 headers.insert(
77 AUTHORIZATION,
78 HeaderValue::from_str(&format!("Bearer {}", self.cfg.token)).unwrap(),
79 );
80 }
81
82 let mut builder = reqwest::ClientBuilder::new();
83 builder = builder.default_headers(headers);
84
85 for proxy in self.proxies {
86 builder = builder.proxy(proxy)
88 }
89
90 if let Some(v) = self.timeout {
91 builder = builder.timeout(v);
92 }
93
94 Ok(Client {
95 cfg: self.cfg,
96 http: builder.build()?,
97 #[cfg(feature = "v1")]
98 prefix: "/v1".to_string(),
99 })
100 }
101}
102
103#[derive(Debug, Default, Serialize, Deserialize)]
104pub struct FilterRequestQuery {
105 pub filter: Option<String>,
106
107 #[cfg(feature = "enterprise")]
108 #[serde(skip_serializing_if = "Option::is_none")]
109 pub ns: Option<String>,
110}
111
112#[derive(Debug, Default, Serialize, Deserialize)]
113pub struct DeregisterCheckRequestQuery {
114 #[serde(skip_serializing)]
115 pub check_id: String,
116
117 #[cfg(feature = "enterprise")]
118 #[serde(skip_serializing_if = "Option::is_none")]
119 pub ns: Option<String>,
120}
121
122#[derive(Debug, Default, Serialize, Deserialize)]
123pub struct AgentTTLCheckRequestQuery {
124 #[serde(skip_serializing)]
125 pub check_id: String,
126
127 pub note: Option<String>,
128
129 #[cfg(feature = "enterprise")]
130 #[serde(skip_serializing_if = "Option::is_none")]
131 pub ns: Option<String>,
132}
133
134#[derive(Debug, Default, Serialize, Deserialize)]
135pub struct AgentTTLCheckUpdateRequestQuery {
136 #[serde(skip_serializing)]
137 pub check_id: String,
138
139 #[cfg(feature = "enterprise")]
140 #[serde(skip_serializing_if = "Option::is_none")]
141 pub ns: Option<String>,
142}
143
144#[derive(Debug, Default, Serialize, Deserialize)]
145pub struct AgentTTLCheckUpdateRequestBody {
146 #[serde(rename = "Status")]
147 pub status: Option<String>,
148
149 #[serde(rename = "Output")]
150 pub output: Option<String>,
151}
152
153#[derive(Debug, Default, Serialize, Deserialize)]
154pub struct ServiceConfigurationRequestQuery {
155 #[serde(skip_serializing)]
156 pub service_id: String,
157
158 #[cfg(feature = "enterprise")]
159 #[serde(skip_serializing_if = "Option::is_none")]
160 pub ns: Option<String>,
161}
162
163#[derive(Debug, Default, Serialize, Deserialize)]
164pub struct LocalServiceHealthByNameRequestQuery {
165 #[cfg(feature = "enterprise")]
166 #[serde(skip_serializing_if = "Option::is_none")]
167 pub ns: Option<String>,
168}
169
170#[derive(Debug, Default, Serialize, Deserialize)]
171pub struct LocalServiceHealthByIDRequestQuery {
172 #[cfg(feature = "enterprise")]
173 #[serde(skip_serializing_if = "Option::is_none")]
174 pub ns: Option<String>,
175}
176
177#[derive(Debug, Default, Serialize, Deserialize)]
178pub struct RegisterServiceRequestQuery {
179 #[serde(rename = "replace-existing-checks")]
183 #[serde(skip_serializing_if = "Option::is_none")]
184 pub replace_existing_checks: Option<String>,
185
186 #[cfg(feature = "enterprise")]
187 #[serde(skip_serializing_if = "Option::is_none")]
188 pub ns: Option<String>,
189}
190
191#[derive(Debug, Default, Serialize, Deserialize)]
192pub struct DeregisterServiceRequestQuery {
193 #[cfg(feature = "enterprise")]
194 #[serde(skip_serializing_if = "Option::is_none")]
195 pub ns: Option<String>,
196
197 #[cfg(feature = "enterprise")]
198 #[serde(skip_serializing_if = "Option::is_none")]
199 pub partition: Option<String>,
200}
201
202#[derive(Debug, Default, Serialize, Deserialize)]
203pub struct EnableMaintenanceModeRequestQuery {
204 #[serde(skip_serializing)]
205 pub service_id: String,
206
207 pub enable: bool,
210
211 #[serde(skip_serializing_if = "Option::is_none")]
216 pub reason: Option<String>,
217
218 #[cfg(feature = "enterprise")]
219 #[serde(skip_serializing_if = "Option::is_none")]
220 pub ns: Option<String>,
221}
222
223#[derive(Debug, Default, Serialize, Deserialize)]
224pub struct ConnectAuthorizeRequestQuery {
225 #[cfg(feature = "enterprise")]
226 #[serde(skip_serializing_if = "Option::is_none")]
227 pub ns: Option<String>,
228}
229
230#[derive(Debug, Default, Serialize, Deserialize)]
231pub struct ConnectAuthorizeRequestReply {
232 #[serde(rename = "Authorized")]
234 pub authorized: bool,
235
236 #[serde(rename = "Reason")]
238 pub reason: String,
239}
240
241#[derive(Debug, Default, Serialize, Deserialize)]
242pub struct KVReadKeyQuery {
243 pub dc: Option<String>,
246
247 pub recurse: Option<bool>,
250
251 pub raw: Option<bool>,
254
255 pub keys: Option<bool>,
258
259 pub separator: Option<String>,
264
265 #[cfg(feature = "enterprise")]
266 #[serde(skip_serializing_if = "Option::is_none")]
267 pub ns: Option<String>,
268
269 #[cfg(feature = "enterprise")]
273 #[serde(skip_serializing_if = "Option::is_none")]
274 pub partition: Option<String>,
275}
276
277#[derive(Debug, Default, Serialize, Deserialize)]
278pub struct KVCreateOrUpdateKeyQuery {
279 pub dc: Option<String>,
282
283 pub flags: Option<u64>,
287
288 pub cas: Option<u64>,
294
295 pub acquire: Option<String>,
307
308 pub release: Option<String>,
313
314 #[cfg(feature = "enterprise")]
315 #[serde(skip_serializing_if = "Option::is_none")]
316 pub ns: Option<String>,
317
318 #[cfg(feature = "enterprise")]
319 #[serde(skip_serializing_if = "Option::is_none")]
320 pub partition: Option<String>,
321}
322
323#[derive(Debug, Default, Serialize, Deserialize)]
324pub struct KVDeleteKeyQuery {
325 pub dc: Option<String>,
329
330 pub recurse: Option<bool>,
333
334 pub cas: Option<u64>,
340
341 #[cfg(feature = "enterprise")]
342 #[serde(skip_serializing_if = "Option::is_none")]
343 pub ns: Option<String>,
344
345 #[cfg(feature = "enterprise")]
346 #[serde(skip_serializing_if = "Option::is_none")]
347 pub partition: Option<String>,
348}
349
350#[derive(Debug, Default, Serialize, Deserialize)]
351pub struct CatalogRegisterEntityQuery {
352 #[cfg(feature = "enterprise")]
353 #[serde(skip_serializing_if = "Option::is_none")]
354 pub ns: Option<String>,
355}
356
357#[derive(Debug, Default, Serialize, Deserialize)]
358pub struct CatalogDeregisterEntityQuery {
359 #[cfg(feature = "enterprise")]
360 #[serde(skip_serializing_if = "Option::is_none")]
361 pub ns: Option<String>,
362}
363
364#[derive(Debug, Default, Serialize, Deserialize)]
365pub struct CatalogListServicesQuery {
366 #[serde(skip_serializing_if = "Option::is_none")]
367 pub dc: Option<String>,
368
369 #[serde(rename = "node-meta")]
370 #[serde(skip_serializing_if = "Option::is_none")]
371 pub node_meta: Option<String>,
372
373 #[serde(skip_serializing_if = "Option::is_none")]
374 pub filter: Option<String>,
375
376 #[cfg(feature = "enterprise")]
377 #[serde(skip_serializing_if = "Option::is_none")]
378 pub ns: Option<String>,
379
380 #[cfg(feature = "enterprise")]
381 #[serde(skip_serializing_if = "Option::is_none")]
382 pub partition: Option<String>,
383}
384
385#[derive(Debug, Default, Serialize, Deserialize)]
386pub struct CatalogListNodesForServiceQuery {
387 #[serde(skip_serializing_if = "Option::is_none")]
388 pub dc: Option<String>,
389
390 #[serde(skip_serializing_if = "Option::is_none")]
391 pub tag: Option<String>,
392
393 #[serde(skip_serializing_if = "Option::is_none")]
394 pub near: Option<String>,
395
396 #[serde(rename = "node-meta")]
397 #[serde(skip_serializing_if = "Option::is_none")]
398 pub node_meta: Option<String>,
399
400 #[serde(skip_serializing_if = "Option::is_none")]
401 pub filter: Option<String>,
402
403 #[cfg(feature = "enterprise")]
404 #[serde(skip_serializing_if = "Option::is_none")]
405 pub ns: Option<String>,
406}
407
408#[derive(Debug, Default, Serialize, Deserialize)]
409pub struct CatalogNodeServicesQuery {
410 #[serde(skip_serializing_if = "Option::is_none")]
411 pub dc: Option<String>,
412
413 #[serde(skip_serializing_if = "Option::is_none")]
414 pub filter: Option<String>,
415
416 #[cfg(feature = "enterprise")]
417 #[serde(skip_serializing_if = "Option::is_none")]
418 pub ns: Option<String>,
419}
420
421#[derive(Debug, Default, Serialize, Deserialize)]
422pub struct CatalogGatewayServicesQuery {
423 #[serde(skip_serializing_if = "Option::is_none")]
424 pub dc: Option<String>,
425
426 #[cfg(feature = "enterprise")]
427 #[serde(skip_serializing_if = "Option::is_none")]
428 pub ns: Option<String>,
429}
430
431#[derive(Debug, Default, Serialize, Deserialize)]
432pub struct EventFireQuery {
433 #[serde(skip_serializing_if = "Option::is_none")]
434 pub dc: Option<String>,
435
436 #[serde(skip_serializing_if = "Option::is_none")]
437 pub node: Option<String>,
438
439 #[serde(skip_serializing_if = "Option::is_none")]
440 pub service: Option<String>,
441
442 #[serde(skip_serializing_if = "Option::is_none")]
443 pub tag: Option<String>,
444}
445
446#[derive(Debug, Default, Serialize, Deserialize)]
447pub struct EventListQuery {
448 #[serde(skip_serializing_if = "Option::is_none")]
449 pub name: Option<String>,
450
451 #[serde(skip_serializing_if = "Option::is_none")]
452 pub node: Option<String>,
453
454 #[serde(skip_serializing_if = "Option::is_none")]
455 pub service: Option<String>,
456
457 #[serde(skip_serializing_if = "Option::is_none")]
458 pub tag: Option<String>,
459}
460
461#[derive(Debug, Default, Serialize, Deserialize)]
462pub struct HealthListNodesQuery {
463 #[serde(skip_serializing_if = "Option::is_none")]
464 pub dc: Option<String>,
465
466 #[serde(skip_serializing_if = "Option::is_none")]
467 pub filter: Option<String>,
468
469 #[cfg(feature = "enterprise")]
470 #[serde(skip_serializing_if = "Option::is_none")]
471 pub ns: Option<String>,
472}
473
474#[derive(Debug, Default, Serialize, Deserialize)]
475pub struct HealthListServicesQuery {
476 #[serde(skip_serializing_if = "Option::is_none")]
477 pub dc: Option<String>,
478
479 #[serde(skip_serializing_if = "Option::is_none")]
480 pub near: Option<String>,
481
482 #[serde(rename = "node-meta")]
483 #[serde(skip_serializing_if = "Option::is_none")]
484 pub node_meta: Option<String>,
485
486 #[serde(skip_serializing_if = "Option::is_none")]
487 pub filter: Option<String>,
488
489 #[cfg(feature = "enterprise")]
490 #[serde(skip_serializing_if = "Option::is_none")]
491 pub ns: Option<String>,
492}
493
494#[derive(Debug, Default, Serialize, Deserialize)]
495pub struct HealthListServiceInstancesQuery {
496 #[serde(skip_serializing_if = "Option::is_none")]
497 pub dc: Option<String>,
498
499 #[serde(skip_serializing_if = "Option::is_none")]
500 pub near: Option<String>,
501
502 #[serde(skip_serializing_if = "Option::is_none")]
503 pub tag: Option<String>,
504
505 #[serde(rename = "node-meta")]
506 #[serde(skip_serializing_if = "Option::is_none")]
507 pub node_meta: Option<String>,
508
509 #[serde(skip_serializing_if = "Option::is_none")]
510 pub passing: Option<bool>,
511
512 #[serde(skip_serializing_if = "Option::is_none")]
513 pub filter: Option<String>,
514
515 #[serde(skip_serializing_if = "Option::is_none")]
516 pub peer: Option<String>,
517
518 #[serde(skip_serializing_if = "Option::is_none")]
519 pub index: Option<u64>,
520
521 #[cfg(feature = "enterprise")]
522 #[serde(skip_serializing_if = "Option::is_none")]
523 pub ns: Option<String>,
524
525 #[cfg(feature = "enterprise")]
526 #[serde(skip_serializing_if = "Option::is_none")]
527 pub sg: Option<String>,
528}
529
530#[derive(Debug, Default, Serialize, Deserialize)]
531pub struct HealthListStateQuery {
532 #[serde(skip_serializing_if = "Option::is_none")]
533 pub dc: Option<String>,
534
535 #[serde(skip_serializing_if = "Option::is_none")]
536 pub near: Option<String>,
537
538 #[serde(rename = "node-meta")]
539 #[serde(skip_serializing_if = "Option::is_none")]
540 pub node_meta: Option<String>,
541
542 #[serde(skip_serializing_if = "Option::is_none")]
543 pub filter: Option<String>,
544
545 #[cfg(feature = "enterprise")]
546 #[serde(skip_serializing_if = "Option::is_none")]
547 pub ns: Option<String>,
548}
549
550#[cfg(feature = "enterprise")]
551#[derive(Debug, Default, Serialize, Deserialize)]
552pub struct NamespaceCreateBody {
553 #[serde(rename = "Name")]
558 pub name: String,
559
560 #[serde(rename = "Description")]
562 #[serde(skip_serializing_if = "Option::is_none")]
563 pub description: Option<String>,
564
565 #[serde(rename = "ACLs")]
566 #[serde(skip_serializing_if = "Option::is_none")]
567 pub acls: Option<NamespaceACLConfig>,
568
569 #[serde(rename = "Meta")]
570 #[serde(skip_serializing_if = "Option::is_none")]
571 pub meta: Option<::std::collections::HashMap<String, String>>,
572
573 #[serde(rename = "Partition")]
574 #[serde(skip_serializing_if = "Option::is_none")]
575 pub partition: Option<String>,
576}
577
578#[cfg(feature = "enterprise")]
579#[derive(Debug, Default, Serialize, Deserialize)]
580pub struct NamespaceDetail {
581 #[serde(rename = "Name")]
583 pub name: String,
584
585 #[serde(rename = "Description")]
587 #[serde(skip_serializing_if = "Option::is_none")]
588 pub description: Option<String>,
589
590 #[serde(rename = "ACLs")]
591 #[serde(skip_serializing_if = "Option::is_none")]
592 pub acls: Option<NamespaceACLConfig>,
593
594 #[serde(rename = "Meta")]
595 #[serde(skip_serializing_if = "Option::is_none")]
596 pub meta: Option<::std::collections::HashMap<String, String>>,
597
598 #[serde(rename = "CreateIndex")]
599 pub create_index: u64,
600
601 #[serde(rename = "ModifyIndex")]
602 pub modify_index: u64,
603}
604
605#[cfg(feature = "enterprise")]
606#[derive(Debug, Default, Serialize, Deserialize)]
607pub struct NamespaceReadQuery {
608 #[serde(skip_serializing_if = "Option::is_none")]
609 pub partition: Option<String>,
610}
611
612#[cfg(feature = "enterprise")]
613#[derive(Debug, Default, Serialize, Deserialize)]
614pub struct NamespaceUpdateBody {
615 #[serde(rename = "Name")]
618 #[serde(skip_serializing_if = "Option::is_none")]
619 pub name: Option<String>,
620
621 #[serde(rename = "Description")]
623 #[serde(skip_serializing_if = "Option::is_none")]
624 pub description: Option<String>,
625
626 #[serde(rename = "ACLs")]
627 #[serde(skip_serializing_if = "Option::is_none")]
628 pub acls: Option<NamespaceACLConfig>,
629
630 #[serde(rename = "Meta")]
631 #[serde(skip_serializing_if = "Option::is_none")]
632 pub meta: Option<::std::collections::HashMap<String, String>>,
633
634 #[serde(rename = "Partition")]
635 #[serde(skip_serializing_if = "Option::is_none")]
636 pub partition: Option<String>,
637}
638
639#[derive(Debug, Default, Serialize, Deserialize)]
640pub struct StatusQuery {
641 #[serde(skip_serializing_if = "Option::is_none")]
642 pub dc: Option<String>,
643}
644
645#[derive(Clone, Debug)]
647pub struct Client {
648 cfg: Config,
649 http: reqwest::Client,
650 prefix: String,
651}
652
653impl Client {
654 pub fn new() -> Self {
656 ClientBuilder::new(Config::default()).build().unwrap()
657 }
658
659 pub async fn agent_checks(
664 &self,
665 q: &FilterRequestQuery,
666 ) -> Result<HashMap<String, HealthCheck>> {
667 let resp = self
668 .execute_request(Method::GET, "/agent/checks", q, None, &())
669 .await?;
670
671 resp.json().await.map_err(|e| anyhow!(e))
672 }
673
674 pub async fn agent_check_register(&self, b: &CheckDefinition) -> Result<bool> {
679 let resp = self
680 .execute_request(Method::PUT, "/agent/check/register", &(), None, b)
681 .await?;
682 Ok(resp.status() == StatusCode::OK)
683 }
684
685 pub async fn agent_check_deregister(&self, q: &DeregisterCheckRequestQuery) -> Result<bool> {
690 let path = format!("/agent/check/deregister/{}", q.check_id);
691 let resp = self
692 .execute_request(Method::PUT, &path, q, None, &())
693 .await?;
694 Ok(resp.status() == StatusCode::OK)
695 }
696
697 pub async fn agent_check_pass(&self, q: &AgentTTLCheckRequestQuery) -> Result<bool> {
701 let path = format!("/agent/check/pass/{}", q.check_id);
702 let resp = self
703 .execute_request(Method::PUT, &path, q, None, &())
704 .await?;
705
706 Ok(resp.status() == StatusCode::OK)
707 }
708
709 pub async fn agent_check_warn(&self, q: &AgentTTLCheckRequestQuery) -> Result<()> {
713 let path = format!("/agent/check/warn/{}", q.check_id);
714 let resp = self
715 .execute_request(Method::PUT, &path, q, None, &())
716 .await?;
717 resp.json().await.map_err(|e| anyhow!(e))
718 }
719
720 pub async fn agent_check_fail(&self, q: &AgentTTLCheckRequestQuery) -> Result<bool> {
724 let path = format!("/agent/check/fail/{}", q.check_id);
725 let resp = self
726 .execute_request(Method::PUT, &path, q, None, &())
727 .await?;
728 Ok(resp.status() == StatusCode::OK)
729 }
730
731 pub async fn agent_check_update(
735 &self,
736 q: &AgentTTLCheckUpdateRequestQuery,
737 b: &AgentTTLCheckUpdateRequestBody,
738 ) -> Result<bool> {
739 let path = format!("/agent/check/update/{}", q.check_id);
740 let resp = self.execute_request(Method::PUT, &path, q, None, b).await?;
741 Ok(resp.status() == StatusCode::OK)
742 }
743
744 pub async fn agent_services(
749 &self,
750 q: &FilterRequestQuery,
751 ) -> Result<HashMap<String, AgentService>> {
752 let resp = self
753 .execute_request(Method::GET, "/agent/services", q, None, &())
754 .await?;
755 resp.json().await.map_err(|e| anyhow!(e))
756 }
757
758 pub async fn agent_service_configuration(
759 &self,
760 q: &ServiceConfigurationRequestQuery,
761 ) -> Result<Option<AgentService>> {
762 let path = format!("/agent/service/{}", q.service_id);
763 let resp = self
764 .execute_request(Method::GET, &path, q, None, &())
765 .await?;
766
767 if resp.status() == StatusCode::NOT_FOUND {
768 return Ok(None);
769 }
770
771 Ok(Some(resp.json().await.map_err(|e| anyhow!(e))?))
772 }
773
774 pub async fn agent_get_service_health_by_name<S: Into<String>>(
782 &self,
783 service_name: S,
784 q: &LocalServiceHealthByNameRequestQuery,
785 ) -> Result<Vec<AgentServiceChecksInfo>> {
786 let path = format!("/agent/health/service/name/{}", service_name.into());
787 let resp = self
788 .execute_request(Method::GET, &path, q, None, &())
789 .await?;
790 resp.json().await.map_err(|e| anyhow!(e))
791 }
792
793 pub async fn agent_get_service_health_by_id<S: Into<String>>(
797 &self,
798 service_id: S,
799 q: &LocalServiceHealthByIDRequestQuery,
800 ) -> Result<Option<AgentServiceChecksInfo>> {
801 let path = format!("/agent/health/service/id/{}", service_id.into());
802 let resp = self
803 .execute_request(Method::GET, &path, q, None, &())
804 .await?;
805
806 if resp.status() == StatusCode::NOT_FOUND {
807 return Ok(None);
808 }
809
810 Ok(Some(resp.json().await.map_err(|e| anyhow!(e))?))
811 }
812
813 pub async fn agent_register_service(
821 &self,
822 q: &RegisterServiceRequestQuery,
823 b: &ServiceDefinition,
824 ) -> Result<bool> {
825 let resp = self
826 .execute_request(Method::PUT, "/agent/service/register", q, None, b)
827 .await?;
828 Ok(resp.status() == StatusCode::OK)
829 }
830
831 pub async fn agent_deregister_service<S: Into<String>>(
838 &self,
839 service_id: S,
840 q: &DeregisterServiceRequestQuery,
841 ) -> Result<bool> {
842 let path = format!("/agent/service/deregister/{}", service_id.into());
843 let resp = self
844 .execute_request(Method::PUT, &path, q, None, &())
845 .await?;
846 Ok(resp.status() == StatusCode::OK)
847 }
848
849 pub async fn agent_enable_maintenance_mode(
857 &self,
858 q: &EnableMaintenanceModeRequestQuery,
859 ) -> Result<bool> {
860 let path = format!("/agent/service/maintenance/{}", q.service_id);
861 let resp = self
862 .execute_request(Method::PUT, &path, q, None, &())
863 .await?;
864 Ok(resp.status() == StatusCode::OK)
865 }
866
867 pub async fn agent_connect_authorize(
868 &self,
869 q: &ConnectAuthorizeRequestQuery,
870 b: &ConnectAuthorizeRequest,
871 ) -> Result<ConnectAuthorizeRequestReply> {
872 let resp = self
873 .execute_request(Method::POST, "/agent/connect/authorize", q, None, b)
874 .await?;
875 resp.json().await.map_err(|e| anyhow!(e))
876 }
877
878 pub async fn catalog_register_entity(
884 &self,
885 q: &CatalogRegisterEntityQuery,
886 b: &RegisterRequest,
887 ) -> Result<bool> {
888 let resp = self
889 .execute_request(Method::PUT, "/catalog/register", q, None, b)
890 .await?;
891
892 Ok(resp.status() == StatusCode::OK)
893 }
894
895 pub async fn catalog_deregister_entity(
901 &self,
902 q: &CatalogDeregisterEntityQuery,
903 b: &DeregisterRequest,
904 ) -> Result<bool> {
905 let resp = self
906 .execute_request(Method::PUT, "/catalog/deregister", q, None, b)
907 .await?;
908
909 Ok(resp.status() == StatusCode::OK)
910 }
911
912 pub async fn catalog_list_datacenters(&self) -> Result<Vec<String>> {
918 let resp = self
919 .execute_request(Method::GET, "/catalog/datacenters", &(), None, &())
920 .await?;
921
922 resp.json().await.map_err(|e| anyhow!(e))
923 }
924
925 pub async fn catalog_list_nodes(&self) -> Result<Vec<Node>> {
928 let resp = self
929 .execute_request(Method::GET, "/catalog/nodes", &(), None, &())
930 .await?;
931
932 resp.json().await.map_err(|e| anyhow!(e))
933 }
934
935 pub async fn catalog_list_services(
938 &self,
939 q: &CatalogListServicesQuery,
940 ) -> Result<::std::collections::HashMap<String, Vec<String>>> {
941 let resp = self
942 .execute_request(Method::GET, "/catalog/services", q, None, &())
943 .await?;
944
945 resp.json().await.map_err(|e| anyhow!(e))
946 }
947
948 pub async fn catalog_list_nodes_for_service<S: Into<String>>(
952 &self,
953 service_name: S,
954 q: &CatalogListNodesForServiceQuery,
955 ) -> Result<Vec<ServiceNode>> {
956 let p = format!("/catalog/service/{}", service_name.into());
957
958 let resp = self.execute_request(Method::GET, &p, q, None, &()).await?;
959
960 resp.json().await.map_err(|e| anyhow!(e))
961 }
962
963 pub async fn catalog_list_nodes_for_mesh_capable_service<S: Into<String>>(
970 &self,
971 service: S,
972 q: &CatalogListNodesForServiceQuery,
973 ) -> Result<Vec<ServiceNode>> {
974 let p = format!("/catalog/connect/{}", service.into());
975
976 let resp = self.execute_request(Method::GET, &p, q, None, &()).await?;
977
978 resp.json().await.map_err(|e| anyhow!(e))
979 }
980
981 pub async fn catalog_node_services<S: Into<String>>(
984 &self,
985 node_name: S,
986 q: &CatalogNodeServicesQuery,
987 ) -> Result<Option<NodeServices>> {
988 let p = format!("/catalog/node/{}", node_name.into());
989
990 let resp = self.execute_request(Method::GET, &p, q, None, &()).await?;
991
992 if resp.status() == StatusCode::NOT_FOUND {
993 return Ok(None);
994 }
995
996 resp.json().await.map_err(|e| anyhow!(e))
997 }
998
999 pub async fn catalog_gateway_services<S: Into<String>>(
1003 &self,
1004 gateway: S,
1005 q: &CatalogGatewayServicesQuery,
1006 ) -> Result<Vec<GatewayService>> {
1007 let p = format!("/catalog/gateway-services/{}", gateway.into());
1008
1009 let resp = self.execute_request(Method::GET, &p, q, None, &()).await?;
1010
1011 resp.json().await.map_err(|e| anyhow!(e))
1012 }
1013
1014 pub async fn event_fire<S: Into<String>>(
1017 &self,
1018 name: S,
1019 body: Option<Vec<u8>>,
1020 q: &EventFireQuery,
1021 ) -> Result<bool> {
1022 let p = format!("/event/fire/{}", name.into());
1023
1024 let resp = self.execute_request(Method::PUT, &p, q, body, &()).await?;
1025
1026 Ok(resp.status() == StatusCode::OK)
1027 }
1028
1029 pub async fn event_list(&self, q: &EventListQuery) -> Result<Vec<UserEvent>> {
1036 let resp = self
1037 .execute_request(Method::GET, "/event/list", q, None, &())
1038 .await?;
1039
1040 let mut list: Vec<UserEvent> = resp.json().await.map_err(|e| anyhow!(e))?;
1041
1042 for item in list.iter_mut() {
1043 item.payload = item.payload.clone().map_or(None, |v| {
1044 if v.0 == "bnVsbA==" {
1046 None
1047 } else {
1048 Some(v)
1049 }
1050 })
1051 }
1052
1053 Ok(list)
1054 }
1055
1056 pub async fn health_list_nodes<S: Into<String>>(
1060 &self,
1061 node: S,
1062 q: &HealthListNodesQuery,
1063 ) -> Result<Vec<HealthCheck>> {
1064 let p = format!("/health/node/{}", node.into());
1065
1066 let resp = self.execute_request(Method::GET, &p, q, None, &()).await?;
1067
1068 resp.json().await.map_err(|e| anyhow!(e))
1069 }
1070
1071 pub async fn health_list_services<S: Into<String>>(
1075 &self,
1076 service: S,
1077 q: &HealthListServicesQuery,
1078 ) -> Result<Vec<HealthCheck>> {
1079 let p = format!("/health/checks/{}", service.into());
1080
1081 let resp = self.execute_request(Method::GET, &p, q, None, &()).await?;
1082
1083 resp.json().await.map_err(|e| anyhow!(e))
1084 }
1085
1086 pub async fn health_list_service_instances<S: Into<String>>(
1091 &self,
1092 service: S,
1093 q: &HealthListServiceInstancesQuery,
1094 ) -> Result<Vec<CheckServiceNode>> {
1095 let p = format!("/health/service/{}", service.into());
1096
1097 let resp = self.execute_request(Method::GET, &p, q, None, &()).await?;
1098
1099 resp.json().await.map_err(|e| anyhow!(e))
1100 }
1101
1102 pub async fn health_list_service_instances_for_mesh_capable<S: Into<String>>(
1111 &self,
1112 service: S,
1113 q: &HealthListServiceInstancesQuery,
1114 ) -> Result<Vec<CheckServiceNode>> {
1115 let p = format!("/health/connect/{}", service.into());
1116
1117 let resp = self.execute_request(Method::GET, &p, q, None, &()).await?;
1118
1119 resp.json().await.map_err(|e| anyhow!(e))
1120 }
1121
1122 pub async fn health_list_service_instances_for_ingress_gateways<S: Into<String>>(
1130 &self,
1131 service: S,
1132 q: &HealthListServiceInstancesQuery,
1133 ) -> Result<Vec<CheckServiceNode>> {
1134 let p = format!("/health/ingress/{}", service.into());
1135
1136 let resp = self.execute_request(Method::GET, &p, q, None, &()).await?;
1137
1138 resp.json().await.map_err(|e| anyhow!(e))
1139 }
1140
1141 pub async fn health_list_state(
1146 &self,
1147 state: Health,
1148 q: &HealthListStateQuery,
1149 ) -> Result<Vec<HealthCheck>> {
1150 let p = format!("/health/state/{}", state);
1151
1152 let resp = self.execute_request(Method::GET, &p, q, None, &()).await?;
1153
1154 resp.json().await.map_err(|e| anyhow!(e))
1155 }
1156
1157 pub async fn kv_read_key<S: Into<String>>(
1161 &self,
1162 key: S,
1163 q: &KVReadKeyQuery,
1164 ) -> Result<Option<Vec<u8>>> {
1165 let path = format!("/kv/{}", key.into());
1166 let resp = self
1167 .execute_request(Method::GET, &path, q, None, &())
1168 .await?;
1169
1170 if resp.status() == StatusCode::NOT_FOUND {
1171 return Ok(None);
1172 }
1173
1174 let full = resp.bytes().await?;
1175
1176 if full.is_empty() {
1177 return Ok(Some(vec![]));
1178 }
1179
1180 Ok(Some(full.to_vec()))
1181 }
1182
1183 pub async fn kv_create_or_update_key<S: Into<String>>(
1187 &self,
1188 key: S,
1189 b: Vec<u8>,
1190 q: &KVCreateOrUpdateKeyQuery,
1191 ) -> Result<bool> {
1192 let path = format!("/kv/{}", key.into());
1193 let resp = self
1194 .execute_request(Method::PUT, &path, q, Some(b), &())
1195 .await?;
1196 resp.json().await.map_err(|e| anyhow!(e))
1197 }
1198
1199 pub async fn kv_delete_key<S: Into<String>>(
1202 &self,
1203 key: S,
1204 q: &KVDeleteKeyQuery,
1205 ) -> Result<bool> {
1206 let path = format!("/kv/{}", key.into());
1207 let resp = self
1208 .execute_request(Method::DELETE, &path, q, None, &())
1209 .await?;
1210 resp.json().await.map_err(|e| anyhow!(e))
1211 }
1212
1213 #[cfg(feature = "enterprise")]
1220 pub async fn namespace_create(&self, b: &NamespaceCreateBody) -> Result<NamespaceDetail> {
1221 let resp = self
1222 .execute_request(Method::PUT, "/namespace", &(), None, b)
1223 .await?;
1224 resp.json().await.map_err(|e| anyhow!(e))
1225 }
1226
1227 #[cfg(feature = "enterprise")]
1234 pub async fn namespace_read<S: Into<String>>(
1235 &self,
1236 name: S,
1237 q: &NamespaceReadQuery,
1238 ) -> Result<Option<NamespaceDetail>> {
1239 let p = format!("/namespace/{}", name.into());
1240
1241 let resp = self.execute_request(Method::GET, &p, &q, None, &()).await?;
1242
1243 if resp.status() == StatusCode::NOT_FOUND {
1244 return Ok(None);
1245 }
1246
1247 Ok(Some(resp.json().await.map_err(|e| anyhow!(e))?))
1248 }
1249
1250 #[cfg(feature = "enterprise")]
1257 pub async fn namespace_update<S: Into<String>>(
1258 &self,
1259 name: S,
1260 b: &NamespaceUpdateBody,
1261 ) -> Result<NamespaceDetail> {
1262 let p = format!("/namespace/{}", name.into());
1263
1264 let resp = self.execute_request(Method::PUT, &p, &(), None, &b).await?;
1265
1266 resp.json().await.map_err(|e| anyhow!(e))
1267 }
1268
1269 pub async fn status_leader(&self, q: &StatusQuery) -> Result<String> {
1275 let resp = self
1276 .execute_request(Method::GET, "/status/leader", q, None, &())
1277 .await?;
1278
1279 resp.text_with_charset("utf-8")
1280 .await
1281 .map_err(|e| anyhow!(e))
1282 }
1283
1284 pub async fn status_peers(&self, q: &StatusQuery) -> Result<Vec<String>> {
1292 let resp = self
1293 .execute_request(Method::GET, "/status/peers", q, None, &())
1294 .await?;
1295
1296 resp.json().await.map_err(|e| anyhow!(e))
1297 }
1298
1299 async fn execute_request<Q, B>(
1300 &self,
1301 method: Method,
1302 path: &str,
1303 query: &Q,
1304 raw_body: Option<Vec<u8>>,
1305 json_body: &B,
1306 ) -> Result<Response>
1307 where
1308 Q: Serialize,
1309 B: Serialize,
1310 {
1311 let path = format!("{}{}{}", self.cfg.address, self.prefix, path);
1312 let mut b = self.http.request(method.clone(), &path);
1313
1314 b = b.query(query);
1315
1316 if method == Method::PUT || method == Method::POST {
1317 if let Some(body) = raw_body {
1318 b = b.body(body)
1319 } else {
1320 b = b.json(json_body);
1321 }
1322 }
1323
1324 let resp = b.send().await?;
1325 Ok(resp)
1326 }
1327}
1328
1329#[inline]
1330fn read_env_or_default(key: &str, default: &str) -> String {
1331 std::env::var(key).unwrap_or_else(|_| default.to_string())
1332}
1333
1334#[cfg(test)]
1335mod tests {
1336 use super::*;
1337
1338 #[test]
1339 fn create_client() {
1340 let _ = Client::new();
1341 }
1342}