casdoor_sdk_rust/sdk/
mod.rs

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        // When adding model, the id parameter is not needed.
100        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    // Query and return some models and the total number of models.
153    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        // Create a new struct to serialize the enum variants
253        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}