use anyhow::{anyhow, Result};
use reqwest::{
header::{HeaderMap, HeaderValue, AUTHORIZATION},
Method, Response, StatusCode,
};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::time::Duration;
#[doc(hidden)]
pub use reqwest::Proxy;
#[cfg(all(feature = "v1", feature = "v1_20_x"))]
mod structs_1_20_x;
#[cfg(all(feature = "v1", feature = "v1_20_x"))]
pub use structs_1_20_x::*;
#[cfg(all(feature = "v1", feature = "v1_22_x"))]
mod structs_1_22_x;
#[cfg(all(feature = "v1", feature = "v1_22_x"))]
pub use structs_1_22_x::*;
#[derive(Clone, Debug)]
pub struct Config {
pub token: String,
pub address: String,
}
impl Config {
pub fn from_env() -> Self {
Self {
token: read_env_or_default("CONSUL_TOKEN", ""),
address: read_env_or_default("CONSUL_ADDRESS", "http://127.0.0.1:8500"),
}
}
}
impl Default for Config {
fn default() -> Self {
Self::from_env()
}
}
pub struct ClientBuilder {
cfg: Config,
proxies: Vec<Proxy>,
timeout: Option<Duration>,
}
impl ClientBuilder {
pub fn new(cfg: Config) -> Self {
Self {
cfg,
proxies: vec![],
timeout: None,
}
}
pub fn with_proxy(mut self, proxy: Proxy) -> Self {
self.proxies.push(proxy);
self
}
pub fn with_timeout(mut self, timeout: Duration) -> Self {
self.timeout = Some(timeout);
self
}
pub fn build(self) -> Result<Client> {
let mut headers = HeaderMap::new();
if !self.cfg.token.is_empty() {
headers.insert(
AUTHORIZATION,
HeaderValue::from_str(&format!("Bearer {}", self.cfg.token)).unwrap(),
);
}
let mut builder = reqwest::ClientBuilder::new();
builder = builder.default_headers(headers);
for proxy in self.proxies {
builder = builder.proxy(proxy)
}
if let Some(v) = self.timeout {
builder = builder.timeout(v);
}
Ok(Client {
cfg: self.cfg,
http: builder.build()?,
#[cfg(feature = "v1")]
prefix: "/v1".to_string(),
})
}
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct FilterRequestQuery {
pub filter: Option<String>,
#[cfg(feature = "enterprise")]
#[serde(skip_serializing_if = "Option::is_none")]
pub ns: Option<String>,
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct DeregisterCheckRequestQuery {
#[serde(skip_serializing)]
pub check_id: String,
#[cfg(feature = "enterprise")]
#[serde(skip_serializing_if = "Option::is_none")]
pub ns: Option<String>,
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct AgentTTLCheckRequestQuery {
#[serde(skip_serializing)]
pub check_id: String,
pub note: Option<String>,
#[cfg(feature = "enterprise")]
#[serde(skip_serializing_if = "Option::is_none")]
pub ns: Option<String>,
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct AgentTTLCheckUpdateRequestQuery {
#[serde(skip_serializing)]
pub check_id: String,
#[cfg(feature = "enterprise")]
#[serde(skip_serializing_if = "Option::is_none")]
pub ns: Option<String>,
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct AgentTTLCheckUpdateRequestBody {
#[serde(rename = "Status")]
pub status: Option<String>,
#[serde(rename = "Output")]
pub output: Option<String>,
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct ServiceConfigurationRequestQuery {
#[serde(skip_serializing)]
pub service_id: String,
#[cfg(feature = "enterprise")]
#[serde(skip_serializing_if = "Option::is_none")]
pub ns: Option<String>,
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct LocalServiceHealthByNameRequestQuery {
#[cfg(feature = "enterprise")]
#[serde(skip_serializing_if = "Option::is_none")]
pub ns: Option<String>,
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct LocalServiceHealthByIDRequestQuery {
#[cfg(feature = "enterprise")]
#[serde(skip_serializing_if = "Option::is_none")]
pub ns: Option<String>,
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct RegisterServiceRequestQuery {
#[serde(rename = "replace-existing-checks")]
#[serde(skip_serializing_if = "Option::is_none")]
pub replace_existing_checks: Option<String>,
#[cfg(feature = "enterprise")]
#[serde(skip_serializing_if = "Option::is_none")]
pub ns: Option<String>,
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct DeregisterServiceRequestQuery {
#[cfg(feature = "enterprise")]
#[serde(skip_serializing_if = "Option::is_none")]
pub ns: Option<String>,
#[cfg(feature = "enterprise")]
#[serde(skip_serializing_if = "Option::is_none")]
pub partition: Option<String>,
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct EnableMaintenanceModeRequestQuery {
#[serde(skip_serializing)]
pub service_id: String,
pub enable: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub reason: Option<String>,
#[cfg(feature = "enterprise")]
#[serde(skip_serializing_if = "Option::is_none")]
pub ns: Option<String>,
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct ConnectAuthorizeRequestQuery {
#[cfg(feature = "enterprise")]
#[serde(skip_serializing_if = "Option::is_none")]
pub ns: Option<String>,
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct ConnectAuthorizeRequestReply {
#[serde(rename = "Authorized")]
pub authorized: bool,
#[serde(rename = "Reason")]
pub reason: String,
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct KVReadKeyQuery {
pub dc: Option<String>,
pub recurse: Option<bool>,
pub raw: Option<bool>,
pub keys: Option<bool>,
pub separator: Option<String>,
#[cfg(feature = "enterprise")]
#[serde(skip_serializing_if = "Option::is_none")]
pub ns: Option<String>,
#[cfg(feature = "enterprise")]
#[serde(skip_serializing_if = "Option::is_none")]
pub partition: Option<String>,
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct KVCreateOrUpdateKeyQuery {
pub dc: Option<String>,
pub flags: Option<u64>,
pub cas: Option<u64>,
pub acquire: Option<String>,
pub release: Option<String>,
#[cfg(feature = "enterprise")]
#[serde(skip_serializing_if = "Option::is_none")]
pub ns: Option<String>,
#[cfg(feature = "enterprise")]
#[serde(skip_serializing_if = "Option::is_none")]
pub partition: Option<String>,
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct KVDeleteKeyQuery {
pub dc: Option<String>,
pub recurse: Option<bool>,
pub cas: Option<u64>,
#[cfg(feature = "enterprise")]
#[serde(skip_serializing_if = "Option::is_none")]
pub ns: Option<String>,
#[cfg(feature = "enterprise")]
#[serde(skip_serializing_if = "Option::is_none")]
pub partition: Option<String>,
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct CatalogRegisterEntityQuery {
#[cfg(feature = "enterprise")]
#[serde(skip_serializing_if = "Option::is_none")]
pub ns: Option<String>,
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct CatalogDeregisterEntityQuery {
#[cfg(feature = "enterprise")]
#[serde(skip_serializing_if = "Option::is_none")]
pub ns: Option<String>,
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct CatalogListServicesQuery {
#[serde(skip_serializing_if = "Option::is_none")]
pub dc: Option<String>,
#[serde(rename = "node-meta")]
#[serde(skip_serializing_if = "Option::is_none")]
pub node_meta: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub filter: Option<String>,
#[cfg(feature = "enterprise")]
#[serde(skip_serializing_if = "Option::is_none")]
pub ns: Option<String>,
#[cfg(feature = "enterprise")]
#[serde(skip_serializing_if = "Option::is_none")]
pub partition: Option<String>,
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct CatalogListNodesForServiceQuery {
#[serde(skip_serializing_if = "Option::is_none")]
pub dc: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tag: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub near: Option<String>,
#[serde(rename = "node-meta")]
#[serde(skip_serializing_if = "Option::is_none")]
pub node_meta: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub filter: Option<String>,
#[cfg(feature = "enterprise")]
#[serde(skip_serializing_if = "Option::is_none")]
pub ns: Option<String>,
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct CatalogNodeServicesQuery {
#[serde(skip_serializing_if = "Option::is_none")]
pub dc: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub filter: Option<String>,
#[cfg(feature = "enterprise")]
#[serde(skip_serializing_if = "Option::is_none")]
pub ns: Option<String>,
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct CatalogGatewayServicesQuery {
#[serde(skip_serializing_if = "Option::is_none")]
pub dc: Option<String>,
#[cfg(feature = "enterprise")]
#[serde(skip_serializing_if = "Option::is_none")]
pub ns: Option<String>,
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct EventFireQuery {
#[serde(skip_serializing_if = "Option::is_none")]
pub dc: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub node: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub service: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tag: Option<String>,
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct EventListQuery {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub node: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub service: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tag: Option<String>,
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct HealthListNodesQuery {
#[serde(skip_serializing_if = "Option::is_none")]
pub dc: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub filter: Option<String>,
#[cfg(feature = "enterprise")]
#[serde(skip_serializing_if = "Option::is_none")]
pub ns: Option<String>,
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct HealthListServicesQuery {
#[serde(skip_serializing_if = "Option::is_none")]
pub dc: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub near: Option<String>,
#[serde(rename = "node-meta")]
#[serde(skip_serializing_if = "Option::is_none")]
pub node_meta: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub filter: Option<String>,
#[cfg(feature = "enterprise")]
#[serde(skip_serializing_if = "Option::is_none")]
pub ns: Option<String>,
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct HealthListServiceInstancesQuery {
#[serde(skip_serializing_if = "Option::is_none")]
pub dc: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub near: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tag: Option<String>,
#[serde(rename = "node-meta")]
#[serde(skip_serializing_if = "Option::is_none")]
pub node_meta: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub passing: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub filter: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub peer: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub index: Option<u64>,
#[cfg(feature = "enterprise")]
#[serde(skip_serializing_if = "Option::is_none")]
pub ns: Option<String>,
#[cfg(feature = "enterprise")]
#[serde(skip_serializing_if = "Option::is_none")]
pub sg: Option<String>,
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct HealthListStateQuery {
#[serde(skip_serializing_if = "Option::is_none")]
pub dc: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub near: Option<String>,
#[serde(rename = "node-meta")]
#[serde(skip_serializing_if = "Option::is_none")]
pub node_meta: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub filter: Option<String>,
#[cfg(feature = "enterprise")]
#[serde(skip_serializing_if = "Option::is_none")]
pub ns: Option<String>,
}
#[cfg(feature = "enterprise")]
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct NamespaceCreateBody {
#[serde(rename = "Name")]
pub name: String,
#[serde(rename = "Description")]
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(rename = "ACLs")]
#[serde(skip_serializing_if = "Option::is_none")]
pub acls: Option<NamespaceACLConfig>,
#[serde(rename = "Meta")]
#[serde(skip_serializing_if = "Option::is_none")]
pub meta: Option<::std::collections::HashMap<String, String>>,
#[serde(rename = "Partition")]
#[serde(skip_serializing_if = "Option::is_none")]
pub partition: Option<String>,
}
#[cfg(feature = "enterprise")]
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct NamespaceDetail {
#[serde(rename = "Name")]
pub name: String,
#[serde(rename = "Description")]
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(rename = "ACLs")]
#[serde(skip_serializing_if = "Option::is_none")]
pub acls: Option<NamespaceACLConfig>,
#[serde(rename = "Meta")]
#[serde(skip_serializing_if = "Option::is_none")]
pub meta: Option<::std::collections::HashMap<String, String>>,
#[serde(rename = "CreateIndex")]
pub create_index: u64,
#[serde(rename = "ModifyIndex")]
pub modify_index: u64,
}
#[cfg(feature = "enterprise")]
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct NamespaceReadQuery {
#[serde(skip_serializing_if = "Option::is_none")]
pub partition: Option<String>,
}
#[cfg(feature = "enterprise")]
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct NamespaceUpdateBody {
#[serde(rename = "Name")]
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(rename = "Description")]
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(rename = "ACLs")]
#[serde(skip_serializing_if = "Option::is_none")]
pub acls: Option<NamespaceACLConfig>,
#[serde(rename = "Meta")]
#[serde(skip_serializing_if = "Option::is_none")]
pub meta: Option<::std::collections::HashMap<String, String>>,
#[serde(rename = "Partition")]
#[serde(skip_serializing_if = "Option::is_none")]
pub partition: Option<String>,
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct StatusQuery {
#[serde(skip_serializing_if = "Option::is_none")]
pub dc: Option<String>,
}
#[derive(Clone, Debug)]
pub struct Client {
cfg: Config,
http: reqwest::Client,
prefix: String,
}
impl Client {
pub fn new() -> Self {
ClientBuilder::new(Config::default()).build().unwrap()
}
pub async fn agent_checks(
&self,
q: &FilterRequestQuery,
) -> Result<HashMap<String, HealthCheck>> {
let resp = self
.execute_request(Method::GET, "/agent/checks", q, None, &())
.await?;
resp.json().await.map_err(|e| anyhow!(e))
}
pub async fn agent_check_register(&self, b: &CheckDefinition) -> Result<bool> {
let resp = self
.execute_request(Method::PUT, "/agent/check/register", &(), None, b)
.await?;
Ok(resp.status() == StatusCode::OK)
}
pub async fn agent_check_deregister(&self, q: &DeregisterCheckRequestQuery) -> Result<bool> {
let path = format!("/agent/check/deregister/{}", q.check_id);
let resp = self
.execute_request(Method::PUT, &path, q, None, &())
.await?;
Ok(resp.status() == StatusCode::OK)
}
pub async fn agent_check_pass(&self, q: &AgentTTLCheckRequestQuery) -> Result<bool> {
let path = format!("/agent/check/pass/{}", q.check_id);
let resp = self
.execute_request(Method::PUT, &path, q, None, &())
.await?;
Ok(resp.status() == StatusCode::OK)
}
pub async fn agent_check_warn(&self, q: &AgentTTLCheckRequestQuery) -> Result<()> {
let path = format!("/agent/check/warn/{}", q.check_id);
let resp = self
.execute_request(Method::PUT, &path, q, None, &())
.await?;
resp.json().await.map_err(|e| anyhow!(e))
}
pub async fn agent_check_fail(&self, q: &AgentTTLCheckRequestQuery) -> Result<bool> {
let path = format!("/agent/check/fail/{}", q.check_id);
let resp = self
.execute_request(Method::PUT, &path, q, None, &())
.await?;
Ok(resp.status() == StatusCode::OK)
}
pub async fn agent_check_update(
&self,
q: &AgentTTLCheckUpdateRequestQuery,
b: &AgentTTLCheckUpdateRequestBody,
) -> Result<bool> {
let path = format!("/agent/check/update/{}", q.check_id);
let resp = self.execute_request(Method::PUT, &path, q, None, b).await?;
Ok(resp.status() == StatusCode::OK)
}
pub async fn agent_services(
&self,
q: &FilterRequestQuery,
) -> Result<HashMap<String, AgentService>> {
let resp = self
.execute_request(Method::GET, "/agent/services", q, None, &())
.await?;
resp.json().await.map_err(|e| anyhow!(e))
}
pub async fn agent_service_configuration(
&self,
q: &ServiceConfigurationRequestQuery,
) -> Result<Option<AgentService>> {
let path = format!("/agent/service/{}", q.service_id);
let resp = self
.execute_request(Method::GET, &path, q, None, &())
.await?;
if resp.status() == StatusCode::NOT_FOUND {
return Ok(None);
}
Ok(Some(resp.json().await.map_err(|e| anyhow!(e))?))
}
pub async fn agent_get_service_health_by_name<S: Into<String>>(
&self,
service_name: S,
q: &LocalServiceHealthByNameRequestQuery,
) -> Result<Vec<AgentServiceChecksInfo>> {
let path = format!("/agent/health/service/name/{}", service_name.into());
let resp = self
.execute_request(Method::GET, &path, q, None, &())
.await?;
resp.json().await.map_err(|e| anyhow!(e))
}
pub async fn agent_get_service_health_by_id<S: Into<String>>(
&self,
service_id: S,
q: &LocalServiceHealthByIDRequestQuery,
) -> Result<Option<AgentServiceChecksInfo>> {
let path = format!("/agent/health/service/id/{}", service_id.into());
let resp = self
.execute_request(Method::GET, &path, q, None, &())
.await?;
if resp.status() == StatusCode::NOT_FOUND {
return Ok(None);
}
Ok(Some(resp.json().await.map_err(|e| anyhow!(e))?))
}
pub async fn agent_register_service(
&self,
q: &RegisterServiceRequestQuery,
b: &ServiceDefinition,
) -> Result<bool> {
let resp = self
.execute_request(Method::PUT, "/agent/service/register", q, None, b)
.await?;
Ok(resp.status() == StatusCode::OK)
}
pub async fn agent_deregister_service<S: Into<String>>(
&self,
service_id: S,
q: &DeregisterServiceRequestQuery,
) -> Result<bool> {
let path = format!("/agent/service/deregister/{}", service_id.into());
let resp = self
.execute_request(Method::PUT, &path, q, None, &())
.await?;
Ok(resp.status() == StatusCode::OK)
}
pub async fn agent_enable_maintenance_mode(
&self,
q: &EnableMaintenanceModeRequestQuery,
) -> Result<bool> {
let path = format!("/agent/service/maintenance/{}", q.service_id);
let resp = self
.execute_request(Method::PUT, &path, q, None, &())
.await?;
Ok(resp.status() == StatusCode::OK)
}
pub async fn agent_connect_authorize(
&self,
q: &ConnectAuthorizeRequestQuery,
b: &ConnectAuthorizeRequest,
) -> Result<ConnectAuthorizeRequestReply> {
let resp = self
.execute_request(Method::POST, "/agent/connect/authorize", q, None, b)
.await?;
resp.json().await.map_err(|e| anyhow!(e))
}
pub async fn catalog_register_entity(
&self,
q: &CatalogRegisterEntityQuery,
b: &RegisterRequest,
) -> Result<bool> {
let resp = self
.execute_request(Method::PUT, "/catalog/register", q, None, b)
.await?;
Ok(resp.status() == StatusCode::OK)
}
pub async fn catalog_deregister_entity(
&self,
q: &CatalogDeregisterEntityQuery,
b: &DeregisterRequest,
) -> Result<bool> {
let resp = self
.execute_request(Method::PUT, "/catalog/deregister", q, None, b)
.await?;
Ok(resp.status() == StatusCode::OK)
}
pub async fn catalog_list_datacenters(&self) -> Result<Vec<String>> {
let resp = self
.execute_request(Method::GET, "/catalog/datacenters", &(), None, &())
.await?;
resp.json().await.map_err(|e| anyhow!(e))
}
pub async fn catalog_list_nodes(&self) -> Result<Vec<Node>> {
let resp = self
.execute_request(Method::GET, "/catalog/nodes", &(), None, &())
.await?;
resp.json().await.map_err(|e| anyhow!(e))
}
pub async fn catalog_list_services(
&self,
q: &CatalogListServicesQuery,
) -> Result<::std::collections::HashMap<String, Vec<String>>> {
let resp = self
.execute_request(Method::GET, "/catalog/services", q, None, &())
.await?;
resp.json().await.map_err(|e| anyhow!(e))
}
pub async fn catalog_list_nodes_for_service<S: Into<String>>(
&self,
service_name: S,
q: &CatalogListNodesForServiceQuery,
) -> Result<Vec<ServiceNode>> {
let p = format!("/catalog/service/{}", service_name.into());
let resp = self.execute_request(Method::GET, &p, q, None, &()).await?;
resp.json().await.map_err(|e| anyhow!(e))
}
pub async fn catalog_list_nodes_for_mesh_capable_service<S: Into<String>>(
&self,
service: S,
q: &CatalogListNodesForServiceQuery,
) -> Result<Vec<ServiceNode>> {
let p = format!("/catalog/connect/{}", service.into());
let resp = self.execute_request(Method::GET, &p, q, None, &()).await?;
resp.json().await.map_err(|e| anyhow!(e))
}
pub async fn catalog_node_services<S: Into<String>>(
&self,
node_name: S,
q: &CatalogNodeServicesQuery,
) -> Result<Option<NodeServices>> {
let p = format!("/catalog/node/{}", node_name.into());
let resp = self.execute_request(Method::GET, &p, q, None, &()).await?;
if resp.status() == StatusCode::NOT_FOUND {
return Ok(None);
}
resp.json().await.map_err(|e| anyhow!(e))
}
pub async fn catalog_gateway_services<S: Into<String>>(
&self,
gateway: S,
q: &CatalogGatewayServicesQuery,
) -> Result<Vec<GatewayService>> {
let p = format!("/catalog/gateway-services/{}", gateway.into());
let resp = self.execute_request(Method::GET, &p, q, None, &()).await?;
resp.json().await.map_err(|e| anyhow!(e))
}
pub async fn event_fire<S: Into<String>>(
&self,
name: S,
body: Option<Vec<u8>>,
q: &EventFireQuery,
) -> Result<bool> {
let p = format!("/event/fire/{}", name.into());
let resp = self.execute_request(Method::PUT, &p, q, body, &()).await?;
Ok(resp.status() == StatusCode::OK)
}
pub async fn event_list(&self, q: &EventListQuery) -> Result<Vec<UserEvent>> {
let resp = self
.execute_request(Method::GET, "/event/list", q, None, &())
.await?;
let mut list: Vec<UserEvent> = resp.json().await.map_err(|e| anyhow!(e))?;
for item in list.iter_mut() {
item.payload = item.payload.clone().map_or(None, |v| {
if v.0 == "bnVsbA==" {
None
} else {
Some(v)
}
})
}
Ok(list)
}
pub async fn health_list_nodes<S: Into<String>>(
&self,
node: S,
q: &HealthListNodesQuery,
) -> Result<Vec<HealthCheck>> {
let p = format!("/health/node/{}", node.into());
let resp = self.execute_request(Method::GET, &p, q, None, &()).await?;
resp.json().await.map_err(|e| anyhow!(e))
}
pub async fn health_list_services<S: Into<String>>(
&self,
service: S,
q: &HealthListServicesQuery,
) -> Result<Vec<HealthCheck>> {
let p = format!("/health/checks/{}", service.into());
let resp = self.execute_request(Method::GET, &p, q, None, &()).await?;
resp.json().await.map_err(|e| anyhow!(e))
}
pub async fn health_list_service_instances<S: Into<String>>(
&self,
service: S,
q: &HealthListServiceInstancesQuery,
) -> Result<Vec<CheckServiceNode>> {
let p = format!("/health/service/{}", service.into());
let resp = self.execute_request(Method::GET, &p, q, None, &()).await?;
resp.json().await.map_err(|e| anyhow!(e))
}
pub async fn health_list_service_instances_for_mesh_capable<S: Into<String>>(
&self,
service: S,
q: &HealthListServiceInstancesQuery,
) -> Result<Vec<CheckServiceNode>> {
let p = format!("/health/connect/{}", service.into());
let resp = self.execute_request(Method::GET, &p, q, None, &()).await?;
resp.json().await.map_err(|e| anyhow!(e))
}
pub async fn health_list_service_instances_for_ingress_gateways<S: Into<String>>(
&self,
service: S,
q: &HealthListServiceInstancesQuery,
) -> Result<Vec<CheckServiceNode>> {
let p = format!("/health/ingress/{}", service.into());
let resp = self.execute_request(Method::GET, &p, q, None, &()).await?;
resp.json().await.map_err(|e| anyhow!(e))
}
pub async fn health_list_state(
&self,
state: Health,
q: &HealthListStateQuery,
) -> Result<Vec<HealthCheck>> {
let p = format!("/health/state/{}", state);
let resp = self.execute_request(Method::GET, &p, q, None, &()).await?;
resp.json().await.map_err(|e| anyhow!(e))
}
pub async fn kv_read_key<S: Into<String>>(
&self,
key: S,
q: &KVReadKeyQuery,
) -> Result<Option<Vec<u8>>> {
let path = format!("/kv/{}", key.into());
let resp = self
.execute_request(Method::GET, &path, q, None, &())
.await?;
if resp.status() == StatusCode::NOT_FOUND {
return Ok(None);
}
let full = resp.bytes().await?;
if full.is_empty() {
return Ok(Some(vec![]));
}
Ok(Some(full.to_vec()))
}
pub async fn kv_create_or_update_key<S: Into<String>>(
&self,
key: S,
b: Vec<u8>,
q: &KVCreateOrUpdateKeyQuery,
) -> Result<bool> {
let path = format!("/kv/{}", key.into());
let resp = self
.execute_request(Method::PUT, &path, q, Some(b), &())
.await?;
resp.json().await.map_err(|e| anyhow!(e))
}
pub async fn kv_delete_key<S: Into<String>>(
&self,
key: S,
q: &KVDeleteKeyQuery,
) -> Result<bool> {
let path = format!("/kv/{}", key.into());
let resp = self
.execute_request(Method::DELETE, &path, q, None, &())
.await?;
resp.json().await.map_err(|e| anyhow!(e))
}
#[cfg(feature = "enterprise")]
pub async fn namespace_create(&self, b: &NamespaceCreateBody) -> Result<NamespaceDetail> {
let resp = self
.execute_request(Method::PUT, "/namespace", &(), None, b)
.await?;
resp.json().await.map_err(|e| anyhow!(e))
}
#[cfg(feature = "enterprise")]
pub async fn namespace_read<S: Into<String>>(
&self,
name: S,
q: &NamespaceReadQuery,
) -> Result<Option<NamespaceDetail>> {
let p = format!("/namespace/{}", name.into());
let resp = self.execute_request(Method::GET, &p, &q, None, &()).await?;
if resp.status() == StatusCode::NOT_FOUND {
return Ok(None);
}
Ok(Some(resp.json().await.map_err(|e| anyhow!(e))?))
}
#[cfg(feature = "enterprise")]
pub async fn namespace_update<S: Into<String>>(
&self,
name: S,
b: &NamespaceUpdateBody,
) -> Result<NamespaceDetail> {
let p = format!("/namespace/{}", name.into());
let resp = self.execute_request(Method::PUT, &p, &(), None, &b).await?;
resp.json().await.map_err(|e| anyhow!(e))
}
pub async fn status_leader(&self, q: &StatusQuery) -> Result<String> {
let resp = self
.execute_request(Method::GET, "/status/leader", q, None, &())
.await?;
resp.text_with_charset("utf-8")
.await
.map_err(|e| anyhow!(e))
}
pub async fn status_peers(&self, q: &StatusQuery) -> Result<Vec<String>> {
let resp = self
.execute_request(Method::GET, "/status/peers", q, None, &())
.await?;
resp.json().await.map_err(|e| anyhow!(e))
}
async fn execute_request<Q, B>(
&self,
method: Method,
path: &str,
query: &Q,
raw_body: Option<Vec<u8>>,
json_body: &B,
) -> Result<Response>
where
Q: Serialize,
B: Serialize,
{
let path = format!("{}{}{}", self.cfg.address, self.prefix, path);
let mut b = self.http.request(method.clone(), &path);
b = b.query(query);
if method == Method::PUT || method == Method::POST {
if let Some(body) = raw_body {
b = b.body(body)
} else {
b = b.json(json_body);
}
}
let resp = b.send().await?;
Ok(resp)
}
}
#[inline]
fn read_env_or_default(key: &str, default: &str) -> String {
std::env::var(key).unwrap_or_else(|_| default.to_string())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn create_client() {
let _ = Client::new();
}
}