use reqwest::Method;
use crate::error::Result;
use crate::http::{push_opt, Envelope, HttpClient, Query};
use crate::models::{
BulkImportRollback, BulkPostAction, BulkResult, CancelResult, CreatePost, Delivery,
DuplicatedPost, ListPosts, Message, Page, Post, PostAnalytics, PreflightResult, PublishOptions,
PublishOutcome, PublishRun, RetryOptions, RetryResult, UpdatePost,
};
#[derive(Debug, Clone)]
pub struct Posts<'a> {
pub(crate) http: &'a HttpClient,
}
impl Posts<'_> {
pub async fn list(&self, params: &ListPosts) -> Result<Page<Post>> {
let mut query: Query = Vec::new();
push_opt(&mut query, "workspace_id", params.workspace_id.as_ref());
push_opt(&mut query, "status", params.status.as_ref());
push_opt(&mut query, "search", params.search.as_ref());
push_opt(&mut query, "platform", params.platform.as_ref());
push_opt(&mut query, "label", params.label.as_ref());
push_opt(&mut query, "account_id", params.account_id.as_ref());
push_opt(&mut query, "date", params.date.as_ref());
push_opt(&mut query, "from", params.from.as_ref());
push_opt(&mut query, "to", params.to.as_ref());
push_opt(&mut query, "sort", params.sort.as_ref());
push_opt(&mut query, "page", params.page);
push_opt(&mut query, "per_page", params.per_page);
self.http
.send::<Page<Post>, ()>(Method::GET, "/posts", Some(query), None)
.await
}
pub async fn list_all(&self, params: &ListPosts) -> Result<Vec<Post>> {
let mut params = params.clone();
let mut page_number = params.page.unwrap_or(1);
let mut out = Vec::new();
loop {
params.page = Some(page_number);
let page = self.list(¶ms).await?;
let has_next = page.has_next() && !page.is_empty();
out.extend(page.items);
if !has_next {
return Ok(out);
}
page_number += 1;
}
}
pub async fn get(&self, id: &str) -> Result<Post> {
self.http
.send::<Post, ()>(Method::GET, &format!("/posts/{id}"), None, None)
.await
}
pub async fn create(&self, post: &CreatePost) -> Result<Post> {
self.http
.send(Method::POST, "/posts", None, Some(post))
.await
}
pub async fn update(&self, id: &str, changes: &UpdatePost) -> Result<Post> {
self.http
.send(Method::PUT, &format!("/posts/{id}"), None, Some(changes))
.await
}
pub async fn delete(&self, id: &str) -> Result<Message> {
self.http
.send::<Message, ()>(Method::DELETE, &format!("/posts/{id}"), None, None)
.await
}
pub async fn duplicate(&self, id: &str) -> Result<DuplicatedPost> {
let body: Envelope<DuplicatedPost> = self
.http
.send::<_, ()>(Method::POST, &format!("/posts/{id}/duplicate"), None, None)
.await?;
Ok(body.data)
}
pub async fn publish(&self, id: &str, options: &PublishOptions) -> Result<PublishOutcome> {
let body: Envelope<PublishOutcome> = self
.http
.send(
Method::POST,
&format!("/posts/{id}/publish"),
None,
Some(options),
)
.await?;
Ok(body.data)
}
pub async fn retry(&self, id: &str, options: &RetryOptions) -> Result<RetryResult> {
let body: Envelope<RetryResult> = self
.http
.send(
Method::POST,
&format!("/posts/{id}/retry"),
None,
Some(options),
)
.await?;
Ok(body.data)
}
pub async fn cancel(&self, id: &str, account_ids: Option<&[String]>) -> Result<CancelResult> {
let payload = serde_json::json!({ "accountIds": account_ids });
let body: Envelope<CancelResult> = self
.http
.send(
Method::POST,
&format!("/posts/{id}/cancel"),
None,
Some(&payload),
)
.await?;
Ok(body.data)
}
pub async fn preflight(&self, id: &str) -> Result<PreflightResult> {
let body: Envelope<PreflightResult> = self
.http
.send::<_, ()>(Method::POST, &format!("/posts/{id}/preflight"), None, None)
.await?;
Ok(body.data)
}
pub async fn deliveries(&self, id: &str) -> Result<Vec<Delivery>> {
let body: Envelope<Vec<Delivery>> = self
.http
.send::<_, ()>(Method::GET, &format!("/posts/{id}/deliveries"), None, None)
.await?;
Ok(body.data)
}
pub async fn publish_runs(&self, id: &str) -> Result<Vec<PublishRun>> {
let body: Envelope<Vec<PublishRun>> = self
.http
.send::<_, ()>(
Method::GET,
&format!("/posts/{id}/publish-runs"),
None,
None,
)
.await?;
Ok(body.data)
}
pub async fn analytics(&self, id: &str) -> Result<PostAnalytics> {
let body: Envelope<PostAnalytics> = self
.http
.send::<_, ()>(Method::GET, &format!("/posts/{id}/analytics"), None, None)
.await?;
Ok(body.data)
}
pub async fn bulk(&self, action: &BulkPostAction) -> Result<BulkResult> {
self.http
.send(Method::POST, "/posts/bulk", None, Some(action))
.await
}
pub async fn rollback_import(&self, batch_id: &str) -> Result<BulkImportRollback> {
self.http
.send::<_, ()>(
Method::DELETE,
&format!("/posts/bulk-import/{batch_id}"),
None,
None,
)
.await
}
}
#[cfg(feature = "multipart")]
#[cfg_attr(docsrs, doc(cfg(feature = "multipart")))]
impl Posts<'_> {
pub async fn validate_import(
&self,
workspace_id: &str,
csv: crate::models::MediaUpload,
) -> Result<crate::models::BulkImportValidation> {
let value = self
.send_csv("/posts/bulk-import/validate", workspace_id, csv)
.await?;
crate::http::decode(value)
}
pub async fn commit_import(
&self,
workspace_id: &str,
csv: crate::models::MediaUpload,
) -> Result<crate::models::BulkImportCommit> {
let value = self
.send_csv("/posts/bulk-import/commit", workspace_id, csv)
.await?;
crate::http::decode(value)
}
async fn send_csv(
&self,
path: &str,
workspace_id: &str,
csv: crate::models::MediaUpload,
) -> Result<serde_json::Value> {
let part = reqwest::multipart::Part::bytes(csv.bytes)
.file_name(csv.filename)
.mime_str(&csv.mime_type)
.map_err(crate::error::Error::Transport)?;
let form = reqwest::multipart::Form::new()
.text("workspace_id", workspace_id.to_string())
.part("file", part);
self.http
.send_request(self.http.raw_request(Method::POST, path)?.multipart(form))
.await
}
}