contentful/
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)?;
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)?;
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 =
196            helpers::convert_json_object_to_typed_entry(json!(updated_entry_json.fields), locale)?;
197        let updated_entry = Entry::new(updated_entry_typed, updated_entry_json.sys);
198        Ok(updated_entry)
199    }
200}
201
202mod helpers {
203    use serde::{de::DeserializeOwned, Serialize};
204    use serde_json::{json, Value};
205
206    pub fn reconstruct_json_object_with_locale<T>(
207        entry: &T,
208        locale: &str,
209    ) -> Result<Value, Box<dyn std::error::Error>>
210    where
211        T: Serialize,
212    {
213        let mut entry_json = json!(entry);
214        let mut fields_map = serde_json::Map::new();
215
216        if entry_json.is_object() {
217            let entry_object = entry_json.as_object_mut().unwrap();
218            for (field_name, field_value) in entry_object {
219                /*if field_value.is_object() {
220                    fields_map.insert(field_name.into(), field_value.clone()); //TODO
221                } else if field_value.is_array() {
222                    fields_map.insert(field_name.into(), field_value.clone()); //TODO
223                } else {*/
224                fields_map.insert(field_name.into(), json!({ locale: field_value }));
225                //}
226            }
227        } else {
228            unimplemented!();
229        }
230
231        Ok(json!(fields_map))
232    }
233
234    pub fn convert_json_object_to_typed_entry<T>(
235        entry_json: Value,
236        locale: &str,
237    ) -> Result<T, Box<dyn std::error::Error>>
238    where
239        T: DeserializeOwned,
240    {
241        let mut entry_created_map = serde_json::Map::new();
242
243        if entry_json.is_object() {
244            let entry_object = entry_json.as_object().unwrap();
245            for (field_name, field_value) in entry_object {
246                if field_value.is_object() {
247                    entry_created_map
248                        .insert(field_name.into(), field_value.get(locale).unwrap().clone());
249                } else if field_value.is_array() {
250                    todo!();
251                } else {
252                    todo!();
253                }
254            }
255        } else {
256            todo!();
257        }
258
259        let entry_string = json!(entry_created_map).to_string();
260        let created_entry = serde_json::from_str::<T>(&entry_string)?;
261        Ok(created_entry)
262    }
263}