use crate::api::RequestFailure;
use crate::node;
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value, json};
use std::fmt;
#[derive(Clone, Deserialize, Debug, Serialize, PartialEq)]
pub enum PropertyFormat {
Checkbox,
Date,
Email,
Files,
MultiSelect,
Number,
Objects,
Phone,
Select,
Text,
Url,
}
impl std::fmt::Display for PropertyFormat {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let label = match self {
PropertyFormat::Checkbox => "checkbox",
PropertyFormat::Date => "date",
PropertyFormat::Email => "email",
PropertyFormat::Files => "files",
PropertyFormat::MultiSelect => "multi_select",
PropertyFormat::Number => "number",
PropertyFormat::Objects => "objects",
PropertyFormat::Phone => "phone",
PropertyFormat::Select => "select",
PropertyFormat::Text => "text",
PropertyFormat::Url => "url",
};
write!(f, "{}", label)
}
}
impl PropertyFormat {
pub fn from_str(s: &str) -> Self {
match s {
"checkbox" => PropertyFormat::Checkbox,
"date" => PropertyFormat::Date,
"email" => PropertyFormat::Email,
"files" => PropertyFormat::Files,
"multi_select" => PropertyFormat::MultiSelect,
"number" => PropertyFormat::Number,
"objects" => PropertyFormat::Objects,
"phone" => PropertyFormat::Phone,
"select" => PropertyFormat::Select,
"text" => PropertyFormat::Text,
"url" => PropertyFormat::Url,
_ => PropertyFormat::Text, }
}
}
#[derive(Clone, Debug)]
pub struct Property {
pub format: PropertyFormat,
pub id: String,
pub key: String,
pub name: String,
pub object: String,
}
impl Property {
pub(crate) fn to_json(&self) -> Value {
let mut values = Map::new();
values.insert("format".to_string(), Value::String(self.format.to_string()));
values.insert("key".to_string(), Value::String(self.key.clone()));
values.insert("name".to_string(), Value::String(self.name.clone()));
Value::Object(values)
}
pub(crate) fn from_json(json: Value) -> Self {
let format = json["format"].as_str().unwrap().to_string();
let key = json["key"].as_str().unwrap().to_string();
let name = json["name"].as_str().unwrap().to_string();
let id = json["id"].as_str().unwrap().to_string();
let object = match json["object"].as_str() {
Some(s) => s.to_string(),
None => "".to_string(),
};
Property {
format: PropertyFormat::from_str(&format),
key,
name,
id,
object,
}
}
pub(crate) async fn from_response(response: reqwest::Response) -> Self {
let json_input = response.text().await.unwrap();
let json: serde_json::Value = serde_json::from_str(json_input.as_ref()).unwrap();
let p = json["property"].clone();
Property::from_json(p)
}
}
#[derive(Debug)]
pub struct ListOfProperties {
pub properties: Vec<Property>,
pub has_more: bool,
pub next_offset: u32,
pub offset: u32,
pub limit: u32,
pub total: u32,
}
#[derive(Debug)]
pub struct CreatePropertyRequest<'a> {
api_key: &'a str,
format: PropertyFormat,
key: &'a str,
name: &'a str,
server: &'a str,
space_id: &'a str,
}
impl<'a> CreatePropertyRequest<'a> {
pub fn new(api_key: &'a str, server: &'a str) -> Self {
CreatePropertyRequest {
api_key,
format: PropertyFormat::Text,
key: "",
name: "",
server,
space_id: "",
}
}
pub fn format(mut self, format: PropertyFormat) -> Self {
self.format = format;
self
}
pub fn key(mut self, key: &'a str) -> Self {
self.key = key;
self
}
pub fn name(mut self, name: &'a str) -> Self {
self.name = name;
self
}
pub fn space_id(mut self, space_id: &'a str) -> Self {
self.space_id = space_id;
self
}
pub async fn send(&self) -> Result<Property, RequestFailure> {
let endpoint = format!("/v1/spaces/{}/properties", self.space_id);
let body = json!({
"format": self.format.to_string(),
"key": self.key,
"name": self.name,
})
.to_string();
match node::post(self.api_key, self.server, &endpoint, &body).await {
Ok(response) => {
if response.status() == http::StatusCode::CREATED {
return Ok(Property::from_response(response).await);
} else {
let possible_status: Vec<http::StatusCode> = Vec::from([
http::StatusCode::UNAUTHORIZED,
http::StatusCode::BAD_REQUEST,
http::StatusCode::TOO_MANY_REQUESTS,
http::StatusCode::INTERNAL_SERVER_ERROR,
]);
return Err(RequestFailure::api_error(response, possible_status).await);
}
}
Err(e) => Err(RequestFailure::reqwest_error(e)),
}
}
}
#[derive(Debug)]
pub struct UpdatePropertyRequest<'a> {
api_key: &'a str,
key: &'a str,
name: &'a str,
property_id: &'a str,
server: &'a str,
space_id: &'a str,
}
impl<'a> UpdatePropertyRequest<'a> {
pub fn new(api_key: &'a str, server: &'a str) -> Self {
UpdatePropertyRequest {
api_key,
key: "",
name: "",
property_id: "",
space_id: "",
server,
}
}
pub fn key(mut self, key: &'a str) -> Self {
self.key = key;
self
}
pub fn name(mut self, name: &'a str) -> Self {
self.name = name;
self
}
pub fn property_id(mut self, property_id: &'a str) -> Self {
self.property_id = property_id;
self
}
pub fn space_id(mut self, space_id: &'a str) -> Self {
self.space_id = space_id;
self
}
pub async fn send(&self) -> Result<Property, RequestFailure> {
let endpoint = format!(
"/v1/spaces/{}/properties/{}",
self.space_id, self.property_id
);
let body = json!({
"key": self.key,
"name": self.name,
})
.to_string();
match node::patch(self.api_key, self.server, &endpoint, &body).await {
Ok(response) => {
if response.status() == http::StatusCode::OK {
return Ok(Property::from_response(response).await);
} else {
let possible_status: Vec<http::StatusCode> = Vec::from([
http::StatusCode::UNAUTHORIZED,
http::StatusCode::BAD_REQUEST,
http::StatusCode::FORBIDDEN,
http::StatusCode::NOT_FOUND,
http::StatusCode::TOO_MANY_REQUESTS,
http::StatusCode::INTERNAL_SERVER_ERROR,
]);
return Err(RequestFailure::api_error(response, possible_status).await);
}
}
Err(e) => Err(RequestFailure::reqwest_error(e)),
}
}
}
#[derive(Debug)]
pub struct DeletePropertyRequest<'a> {
api_key: &'a str,
property_id: &'a str,
server: &'a str,
space_id: &'a str,
}
impl<'a> DeletePropertyRequest<'a> {
pub fn new(api_key: &'a str, server: &'a str) -> Self {
DeletePropertyRequest {
api_key,
property_id: "",
server,
space_id: "",
}
}
pub fn property_id(mut self, property_id: &'a str) -> Self {
self.property_id = property_id;
self
}
pub fn space_id(mut self, space_id: &'a str) -> Self {
self.space_id = space_id;
self
}
pub async fn send(&self) -> Result<Property, RequestFailure> {
let endpoint = format!(
"/v1/spaces/{}/properties/{}",
self.space_id, self.property_id
);
match node::delete(self.api_key, self.server, &endpoint).await {
Ok(response) => {
if response.status() == http::StatusCode::OK {
return Ok(Property::from_response(response).await);
} else {
let possible_status: Vec<http::StatusCode> = Vec::from([
http::StatusCode::UNAUTHORIZED,
http::StatusCode::FORBIDDEN,
http::StatusCode::NOT_FOUND,
http::StatusCode::GONE,
http::StatusCode::TOO_MANY_REQUESTS,
http::StatusCode::INTERNAL_SERVER_ERROR,
]);
return Err(RequestFailure::api_error(response, possible_status).await);
}
}
Err(e) => Err(RequestFailure::reqwest_error(e)),
}
}
}
#[derive(Debug)]
pub struct GetPropertyRequest<'a> {
api_key: &'a str,
property_id: &'a str,
server: &'a str,
space_id: &'a str,
}
impl<'a> GetPropertyRequest<'a> {
pub fn new(api_key: &'a str, server: &'a str) -> Self {
GetPropertyRequest {
api_key,
property_id: "",
server,
space_id: "",
}
}
pub fn property_id(mut self, property_id: &'a str) -> Self {
self.property_id = property_id;
self
}
pub fn space_id(mut self, space_id: &'a str) -> Self {
self.space_id = space_id;
self
}
pub async fn send(&self) -> Result<Property, RequestFailure> {
let endpoint = format!(
"/v1/spaces/{}/properties/{}",
self.space_id, self.property_id
);
match node::get(self.api_key, self.server, &endpoint).await {
Ok(response) => {
if response.status() == http::StatusCode::OK {
return Ok(Property::from_response(response).await);
} else {
let possible_status: Vec<http::StatusCode> = Vec::from([
http::StatusCode::UNAUTHORIZED,
http::StatusCode::INTERNAL_SERVER_ERROR,
]);
return Err(RequestFailure::api_error(response, possible_status).await);
}
}
Err(e) => Err(RequestFailure::reqwest_error(e)),
}
}
}
#[derive(Debug)]
pub struct ListPropertiesRequest<'a> {
api_key: &'a str,
offset: u32,
limit: u32,
server: &'a str,
space_id: &'a str,
}
impl<'a> ListPropertiesRequest<'a> {
pub fn new(api_key: &'a str, server: &'a str) -> Self {
ListPropertiesRequest {
api_key,
space_id: "",
offset: 0,
limit: 100,
server,
}
}
pub fn offset(mut self, offset: u32) -> Self {
self.offset = offset;
self
}
pub fn limit(mut self, limit: u32) -> Self {
self.limit = limit;
self
}
pub fn space_id(mut self, space_id: &'a str) -> Self {
self.space_id = space_id;
self
}
pub async fn send(&self) -> Result<ListOfProperties, RequestFailure> {
let endpoint = format!(
"/v1/spaces/{}/properties?offset={}&limit={}",
self.space_id, self.offset, self.limit
);
match node::get(self.api_key, self.server, &endpoint).await {
Ok(response) => {
if response.status() == http::StatusCode::OK {
return Ok(self.response_to_list(response).await);
} else {
let possible_status: Vec<http::StatusCode> = Vec::from([
http::StatusCode::UNAUTHORIZED,
http::StatusCode::INTERNAL_SERVER_ERROR,
]);
return Err(RequestFailure::api_error(response, possible_status).await);
}
}
Err(e) => Err(RequestFailure::reqwest_error(e)),
}
}
async fn response_to_list(&self, response: reqwest::Response) -> ListOfProperties {
let json_input = response.text().await.unwrap();
let json: serde_json::Value = serde_json::from_str(json_input.as_ref()).unwrap();
let received = json["data"].as_array().unwrap();
let mut data: Vec<Property> = Vec::new();
for p in 0..received.len() {
let property = Property::from_json(received[p].clone());
data.push(property);
}
let has_more: bool = json["pagination"]["has_more"].as_bool().unwrap();
let total = json["pagination"]["total"].as_u64().unwrap() as u32;
let offset = json["pagination"]["offset"].as_u64().unwrap() as u32;
let limit = json["pagination"]["limit"].as_u64().unwrap() as u32;
let next_offset = (offset as u32) + received.len() as u32;
ListOfProperties {
properties: data,
has_more,
next_offset,
offset,
limit,
total,
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
pub fn test_list_properties_request() {
let request = ListPropertiesRequest::new("secret_api_key", "server_url")
.offset(0)
.limit(42);
let internals = format!("{:#?}", request);
assert!(internals.contains("api_key: \"secret_api_key\""));
assert!(internals.contains("offset: 0"));
assert!(internals.contains("limit: 42"));
assert!(internals.contains("server: \"server_url\""));
println!("{}", internals);
}
}