1use crate::models::{LabelCreateRequest, LabelResponse, LabelUpdate, LabelsResponse};
4use crate::{Client, Http, RequestError, ReqwestProcessing, Serializing};
5use reqwest::{Method, StatusCode};
6use snafu::ResultExt;
7use std::collections::HashMap;
8
9impl Client {
10 pub async fn labels(&self) -> Result<LabelsResponse, RequestError> {
12 self.get_labels(None).await
13 }
14
15 pub async fn labels_by_org(&self, org_id: &str) -> Result<LabelsResponse, RequestError> {
17 self.get_labels(Some(org_id)).await
18 }
19
20 async fn get_labels(&self, org_id: Option<&str>) -> Result<LabelsResponse, RequestError> {
21 let labels_url = self.url("/api/v2/labels");
22 let mut request = self.request(Method::GET, &labels_url);
23
24 if let Some(id) = org_id {
25 request = request.query(&[("orgID", id)]);
26 }
27
28 let response = request.send().await.context(ReqwestProcessing)?;
29 match response.status() {
30 StatusCode::OK => Ok(response
31 .json::<LabelsResponse>()
32 .await
33 .context(ReqwestProcessing)?),
34 status => {
35 let text = response.text().await.context(ReqwestProcessing)?;
36 Http { status, text }.fail()?
37 }
38 }
39 }
40
41 pub async fn find_label(&self, label_id: &str) -> Result<LabelResponse, RequestError> {
43 let labels_by_id_url = self.url(&format!("/api/v2/labels/{}", label_id));
44 let response = self
45 .request(Method::GET, &labels_by_id_url)
46 .send()
47 .await
48 .context(ReqwestProcessing)?;
49 match response.status() {
50 StatusCode::OK => Ok(response
51 .json::<LabelResponse>()
52 .await
53 .context(ReqwestProcessing)?),
54 status => {
55 let text = response.text().await.context(ReqwestProcessing)?;
56 Http { status, text }.fail()?
57 }
58 }
59 }
60
61 pub async fn create_label(
63 &self,
64 org_id: &str,
65 name: &str,
66 properties: Option<HashMap<String, String>>,
67 ) -> Result<LabelResponse, RequestError> {
68 let create_label_url = self.url("/api/v2/labels");
69 let body = LabelCreateRequest {
70 org_id: org_id.into(),
71 name: name.into(),
72 properties,
73 };
74 let response = self
75 .request(Method::POST, &create_label_url)
76 .body(serde_json::to_string(&body).context(Serializing)?)
77 .send()
78 .await
79 .context(ReqwestProcessing)?;
80 match response.status() {
81 StatusCode::CREATED => Ok(response
82 .json::<LabelResponse>()
83 .await
84 .context(ReqwestProcessing)?),
85 status => {
86 let text = response.text().await.context(ReqwestProcessing)?;
87 Http { status, text }.fail()?
88 }
89 }
90 }
91
92 pub async fn update_label(
94 &self,
95 name: Option<String>,
96 properties: Option<HashMap<String, String>>,
97 label_id: &str,
98 ) -> Result<LabelResponse, RequestError> {
99 let update_label_url = self.url(&format!("/api/v2/labels/{}", label_id));
100 let body = LabelUpdate { name, properties };
101 let response = self
102 .request(Method::PATCH, &update_label_url)
103 .body(serde_json::to_string(&body).context(Serializing)?)
104 .send()
105 .await
106 .context(ReqwestProcessing)?;
107 match response.status() {
108 StatusCode::OK => Ok(response
109 .json::<LabelResponse>()
110 .await
111 .context(ReqwestProcessing)?),
112 status => {
113 let text = response.text().await.context(ReqwestProcessing)?;
114 Http { status, text }.fail()?
115 }
116 }
117 }
118
119 pub async fn delete_label(&self, label_id: &str) -> Result<(), RequestError> {
121 let delete_label_url = self.url(&format!("/api/v2/labels/{}", label_id));
122 let response = self
123 .request(Method::DELETE, &delete_label_url)
124 .send()
125 .await
126 .context(ReqwestProcessing)?;
127 match response.status() {
128 StatusCode::NO_CONTENT => Ok(()),
129 status => {
130 let text = response.text().await.context(ReqwestProcessing)?;
131 Http { status, text }.fail()?
132 }
133 }
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140 use mockito::mock;
141
142 const BASE_PATH: &str = "/api/v2/labels";
143
144 #[tokio::test]
145 async fn labels() {
146 let token = "some-token";
147
148 let mock_server = mock("GET", BASE_PATH)
149 .match_header("Authorization", format!("Token {}", token).as_str())
150 .create();
151
152 let client = Client::new(mockito::server_url(), "", token);
153
154 let _result = client.labels().await;
155
156 mock_server.assert();
157 }
158
159 #[tokio::test]
160 async fn labels_by_org() {
161 let token = "some-token";
162 let org_id = "some-org_id";
163
164 let mock_server = mock("GET", format!("{}?orgID={}", BASE_PATH, org_id).as_str())
165 .match_header("Authorization", format!("Token {}", token).as_str())
166 .create();
167
168 let client = Client::new(mockito::server_url(), "", token);
169
170 let _result = client.labels_by_org(org_id).await;
171
172 mock_server.assert();
173 }
174
175 #[tokio::test]
176 async fn find_label() {
177 let token = "some-token";
178 let label_id = "some-id";
179
180 let mock_server = mock("GET", format!("{}/{}", BASE_PATH, label_id).as_str())
181 .match_header("Authorization", format!("Token {}", token).as_str())
182 .create();
183
184 let client = Client::new(mockito::server_url(), "", token);
185
186 let _result = client.find_label(label_id).await;
187
188 mock_server.assert();
189 }
190
191 #[tokio::test]
192 async fn create_label() {
193 let token = "some-token";
194 let org_id = "some-org";
195 let name = "some-user";
196 let mut properties = HashMap::new();
197 properties.insert("some-key".to_string(), "some-value".to_string());
198
199 let mock_server = mock("POST", BASE_PATH)
200 .match_header("Authorization", format!("Token {}", token).as_str())
201 .match_body(
202 format!(
203 r#"{{"orgID":"{}","name":"{}","properties":{{"some-key":"some-value"}}}}"#,
204 org_id, name
205 )
206 .as_str(),
207 )
208 .create();
209
210 let client = Client::new(mockito::server_url(), org_id, token);
211
212 let _result = client.create_label(org_id, name, Some(properties)).await;
213
214 mock_server.assert();
215 }
216
217 #[tokio::test]
218 async fn create_label_opt() {
219 let token = "some-token";
220 let org_id = "some-org_id";
221 let name = "some-user";
222
223 let mock_server = mock("POST", BASE_PATH)
224 .match_header("Authorization", format!("Token {}", token).as_str())
225 .match_body(format!(r#"{{"orgID":"{}","name":"{}"}}"#, org_id, name).as_str())
226 .create();
227
228 let client = Client::new(mockito::server_url(), org_id, token);
229
230 let _result = client.create_label(org_id, name, None).await;
231
232 mock_server.assert();
233 }
234
235 #[tokio::test]
236 async fn update_label() {
237 let token = "some-token";
238 let name = "some-user";
239 let label_id = "some-label_id";
240 let mut properties = HashMap::new();
241 properties.insert("some-key".to_string(), "some-value".to_string());
242
243 let mock_server = mock("PATCH", format!("{}/{}", BASE_PATH, label_id).as_str())
244 .match_header("Authorization", format!("Token {}", token).as_str())
245 .match_body(
246 format!(
247 r#"{{"name":"{}","properties":{{"some-key":"some-value"}}}}"#,
248 name
249 )
250 .as_str(),
251 )
252 .create();
253
254 let client = Client::new(mockito::server_url(), "", token);
255
256 let _result = client
257 .update_label(Some(name.to_string()), Some(properties), label_id)
258 .await;
259
260 mock_server.assert();
261 }
262
263 #[tokio::test]
264 async fn update_label_opt() {
265 let token = "some-token";
266 let label_id = "some-label_id";
267
268 let mock_server = mock("PATCH", format!("{}/{}", BASE_PATH, label_id).as_str())
269 .match_header("Authorization", format!("Token {}", token).as_str())
270 .match_body("{}")
271 .create();
272
273 let client = Client::new(mockito::server_url(), "", token);
274
275 let _result = client.update_label(None, None, label_id).await;
276
277 mock_server.assert();
278 }
279
280 #[tokio::test]
281 async fn delete_label() {
282 let token = "some-token";
283 let label_id = "some-label_id";
284
285 let mock_server = mock("DELETE", format!("{}/{}", BASE_PATH, label_id).as_str())
286 .match_header("Authorization", format!("Token {}", token).as_str())
287 .create();
288
289 let client = Client::new(mockito::server_url(), "", token);
290
291 let _result = client.delete_label(label_id).await;
292
293 mock_server.assert();
294 }
295}