contentful_fork/
contentful_management_client.rs

1use crate::{http_client, models::Entry};
2use serde::{de::DeserializeOwned, Serialize};
3use serde_json::json;
4use serde_json::Value;
5
6pub struct ContentfulManagementClient {
7    management_api_access_token: String,
8    space_id: String,
9    base_url: String,
10    environment_id: String,
11}
12
13impl ContentfulManagementClient {
14    pub fn new(management_api_access_token: &str, space_id: &str) -> ContentfulManagementClient {
15        let environment_id = "master".into();
16        ContentfulManagementClient {
17            base_url: "https://api.contentful.com/spaces".into(),
18            management_api_access_token: management_api_access_token.into(),
19            space_id: space_id.into(),
20            environment_id,
21        }
22    }
23
24    pub fn with_environment<S>(
25        management_api_access_token: &str,
26        space_id: &str,
27        environment_id: &str,
28    ) -> ContentfulManagementClient {
29        ContentfulManagementClient {
30            base_url: "https://api.contentful.com/spaces".into(),
31            management_api_access_token: management_api_access_token.into(),
32            space_id: space_id.into(),
33            environment_id: environment_id.into(),
34        }
35    }
36
37    fn get_entry_url(&self, entry_id: &str) -> String {
38        let url = format!(
39            "{base_url}/{space_id}/environments/{environment_id}/entries/{entry_id}",
40            base_url = &self.base_url,
41            space_id = &self.space_id,
42            environment_id = &self.environment_id,
43            entry_id = &entry_id
44        );
45        url
46    }
47    fn get_entries_url(&self) -> String {
48        let url = format!(
49            "{base_url}/{space_id}/environments/{environment_id}/entries",
50            base_url = &self.base_url,
51            space_id = &self.space_id,
52            environment_id = &self.environment_id,
53        );
54        url
55    }
56
57    pub async fn get_entry(
58        &self,
59        entry_id: &str,
60    ) -> Result<Option<Entry<Value>>, Box<dyn std::error::Error>> {
61        let url = self.get_entry_url(&entry_id);
62        let entry =
63            http_client::get::<Entry<Value>>(&url, &self.management_api_access_token).await?;
64        Ok(entry)
65    }
66
67    pub async fn get_entry_for_locale<T>(
68        &self,
69        entry_id: &str,
70        locale: &str,
71    ) -> Result<Option<Entry<T>>, Box<dyn std::error::Error>>
72    where
73        T: DeserializeOwned + Serialize,
74    {
75        let url = self.get_entry_url(&entry_id);
76        if let Some(entry_json) =
77            http_client::get::<Entry<Value>>(&url, &self.management_api_access_token).await?
78        {
79            let entry_typed =
80                helpers::convert_json_object_to_typed_entry(entry_json.fields.clone(), locale)?;
81            let entry = Entry::new(entry_typed, entry_json.sys.clone());
82            Ok(Some(entry))
83        } else {
84            Ok(None)
85        }
86    }
87
88    pub async fn create_entry_from_json<T>(
89        &self,
90        entry: &Value,
91        content_type_id: &str,
92    ) -> Result<T, Box<dyn std::error::Error>>
93    where
94        T: DeserializeOwned,
95    {
96        let url = self.get_entries_url();
97        let mut json = http_client::post(
98            &url,
99            &self.management_api_access_token,
100            content_type_id,
101            entry,
102        )
103        .await?;
104        let entry_created_fields = json.get_mut("fields").unwrap();
105        let entry_created_string = entry_created_fields.to_string();
106        let entry_created = serde_json::from_str::<T>(&entry_created_string.as_str())?;
107
108        Ok(entry_created)
109    }
110
111    pub async fn create_entry<T>(
112        &self,
113        entry: &T,
114        content_type_id: &str,
115    ) -> Result<T, Box<dyn std::error::Error>>
116    where
117        T: DeserializeOwned + Serialize,
118    {
119        let entry_json = json!({ "fields": entry });
120        self.create_entry_from_json::<T>(&entry_json, content_type_id)
121            .await
122    }
123
124    pub async fn create_entry_for_locale<T>(
125        &self,
126        entry: &T,
127        content_type_id: &str,
128        locale: &str,
129    ) -> Result<T, Box<dyn std::error::Error>>
130    where
131        T: DeserializeOwned + Serialize,
132    {
133        let entry_to_create = helpers::reconstruct_json_object_with_locale(entry, locale)?;
134        let updated_entry_json = self
135            .create_entry::<Value>(&entry_to_create, content_type_id)
136            .await?;
137        let updated_entry =
138            helpers::convert_json_object_to_typed_entry(updated_entry_json, locale)?;
139        Ok(updated_entry)
140    }
141
142    pub async fn create_or_update_entry_from_json(
143        &self,
144        entry: &Value,
145        entry_id: &str,
146        version: &Option<i32>,
147        content_type_id: &str,
148    ) -> Result<Value, Box<dyn std::error::Error>> {
149        let url = self.get_entry_url(entry_id);
150        let json = http_client::put(
151            &url,
152            &self.management_api_access_token,
153            version,
154            content_type_id,
155            entry,
156        )
157        .await?;
158        Ok(json)
159    }
160
161    pub async fn create_or_update_entry(
162        &self,
163        entry: &Entry<Value>,
164        id: &str,
165        content_type_id: &str,
166    ) -> Result<Entry<Value>, Box<dyn std::error::Error>> {
167        let entry_updated = self
168            .create_or_update_entry_from_json(
169                &json!(entry),
170                id,
171                &entry.sys.version,
172                content_type_id,
173            )
174            .await?;
175        let entry_updated_string = entry_updated.to_string();
176        let entry = serde_json::from_str::<Entry<Value>>(&entry_updated_string.as_str())?;
177        Ok(entry)
178    }
179
180    pub async fn create_or_update_entry_for_locale<T>(
181        &self,
182        entry: &Entry<T>,
183        id: &str,
184        locale: &str,
185        content_type_id: &str,
186    ) -> Result<Entry<T>, Box<dyn std::error::Error>>
187    where
188        T: DeserializeOwned + Serialize,
189    {
190        let entry_json = helpers::reconstruct_json_object_with_locale(&entry.fields, locale)?;
191        let entry_to_update = Entry::new(entry_json, entry.sys.clone());
192        let updated_entry_json = self
193            .create_or_update_entry(&entry_to_update, id, content_type_id)
194            .await?;
195        let updated_entry_typed = helpers::convert_json_object_to_typed_entry(
196            json!(updated_entry_json.fields),
197            locale,
198        )?;
199        let updated_entry = Entry::new(updated_entry_typed, updated_entry_json.sys);
200        Ok(updated_entry)
201    }
202}
203
204mod helpers {
205    use serde::{de::DeserializeOwned, Serialize};
206    use serde_json::{json, Value};
207
208    pub fn reconstruct_json_object_with_locale<T>(
209        entry: &T,
210        locale: &str,
211    ) -> Result<Value, Box<dyn std::error::Error>>
212    where
213        T: Serialize,
214    {
215        let mut entry_json = json!(entry);
216        let mut fields_map = serde_json::Map::new();
217
218        if entry_json.is_object() {
219            let entry_object = entry_json.as_object_mut().unwrap();
220            for (field_name, field_value) in entry_object {
221                /*if field_value.is_object() {
222                    fields_map.insert(field_name.into(), field_value.clone()); //TODO
223                } else if field_value.is_array() {
224                    fields_map.insert(field_name.into(), field_value.clone()); //TODO
225                } else {*/
226                fields_map.insert(field_name.into(), json!({ locale: field_value }));
227                //}
228            }
229        } else {
230            unimplemented!();
231        }
232        return Ok(json!(fields_map));
233    }
234
235    pub fn convert_json_object_to_typed_entry<T>(
236        entry_json: Value,
237        locale: &str,
238    ) -> Result<T, Box<dyn std::error::Error>>
239    where
240        T: DeserializeOwned,
241    {
242        let mut entry_created_map = serde_json::Map::new();
243
244        if entry_json.is_object() {
245            let entry_object = entry_json.as_object().unwrap();
246            for (field_name, field_value) in entry_object {
247                if field_value.is_object() {
248                    entry_created_map
249                        .insert(field_name.into(), field_value.get(locale).unwrap().clone());
250                } else if field_value.is_array() {
251                    todo!();
252                } else {
253                    todo!();
254                }
255            }
256        } else {
257            todo!();
258        }
259
260        let entry_string = json!(entry_created_map).to_string();
261        let created_entry = serde_json::from_str::<T>(&entry_string.as_str())?;
262        Ok(created_entry)
263    }
264}