1#![allow(dead_code)] use color_eyre::eyre::{WrapErr, eyre};
20
21use crate::config::NodeConfig;
22
23#[derive(Clone, Debug)]
26pub struct ApiClient {
27 pub name: String,
29 pub url: String,
32 pub authenticated: bool,
35 inner: bee::Client,
36}
37
38impl ApiClient {
39 pub fn from_node(node: &NodeConfig) -> color_eyre::Result<Self> {
43 let url = node.url.trim_end_matches('/').to_string();
44 let token = node.resolved_token();
45 let authenticated = token.is_some();
46 let inner = match token.as_deref() {
47 Some(t) => bee::Client::with_token(&url, t)
48 .map_err(|e| eyre!("invalid bee endpoint or token: {e}"))?,
49 None => bee::Client::new(&url).map_err(|e| eyre!("invalid bee endpoint: {e}"))?,
50 };
51 Ok(Self {
52 name: node.name.clone(),
53 url,
54 authenticated,
55 inner,
56 })
57 }
58
59 pub fn bee(&self) -> &bee::Client {
61 &self.inner
62 }
63
64 pub async fn ping(&self) -> color_eyre::Result<std::time::Duration> {
67 self.inner
68 .ping()
69 .await
70 .wrap_err_with(|| format!("ping {} ({}) failed", self.name, self.url))
71 }
72}
73
74#[cfg(test)]
75mod tests {
76 use super::*;
77
78 fn node(url: &str, token: Option<&str>) -> NodeConfig {
79 NodeConfig {
80 name: "test".into(),
81 url: url.into(),
82 token: token.map(String::from),
83 default: false,
84 }
85 }
86
87 #[test]
88 fn from_node_strips_trailing_slash() {
89 let c = ApiClient::from_node(&node("http://localhost:1633/", None)).unwrap();
90 assert_eq!(c.url, "http://localhost:1633");
91 assert!(!c.authenticated);
92 }
93
94 #[test]
95 fn from_node_with_token_marks_authenticated() {
96 let c = ApiClient::from_node(&node("http://localhost:1633", Some("dummy-jwt"))).unwrap();
97 assert!(c.authenticated);
98 }
99
100 #[test]
101 fn from_node_rejects_bogus_url() {
102 let err = ApiClient::from_node(&node("not a url", None)).unwrap_err();
103 assert!(format!("{err}").contains("invalid"));
104 }
105}