1mod errors;
2mod models;
3use std::{ops::Deref, sync::Arc};
4
5pub use errors::*;
6pub use models::*;
7use serde::{
8 de::{DeserializeOwned, Deserializer},
9 ser::{SerializeStruct, Serializer},
10 Deserialize, Serialize,
11};
12
13use crate::{Config, Method, SdkResult, StatusCode};
14
15#[derive(Debug, Clone)]
16pub struct Sdk {
17 config: Arc<Config>,
18}
19impl Deref for Sdk {
20 type Target = Config;
21
22 fn deref(&self) -> &Self::Target {
23 &self.config
24 }
25}
26
27impl Config {
28 pub fn into_sdk(self) -> Sdk {
29 Sdk::new(self)
30 }
31}
32
33pub const NO_BODY: Body<'static, ()> = Body::NoBody::<'static, ()>;
34
35impl Sdk {
36 pub fn new(config: Config) -> Self {
37 Self { config: Arc::new(config) }
38 }
39
40 pub fn id(&self, name: &str) -> String {
41 format!("{}/{}", self.org_name, name)
42 }
43
44 pub fn user_id_query(&self, user_name: &str) -> String {
45 serde_urlencoded::to_string([("userId", format!("{}/{}", self.org_name, user_name))]).unwrap()
46 }
47
48 pub async fn request<Data, Data2>(
49 &self,
50 method: Method,
51 url_path: impl AsRef<str>,
52 body: Body<'_, impl Serialize>,
53 ) -> SdkResult<ApiResponse<Data, Data2>>
54 where
55 Data: DeserializeOwned,
56 Data2: DeserializeOwned,
57 {
58 let mut url = String::from(&self.config.endpoint);
59 url.push_str(&url_path.as_ref().to_string());
60
61 println!("{url}");
62
63 let mut req = reqwest::Client::new()
64 .request(method, url)
65 .basic_auth(&self.config.client_id, Some(&self.config.client_secret));
66 match body {
67 Body::Json(body) => req = req.json(body),
68 Body::Form(body) => req = req.form(body),
69 Body::NoBody => {}
70 }
71 Ok(req.send().await?.json::<ApiResponse<Data, Data2>>().await?)
72 }
73
74 pub async fn request_data<Data>(
75 &self,
76 method: Method,
77 url_path: impl AsRef<str>,
78 body: Body<'_, impl Serialize>,
79 ) -> SdkResult<ApiResponse<Data, ()>>
80 where
81 Data: DeserializeOwned,
82 {
83 self.request::<Data, ()>(method, url_path, body).await
84 }
85
86 pub async fn request_data2<Data2>(
87 &self,
88 method: Method,
89 url_path: impl AsRef<str>,
90 body: Body<'_, impl Serialize>,
91 ) -> SdkResult<ApiResponse<(), Data2>>
92 where
93 Data2: DeserializeOwned,
94 {
95 self.request::<(), Data2>(method, url_path, body).await
96 }
97
98 pub async fn modify_model<T: Model>(&self, args: ModelModifyArgs<T>) -> SdkResult<bool> {
99 let mut url_path = format!("/api/{}-{}?id={}", args.action, T::ident(), args.model.id());
101 if matches!(args.action, ModelAction::Update) && args.columns.is_some() {
102 let columns = args.columns.unwrap();
103 if !columns.is_empty() {
104 url_path += &format!("&columns={}", columns.join(","));
105 }
106 }
107 self.request_data::<ModelActionAffect>(Method::POST, url_path, Body::Json(&args.model))
108 .await?
109 .into_data_default()
110 .map(|v| v.is_affected())
111 }
112
113 pub async fn add_model<T: Model>(&self, args: ModelAddArgs<T>) -> SdkResult<bool> {
114 self.modify_model(ModelModifyArgs {
115 action: ModelAction::Add,
116 model: args.model,
117 columns: None,
118 })
119 .await
120 }
121
122 pub async fn update_model<T: Model>(&self, args: ModelUpdateArgs<T>) -> SdkResult<bool> {
123 self.modify_model(ModelModifyArgs {
124 action: ModelAction::Update,
125 model: args.model,
126 columns: Some(args.columns),
127 })
128 .await
129 }
130
131 pub async fn delete_model<T: Model>(&self, args: ModelDeleteArgs<T>) -> SdkResult<bool> {
132 self.modify_model(ModelModifyArgs {
133 action: ModelAction::Delete,
134 model: args.model,
135 columns: None,
136 })
137 .await
138 }
139
140 pub async fn get_model_by_name<M: Model>(&self, name: String) -> Result<Option<M>, SdkError> {
141 self.request_data(Method::GET, format!("/api/get-{}?id={}", M::ident(), self.id(&name)), NO_BODY)
142 .await?
143 .into_data()
144 }
145
146 pub async fn get_default_model<M: Model>(&self, name: String) -> Result<Option<M>, SdkError> {
147 self.request_data(Method::GET, format!("/api/get-default-{}?id={}", M::ident(), self.id(&name)), NO_BODY)
148 .await?
149 .into_data()
150 }
151
152 pub(crate) async fn get_models<M: Model>(&self, mid_ident: Option<String>, query_args: impl IsQueryArgs) -> SdkResult<QueryResult<M>> {
154 let ident = if let Some(mid) = mid_ident {
155 "get-".to_owned() + &mid + "-"
156 } else {
157 "get-".to_owned()
158 } + M::plural_ident();
159
160 self.request(Method::GET, self.get_url_path(ident, true, query_args)?, NO_BODY)
161 .await?
162 .into_result_default()
163 .map(Into::into)
164 }
165
166 pub fn get_url_path(&self, ident: impl Into<String>, add_owner_query: bool, query_args: impl Serialize) -> SdkResult<String> {
167 Ok(format!("/api/{}?{}", ident.into(), self.get_url_query_part(add_owner_query, query_args)?))
168 }
169
170 pub fn get_url_query_part(&self, add_owner_query: bool, query_args: impl Serialize) -> SdkResult<String> {
171 let mut query = if add_owner_query {
172 format!("owner={}", self.org_name)
173 } else {
174 String::new()
175 };
176 let query_args = serde_urlencoded::to_string(query_args)?;
177 if !query_args.is_empty() {
178 query = format!("{query}&{query_args}")
179 }
180 Ok(query)
181 }
182}
183
184pub enum Body<'a, T = ()> {
185 Json(&'a T),
186 Form(&'a T),
187 NoBody,
188}
189
190#[derive(Debug, Serialize, Deserialize)]
191#[serde(rename_all = "camelCase", default)]
192pub struct ApiResponse<Data, Data2 = ()> {
193 pub data: Option<Data>,
194 pub data2: Option<Data2>,
195 pub name: String,
196 #[serde(flatten)]
197 pub status: Status,
198 pub sub: String,
199}
200
201impl<Data, Data2> Default for ApiResponse<Data, Data2> {
202 fn default() -> Self {
203 Self {
204 data: Default::default(),
205 data2: Default::default(),
206 name: Default::default(),
207 status: Default::default(),
208 sub: Default::default(),
209 }
210 }
211}
212
213#[derive(Debug)]
214pub enum Status {
215 Ok(String),
216 Err(String),
217 Other { status: String, msg: String },
218}
219
220impl Default for Status {
221 fn default() -> Self {
222 Self::Ok(Default::default())
223 }
224}
225impl<'de> Deserialize<'de> for Status {
226 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
227 where
228 D: Deserializer<'de>,
229 {
230 #[derive(Deserialize)]
231 struct StatusContainer {
232 status: String,
233 msg: String,
234 }
235
236 let container = StatusContainer::deserialize(deserializer)?;
237 match container.status.as_str() {
238 "ok" => Ok(Status::Ok(container.msg)),
239 "error" => Ok(Status::Err(container.msg)),
240 _ => Ok(Status::Other {
241 status: container.status,
242 msg: container.msg,
243 }),
244 }
245 }
246}
247impl Serialize for Status {
248 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
249 where
250 S: Serializer,
251 {
252 let mut state = serializer.serialize_struct("Status", 2)?;
254 match self {
255 Status::Ok(msg) => {
256 state.serialize_field("status", "ok")?;
257 state.serialize_field("msg", msg)?;
258 }
259 Status::Err(msg) => {
260 state.serialize_field("status", "error")?;
261 state.serialize_field("msg", msg)?;
262 }
263 Status::Other { status, msg } => {
264 state.serialize_field("status", status)?;
265 state.serialize_field("msg", msg)?;
266 }
267 }
268 state.end()
269 }
270}
271
272impl<Data, Data2> ApiResponse<Data, Data2> {
273 pub fn into_result(self) -> SdkResult<(Option<Data>, Option<Data2>)> {
274 match self.status {
275 Status::Ok(_) => Ok((self.data, self.data2)),
276 Status::Err(e) => Err(SdkError::new(StatusCode::INTERNAL_SERVER_ERROR, e)),
277 Status::Other { status, msg } => Err(SdkError::new(
278 StatusCode::INTERNAL_SERVER_ERROR,
279 format!("Unknown: status={status}, msg={msg}"),
280 )),
281 }
282 }
283
284 pub fn into_result_default(self) -> SdkResult<(Data, Data2)>
285 where
286 Data: Default,
287 Data2: Default,
288 {
289 let (data, data2) = self.into_result()?;
290 Ok((data.unwrap_or_default(), data2.unwrap_or_default()))
291 }
292
293 pub fn into_data(self) -> SdkResult<Option<Data>> {
294 let (data, _) = self.into_result()?;
295 Ok(data)
296 }
297
298 pub fn into_data_value(self) -> SdkResult<Data> {
299 let (data, _) = self.into_result()?;
300 data.ok_or(SdkError::new(StatusCode::NOT_FOUND, "Unexpected empty data."))
301 }
302
303 pub fn into_data_default(self) -> SdkResult<Data>
304 where
305 Data: Default,
306 {
307 let (data, _) = self.into_result()?;
308 Ok(data.unwrap_or_default())
309 }
310
311 pub fn into_data2(self) -> SdkResult<Option<Data2>> {
312 let (_, data2) = self.into_result()?;
313 Ok(data2)
314 }
315
316 pub fn into_data2_value(self) -> SdkResult<Data2> {
317 let (_, data2) = self.into_result()?;
318 data2.ok_or(SdkError::new(StatusCode::NOT_FOUND, "Unexpected empty data2."))
319 }
320
321 pub fn into_data2_default(self) -> SdkResult<Data2>
322 where
323 Data2: Default,
324 {
325 let (_, data2) = self.into_result()?;
326 Ok(data2.unwrap_or_default())
327 }
328}
329
330#[cfg(test)]
331mod tests {
332 use std::collections::HashMap;
333
334 use super::*;
335
336 #[test]
337 fn test_res_json() {
338 let json_data = r#"{"data":{"accessKey":"test"},"data2":null,"name":"","status":"ok","msg":"test","sub":""}"#;
339 let obj: ApiResponse<HashMap<String, String>, ()> = serde_json::from_str(json_data).unwrap();
340 println!("{obj:?}");
341 let json_data2 = serde_json::to_string(&obj).unwrap();
342 assert_eq!(json_data, json_data2);
343 }
344 #[test]
345 fn test_query_url() {
346 let query = serde_urlencoded::to_string([
347 ("bread", "ba/guette".to_owned()),
348 ("cheese", "comté".to_owned()),
349 ("meat", "ham".to_owned()),
350 ("fat", "butter".to_owned()),
351 ])
352 .unwrap();
353 assert_eq!("bread=ba%2Fguette&cheese=comt%C3%A9&meat=ham&fat=butter", query);
354 assert_eq!("", serde_urlencoded::to_string(()).unwrap());
355 assert_eq!("", serde_urlencoded::to_string(Vec::<()>::new()).unwrap());
356 }
357 #[test]
358 #[should_panic]
359 fn test_query_url4() {
360 let _ = serde_urlencoded::to_string(("k", "v")).unwrap();
361 }
362}