style: use 2-space indentation across entire codebase
Add rustfmt.toml with tab_spaces = 2 and reformat all source files, tests, and README code examples to use 2-space indentation.
This commit is contained in:
+180
-180
@@ -10,221 +10,221 @@ use crate::error::{OnspringError, Result};
|
||||
|
||||
/// Client for interacting with the Onspring API v2.
|
||||
pub struct OnspringClient {
|
||||
http_client: reqwest::Client,
|
||||
base_url: String,
|
||||
api_key: String,
|
||||
http_client: reqwest::Client,
|
||||
base_url: String,
|
||||
api_key: String,
|
||||
}
|
||||
|
||||
/// Builder for constructing an [`OnspringClient`].
|
||||
pub struct OnspringClientBuilder {
|
||||
base_url: String,
|
||||
api_key: String,
|
||||
http_client: Option<reqwest::Client>,
|
||||
timeout: Option<Duration>,
|
||||
base_url: String,
|
||||
api_key: String,
|
||||
http_client: Option<reqwest::Client>,
|
||||
timeout: Option<Duration>,
|
||||
}
|
||||
|
||||
impl OnspringClientBuilder {
|
||||
/// Creates a new builder with the given API key.
|
||||
pub fn new(api_key: impl Into<String>) -> Self {
|
||||
Self {
|
||||
base_url: "https://api.onspring.com".to_string(),
|
||||
api_key: api_key.into(),
|
||||
http_client: None,
|
||||
timeout: None,
|
||||
}
|
||||
/// Creates a new builder with the given API key.
|
||||
pub fn new(api_key: impl Into<String>) -> Self {
|
||||
Self {
|
||||
base_url: "https://api.onspring.com".to_string(),
|
||||
api_key: api_key.into(),
|
||||
http_client: None,
|
||||
timeout: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the base URL for the API.
|
||||
pub fn base_url(mut self, url: impl Into<String>) -> Self {
|
||||
self.base_url = url.into();
|
||||
self
|
||||
}
|
||||
/// Sets the base URL for the API.
|
||||
pub fn base_url(mut self, url: impl Into<String>) -> Self {
|
||||
self.base_url = url.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets a custom `reqwest::Client` to use for HTTP requests.
|
||||
pub fn http_client(mut self, client: reqwest::Client) -> Self {
|
||||
self.http_client = Some(client);
|
||||
self
|
||||
}
|
||||
/// Sets a custom `reqwest::Client` to use for HTTP requests.
|
||||
pub fn http_client(mut self, client: reqwest::Client) -> Self {
|
||||
self.http_client = Some(client);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the request timeout.
|
||||
pub fn timeout(mut self, timeout: Duration) -> Self {
|
||||
self.timeout = Some(timeout);
|
||||
self
|
||||
}
|
||||
/// Sets the request timeout.
|
||||
pub fn timeout(mut self, timeout: Duration) -> Self {
|
||||
self.timeout = Some(timeout);
|
||||
self
|
||||
}
|
||||
|
||||
/// Builds the [`OnspringClient`].
|
||||
pub fn build(self) -> OnspringClient {
|
||||
let http_client = self.http_client.unwrap_or_else(|| {
|
||||
reqwest::Client::builder()
|
||||
.timeout(self.timeout.unwrap_or(Duration::from_secs(120)))
|
||||
.build()
|
||||
.expect("failed to build HTTP client")
|
||||
});
|
||||
/// Builds the [`OnspringClient`].
|
||||
pub fn build(self) -> OnspringClient {
|
||||
let http_client = self.http_client.unwrap_or_else(|| {
|
||||
reqwest::Client::builder()
|
||||
.timeout(self.timeout.unwrap_or(Duration::from_secs(120)))
|
||||
.build()
|
||||
.expect("failed to build HTTP client")
|
||||
});
|
||||
|
||||
OnspringClient {
|
||||
http_client,
|
||||
base_url: self.base_url.trim_end_matches('/').to_string(),
|
||||
api_key: self.api_key,
|
||||
}
|
||||
OnspringClient {
|
||||
http_client,
|
||||
base_url: self.base_url.trim_end_matches('/').to_string(),
|
||||
api_key: self.api_key,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl OnspringClient {
|
||||
/// Creates a new [`OnspringClientBuilder`].
|
||||
pub fn builder(api_key: impl Into<String>) -> OnspringClientBuilder {
|
||||
OnspringClientBuilder::new(api_key)
|
||||
/// Creates a new [`OnspringClientBuilder`].
|
||||
pub fn builder(api_key: impl Into<String>) -> OnspringClientBuilder {
|
||||
OnspringClientBuilder::new(api_key)
|
||||
}
|
||||
|
||||
fn default_headers(&self) -> HeaderMap {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(
|
||||
"x-apikey",
|
||||
HeaderValue::from_str(&self.api_key).expect("invalid API key"),
|
||||
);
|
||||
headers.insert("x-api-version", HeaderValue::from_static("2"));
|
||||
headers
|
||||
}
|
||||
|
||||
pub(crate) async fn request<T: DeserializeOwned>(
|
||||
&self,
|
||||
method: Method,
|
||||
path: &str,
|
||||
query: &[(&str, String)],
|
||||
body: Option<&impl Serialize>,
|
||||
) -> Result<T> {
|
||||
let url = format!("{}{}", self.base_url, path);
|
||||
let mut req = self
|
||||
.http_client
|
||||
.request(method, &url)
|
||||
.headers(self.default_headers())
|
||||
.query(query);
|
||||
|
||||
if let Some(body) = body {
|
||||
req = req.json(body);
|
||||
}
|
||||
|
||||
fn default_headers(&self) -> HeaderMap {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(
|
||||
"x-apikey",
|
||||
HeaderValue::from_str(&self.api_key).expect("invalid API key"),
|
||||
);
|
||||
headers.insert("x-api-version", HeaderValue::from_static("2"));
|
||||
headers
|
||||
let response = req.send().await?;
|
||||
let status = response.status();
|
||||
|
||||
if !status.is_success() {
|
||||
let message = response
|
||||
.json::<serde_json::Value>()
|
||||
.await
|
||||
.ok()
|
||||
.and_then(|v| v.get("message")?.as_str().map(String::from))
|
||||
.unwrap_or_default();
|
||||
return Err(OnspringError::Api {
|
||||
status_code: status.as_u16(),
|
||||
message,
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) async fn request<T: DeserializeOwned>(
|
||||
&self,
|
||||
method: Method,
|
||||
path: &str,
|
||||
query: &[(&str, String)],
|
||||
body: Option<&impl Serialize>,
|
||||
) -> Result<T> {
|
||||
let url = format!("{}{}", self.base_url, path);
|
||||
let mut req = self
|
||||
.http_client
|
||||
.request(method, &url)
|
||||
.headers(self.default_headers())
|
||||
.query(query);
|
||||
let body = response.text().await?;
|
||||
serde_json::from_str(&body).map_err(OnspringError::Serialization)
|
||||
}
|
||||
|
||||
if let Some(body) = body {
|
||||
req = req.json(body);
|
||||
}
|
||||
pub(crate) async fn request_no_content(
|
||||
&self,
|
||||
method: Method,
|
||||
path: &str,
|
||||
query: &[(&str, String)],
|
||||
body: Option<&impl Serialize>,
|
||||
) -> Result<()> {
|
||||
let url = format!("{}{}", self.base_url, path);
|
||||
let mut req = self
|
||||
.http_client
|
||||
.request(method, &url)
|
||||
.headers(self.default_headers())
|
||||
.query(query);
|
||||
|
||||
let response = req.send().await?;
|
||||
let status = response.status();
|
||||
|
||||
if !status.is_success() {
|
||||
let message = response
|
||||
.json::<serde_json::Value>()
|
||||
.await
|
||||
.ok()
|
||||
.and_then(|v| v.get("message")?.as_str().map(String::from))
|
||||
.unwrap_or_default();
|
||||
return Err(OnspringError::Api {
|
||||
status_code: status.as_u16(),
|
||||
message,
|
||||
});
|
||||
}
|
||||
|
||||
let body = response.text().await?;
|
||||
serde_json::from_str(&body).map_err(OnspringError::Serialization)
|
||||
if let Some(body) = body {
|
||||
req = req.json(body);
|
||||
}
|
||||
|
||||
pub(crate) async fn request_no_content(
|
||||
&self,
|
||||
method: Method,
|
||||
path: &str,
|
||||
query: &[(&str, String)],
|
||||
body: Option<&impl Serialize>,
|
||||
) -> Result<()> {
|
||||
let url = format!("{}{}", self.base_url, path);
|
||||
let mut req = self
|
||||
.http_client
|
||||
.request(method, &url)
|
||||
.headers(self.default_headers())
|
||||
.query(query);
|
||||
let response = req.send().await?;
|
||||
let status = response.status();
|
||||
|
||||
if let Some(body) = body {
|
||||
req = req.json(body);
|
||||
}
|
||||
|
||||
let response = req.send().await?;
|
||||
let status = response.status();
|
||||
|
||||
if !status.is_success() {
|
||||
let message = response
|
||||
.json::<serde_json::Value>()
|
||||
.await
|
||||
.ok()
|
||||
.and_then(|v| v.get("message")?.as_str().map(String::from))
|
||||
.unwrap_or_default();
|
||||
return Err(OnspringError::Api {
|
||||
status_code: status.as_u16(),
|
||||
message,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
if !status.is_success() {
|
||||
let message = response
|
||||
.json::<serde_json::Value>()
|
||||
.await
|
||||
.ok()
|
||||
.and_then(|v| v.get("message")?.as_str().map(String::from))
|
||||
.unwrap_or_default();
|
||||
return Err(OnspringError::Api {
|
||||
status_code: status.as_u16(),
|
||||
message,
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) async fn request_bytes(
|
||||
&self,
|
||||
method: Method,
|
||||
path: &str,
|
||||
query: &[(&str, String)],
|
||||
) -> Result<(StatusCode, HeaderMap, Bytes)> {
|
||||
let url = format!("{}{}", self.base_url, path);
|
||||
let response = self
|
||||
.http_client
|
||||
.request(method, &url)
|
||||
.headers(self.default_headers())
|
||||
.query(query)
|
||||
.send()
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
let status = response.status();
|
||||
pub(crate) async fn request_bytes(
|
||||
&self,
|
||||
method: Method,
|
||||
path: &str,
|
||||
query: &[(&str, String)],
|
||||
) -> Result<(StatusCode, HeaderMap, Bytes)> {
|
||||
let url = format!("{}{}", self.base_url, path);
|
||||
let response = self
|
||||
.http_client
|
||||
.request(method, &url)
|
||||
.headers(self.default_headers())
|
||||
.query(query)
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
if !status.is_success() {
|
||||
let message = response
|
||||
.json::<serde_json::Value>()
|
||||
.await
|
||||
.ok()
|
||||
.and_then(|v| v.get("message")?.as_str().map(String::from))
|
||||
.unwrap_or_default();
|
||||
return Err(OnspringError::Api {
|
||||
status_code: status.as_u16(),
|
||||
message,
|
||||
});
|
||||
}
|
||||
let status = response.status();
|
||||
|
||||
let headers = response.headers().clone();
|
||||
let bytes = response.bytes().await?;
|
||||
Ok((status, headers, bytes))
|
||||
if !status.is_success() {
|
||||
let message = response
|
||||
.json::<serde_json::Value>()
|
||||
.await
|
||||
.ok()
|
||||
.and_then(|v| v.get("message")?.as_str().map(String::from))
|
||||
.unwrap_or_default();
|
||||
return Err(OnspringError::Api {
|
||||
status_code: status.as_u16(),
|
||||
message,
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) async fn request_multipart<T: DeserializeOwned>(
|
||||
&self,
|
||||
path: &str,
|
||||
form: reqwest::multipart::Form,
|
||||
) -> Result<T> {
|
||||
let url = format!("{}{}", self.base_url, path);
|
||||
let response = self
|
||||
.http_client
|
||||
.post(&url)
|
||||
.headers(self.default_headers())
|
||||
.multipart(form)
|
||||
.send()
|
||||
.await?;
|
||||
let headers = response.headers().clone();
|
||||
let bytes = response.bytes().await?;
|
||||
Ok((status, headers, bytes))
|
||||
}
|
||||
|
||||
let status = response.status();
|
||||
pub(crate) async fn request_multipart<T: DeserializeOwned>(
|
||||
&self,
|
||||
path: &str,
|
||||
form: reqwest::multipart::Form,
|
||||
) -> Result<T> {
|
||||
let url = format!("{}{}", self.base_url, path);
|
||||
let response = self
|
||||
.http_client
|
||||
.post(&url)
|
||||
.headers(self.default_headers())
|
||||
.multipart(form)
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
if !status.is_success() {
|
||||
let message = response
|
||||
.json::<serde_json::Value>()
|
||||
.await
|
||||
.ok()
|
||||
.and_then(|v| v.get("message")?.as_str().map(String::from))
|
||||
.unwrap_or_default();
|
||||
return Err(OnspringError::Api {
|
||||
status_code: status.as_u16(),
|
||||
message,
|
||||
});
|
||||
}
|
||||
let status = response.status();
|
||||
|
||||
let body = response.text().await?;
|
||||
serde_json::from_str(&body).map_err(OnspringError::Serialization)
|
||||
if !status.is_success() {
|
||||
let message = response
|
||||
.json::<serde_json::Value>()
|
||||
.await
|
||||
.ok()
|
||||
.and_then(|v| v.get("message")?.as_str().map(String::from))
|
||||
.unwrap_or_default();
|
||||
return Err(OnspringError::Api {
|
||||
status_code: status.as_u16(),
|
||||
message,
|
||||
});
|
||||
}
|
||||
|
||||
let body = response.text().await?;
|
||||
serde_json::from_str(&body).map_err(OnspringError::Serialization)
|
||||
}
|
||||
}
|
||||
|
||||
+24
-21
@@ -5,28 +5,31 @@ use crate::error::Result;
|
||||
use crate::models::{App, CollectionResponse, PagedResponse, PagingRequest};
|
||||
|
||||
impl OnspringClient {
|
||||
/// Gets all apps for the current client, with optional pagination.
|
||||
pub async fn list_apps(&self, paging: Option<PagingRequest>) -> Result<PagedResponse<App>> {
|
||||
let mut query = Vec::new();
|
||||
if let Some(p) = paging {
|
||||
query.push(("PageNumber", p.page_number.to_string()));
|
||||
query.push(("PageSize", p.page_size.to_string()));
|
||||
}
|
||||
let query_refs: Vec<(&str, String)> = query.iter().map(|(k, v)| (*k, v.clone())).collect();
|
||||
self.request(Method::GET, "/Apps", &query_refs, Option::<&()>::None)
|
||||
.await
|
||||
/// Gets all apps for the current client, with optional pagination.
|
||||
pub async fn list_apps(&self, paging: Option<PagingRequest>) -> Result<PagedResponse<App>> {
|
||||
let mut query = Vec::new();
|
||||
if let Some(p) = paging {
|
||||
query.push(("PageNumber", p.page_number.to_string()));
|
||||
query.push(("PageSize", p.page_size.to_string()));
|
||||
}
|
||||
let query_refs: Vec<(&str, String)> = query.iter().map(|(k, v)| (*k, v.clone())).collect();
|
||||
self
|
||||
.request(Method::GET, "/Apps", &query_refs, Option::<&()>::None)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Gets an app by its identifier.
|
||||
pub async fn get_app(&self, app_id: i32) -> Result<App> {
|
||||
let path = format!("/Apps/id/{}", app_id);
|
||||
self.request(Method::GET, &path, &[], Option::<&()>::None)
|
||||
.await
|
||||
}
|
||||
/// Gets an app by its identifier.
|
||||
pub async fn get_app(&self, app_id: i32) -> Result<App> {
|
||||
let path = format!("/Apps/id/{}", app_id);
|
||||
self
|
||||
.request(Method::GET, &path, &[], Option::<&()>::None)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Gets up to 100 apps by their identifiers.
|
||||
pub async fn batch_get_apps(&self, ids: &[i32]) -> Result<CollectionResponse<App>> {
|
||||
self.request(Method::POST, "/Apps/batch-get", &[], Some(&ids))
|
||||
.await
|
||||
}
|
||||
/// Gets up to 100 apps by their identifiers.
|
||||
pub async fn batch_get_apps(&self, ids: &[i32]) -> Result<CollectionResponse<App>> {
|
||||
self
|
||||
.request(Method::POST, "/Apps/batch-get", &[], Some(&ids))
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
+29
-26
@@ -5,33 +5,36 @@ use crate::error::Result;
|
||||
use crate::models::{CollectionResponse, Field, PagedResponse, PagingRequest};
|
||||
|
||||
impl OnspringClient {
|
||||
/// Gets a field by its identifier.
|
||||
pub async fn get_field(&self, field_id: i32) -> Result<Field> {
|
||||
let path = format!("/Fields/id/{}", field_id);
|
||||
self.request(Method::GET, &path, &[], Option::<&()>::None)
|
||||
.await
|
||||
}
|
||||
/// Gets a field by its identifier.
|
||||
pub async fn get_field(&self, field_id: i32) -> Result<Field> {
|
||||
let path = format!("/Fields/id/{}", field_id);
|
||||
self
|
||||
.request(Method::GET, &path, &[], Option::<&()>::None)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Gets up to 100 fields by their identifiers.
|
||||
pub async fn batch_get_fields(&self, ids: &[i32]) -> Result<CollectionResponse<Field>> {
|
||||
self.request(Method::POST, "/Fields/batch-get", &[], Some(&ids))
|
||||
.await
|
||||
}
|
||||
/// Gets up to 100 fields by their identifiers.
|
||||
pub async fn batch_get_fields(&self, ids: &[i32]) -> Result<CollectionResponse<Field>> {
|
||||
self
|
||||
.request(Method::POST, "/Fields/batch-get", &[], Some(&ids))
|
||||
.await
|
||||
}
|
||||
|
||||
/// Gets a paginated list of fields for a given application.
|
||||
pub async fn list_fields(
|
||||
&self,
|
||||
app_id: i32,
|
||||
paging: Option<PagingRequest>,
|
||||
) -> Result<PagedResponse<Field>> {
|
||||
let path = format!("/Fields/appId/{}", app_id);
|
||||
let mut query = Vec::new();
|
||||
if let Some(p) = paging {
|
||||
query.push(("PageNumber", p.page_number.to_string()));
|
||||
query.push(("PageSize", p.page_size.to_string()));
|
||||
}
|
||||
let query_refs: Vec<(&str, String)> = query.iter().map(|(k, v)| (*k, v.clone())).collect();
|
||||
self.request(Method::GET, &path, &query_refs, Option::<&()>::None)
|
||||
.await
|
||||
/// Gets a paginated list of fields for a given application.
|
||||
pub async fn list_fields(
|
||||
&self,
|
||||
app_id: i32,
|
||||
paging: Option<PagingRequest>,
|
||||
) -> Result<PagedResponse<Field>> {
|
||||
let path = format!("/Fields/appId/{}", app_id);
|
||||
let mut query = Vec::new();
|
||||
if let Some(p) = paging {
|
||||
query.push(("PageNumber", p.page_number.to_string()));
|
||||
query.push(("PageSize", p.page_size.to_string()));
|
||||
}
|
||||
let query_refs: Vec<(&str, String)> = query.iter().map(|(k, v)| (*k, v.clone())).collect();
|
||||
self
|
||||
.request(Method::GET, &path, &query_refs, Option::<&()>::None)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
+79
-77
@@ -5,84 +5,86 @@ use crate::error::Result;
|
||||
use crate::models::{CreatedWithIdResponse, FileInfo, FileResponse, SaveFileRequest};
|
||||
|
||||
impl OnspringClient {
|
||||
/// Gets a file's metadata information.
|
||||
pub async fn get_file_info(
|
||||
&self,
|
||||
record_id: i32,
|
||||
field_id: i32,
|
||||
file_id: i32,
|
||||
) -> Result<FileInfo> {
|
||||
let path = format!(
|
||||
"/Files/recordId/{}/fieldId/{}/fileId/{}",
|
||||
record_id, field_id, file_id
|
||||
);
|
||||
self.request(Method::GET, &path, &[], Option::<&()>::None)
|
||||
.await
|
||||
/// Gets a file's metadata information.
|
||||
pub async fn get_file_info(
|
||||
&self,
|
||||
record_id: i32,
|
||||
field_id: i32,
|
||||
file_id: i32,
|
||||
) -> Result<FileInfo> {
|
||||
let path = format!(
|
||||
"/Files/recordId/{}/fieldId/{}/fileId/{}",
|
||||
record_id, field_id, file_id
|
||||
);
|
||||
self
|
||||
.request(Method::GET, &path, &[], Option::<&()>::None)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Downloads a file's content.
|
||||
pub async fn get_file(
|
||||
&self,
|
||||
record_id: i32,
|
||||
field_id: i32,
|
||||
file_id: i32,
|
||||
) -> Result<FileResponse> {
|
||||
let path = format!(
|
||||
"/Files/recordId/{}/fieldId/{}/fileId/{}/file",
|
||||
record_id, field_id, file_id
|
||||
);
|
||||
let (_status, headers, data) = self.request_bytes(Method::GET, &path, &[]).await?;
|
||||
|
||||
let content_type = headers
|
||||
.get("content-type")
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.map(String::from);
|
||||
|
||||
let file_name = headers
|
||||
.get("content-disposition")
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.and_then(|v| {
|
||||
v.split("filename=")
|
||||
.nth(1)
|
||||
.map(|s| s.trim_matches('"').to_string())
|
||||
});
|
||||
|
||||
Ok(FileResponse {
|
||||
content_type,
|
||||
file_name,
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
/// Uploads a file attachment.
|
||||
pub async fn upload_file(&self, request: SaveFileRequest) -> Result<CreatedWithIdResponse> {
|
||||
let file_part = reqwest::multipart::Part::bytes(request.file_data)
|
||||
.file_name(request.file_name)
|
||||
.mime_str(&request.content_type)
|
||||
.map_err(|e| crate::error::OnspringError::InvalidArgument(e.to_string()))?;
|
||||
|
||||
let mut form = reqwest::multipart::Form::new()
|
||||
.text("RecordId", request.record_id.to_string())
|
||||
.text("FieldId", request.field_id.to_string())
|
||||
.part("File", file_part);
|
||||
|
||||
if let Some(notes) = request.notes {
|
||||
form = form.text("Notes", notes);
|
||||
}
|
||||
if let Some(date) = request.modified_date {
|
||||
form = form.text("ModifiedDate", date.to_rfc3339());
|
||||
}
|
||||
|
||||
/// Downloads a file's content.
|
||||
pub async fn get_file(
|
||||
&self,
|
||||
record_id: i32,
|
||||
field_id: i32,
|
||||
file_id: i32,
|
||||
) -> Result<FileResponse> {
|
||||
let path = format!(
|
||||
"/Files/recordId/{}/fieldId/{}/fileId/{}/file",
|
||||
record_id, field_id, file_id
|
||||
);
|
||||
let (_status, headers, data) = self.request_bytes(Method::GET, &path, &[]).await?;
|
||||
self.request_multipart("/Files", form).await
|
||||
}
|
||||
|
||||
let content_type = headers
|
||||
.get("content-type")
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.map(String::from);
|
||||
|
||||
let file_name = headers
|
||||
.get("content-disposition")
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.and_then(|v| {
|
||||
v.split("filename=")
|
||||
.nth(1)
|
||||
.map(|s| s.trim_matches('"').to_string())
|
||||
});
|
||||
|
||||
Ok(FileResponse {
|
||||
content_type,
|
||||
file_name,
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
/// Uploads a file attachment.
|
||||
pub async fn upload_file(&self, request: SaveFileRequest) -> Result<CreatedWithIdResponse> {
|
||||
let file_part = reqwest::multipart::Part::bytes(request.file_data)
|
||||
.file_name(request.file_name)
|
||||
.mime_str(&request.content_type)
|
||||
.map_err(|e| crate::error::OnspringError::InvalidArgument(e.to_string()))?;
|
||||
|
||||
let mut form = reqwest::multipart::Form::new()
|
||||
.text("RecordId", request.record_id.to_string())
|
||||
.text("FieldId", request.field_id.to_string())
|
||||
.part("File", file_part);
|
||||
|
||||
if let Some(notes) = request.notes {
|
||||
form = form.text("Notes", notes);
|
||||
}
|
||||
if let Some(date) = request.modified_date {
|
||||
form = form.text("ModifiedDate", date.to_rfc3339());
|
||||
}
|
||||
|
||||
self.request_multipart("/Files", form).await
|
||||
}
|
||||
|
||||
/// Deletes a file attachment.
|
||||
pub async fn delete_file(&self, record_id: i32, field_id: i32, file_id: i32) -> Result<()> {
|
||||
let path = format!(
|
||||
"/Files/recordId/{}/fieldId/{}/fileId/{}",
|
||||
record_id, field_id, file_id
|
||||
);
|
||||
self.request_no_content(Method::DELETE, &path, &[], Option::<&()>::None)
|
||||
.await
|
||||
}
|
||||
/// Deletes a file attachment.
|
||||
pub async fn delete_file(&self, record_id: i32, field_id: i32, file_id: i32) -> Result<()> {
|
||||
let path = format!(
|
||||
"/Files/recordId/{}/fieldId/{}/fileId/{}",
|
||||
record_id, field_id, file_id
|
||||
);
|
||||
self
|
||||
.request_no_content(Method::DELETE, &path, &[], Option::<&()>::None)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
+16
-15
@@ -6,20 +6,21 @@ use crate::error::Result;
|
||||
use crate::models::{SaveListItemRequest, SaveListItemResponse};
|
||||
|
||||
impl OnspringClient {
|
||||
/// Creates or updates a list item in the specified list.
|
||||
pub async fn save_list_item(
|
||||
&self,
|
||||
list_id: i32,
|
||||
request: SaveListItemRequest,
|
||||
) -> Result<SaveListItemResponse> {
|
||||
let path = format!("/Lists/id/{}/items", list_id);
|
||||
self.request(Method::PUT, &path, &[], Some(&request)).await
|
||||
}
|
||||
/// Creates or updates a list item in the specified list.
|
||||
pub async fn save_list_item(
|
||||
&self,
|
||||
list_id: i32,
|
||||
request: SaveListItemRequest,
|
||||
) -> Result<SaveListItemResponse> {
|
||||
let path = format!("/Lists/id/{}/items", list_id);
|
||||
self.request(Method::PUT, &path, &[], Some(&request)).await
|
||||
}
|
||||
|
||||
/// Deletes a list item from the specified list.
|
||||
pub async fn delete_list_item(&self, list_id: i32, item_id: Uuid) -> Result<()> {
|
||||
let path = format!("/Lists/id/{}/itemId/{}", list_id, item_id);
|
||||
self.request_no_content(Method::DELETE, &path, &[], Option::<&()>::None)
|
||||
.await
|
||||
}
|
||||
/// Deletes a list item from the specified list.
|
||||
pub async fn delete_list_item(&self, list_id: i32, item_id: Uuid) -> Result<()> {
|
||||
let path = format!("/Lists/id/{}/itemId/{}", list_id, item_id);
|
||||
self
|
||||
.request_no_content(Method::DELETE, &path, &[], Option::<&()>::None)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,10 @@ use crate::client::OnspringClient;
|
||||
use crate::error::Result;
|
||||
|
||||
impl OnspringClient {
|
||||
/// Checks if the Onspring API is reachable.
|
||||
pub async fn ping(&self) -> Result<()> {
|
||||
self.request_no_content(Method::GET, "/Ping", &[], Option::<&()>::None)
|
||||
.await
|
||||
}
|
||||
/// Checks if the Onspring API is reachable.
|
||||
pub async fn ping(&self) -> Result<()> {
|
||||
self
|
||||
.request_no_content(Method::GET, "/Ping", &[], Option::<&()>::None)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
+99
-93
@@ -3,109 +3,115 @@ use reqwest::Method;
|
||||
use crate::client::OnspringClient;
|
||||
use crate::error::Result;
|
||||
use crate::models::{
|
||||
BatchDeleteRecordsRequest, BatchGetRecordsRequest, CollectionResponse, DataFormat,
|
||||
PagedResponse, PagingRequest, QueryRecordsRequest, Record, SaveRecordRequest,
|
||||
SaveRecordResponse,
|
||||
BatchDeleteRecordsRequest, BatchGetRecordsRequest, CollectionResponse, DataFormat, PagedResponse,
|
||||
PagingRequest, QueryRecordsRequest, Record, SaveRecordRequest, SaveRecordResponse,
|
||||
};
|
||||
|
||||
impl OnspringClient {
|
||||
/// Gets a paginated collection of records for a given app.
|
||||
pub async fn list_records(
|
||||
&self,
|
||||
app_id: i32,
|
||||
paging: Option<PagingRequest>,
|
||||
field_ids: Option<&[i32]>,
|
||||
data_format: Option<DataFormat>,
|
||||
) -> Result<PagedResponse<Record>> {
|
||||
let path = format!("/Records/appId/{}", app_id);
|
||||
let mut query = Vec::new();
|
||||
if let Some(p) = paging {
|
||||
query.push(("PageNumber", p.page_number.to_string()));
|
||||
query.push(("PageSize", p.page_size.to_string()));
|
||||
}
|
||||
if let Some(ids) = field_ids {
|
||||
let ids_str = ids
|
||||
.iter()
|
||||
.map(|id| id.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(",");
|
||||
query.push(("fieldIds", ids_str));
|
||||
}
|
||||
if let Some(fmt) = data_format {
|
||||
query.push(("dataFormat", format!("{:?}", fmt)));
|
||||
}
|
||||
let query_refs: Vec<(&str, String)> = query.iter().map(|(k, v)| (*k, v.clone())).collect();
|
||||
self.request(Method::GET, &path, &query_refs, Option::<&()>::None)
|
||||
.await
|
||||
/// Gets a paginated collection of records for a given app.
|
||||
pub async fn list_records(
|
||||
&self,
|
||||
app_id: i32,
|
||||
paging: Option<PagingRequest>,
|
||||
field_ids: Option<&[i32]>,
|
||||
data_format: Option<DataFormat>,
|
||||
) -> Result<PagedResponse<Record>> {
|
||||
let path = format!("/Records/appId/{}", app_id);
|
||||
let mut query = Vec::new();
|
||||
if let Some(p) = paging {
|
||||
query.push(("PageNumber", p.page_number.to_string()));
|
||||
query.push(("PageSize", p.page_size.to_string()));
|
||||
}
|
||||
if let Some(ids) = field_ids {
|
||||
let ids_str = ids
|
||||
.iter()
|
||||
.map(|id| id.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(",");
|
||||
query.push(("fieldIds", ids_str));
|
||||
}
|
||||
if let Some(fmt) = data_format {
|
||||
query.push(("dataFormat", format!("{:?}", fmt)));
|
||||
}
|
||||
let query_refs: Vec<(&str, String)> = query.iter().map(|(k, v)| (*k, v.clone())).collect();
|
||||
self
|
||||
.request(Method::GET, &path, &query_refs, Option::<&()>::None)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Gets a record by its identifier.
|
||||
pub async fn get_record(
|
||||
&self,
|
||||
app_id: i32,
|
||||
record_id: i32,
|
||||
field_ids: Option<&[i32]>,
|
||||
data_format: Option<DataFormat>,
|
||||
) -> Result<Record> {
|
||||
let path = format!("/Records/appId/{}/recordId/{}", app_id, record_id);
|
||||
let mut query = Vec::new();
|
||||
if let Some(ids) = field_ids {
|
||||
let ids_str = ids
|
||||
.iter()
|
||||
.map(|id| id.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(",");
|
||||
query.push(("fieldIds", ids_str));
|
||||
}
|
||||
if let Some(fmt) = data_format {
|
||||
query.push(("dataFormat", format!("{:?}", fmt)));
|
||||
}
|
||||
let query_refs: Vec<(&str, String)> = query.iter().map(|(k, v)| (*k, v.clone())).collect();
|
||||
self.request(Method::GET, &path, &query_refs, Option::<&()>::None)
|
||||
.await
|
||||
/// Gets a record by its identifier.
|
||||
pub async fn get_record(
|
||||
&self,
|
||||
app_id: i32,
|
||||
record_id: i32,
|
||||
field_ids: Option<&[i32]>,
|
||||
data_format: Option<DataFormat>,
|
||||
) -> Result<Record> {
|
||||
let path = format!("/Records/appId/{}/recordId/{}", app_id, record_id);
|
||||
let mut query = Vec::new();
|
||||
if let Some(ids) = field_ids {
|
||||
let ids_str = ids
|
||||
.iter()
|
||||
.map(|id| id.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(",");
|
||||
query.push(("fieldIds", ids_str));
|
||||
}
|
||||
if let Some(fmt) = data_format {
|
||||
query.push(("dataFormat", format!("{:?}", fmt)));
|
||||
}
|
||||
let query_refs: Vec<(&str, String)> = query.iter().map(|(k, v)| (*k, v.clone())).collect();
|
||||
self
|
||||
.request(Method::GET, &path, &query_refs, Option::<&()>::None)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Creates or updates a record.
|
||||
pub async fn save_record(&self, request: SaveRecordRequest) -> Result<SaveRecordResponse> {
|
||||
self.request(Method::PUT, "/Records", &[], Some(&request))
|
||||
.await
|
||||
}
|
||||
/// Creates or updates a record.
|
||||
pub async fn save_record(&self, request: SaveRecordRequest) -> Result<SaveRecordResponse> {
|
||||
self
|
||||
.request(Method::PUT, "/Records", &[], Some(&request))
|
||||
.await
|
||||
}
|
||||
|
||||
/// Deletes a record by its identifier.
|
||||
pub async fn delete_record(&self, app_id: i32, record_id: i32) -> Result<()> {
|
||||
let path = format!("/Records/appId/{}/recordId/{}", app_id, record_id);
|
||||
self.request_no_content(Method::DELETE, &path, &[], Option::<&()>::None)
|
||||
.await
|
||||
}
|
||||
/// Deletes a record by its identifier.
|
||||
pub async fn delete_record(&self, app_id: i32, record_id: i32) -> Result<()> {
|
||||
let path = format!("/Records/appId/{}/recordId/{}", app_id, record_id);
|
||||
self
|
||||
.request_no_content(Method::DELETE, &path, &[], Option::<&()>::None)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Gets a batch of records.
|
||||
pub async fn batch_get_records(
|
||||
&self,
|
||||
request: BatchGetRecordsRequest,
|
||||
) -> Result<CollectionResponse<Record>> {
|
||||
self.request(Method::POST, "/Records/batch-get", &[], Some(&request))
|
||||
.await
|
||||
}
|
||||
/// Gets a batch of records.
|
||||
pub async fn batch_get_records(
|
||||
&self,
|
||||
request: BatchGetRecordsRequest,
|
||||
) -> Result<CollectionResponse<Record>> {
|
||||
self
|
||||
.request(Method::POST, "/Records/batch-get", &[], Some(&request))
|
||||
.await
|
||||
}
|
||||
|
||||
/// Queries records using a filter expression.
|
||||
pub async fn query_records(
|
||||
&self,
|
||||
request: QueryRecordsRequest,
|
||||
paging: Option<PagingRequest>,
|
||||
) -> Result<PagedResponse<Record>> {
|
||||
let mut query = Vec::new();
|
||||
if let Some(p) = paging {
|
||||
query.push(("PageNumber", p.page_number.to_string()));
|
||||
query.push(("PageSize", p.page_size.to_string()));
|
||||
}
|
||||
let query_refs: Vec<(&str, String)> = query.iter().map(|(k, v)| (*k, v.clone())).collect();
|
||||
self.request(Method::POST, "/Records/Query", &query_refs, Some(&request))
|
||||
.await
|
||||
/// Queries records using a filter expression.
|
||||
pub async fn query_records(
|
||||
&self,
|
||||
request: QueryRecordsRequest,
|
||||
paging: Option<PagingRequest>,
|
||||
) -> Result<PagedResponse<Record>> {
|
||||
let mut query = Vec::new();
|
||||
if let Some(p) = paging {
|
||||
query.push(("PageNumber", p.page_number.to_string()));
|
||||
query.push(("PageSize", p.page_size.to_string()));
|
||||
}
|
||||
let query_refs: Vec<(&str, String)> = query.iter().map(|(k, v)| (*k, v.clone())).collect();
|
||||
self
|
||||
.request(Method::POST, "/Records/Query", &query_refs, Some(&request))
|
||||
.await
|
||||
}
|
||||
|
||||
/// Deletes a batch of records.
|
||||
pub async fn batch_delete_records(&self, request: BatchDeleteRecordsRequest) -> Result<()> {
|
||||
self.request_no_content(Method::POST, "/Records/batch-delete", &[], Some(&request))
|
||||
.await
|
||||
}
|
||||
/// Deletes a batch of records.
|
||||
pub async fn batch_delete_records(&self, request: BatchDeleteRecordsRequest) -> Result<()> {
|
||||
self
|
||||
.request_no_content(Method::POST, "/Records/batch-delete", &[], Some(&request))
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
+36
-34
@@ -3,44 +3,46 @@ use reqwest::Method;
|
||||
use crate::client::OnspringClient;
|
||||
use crate::error::Result;
|
||||
use crate::models::{
|
||||
DataFormat, PagedResponse, PagingRequest, ReportData, ReportDataType, ReportInfo,
|
||||
DataFormat, PagedResponse, PagingRequest, ReportData, ReportDataType, ReportInfo,
|
||||
};
|
||||
|
||||
impl OnspringClient {
|
||||
/// Gets report data by report ID.
|
||||
pub async fn get_report(
|
||||
&self,
|
||||
report_id: i32,
|
||||
data_format: Option<DataFormat>,
|
||||
data_type: Option<ReportDataType>,
|
||||
) -> Result<ReportData> {
|
||||
let path = format!("/Reports/id/{}", report_id);
|
||||
let mut query = Vec::new();
|
||||
if let Some(fmt) = data_format {
|
||||
query.push(("apiDataFormat", format!("{:?}", fmt)));
|
||||
}
|
||||
if let Some(dt) = data_type {
|
||||
query.push(("dataType", format!("{:?}", dt)));
|
||||
}
|
||||
let query_refs: Vec<(&str, String)> = query.iter().map(|(k, v)| (*k, v.clone())).collect();
|
||||
self.request(Method::GET, &path, &query_refs, Option::<&()>::None)
|
||||
.await
|
||||
/// Gets report data by report ID.
|
||||
pub async fn get_report(
|
||||
&self,
|
||||
report_id: i32,
|
||||
data_format: Option<DataFormat>,
|
||||
data_type: Option<ReportDataType>,
|
||||
) -> Result<ReportData> {
|
||||
let path = format!("/Reports/id/{}", report_id);
|
||||
let mut query = Vec::new();
|
||||
if let Some(fmt) = data_format {
|
||||
query.push(("apiDataFormat", format!("{:?}", fmt)));
|
||||
}
|
||||
if let Some(dt) = data_type {
|
||||
query.push(("dataType", format!("{:?}", dt)));
|
||||
}
|
||||
let query_refs: Vec<(&str, String)> = query.iter().map(|(k, v)| (*k, v.clone())).collect();
|
||||
self
|
||||
.request(Method::GET, &path, &query_refs, Option::<&()>::None)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Gets a paginated list of reports for a given application.
|
||||
pub async fn list_reports(
|
||||
&self,
|
||||
app_id: i32,
|
||||
paging: Option<PagingRequest>,
|
||||
) -> Result<PagedResponse<ReportInfo>> {
|
||||
let path = format!("/Reports/appId/{}", app_id);
|
||||
let mut query = Vec::new();
|
||||
if let Some(p) = paging {
|
||||
query.push(("PageNumber", p.page_number.to_string()));
|
||||
query.push(("PageSize", p.page_size.to_string()));
|
||||
}
|
||||
let query_refs: Vec<(&str, String)> = query.iter().map(|(k, v)| (*k, v.clone())).collect();
|
||||
self.request(Method::GET, &path, &query_refs, Option::<&()>::None)
|
||||
.await
|
||||
/// Gets a paginated list of reports for a given application.
|
||||
pub async fn list_reports(
|
||||
&self,
|
||||
app_id: i32,
|
||||
paging: Option<PagingRequest>,
|
||||
) -> Result<PagedResponse<ReportInfo>> {
|
||||
let path = format!("/Reports/appId/{}", app_id);
|
||||
let mut query = Vec::new();
|
||||
if let Some(p) = paging {
|
||||
query.push(("PageNumber", p.page_number.to_string()));
|
||||
query.push(("PageSize", p.page_size.to_string()));
|
||||
}
|
||||
let query_refs: Vec<(&str, String)> = query.iter().map(|(k, v)| (*k, v.clone())).collect();
|
||||
self
|
||||
.request(Method::GET, &path, &query_refs, Option::<&()>::None)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
+12
-12
@@ -1,21 +1,21 @@
|
||||
/// Errors that can occur when using the Onspring API client.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum OnspringError {
|
||||
/// An HTTP transport error occurred.
|
||||
#[error("HTTP request failed: {0}")]
|
||||
Http(#[from] reqwest::Error),
|
||||
/// An HTTP transport error occurred.
|
||||
#[error("HTTP request failed: {0}")]
|
||||
Http(#[from] reqwest::Error),
|
||||
|
||||
/// The API returned a non-success status code.
|
||||
#[error("API error (status {status_code}): {message}")]
|
||||
Api { status_code: u16, message: String },
|
||||
/// The API returned a non-success status code.
|
||||
#[error("API error (status {status_code}): {message}")]
|
||||
Api { status_code: u16, message: String },
|
||||
|
||||
/// A serialization or deserialization error occurred.
|
||||
#[error("Serialization error: {0}")]
|
||||
Serialization(#[from] serde_json::Error),
|
||||
/// A serialization or deserialization error occurred.
|
||||
#[error("Serialization error: {0}")]
|
||||
Serialization(#[from] serde_json::Error),
|
||||
|
||||
/// An invalid argument was provided to an SDK method.
|
||||
#[error("Invalid argument: {0}")]
|
||||
InvalidArgument(String),
|
||||
/// An invalid argument was provided to an SDK method.
|
||||
#[error("Invalid argument: {0}")]
|
||||
InvalidArgument(String),
|
||||
}
|
||||
|
||||
/// A type alias for `Result<T, OnspringError>`.
|
||||
|
||||
+3
-3
@@ -4,7 +4,7 @@ use serde::Deserialize;
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct App {
|
||||
pub href: Option<String>,
|
||||
pub id: i32,
|
||||
pub name: Option<String>,
|
||||
pub href: Option<String>,
|
||||
pub id: i32,
|
||||
pub name: Option<String>,
|
||||
}
|
||||
|
||||
+22
-22
@@ -3,46 +3,46 @@ use serde::{Deserialize, Serialize};
|
||||
/// The format of data returned by the API.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum DataFormat {
|
||||
Raw,
|
||||
Formatted,
|
||||
Raw,
|
||||
Formatted,
|
||||
}
|
||||
|
||||
/// The type of report data to retrieve.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum ReportDataType {
|
||||
ReportData,
|
||||
ChartData,
|
||||
ReportData,
|
||||
ChartData,
|
||||
}
|
||||
|
||||
/// The type of a field's value in a record.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum ValueType {
|
||||
String,
|
||||
Integer,
|
||||
Decimal,
|
||||
Date,
|
||||
TimeSpan,
|
||||
Guid,
|
||||
StringList,
|
||||
IntegerList,
|
||||
GuidList,
|
||||
AttachmentList,
|
||||
ScoringGroupList,
|
||||
FileList,
|
||||
String,
|
||||
Integer,
|
||||
Decimal,
|
||||
Date,
|
||||
TimeSpan,
|
||||
Guid,
|
||||
StringList,
|
||||
IntegerList,
|
||||
GuidList,
|
||||
AttachmentList,
|
||||
ScoringGroupList,
|
||||
FileList,
|
||||
}
|
||||
|
||||
/// The output type of a formula field.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum FormulaOutputType {
|
||||
Text,
|
||||
Numeric,
|
||||
DateAndTime,
|
||||
ListValue,
|
||||
Text,
|
||||
Numeric,
|
||||
DateAndTime,
|
||||
ListValue,
|
||||
}
|
||||
|
||||
/// Whether a field allows single or multiple selections.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum Multiplicity {
|
||||
SingleSelect,
|
||||
MultiSelect,
|
||||
SingleSelect,
|
||||
MultiSelect,
|
||||
}
|
||||
|
||||
+19
-19
@@ -7,29 +7,29 @@ use super::enums::{FormulaOutputType, Multiplicity};
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Field {
|
||||
pub id: i32,
|
||||
pub app_id: i32,
|
||||
pub name: Option<String>,
|
||||
#[serde(rename = "type")]
|
||||
pub field_type: Option<String>,
|
||||
pub status: Option<String>,
|
||||
pub is_required: bool,
|
||||
pub is_unique: bool,
|
||||
pub multiplicity: Option<Multiplicity>,
|
||||
pub list_id: Option<i32>,
|
||||
pub values: Option<Vec<ListFieldValue>>,
|
||||
#[serde(rename = "outputType")]
|
||||
pub output_type: Option<FormulaOutputType>,
|
||||
pub referenced_app_id: Option<i32>,
|
||||
pub id: i32,
|
||||
pub app_id: i32,
|
||||
pub name: Option<String>,
|
||||
#[serde(rename = "type")]
|
||||
pub field_type: Option<String>,
|
||||
pub status: Option<String>,
|
||||
pub is_required: bool,
|
||||
pub is_unique: bool,
|
||||
pub multiplicity: Option<Multiplicity>,
|
||||
pub list_id: Option<i32>,
|
||||
pub values: Option<Vec<ListFieldValue>>,
|
||||
#[serde(rename = "outputType")]
|
||||
pub output_type: Option<FormulaOutputType>,
|
||||
pub referenced_app_id: Option<i32>,
|
||||
}
|
||||
|
||||
/// Represents a value in a list field.
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ListFieldValue {
|
||||
pub id: Uuid,
|
||||
pub name: String,
|
||||
pub sort_order: i32,
|
||||
pub numeric_value: Option<f64>,
|
||||
pub color: Option<String>,
|
||||
pub id: Uuid,
|
||||
pub name: String,
|
||||
pub sort_order: i32,
|
||||
pub numeric_value: Option<f64>,
|
||||
pub color: Option<String>,
|
||||
}
|
||||
|
||||
+20
-20
@@ -6,40 +6,40 @@ use serde::Deserialize;
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct FileInfo {
|
||||
#[serde(rename = "type")]
|
||||
pub file_type: Option<String>,
|
||||
pub content_type: Option<String>,
|
||||
pub name: Option<String>,
|
||||
pub created_date: Option<DateTime<Utc>>,
|
||||
pub modified_date: Option<DateTime<Utc>>,
|
||||
pub owner: Option<String>,
|
||||
pub notes: Option<String>,
|
||||
pub file_href: Option<String>,
|
||||
#[serde(rename = "type")]
|
||||
pub file_type: Option<String>,
|
||||
pub content_type: Option<String>,
|
||||
pub name: Option<String>,
|
||||
pub created_date: Option<DateTime<Utc>>,
|
||||
pub modified_date: Option<DateTime<Utc>>,
|
||||
pub owner: Option<String>,
|
||||
pub notes: Option<String>,
|
||||
pub file_href: Option<String>,
|
||||
}
|
||||
|
||||
/// The content of a downloaded file.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FileResponse {
|
||||
pub content_type: Option<String>,
|
||||
pub file_name: Option<String>,
|
||||
pub data: Bytes,
|
||||
pub content_type: Option<String>,
|
||||
pub file_name: Option<String>,
|
||||
pub data: Bytes,
|
||||
}
|
||||
|
||||
/// Request to upload a file.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SaveFileRequest {
|
||||
pub record_id: i32,
|
||||
pub field_id: i32,
|
||||
pub notes: Option<String>,
|
||||
pub modified_date: Option<DateTime<Utc>>,
|
||||
pub file_name: String,
|
||||
pub file_data: Vec<u8>,
|
||||
pub content_type: String,
|
||||
pub record_id: i32,
|
||||
pub field_id: i32,
|
||||
pub notes: Option<String>,
|
||||
pub modified_date: Option<DateTime<Utc>>,
|
||||
pub file_name: String,
|
||||
pub file_data: Vec<u8>,
|
||||
pub content_type: String,
|
||||
}
|
||||
|
||||
/// Response from creating a file, containing the new file ID.
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreatedWithIdResponse {
|
||||
pub id: i32,
|
||||
pub id: i32,
|
||||
}
|
||||
|
||||
+8
-8
@@ -5,18 +5,18 @@ use uuid::Uuid;
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SaveListItemRequest {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub id: Option<Uuid>,
|
||||
pub name: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub numeric_value: Option<f64>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub color: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub id: Option<Uuid>,
|
||||
pub name: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub numeric_value: Option<f64>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub color: Option<String>,
|
||||
}
|
||||
|
||||
/// Response from saving a list item.
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SaveListItemResponse {
|
||||
pub id: Uuid,
|
||||
pub id: Uuid,
|
||||
}
|
||||
|
||||
+14
-14
@@ -3,34 +3,34 @@ use serde::Deserialize;
|
||||
/// Parameters for paginated API requests.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PagingRequest {
|
||||
pub page_number: i32,
|
||||
pub page_size: i32,
|
||||
pub page_number: i32,
|
||||
pub page_size: i32,
|
||||
}
|
||||
|
||||
impl Default for PagingRequest {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
page_number: 1,
|
||||
page_size: 50,
|
||||
}
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
page_number: 1,
|
||||
page_size: 50,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A paginated response from the API.
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PagedResponse<T> {
|
||||
pub page_number: Option<i32>,
|
||||
pub page_size: Option<i32>,
|
||||
pub total_pages: Option<i32>,
|
||||
pub total_records: Option<i32>,
|
||||
pub items: Option<Vec<T>>,
|
||||
pub page_number: Option<i32>,
|
||||
pub page_size: Option<i32>,
|
||||
pub total_pages: Option<i32>,
|
||||
pub total_records: Option<i32>,
|
||||
pub items: Option<Vec<T>>,
|
||||
}
|
||||
|
||||
/// A collection response from the API (non-paginated).
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CollectionResponse<T> {
|
||||
pub count: Option<i32>,
|
||||
pub items: Option<Vec<T>>,
|
||||
pub count: Option<i32>,
|
||||
pub items: Option<Vec<T>>,
|
||||
}
|
||||
|
||||
+27
-27
@@ -8,67 +8,67 @@ use super::enums::{DataFormat, ValueType};
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Record {
|
||||
pub app_id: i32,
|
||||
pub record_id: i32,
|
||||
pub field_data: Option<Vec<RecordFieldValue>>,
|
||||
pub app_id: i32,
|
||||
pub record_id: i32,
|
||||
pub field_data: Option<Vec<RecordFieldValue>>,
|
||||
}
|
||||
|
||||
/// Represents a single field value within a record.
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RecordFieldValue {
|
||||
#[serde(rename = "type")]
|
||||
pub value_type: ValueType,
|
||||
pub field_id: i32,
|
||||
pub value: serde_json::Value,
|
||||
#[serde(rename = "type")]
|
||||
pub value_type: ValueType,
|
||||
pub field_id: i32,
|
||||
pub value: serde_json::Value,
|
||||
}
|
||||
|
||||
/// Request to create or update a record.
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SaveRecordRequest {
|
||||
pub app_id: i32,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub record_id: Option<i32>,
|
||||
pub fields: HashMap<String, serde_json::Value>,
|
||||
pub app_id: i32,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub record_id: Option<i32>,
|
||||
pub fields: HashMap<String, serde_json::Value>,
|
||||
}
|
||||
|
||||
/// Response from saving a record.
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SaveRecordResponse {
|
||||
pub id: i32,
|
||||
pub warnings: Option<Vec<String>>,
|
||||
pub id: i32,
|
||||
pub warnings: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
/// Request to query records with a filter.
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct QueryRecordsRequest {
|
||||
pub app_id: i32,
|
||||
pub filter: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub field_ids: Option<Vec<i32>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub data_format: Option<DataFormat>,
|
||||
pub app_id: i32,
|
||||
pub filter: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub field_ids: Option<Vec<i32>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub data_format: Option<DataFormat>,
|
||||
}
|
||||
|
||||
/// Request to get a batch of records.
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BatchGetRecordsRequest {
|
||||
pub app_id: i32,
|
||||
pub record_ids: Vec<i32>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub field_ids: Option<Vec<i32>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub data_format: Option<DataFormat>,
|
||||
pub app_id: i32,
|
||||
pub record_ids: Vec<i32>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub field_ids: Option<Vec<i32>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub data_format: Option<DataFormat>,
|
||||
}
|
||||
|
||||
/// Request to delete a batch of records.
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BatchDeleteRecordsRequest {
|
||||
pub app_id: i32,
|
||||
pub record_ids: Vec<i32>,
|
||||
pub app_id: i32,
|
||||
pub record_ids: Vec<i32>,
|
||||
}
|
||||
|
||||
@@ -4,24 +4,24 @@ use serde::Deserialize;
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ReportInfo {
|
||||
pub app_id: i32,
|
||||
pub id: i32,
|
||||
pub name: Option<String>,
|
||||
pub description: Option<String>,
|
||||
pub app_id: i32,
|
||||
pub id: i32,
|
||||
pub name: Option<String>,
|
||||
pub description: Option<String>,
|
||||
}
|
||||
|
||||
/// Represents the data returned by a report.
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ReportData {
|
||||
pub columns: Option<Vec<String>>,
|
||||
pub rows: Option<Vec<ReportRow>>,
|
||||
pub columns: Option<Vec<String>>,
|
||||
pub rows: Option<Vec<ReportRow>>,
|
||||
}
|
||||
|
||||
/// Represents a single row in a report.
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ReportRow {
|
||||
pub record_id: Option<i32>,
|
||||
pub cells: Option<Vec<serde_json::Value>>,
|
||||
pub record_id: Option<i32>,
|
||||
pub cells: Option<Vec<serde_json::Value>>,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user