lenra_app/
api.rs

1use crate::Result;
2use serde::{de::DeserializeOwned, Deserialize, Serialize};
3use serde_json::{json, Value};
4
5pub trait ApiTrait {
6    fn url(&self) -> String;
7    fn token(&self) -> String;
8}
9
10#[derive(Deserialize, Debug, PartialEq, Default, Clone)]
11pub struct ApiParam {
12    pub url: String,
13    pub token: String,
14}
15
16#[derive(Debug, PartialEq)]
17pub struct Api {
18    pub(self) params: ApiParam,
19    pub data: DataApi,
20}
21
22impl Api {
23    pub(crate) fn new(api: ApiParam) -> Api {
24        let params: ApiParam = api.clone();
25        Api {
26            params: params.clone(),
27            data: DataApi {
28                params: params.clone(),
29            },
30        }
31    }
32}
33
34impl ApiTrait for Api {
35    fn url(&self) -> String {
36        self.params.url.clone()
37    }
38
39    fn token(&self) -> String {
40        self.params.token.clone()
41    }
42}
43
44pub trait CollectionGetter: ApiTrait + Sized {
45    fn coll(&self, coll: &str) -> Collection {
46        Collection {
47            coll: coll.to_string(),
48            api: ApiParam {
49                url: self.url(),
50                token: self.token(),
51            },
52        }
53    }
54}
55
56pub struct Collection {
57    coll: String,
58    api: ApiParam,
59}
60
61impl Collection {
62    pub fn get_doc<T: Doc>(&self, id: &str) -> Result<T> {
63        log::debug!("get_doc {}[{}]", self.coll, id);
64        let request_url = format!(
65            "{url}/app-api/v1/data/colls/{coll}/docs/{id}",
66            url = self.api.url,
67            id = id,
68            coll = self.coll
69        );
70
71        ureq::get(request_url.as_str())
72            .set(
73                "Authorization",
74                format!("Bearer {}", self.api.token).as_str(),
75            )
76            .call()?
77            .into_json()
78            .map_err(|e| e.into())
79    }
80
81    pub fn create_doc<T: Doc>(&self, doc: T) -> Result<T> {
82        log::debug!("create_doc {}", serde_json::to_string(&doc).unwrap());
83
84        let request_url = format!(
85            "{url}/app-api/v1/data/colls/{coll}/docs",
86            url = self.api.url,
87            coll = self.coll
88        );
89
90        ureq::post(request_url.as_str())
91            .set(
92                "Authorization",
93                format!("Bearer {}", self.api.token).as_str(),
94            )
95            .send_json(doc)?
96            .into_json()
97            .map_err(|e| e.into())
98    }
99
100    pub fn update_doc<T: Doc>(&self, doc: T) -> Result<T> {
101        log::debug!("update_doc {}", serde_json::to_string(&doc).unwrap());
102
103        let request_url = format!(
104            "{url}/app-api/v1/data/colls/{coll}/docs/{id}",
105            url = self.api.url,
106            id = doc.id().unwrap(),
107            coll = self.coll
108        );
109
110        ureq::put(request_url.as_str())
111            .set(
112                "Authorization",
113                format!("Bearer {}", self.api.token).as_str(),
114            )
115            .send_json(doc)?
116            .into_json()
117            .map_err(|e| e.into())
118    }
119
120    pub fn delete_doc<T: Doc>(&self, doc: T) -> Result<()> {
121        let request_url = format!(
122            "{url}/app-api/v1/data/colls/{coll}/docs/{id}",
123            url = self.api.url,
124            id = doc.id().unwrap(),
125            coll = self.coll
126        );
127
128        ureq::delete(request_url.as_str())
129            .set(
130                "Authorization",
131                format!("Bearer {}", self.api.token).as_str(),
132            )
133            .call()?;
134
135        Ok(())
136    }
137
138    pub fn find<T: Doc, Q: Serialize, P: Serialize>(
139        &self,
140        query: Q,
141        projection: Option<P>,
142    ) -> Result<Vec<T>> {
143        log::debug!("find {}", serde_json::to_string(&query).unwrap());
144        let request_url = format!(
145            "{url}/app-api/v1/data/colls/{coll}/find",
146            url = self.api.url,
147            coll = self.coll
148        );
149
150        ureq::post(request_url.as_str())
151            .set(
152                "Authorization",
153                format!("Bearer {}", self.api.token).as_str(),
154            )
155            .send_json(json!({ "query": query, "projection": projection }))?
156            .into_json()
157            .map_err(|e| e.into())
158    }
159
160    pub fn update_many<T: Doc, Q: Serialize, U: Serialize>(
161        &self,
162        filter: Q,
163        update: U,
164    ) -> Result<Vec<T>> {
165        log::debug!(
166            "updateMany {}, {}",
167            serde_json::to_string(&filter).unwrap(),
168            serde_json::to_string(&update).unwrap()
169        );
170        let request_url = format!(
171            "{url}/app-api/v1/data/colls/{coll}/updateMany",
172            url = self.api.url,
173            coll = self.coll
174        );
175
176        ureq::post(request_url.as_str())
177            .set(
178                "Authorization",
179                format!("Bearer {}", self.api.token).as_str(),
180            )
181            .send_json(json!({"filter": filter, "update": update}))?
182            .into_json()
183            .map_err(|e| e.into())
184    }
185}
186
187#[derive(Debug, PartialEq)]
188pub struct DataApi {
189    pub(self) params: ApiParam,
190}
191
192impl DataApi {
193    pub fn start_transaction(self) -> Result<Transaction> {
194        log::debug!("start_transaction");
195
196        let request_url = format!("{url}/app/transaction", url = self.url());
197
198        ureq::post(request_url.as_str())
199            .set("Authorization", format!("Bearer {}", self.token()).as_str())
200            .send_json(json!({}))?
201            .into_string()
202            .map(|token| Transaction {
203                params: self.params.clone(),
204                token,
205            })
206            .map_err(|e| e.into())
207    }
208}
209
210impl ApiTrait for DataApi {
211    fn url(&self) -> String {
212        self.params.url.clone()
213    }
214
215    fn token(&self) -> String {
216        self.params.token.clone()
217    }
218}
219
220impl CollectionGetter for DataApi {}
221
222#[derive(Debug, PartialEq)]
223pub struct Transaction {
224    pub(self) params: ApiParam,
225    token: String,
226}
227
228impl ApiTrait for Transaction {
229    fn url(&self) -> String {
230        self.params.url.clone()
231    }
232
233    fn token(&self) -> String {
234        self.token.clone()
235    }
236}
237
238impl CollectionGetter for Transaction {}
239
240impl Transaction {
241    pub fn commit(&self) -> Result<()> {
242        log::debug!("transaction commit");
243
244        let request_url = format!("{url}/app/transaction/commit", url = self.url());
245
246        ureq::post(request_url.as_str())
247            .set("Authorization", format!("Bearer {}", self.token()).as_str())
248            .send_json(json!({}))?
249            .into_string()
250            .map(|_| ())
251            .map_err(|e| e.into())
252    }
253
254    pub fn abort(&self) -> Result<()> {
255        log::debug!("transaction commit");
256
257        let request_url = format!("{url}/app/transaction/abort", url = self.url());
258
259        ureq::post(request_url.as_str())
260            .set("Authorization", format!("Bearer {}", self.token()).as_str())
261            .send_json(json!({}))?
262            .into_string()
263            .map(|_| ())
264            .map_err(|e| e.into())
265    }
266}
267
268pub trait Doc: Sized + DeserializeOwned + Serialize + 'static + Clone {
269    fn id(&self) -> Option<String>;
270}
271
272impl Doc for Value {
273    fn id(&self) -> Option<String> {
274        None
275    }
276}