use super::super::{
AddItemParams, AddItemResult, DeleteItemResult, ItemInfo, UpdateItemParams, UpdateItemResult,
};
use super::PortalClient;
use crate::Result;
use tracing::instrument;
impl<'a> PortalClient<'a> {
#[instrument(skip(self, item_id))]
pub async fn get_item(&self, item_id: impl AsRef<str>) -> Result<ItemInfo> {
let item_id = item_id.as_ref();
tracing::debug!(item_id = %item_id, "Getting item");
let url = format!("{}/content/items/{}", self.base_url, item_id);
tracing::debug!(url = %url, "Sending getItem request");
let mut request = self.client.http().get(&url).query(&[("f", "json")]);
if let Some(token) = self.client.get_token_if_required().await? {
request = request.query(&[("token", token)]);
}
let response = request.send().await?;
let status = response.status();
if !status.is_success() {
let error_text = response
.text()
.await
.unwrap_or_else(|e| format!("Failed to read error response: {}", e));
tracing::error!(status = %status, error = %error_text, "getItem request failed");
return Err(crate::Error::from(crate::ErrorKind::Api {
code: status.as_u16() as i32,
message: format!("HTTP {}: {}", status, error_text),
}));
}
let item: ItemInfo = response.json().await?;
tracing::debug!(title = %item.title(), item_type = %item.item_type(), "Got item");
Ok(item)
}
#[instrument(skip(self, params))]
pub async fn add_item(&self, params: AddItemParams) -> Result<AddItemResult> {
tracing::debug!(title = %params.title(), item_type = %params.item_type(), "Adding item");
let user = self.get_self().await?;
let username = user.effective_username().ok_or_else(|| {
crate::Error::from(crate::ErrorKind::Api {
code: 401,
message: "Username not available in user info".to_string(),
})
})?;
let url = format!("{}/content/users/{}/addItem", self.base_url, username);
tracing::debug!(url = %url, username = %username, "Sending addItem request");
let mut form = reqwest::multipart::Form::new()
.text("f", "json")
.text("title", params.title().to_string())
.text("type", params.item_type().to_string());
if let Some(desc) = params.description() {
form = form.text("description", desc.to_string());
}
if let Some(tags) = params.tags() {
form = form.text("tags", tags.join(","));
}
if let Some(snippet) = params.snippet() {
form = form.text("snippet", snippet.to_string());
}
if let Some(categories) = params.categories() {
form = form.text("categories", categories.join(","));
}
if let Some(keywords) = params.type_keywords() {
form = form.text("typeKeywords", keywords.join(","));
}
if let Some(url_str) = params.url() {
form = form.text("url", url_str.to_string());
}
if let Some(wkid) = params.spatial_reference() {
form = form.text("spatialReference", wkid.to_string());
}
if let Some(extent) = params.extent() {
let extent_str = serde_json::to_string(extent)?;
form = form.text("extent", extent_str);
}
if let Some(access) = params.access() {
form = form.text("access", access.to_string());
}
if let Some(properties) = params.properties() {
let props_str = serde_json::to_string(properties)?;
form = form.text("properties", props_str);
}
if let Some(folder) = params.folder() {
form = form.text("folder", folder.to_string());
}
if let Some(text) = params.text() {
form = form.text("text", text.to_string());
}
if let Some(token) = self.client.get_token_if_required().await? {
form = form.text("token", token);
}
let response = self.client.http().post(&url).multipart(form).send().await?;
let status = response.status();
if !status.is_success() {
let error_text = response
.text()
.await
.unwrap_or_else(|e| format!("Failed to read error response: {}", e));
tracing::error!(status = %status, error = %error_text, "addItem request failed");
return Err(crate::Error::from(crate::ErrorKind::Api {
code: status.as_u16() as i32,
message: format!("HTTP {}: {}", status, error_text),
}));
}
let response_text = response.text().await?;
tracing::debug!(response = %response_text, "addItem raw response");
let result: AddItemResult = serde_json::from_str(&response_text)?;
tracing::debug!(item_id = %result.id(), success = result.success(), "Item added");
Ok(result)
}
#[instrument(skip(self, item_id, params))]
pub async fn update_item(
&self,
item_id: impl AsRef<str>,
params: UpdateItemParams,
) -> Result<UpdateItemResult> {
let item_id = item_id.as_ref();
tracing::debug!(item_id = %item_id, "Updating item");
let item = self.get_item(item_id).await?;
let url = format!(
"{}/content/users/{}/items/{}/update",
self.base_url,
item.owner(),
item_id
);
tracing::debug!(url = %url, owner = %item.owner(), "Sending updateItem request");
let mut form = reqwest::multipart::Form::new().text("f", "json");
if let Some(title) = params.title() {
form = form.text("title", title.to_string());
}
if let Some(desc) = params.description() {
form = form.text("description", desc.to_string());
}
if let Some(tags) = params.tags() {
form = form.text("tags", tags.join(","));
}
if let Some(snippet) = params.snippet() {
form = form.text("snippet", snippet.to_string());
}
if let Some(categories) = params.categories() {
form = form.text("categories", categories.join(","));
}
if let Some(keywords) = params.type_keywords() {
form = form.text("typeKeywords", keywords.join(","));
}
if let Some(url_str) = params.url() {
form = form.text("url", url_str.to_string());
}
if let Some(wkid) = params.spatial_reference() {
form = form.text("spatialReference", wkid.to_string());
}
if let Some(extent) = params.extent() {
let extent_str = serde_json::to_string(extent)?;
form = form.text("extent", extent_str);
}
if let Some(access) = params.access() {
form = form.text("access", access.to_string());
}
if let Some(token) = self.client.get_token_if_required().await? {
form = form.text("token", token);
}
let response = self.client.http().post(&url).multipart(form).send().await?;
let status = response.status();
if !status.is_success() {
let error_text = response
.text()
.await
.unwrap_or_else(|e| format!("Failed to read error response: {}", e));
tracing::error!(status = %status, error = %error_text, "updateItem request failed");
return Err(crate::Error::from(crate::ErrorKind::Api {
code: status.as_u16() as i32,
message: format!("HTTP {}: {}", status, error_text),
}));
}
let result: UpdateItemResult = response.json().await?;
tracing::debug!(success = result.success(), "Item updated");
Ok(result)
}
#[instrument(skip(self, item_id))]
pub async fn delete_item(&self, item_id: impl AsRef<str>) -> Result<DeleteItemResult> {
let item_id = item_id.as_ref();
tracing::debug!(item_id = %item_id, "Deleting item");
let item = self.get_item(item_id).await?;
let url = format!(
"{}/content/users/{}/items/{}/delete",
self.base_url,
item.owner(),
item_id
);
tracing::debug!(url = %url, owner = %item.owner(), "Sending deleteItem request");
let mut form_data = vec![("f", "json")];
let token_opt = self.client.get_token_if_required().await?;
let token_str;
if let Some(token) = token_opt {
token_str = token;
form_data.push(("token", token_str.as_str()));
}
let response = self
.client
.http()
.post(&url)
.form(&form_data)
.send()
.await?;
let status = response.status();
if !status.is_success() {
let error_text = response
.text()
.await
.unwrap_or_else(|e| format!("Failed to read error response: {}", e));
tracing::error!(status = %status, error = %error_text, "deleteItem request failed");
return Err(crate::Error::from(crate::ErrorKind::Api {
code: status.as_u16() as i32,
message: format!("HTTP {}: {}", status, error_text),
}));
}
let result: DeleteItemResult = response.json().await?;
tracing::debug!(success = result.success(), "Item deleted");
Ok(result)
}
#[instrument(skip(self, item_id))]
pub async fn get_item_data(&self, item_id: impl AsRef<str>) -> Result<bytes::Bytes> {
let item_id = item_id.as_ref();
tracing::debug!(item_id = %item_id, "Getting item data");
let url = format!("{}/content/items/{}/data", self.base_url, item_id);
tracing::debug!(url = %url, "Sending getItemData request");
let mut request = self.client.http().get(&url).query(&[("f", "json")]);
if let Some(token) = self.client.get_token_if_required().await? {
request = request.query(&[("token", token)]);
}
let response = request.send().await?;
let status = response.status();
if !status.is_success() {
let error_text = response
.text()
.await
.unwrap_or_else(|e| format!("Failed to read error response: {}", e));
tracing::error!(status = %status, error = %error_text, "getItemData request failed");
return Err(crate::Error::from(crate::ErrorKind::Api {
code: status.as_u16() as i32,
message: format!("HTTP {}: {}", status, error_text),
}));
}
let bytes = response.bytes().await?;
tracing::debug!(size = bytes.len(), "Retrieved item data");
Ok(bytes)
}
#[instrument(skip(self, item_id, data))]
pub async fn update_item_data(
&self,
item_id: impl AsRef<str>,
data: Vec<u8>,
) -> Result<UpdateItemResult> {
let item_id = item_id.as_ref();
tracing::debug!(item_id = %item_id, size = data.len(), "Updating item data");
let item = self.get_item(item_id).await?;
let url = format!(
"{}/content/users/{}/items/{}/update",
self.base_url,
item.owner(),
item_id
);
tracing::debug!(url = %url, owner = %item.owner(), "Sending updateItemData request");
let part = reqwest::multipart::Part::bytes(data)
.file_name("data.json")
.mime_str("application/json")?;
let mut form = reqwest::multipart::Form::new()
.text("f", "json")
.part("file", part);
if let Some(token) = self.client.get_token_if_required().await? {
form = form.text("token", token);
}
let response = self.client.http().post(&url).multipart(form).send().await?;
let status = response.status();
if !status.is_success() {
let error_text = response
.text()
.await
.unwrap_or_else(|e| format!("Failed to read error response: {}", e));
tracing::error!(status = %status, error = %error_text, "updateItemData request failed");
return Err(crate::Error::from(crate::ErrorKind::Api {
code: status.as_u16() as i32,
message: format!("HTTP {}: {}", status, error_text),
}));
}
let result: UpdateItemResult = response.json().await?;
tracing::debug!(success = result.success(), "Item data updated");
Ok(result)
}
}