#![deny(missing_docs)]
#[cfg_attr(test, macro_use)]
extern crate log;
extern crate derive_builder;
use std::fs;
use std::path::Path;
use reqwest::{Client, ClientBuilder, header::HeaderMap, multipart::{Form, Part}, Response};
use walkdir::WalkDir;
use serde::{Serialize};
use serde::de::DeserializeOwned;
use errors::Error;
use utils::api_url;
use api::internal::*;
pub use api::data::*;
pub use api::metadata::*;
pub use errors::ApiError;
mod api;
mod utils;
mod errors;
pub struct PinataApi {
client: Client,
}
impl PinataApi {
pub fn new<S: Into<String>>(api_key: S, secret_api_key: S) -> Result<PinataApi, Error> {
let owned_key = api_key.into();
let owned_secret = secret_api_key.into();
utils::validate_keys(&owned_key, &owned_secret)?;
let mut default_headers = HeaderMap::new();
default_headers.insert("pinata_api_key", (&owned_key).parse().unwrap());
default_headers.insert("pinata_secret_api_key", (&owned_secret).parse().unwrap());
let client = ClientBuilder::new()
.default_headers(default_headers)
.build()?;
Ok(PinataApi {
client,
})
}
pub async fn test_authentication(&self) -> Result<(), ApiError> {
let response = self.client.get(&api_url("/data/testAuthentication"))
.send()
.await?;
self.parse_ok_result(response).await
}
pub async fn set_hash_pin_policy(&self, policy: HashPinPolicy) -> Result<(), ApiError> {
let response = self.client.put(&api_url("/pinning/hashPinPolicy"))
.json(&policy)
.send()
.await?;
self.parse_ok_result(response).await
}
pub async fn pin_by_hash(&self, hash: PinByHash) -> Result<PinByHashResult, ApiError> {
let response = self.client.post(&api_url("/pinning/pinByHash"))
.json(&hash)
.send()
.await?;
self.parse_result(response).await
}
pub async fn get_pin_jobs(&self, filters: PinJobsFilter) -> Result<PinJobs, ApiError> {
let response = self.client.get(&api_url("/pinning/pinJobs"))
.query(&filters)
.send()
.await?;
self.parse_result(response).await
}
pub async fn pin_json<S>(&self, pin_data: PinByJson<S>) -> Result<PinnedObject, ApiError>
where S: Serialize
{
let response = self.client.post(&api_url("/pinning/pinJSONToIPFS"))
.json(&pin_data)
.send()
.await?;
self.parse_result(response).await
}
pub async fn pin_file(&self, pin_data: PinByFile) -> Result<PinnedObject, ApiError> {
let mut form = Form::new();
for file_data in pin_data.files {
let base_path = Path::new(&file_data.file_path);
if base_path.is_dir() {
for entry_result in WalkDir::new(base_path) {
let entry = entry_result?;
let path = entry.path();
if path.is_dir() { continue }
let path_name = path.strip_prefix(base_path)?;
let part_file_name = format!(
"{}/{}",
base_path.file_name().unwrap().to_str().unwrap(),
path_name.to_str().unwrap()
);
let part = Part::bytes(fs::read(path)?)
.file_name(part_file_name);
form = form.part("file", part);
}
} else {
let file_name = base_path.file_name().unwrap().to_str().unwrap();
let part = Part::bytes(fs::read(base_path)?);
form = form.part("file", part.file_name(String::from(file_name)));
}
}
if let Some(metadata) = pin_data.pinata_metadata {
form = form.text("pinataMetadata", serde_json::to_string(&metadata).unwrap());
}
if let Some(option) = pin_data.pinata_option {
form = form.text("pinataOptions", serde_json::to_string(&option).unwrap());
}
let response = self.client.post(&api_url("/pinning/pinFileToIPFS"))
.multipart(form)
.send()
.await?;
self.parse_result(response).await
}
pub async fn unpin(&self, hash: &str) -> Result<(), ApiError> {
let response = self.client.delete(&api_url(&format!("/pinning/unpin/{}", hash)))
.send()
.await?;
self.parse_ok_result(response).await
}
pub async fn change_hash_metadata(&self, change: ChangePinMetadata) -> Result<(), ApiError> {
let response = self.client.put(&api_url("/pinning/hashMetadata"))
.json(&change)
.send()
.await?;
self.parse_ok_result(response).await
}
pub async fn get_total_user_pinned_data(&self) -> Result<TotalPinnedData, ApiError> {
let response = self.client.get(&api_url("/data/userPinnedDataTotal"))
.send()
.await?;
self.parse_result(response).await
}
pub async fn get_pin_list(&self, filters: PinListFilter) -> Result<PinList, ApiError> {
let response = self.client.get(&api_url("/data/pinList"))
.query(&filters)
.send()
.await?;
self.parse_result(response).await
}
async fn parse_result<R>(&self, response: Response) -> Result<R, ApiError>
where R: DeserializeOwned
{
if response.status().is_success() {
let result = response.json::<R>().await?;
Ok(result)
} else {
let error = response.json::<PinataApiError>().await?;
Err(ApiError::GenericError(error.message()))
}
}
async fn parse_ok_result(&self, response: Response) -> Result<(), ApiError> {
if response.status().is_success() {
Ok(())
} else {
let error = response.json::<PinataApiError>().await?;
Err(ApiError::GenericError(error.message()))
}
}
}
#[cfg(test)]
mod tests;