use crate::{http_client, models::Entry}; use serde::{de::DeserializeOwned, Serialize}; use serde_json::json; use serde_json::Value; pub struct ContentfulManagementClient { management_api_access_token: String, space_id: String, base_url: String, environment_id: String, } impl ContentfulManagementClient { pub fn new(management_api_access_token: &str, space_id: &str) -> ContentfulManagementClient { let environment_id = "master".into(); ContentfulManagementClient { base_url: "https://api.contentful.com/spaces".into(), management_api_access_token: management_api_access_token.into(), space_id: space_id.into(), environment_id, } } pub fn with_environment( management_api_access_token: &str, space_id: &str, environment_id: &str, ) -> ContentfulManagementClient { ContentfulManagementClient { base_url: "https://api.contentful.com/spaces".into(), management_api_access_token: management_api_access_token.into(), space_id: space_id.into(), environment_id: environment_id.into(), } } fn get_entry_url(&self, entry_id: &str) -> String { let url = format!( "{base_url}/{space_id}/environments/{environment_id}/entries/{entry_id}", base_url = &self.base_url, space_id = &self.space_id, environment_id = &self.environment_id, entry_id = &entry_id ); url } fn get_entries_url(&self) -> String { let url = format!( "{base_url}/{space_id}/environments/{environment_id}/entries", base_url = &self.base_url, space_id = &self.space_id, environment_id = &self.environment_id, ); url } pub async fn get_entry( &self, entry_id: &str, ) -> Result>, Box> { let url = self.get_entry_url(entry_id); let entry = http_client::get::>(&url, &self.management_api_access_token).await?; Ok(entry) } pub async fn get_entry_for_locale( &self, entry_id: &str, locale: &str, ) -> Result>, Box> where T: DeserializeOwned + Serialize, { let url = self.get_entry_url(entry_id); if let Some(entry_json) = http_client::get::>(&url, &self.management_api_access_token).await? { let entry_typed = helpers::convert_json_object_to_typed_entry(entry_json.fields.clone(), locale)?; let entry = Entry::new(entry_typed, entry_json.sys.clone()); Ok(Some(entry)) } else { Ok(None) } } pub async fn create_entry_from_json( &self, entry: &Value, content_type_id: &str, ) -> Result> where T: DeserializeOwned, { let url = self.get_entries_url(); let mut json = http_client::post( &url, &self.management_api_access_token, content_type_id, entry, ) .await?; let entry_created_fields = json.get_mut("fields").unwrap(); let entry_created_string = entry_created_fields.to_string(); let entry_created = serde_json::from_str::(&entry_created_string)?; Ok(entry_created) } pub async fn create_entry( &self, entry: &T, content_type_id: &str, ) -> Result> where T: DeserializeOwned + Serialize, { let entry_json = json!({ "fields": entry }); self.create_entry_from_json::(&entry_json, content_type_id) .await } pub async fn create_entry_for_locale( &self, entry: &T, content_type_id: &str, locale: &str, ) -> Result> where T: DeserializeOwned + Serialize, { let entry_to_create = helpers::reconstruct_json_object_with_locale(entry, locale)?; let updated_entry_json = self .create_entry::(&entry_to_create, content_type_id) .await?; let updated_entry = helpers::convert_json_object_to_typed_entry(updated_entry_json, locale)?; Ok(updated_entry) } pub async fn create_or_update_entry_from_json( &self, entry: &Value, entry_id: &str, version: &Option, content_type_id: &str, ) -> Result> { let url = self.get_entry_url(entry_id); let json = http_client::put( &url, &self.management_api_access_token, version, content_type_id, entry, ) .await?; Ok(json) } pub async fn create_or_update_entry( &self, entry: &Entry, id: &str, content_type_id: &str, ) -> Result, Box> { let entry_updated = self .create_or_update_entry_from_json( &json!(entry), id, &entry.sys.version, content_type_id, ) .await?; let entry_updated_string = entry_updated.to_string(); let entry = serde_json::from_str::>(&entry_updated_string)?; Ok(entry) } pub async fn create_or_update_entry_for_locale( &self, entry: &Entry, id: &str, locale: &str, content_type_id: &str, ) -> Result, Box> where T: DeserializeOwned + Serialize, { let entry_json = helpers::reconstruct_json_object_with_locale(&entry.fields, locale)?; let entry_to_update = Entry::new(entry_json, entry.sys.clone()); let updated_entry_json = self .create_or_update_entry(&entry_to_update, id, content_type_id) .await?; let updated_entry_typed = helpers::convert_json_object_to_typed_entry(json!(updated_entry_json.fields), locale)?; let updated_entry = Entry::new(updated_entry_typed, updated_entry_json.sys); Ok(updated_entry) } } mod helpers { use serde::{de::DeserializeOwned, Serialize}; use serde_json::{json, Value}; pub fn reconstruct_json_object_with_locale( entry: &T, locale: &str, ) -> Result> where T: Serialize, { let mut entry_json = json!(entry); let mut fields_map = serde_json::Map::new(); if entry_json.is_object() { let entry_object = entry_json.as_object_mut().unwrap(); for (field_name, field_value) in entry_object { /*if field_value.is_object() { fields_map.insert(field_name.into(), field_value.clone()); //TODO } else if field_value.is_array() { fields_map.insert(field_name.into(), field_value.clone()); //TODO } else {*/ fields_map.insert(field_name.into(), json!({ locale: field_value })); //} } } else { unimplemented!(); } Ok(json!(fields_map)) } pub fn convert_json_object_to_typed_entry( entry_json: Value, locale: &str, ) -> Result> where T: DeserializeOwned, { let mut entry_created_map = serde_json::Map::new(); if entry_json.is_object() { let entry_object = entry_json.as_object().unwrap(); for (field_name, field_value) in entry_object { if field_value.is_object() { entry_created_map .insert(field_name.into(), field_value.get(locale).unwrap().clone()); } else if field_value.is_array() { todo!(); } else { todo!(); } } } else { todo!(); } let entry_string = json!(entry_created_map).to_string(); let created_entry = serde_json::from_str::(&entry_string)?; Ok(created_entry) } }