1use std::collections::HashMap;
9
10use log::{debug, max_level, trace, LevelFilter};
12use serde::de::DeserializeOwned;
13use serde::ser::Serialize;
14use surf::http::{self, Method};
15use surf::Url;
16use surf::{http::StatusCode, Body};
17use surf::{Request, Response};
18
19use crate::errors::{
21 ClientError, ClientResult, CodeMessage, ResponseCodeMessageError, ResponseError,
22};
23use crate::types::{
24 Annotation, AnnotationRows, Annotations, Config, DeletedEntry, DeletedTag, Entries,
25 EntriesExistParams, EntriesFilter, EntriesPage, Entry, ExistsInfo, ExistsResponse, Format,
26 NewAnnotation, NewEntry, NewlyRegisteredInfo, PaginatedEntries, PatchEntry, RegisterInfo,
27 RequestEntriesFilter, Tag, TagString, Tags, TokenInfo, User, ID, UNIT,
28};
29use crate::utils::{EndPoint, UrlBuilder};
30
31#[derive(Debug)]
34pub struct Client {
35 client_id: String,
36 client_secret: String,
37 username: String,
38 password: String,
39 token_info: Option<TokenInfo>,
40 url_base: UrlBuilder,
41}
42
43impl Client {
44 pub fn new(config: Config) -> Self {
46 Self {
47 client_id: config.client_id,
48 client_secret: config.client_secret,
49 username: config.username,
50 password: config.password,
51 token_info: None,
52 url_base: UrlBuilder::new(config.base_url),
53 }
54 }
55
56 async fn get_token(&mut self) -> ClientResult<String> {
59 if let Some(ref t) = self.token_info {
60 Ok(t.access_token.clone())
61 } else {
62 debug!("No api token loaded yet");
63 self.load_token().await
64 }
65 }
66
67 async fn load_token(&mut self) -> ClientResult<String> {
69 debug!("Requesting auth token");
70 let mut fields = HashMap::new();
71 fields.insert("grant_type".to_owned(), "password".to_owned());
72 fields.insert("client_id".to_owned(), self.client_id.clone());
73 fields.insert("client_secret".to_owned(), self.client_secret.clone());
74 fields.insert("username".to_owned(), self.username.clone());
75 fields.insert("password".to_owned(), self.password.clone());
76
77 let token_info: TokenInfo = self
78 .json_q(Method::Post, EndPoint::Token, UNIT, &fields, false)
79 .await?;
80 self.token_info = Some(token_info);
81
82 Ok(self.token_info.as_ref().unwrap().access_token.clone())
83 }
84
85 async fn refresh_token(&mut self) -> ClientResult<String> {
87 if self.token_info.is_none() {
88 return self.load_token().await;
89 }
90
91 let mut fields = HashMap::new();
92 fields.insert("grant_type".to_owned(), "refresh_token".to_owned());
93 fields.insert("client_id".to_owned(), self.client_id.clone());
94 fields.insert("client_secret".to_owned(), self.client_secret.clone());
95 fields.insert(
96 "refresh_token".to_owned(),
97 self.token_info.as_ref().unwrap().refresh_token.clone(),
98 );
99
100 let token_info: TokenInfo = self
101 .json_q(Method::Post, EndPoint::Token, UNIT, &fields, false)
102 .await?;
103 self.token_info = Some(token_info);
104
105 Ok(self.token_info.as_ref().unwrap().access_token.clone())
106 }
107
108 async fn smart_text_q<J, Q>(
111 &mut self,
112 method: Method,
113 end_point: EndPoint,
114 query: &Q,
115 json: &J,
116 ) -> ClientResult<String>
117 where
118 J: Serialize,
119 Q: Serialize,
120 {
121 Ok(self
122 .smart_q(method, end_point, query, json)
123 .await?
124 .body_string()
125 .await?)
126 }
127
128 async fn smart_json_q<T, J, Q>(
131 &mut self,
132 method: Method,
133 end_point: EndPoint,
134 query: &Q,
135 json: &J,
136 ) -> ClientResult<T>
137 where
138 T: DeserializeOwned,
139 J: Serialize,
140 Q: Serialize,
141 {
142 if max_level() >= LevelFilter::Debug {
143 let text = self
144 .smart_q(method, end_point, query, json)
145 .await?
146 .body_string()
147 .await?;
148 match serde_json::from_str(&text) {
149 Ok(j) => {
150 debug!("Deserialized json response body: {}", &text);
151 Ok(j)
152 }
153 Err(e) => {
154 debug!("Deserialize json failed for: {}", &text);
155 Err(ClientError::SerdeJsonError(e))
156 }
157 }
158 } else {
159 Ok(self
160 .smart_q(method, end_point, query, json)
161 .await?
162 .body_json()
163 .await?)
164 }
165 }
166
167 async fn smart_q<J, Q>(
170 &mut self,
171 method: Method,
172 end_point: EndPoint,
173 query: &Q,
174 json: &J,
175 ) -> ClientResult<Response>
176 where
177 J: Serialize,
178 Q: Serialize,
179 {
180 let _ = self.get_token().await?;
183 let response_result = self.q(method.clone(), end_point, query, json, true).await;
184
185 if let Err(ClientError::ExpiredToken) = response_result {
186 debug!("Token expired; refreshing");
187 self.refresh_token().await?;
188
189 Ok(self.q(method, end_point, query, json, true).await?)
191 } else {
192 Ok(response_result?)
193 }
194 }
195
196 async fn json_q<T, J, Q>(
199 &mut self,
200 method: Method,
201 end_point: EndPoint,
202 query: &Q,
203 json: &J,
204 use_token: bool,
205 ) -> ClientResult<T>
206 where
207 T: DeserializeOwned,
208 J: Serialize,
209 Q: Serialize,
210 {
211 if max_level() >= LevelFilter::Debug {
212 let text = self
213 .q(method, end_point, query, json, use_token)
214 .await?
215 .body_string()
216 .await?;
217 match serde_json::from_str(&text) {
218 Ok(j) => {
219 debug!("Deserialized json response body: {}", &text);
220 Ok(j)
221 }
222 Err(e) => {
223 debug!("Deserialize json failed for: {}", &text);
224 Err(ClientError::SerdeJsonError(e))
225 }
226 }
227 } else {
228 Ok(self
229 .q(method, end_point, query, json, use_token)
230 .await?
231 .body_json()
232 .await?)
233 }
234 }
235
236 async fn q<J, Q>(
238 &mut self,
239 method: Method,
240 end_point: EndPoint,
241 query: &Q,
242 json: &J,
243 use_token: bool,
244 ) -> ClientResult<Response>
245 where
246 J: Serialize,
247 Q: Serialize,
248 {
249 let url = self.url_base.build(end_point);
250 trace!("Sending request to {}", url);
251
252 let mut request = Request::builder(method, Url::parse(&url)?)
253 .body(Body::from_json(json)?)
254 .query(query)?;
255
256 if use_token {
257 if let Some(ref t) = self.token_info {
258 request = request.header(
259 http::headers::AUTHORIZATION,
260 format!("Bearer {}", t.access_token.clone()),
261 );
262 }
263 }
264
265 let mut response = request.await?;
266
267 trace!("response status: {:?}", response.status());
268 match response.status() {
269 StatusCode::Unauthorized => {
270 let info: ResponseError = response.body_json().await?;
271 if info.error_description.as_str().contains("expired") {
272 Err(ClientError::ExpiredToken)
273 } else {
274 Err(ClientError::Unauthorized(info))
275 }
276 }
277 StatusCode::Forbidden => {
278 let info: ResponseCodeMessageError = response.body_json().await?;
279 Err(ClientError::Forbidden(info))
280 }
281 StatusCode::NotFound => {
282 let info: ResponseCodeMessageError = match response.body_json().await {
283 Ok(info) => info,
284 Err(_) => ResponseCodeMessageError {
285 error: CodeMessage {
286 code: 404,
287 message: "Not supplied".to_owned(),
288 },
289 },
290 };
291 Err(ClientError::NotFound(info))
292 }
293 StatusCode::NotModified => {
294 Err(ClientError::NotModified)
296 }
297 status if status.is_success() => Ok(response),
298 status => Err(ClientError::Other(status, response.body_string().await?)),
299 }
300 }
301
302 pub async fn check_urls_exist<T: Into<String>>(
309 &mut self,
310 urls: Vec<T>,
311 ) -> ClientResult<ExistsInfo> {
312 let params = EntriesExistParams {
313 return_id: 1,
314 urls: urls
315 .into_iter()
316 .map(|url| url.into())
317 .collect::<Vec<String>>(),
318 };
319
320 self.smart_json_q(Method::Get, EndPoint::Exists, ¶ms, UNIT)
321 .await
322 }
323
324 pub async fn check_url_exists<T: Into<String>>(&mut self, url: T) -> ClientResult<Option<ID>> {
327 let mut params = HashMap::new();
328 params.insert("url".to_owned(), url.into());
329 params.insert("return_id".to_owned(), "1".to_owned());
330
331 let exists_info: ExistsResponse = self
332 .smart_json_q(Method::Get, EndPoint::Exists, ¶ms, UNIT)
333 .await?;
334
335 Ok(exists_info.exists)
337 }
338
339 pub async fn create_entry(&mut self, new_entry: &NewEntry) -> ClientResult<Entry> {
341 self.smart_json_q(Method::Post, EndPoint::Entries, UNIT, new_entry)
342 .await
343 }
344
345 pub async fn update_entry<T: Into<ID>>(
347 &mut self,
348 id: T,
349 entry: &PatchEntry,
350 ) -> ClientResult<Entry> {
351 self.smart_json_q(Method::Patch, EndPoint::Entry(id.into()), UNIT, entry)
352 .await
353 }
354
355 pub async fn reload_entry<T: Into<ID>>(&mut self, id: T) -> ClientResult<Entry> {
361 self.smart_json_q(Method::Patch, EndPoint::EntryReload(id.into()), UNIT, UNIT)
362 .await
363 }
364
365 pub async fn get_entry<T: Into<ID>>(&mut self, id: T) -> ClientResult<Entry> {
367 self.smart_json_q(Method::Get, EndPoint::Entry(id.into()), UNIT, UNIT)
368 .await
369 }
370
371 pub async fn delete_entry<T: Into<ID>>(&mut self, id: T) -> ClientResult<Entry> {
373 let id = id.into();
374 let json: DeletedEntry = self
375 .smart_json_q(Method::Delete, EndPoint::Entry(id), UNIT, UNIT)
376 .await?;
377
378 let entry = Entry {
381 id,
382 annotations: json.annotations,
383 content: json.content,
384 created_at: json.created_at,
385 domain_name: json.domain_name,
386 headers: json.headers,
387 http_status: json.http_status,
388 is_archived: json.is_archived,
389 is_public: json.is_public,
390 is_starred: json.is_starred,
391 language: json.language,
392 mimetype: json.mimetype,
393 origin_url: json.origin_url,
394 preview_picture: json.preview_picture,
395 published_at: json.published_at,
396 published_by: json.published_by,
397 reading_time: json.reading_time,
398 starred_at: json.starred_at,
399 tags: json.tags,
400 title: json.title,
401 uid: json.uid,
402 updated_at: json.updated_at,
403 url: json.url,
404 user_email: json.user_email,
405 user_id: json.user_id,
406 user_name: json.user_name,
407 };
408
409 Ok(entry)
410 }
411
412 pub async fn update_annotation(&mut self, annotation: &Annotation) -> ClientResult<Annotation> {
414 self.smart_json_q(
415 Method::Put,
416 EndPoint::Annotation(annotation.id),
417 UNIT,
418 annotation,
419 )
420 .await
421 }
422
423 pub async fn create_annotation<T: Into<ID>>(
425 &mut self,
426 entry_id: T,
427 annotation: &NewAnnotation,
428 ) -> ClientResult<Annotation> {
429 self.smart_json_q(
430 Method::Post,
431 EndPoint::Annotation(entry_id.into()),
432 UNIT,
433 annotation,
434 )
435 .await
436 }
437
438 pub async fn delete_annotation<T: Into<ID>>(&mut self, id: T) -> ClientResult<Annotation> {
440 self.smart_json_q(Method::Delete, EndPoint::Annotation(id.into()), UNIT, UNIT)
441 .await
442 }
443
444 pub async fn get_annotations<T: Into<ID>>(&mut self, id: T) -> ClientResult<Annotations> {
446 let json: AnnotationRows = self
447 .smart_json_q(Method::Get, EndPoint::Annotation(id.into()), UNIT, UNIT)
448 .await?;
449 Ok(json.rows)
450 }
451
452 pub async fn get_entries(&mut self) -> ClientResult<Entries> {
454 self._get_entries(&EntriesFilter::default()).await
455 }
456
457 pub async fn get_entries_with_filter(
459 &mut self,
460 filter: &EntriesFilter,
461 ) -> ClientResult<Entries> {
462 self._get_entries(filter).await
463 }
464
465 pub async fn get_entries_page(
469 &mut self,
470 filter: &EntriesFilter,
471 page_number: u32,
472 ) -> ClientResult<EntriesPage> {
473 let params = RequestEntriesFilter {
474 page: page_number,
475 filter,
476 };
477 let json: PaginatedEntries = self
478 .smart_json_q(Method::Get, EndPoint::Entries, ¶ms, UNIT)
479 .await?;
480
481 Ok(EntriesPage {
482 per_page: json.limit,
483 current_page: json.page,
484 total_pages: json.pages,
485 total_entries: json.total,
486 entries: json.embedded.items,
487 })
488 }
489
490 async fn _get_entries(&mut self, filter: &EntriesFilter) -> ClientResult<Entries> {
492 let mut entries = Entries::new();
493
494 let mut params = RequestEntriesFilter { page: 1, filter };
495
496 loop {
499 debug!("retrieving PaginatedEntries page {}", params.page);
500 let json: PaginatedEntries = self
501 .smart_json_q(Method::Get, EndPoint::Entries, ¶ms, UNIT)
502 .await?;
503
504 entries.extend(json.embedded.items.into_iter());
505
506 if json.page < json.pages {
507 params.page = json.page + 1;
508 } else {
509 break;
510 }
511 }
512
513 Ok(entries)
514 }
515
516 pub async fn export_entry<T: Into<ID>>(
518 &mut self,
519 entry_id: T,
520 fmt: Format,
521 ) -> ClientResult<String> {
522 self.smart_text_q(
523 Method::Get,
524 EndPoint::Export(entry_id.into(), fmt),
525 UNIT,
526 UNIT,
527 )
528 .await
529 }
530
531 pub async fn get_tags_for_entry<T: Into<ID>>(&mut self, entry_id: T) -> ClientResult<Tags> {
533 self.smart_json_q(
534 Method::Get,
535 EndPoint::EntryTags(entry_id.into()),
536 UNIT,
537 UNIT,
538 )
539 .await
540 }
541
542 pub async fn add_tags_to_entry<T: Into<ID>, U: Into<String>>(
545 &mut self,
546 entry_id: T,
547 tags: Vec<U>,
548 ) -> ClientResult<Entry> {
549 let mut data = HashMap::new();
550 data.insert(
551 "tags",
552 tags.into_iter().map(|x| x.into()).collect::<Vec<String>>(),
553 );
554
555 self.smart_json_q(
556 Method::Post,
557 EndPoint::EntryTags(entry_id.into()),
558 UNIT,
559 &data,
560 )
561 .await
562 }
563
564 pub async fn delete_tag_from_entry<T: Into<ID>, U: Into<ID>>(
568 &mut self,
569 entry_id: T,
570 tag_id: U,
571 ) -> ClientResult<Entry> {
572 self.smart_json_q(
573 Method::Delete,
574 EndPoint::DeleteEntryTag(entry_id.into(), tag_id.into()),
575 UNIT,
576 UNIT,
577 )
578 .await
579 }
580
581 pub async fn get_tags(&mut self) -> ClientResult<Tags> {
583 self.smart_json_q(Method::Get, EndPoint::Tags, UNIT, UNIT)
584 .await
585 }
586
587 pub async fn delete_tag<T: Into<ID>>(&mut self, id: T) -> ClientResult<Tag> {
591 let id = id.into();
592
593 let dt: DeletedTag = self
595 .smart_json_q(Method::Delete, EndPoint::Tag(id), UNIT, UNIT)
596 .await?;
597
598 Ok(Tag {
599 id,
600 label: dt.label,
601 slug: dt.slug,
602 })
603 }
604
605 pub async fn delete_tag_by_label<T: Into<String>>(
613 &mut self,
614 label: T,
615 ) -> ClientResult<DeletedTag> {
616 let mut params = HashMap::new();
617 params.insert("tag".to_owned(), label.into());
618
619 let deleted_tag: DeletedTag = self
620 .smart_json_q(Method::Delete, EndPoint::TagLabel, ¶ms, UNIT)
621 .await?;
622 Ok(deleted_tag)
623 }
624
625 pub async fn delete_tags_by_label(
637 &mut self,
638 tags: Vec<TagString>,
639 ) -> ClientResult<Vec<DeletedTag>> {
640 let mut params = HashMap::new();
641 params.insert(
642 "tags",
643 tags.into_iter()
644 .map(|x| x.into_string())
645 .collect::<Vec<String>>()
646 .join(","),
647 );
648
649 self.smart_json_q(Method::Delete, EndPoint::TagsLabel, ¶ms, UNIT)
652 .await
653 }
654
655 pub async fn get_api_version(&mut self) -> ClientResult<String> {
658 self.smart_json_q(Method::Get, EndPoint::Version, UNIT, UNIT)
659 .await
660 }
661
662 pub async fn get_user(&mut self) -> ClientResult<User> {
664 self.smart_json_q(Method::Get, EndPoint::User, UNIT, UNIT)
665 .await
666 }
667
668 pub async fn register_user(
670 &mut self,
671 info: &RegisterInfo,
672 ) -> ClientResult<NewlyRegisteredInfo> {
673 self.json_q(Method::Put, EndPoint::User, UNIT, info, false)
674 .await
675 }
676}