urbit_http_api/
interface.rs1use crate::channel::Channel;
2use crate::error::{Result, UrbitAPIError};
3use json::JsonValue;
4use reqwest::blocking::{Client, Response};
5use reqwest::header::{HeaderValue, COOKIE};
6
7#[derive(Debug, Clone)]
9pub struct ShipInterface {
10 pub url: String,
13 pub session_auth: HeaderValue,
15 pub ship_name: String,
17 req_client: Client,
19}
20
21impl ShipInterface {
22 pub fn new(ship_url: &str, ship_code: &str) -> Result<ShipInterface> {
27 let client = Client::new();
28 let login_url = format!("{}/~/login", ship_url);
29 let resp = client
30 .post(&login_url)
31 .body("password=".to_string() + &ship_code)
32 .send()?;
33
34 if resp.status().as_u16() != 204 {
36 return Err(UrbitAPIError::FailedToLogin);
37 }
38
39 let session_auth = resp
41 .headers()
42 .get("set-cookie")
43 .ok_or(UrbitAPIError::FailedToLogin)?;
44
45 let auth_string = session_auth
47 .to_str()
48 .map_err(|_| UrbitAPIError::FailedToLogin)?;
49
50 let end_pos = auth_string.find('=').ok_or(UrbitAPIError::FailedToLogin)?;
52 let ship_name = &auth_string[9..end_pos];
53
54 Ok(ShipInterface {
55 url: ship_url.to_string(),
56 session_auth: session_auth.clone(),
57 ship_name: ship_name.to_string(),
58 req_client: client,
59 })
60 }
61
62 pub fn ship_name_with_sig(&self) -> String {
64 format!("~{}", self.ship_name)
65 }
66
67 pub fn create_channel(&self) -> Result<Channel> {
69 Channel::new(self.clone())
70 }
71
72 pub fn send_put_request(&self, url: &str, body: &JsonValue) -> Result<Response> {
74 let json = body.dump();
75 let resp = self
76 .req_client
77 .put(url)
78 .header(COOKIE, self.session_auth.clone())
79 .header("Content-Type", "application/json")
80 .body(json);
81
82 Ok(resp.send()?)
83 }
84
85 pub fn scry(&self, app: &str, path: &str, mark: &str) -> Result<Response> {
87 let scry_url = format!("{}/~/scry/{}{}.{}", self.url, app, path, mark);
88 let resp = self
89 .req_client
90 .get(&scry_url)
91 .header(COOKIE, self.session_auth.clone())
92 .header("Content-Type", "application/json");
93
94 Ok(resp.send()?)
95 }
96
97 pub fn spider(
99 &self,
100 input_mark: &str,
101 output_mark: &str,
102 thread_name: &str,
103 body: &JsonValue,
104 ) -> Result<Response> {
105 let json = body.dump();
106 let spider_url = format!(
107 "{}/spider/{}/{}/{}.json",
108 self.url, input_mark, thread_name, output_mark
109 );
110
111 let resp = self
112 .req_client
113 .post(&spider_url)
114 .header(COOKIE, self.session_auth.clone())
115 .header("Content-Type", "application/json")
116 .body(json);
117
118 Ok(resp.send()?)
119 }
120}
121
122impl Default for ShipInterface {
123 fn default() -> Self {
124 ShipInterface::new("http://0.0.0.0:8080", "lidlut-tabwed-pillex-ridrup").unwrap()
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131 use crate::subscription::Subscription;
132 use json::object;
133 #[test]
134 fn can_login() {
136 let ship_interface =
137 ShipInterface::new("http://0.0.0.0:8080", "lidlut-tabwed-pillex-ridrup").unwrap();
138 }
139
140 #[test]
141 fn can_create_channel() {
143 let ship_interface =
144 ShipInterface::new("http://0.0.0.0:8080", "lidlut-tabwed-pillex-ridrup").unwrap();
145 let channel = ship_interface.create_channel().unwrap();
146 channel.delete_channel();
147 }
148
149 #[test]
150 fn can_subscribe() {
152 let ship_interface =
153 ShipInterface::new("http://0.0.0.0:8080", "lidlut-tabwed-pillex-ridrup").unwrap();
154 let mut channel = ship_interface.create_channel().unwrap();
155 channel
156 .create_new_subscription("chat-view", "/primary")
157 .unwrap();
158
159 channel.find_subscription("chat-view", "/primary");
160 channel.unsubscribe("chat-view", "/primary");
161 channel.delete_channel();
162 }
163
164 #[test]
165 fn can_poke() {
167 let ship_interface =
168 ShipInterface::new("http://0.0.0.0:8080", "lidlut-tabwed-pillex-ridrup").unwrap();
169 let mut channel = ship_interface.create_channel().unwrap();
170 let poke_res = channel
171 .poke("hood", "helm-hi", &"A poke has been made".into())
172 .unwrap();
173 assert!(poke_res.status().as_u16() == 204);
174 channel.delete_channel();
175 }
176
177 #[test]
178 fn can_scry() {
180 let mut ship_interface =
181 ShipInterface::new("http://0.0.0.0:8080", "lidlut-tabwed-pillex-ridrup").unwrap();
182 let scry_res = ship_interface.scry("graph-store", "/keys", "json").unwrap();
183
184 assert!(scry_res.status().as_u16() == 200);
185 }
186
187 #[test]
188 fn can_spider() {
190 let mut ship_interface =
191 ShipInterface::new("http://0.0.0.0:8080", "lidlut-tabwed-pillex-ridrup").unwrap();
192 let create_req = object! {
193 "create": {
194 "resource": {
195 "ship": "~zod",
196 "name": "test",
197 },
198 "title": "Testing creation",
199 "description": "test",
200 "associated": {
201 "policy": {
202 "invite": {
203 "pending": []
204 }
205 }
206 },
207 "module": "chat",
208 "mark": "graph-validator-chat"
209 }
210 };
211
212 let spider_res = ship_interface
213 .spider("graph-view-action", "json", "graph-create", &create_req)
214 .unwrap();
215
216 assert!(spider_res.status().as_u16() == 200);
217 }
218}