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<S>(
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<Option<Entry<Value>>, Box<dyn std::error::Error>> {
let url = self.get_entry_url(entry_id);
let entry =
http_client::get::<Entry<Value>>(&url, &self.management_api_access_token).await?;
Ok(entry)
}
pub async fn get_entry_for_locale<T>(
&self,
entry_id: &str,
locale: &str,
) -> Result<Option<Entry<T>>, Box<dyn std::error::Error>>
where
T: DeserializeOwned + Serialize,
{
let url = self.get_entry_url(entry_id);
if let Some(entry_json) =
http_client::get::<Entry<Value>>(&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<T>(
&self,
entry: &Value,
content_type_id: &str,
) -> Result<T, Box<dyn std::error::Error>>
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::<T>(&entry_created_string)?;
Ok(entry_created)
}
pub async fn create_entry<T>(
&self,
entry: &T,
content_type_id: &str,
) -> Result<T, Box<dyn std::error::Error>>
where
T: DeserializeOwned + Serialize,
{
let entry_json = json!({ "fields": entry });
self.create_entry_from_json::<T>(&entry_json, content_type_id)
.await
}
pub async fn create_entry_for_locale<T>(
&self,
entry: &T,
content_type_id: &str,
locale: &str,
) -> Result<T, Box<dyn std::error::Error>>
where
T: DeserializeOwned + Serialize,
{
let entry_to_create = helpers::reconstruct_json_object_with_locale(entry, locale)?;
let updated_entry_json = self
.create_entry::<Value>(&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<i32>,
content_type_id: &str,
) -> Result<Value, Box<dyn std::error::Error>> {
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<Value>,
id: &str,
content_type_id: &str,
) -> Result<Entry<Value>, Box<dyn std::error::Error>> {
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<Value>>(&entry_updated_string)?;
Ok(entry)
}
pub async fn create_or_update_entry_for_locale<T>(
&self,
entry: &Entry<T>,
id: &str,
locale: &str,
content_type_id: &str,
) -> Result<Entry<T>, Box<dyn std::error::Error>>
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<T>(
entry: &T,
locale: &str,
) -> Result<Value, Box<dyn std::error::Error>>
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 {
fields_map.insert(field_name.into(), json!({ locale: field_value }));
}
} else {
unimplemented!();
}
Ok(json!(fields_map))
}
pub fn convert_json_object_to_typed_entry<T>(
entry_json: Value,
locale: &str,
) -> Result<T, Box<dyn std::error::Error>>
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::<T>(&entry_string)?;
Ok(created_entry)
}
}