feat: add records endpoint with all CRUD and query methods
Implement Record, RecordFieldValue, SaveRecordRequest, QueryRecordsRequest, BatchGetRecordsRequest, and BatchDeleteRecordsRequest models. Add 7 endpoint methods: list_records, get_record, save_record, delete_record, batch_get_records, query_records, batch_delete_records. Add 9 tests covering all methods including parameterized queries.
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
mod apps;
|
||||
mod fields;
|
||||
mod ping;
|
||||
mod records;
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
use reqwest::Method;
|
||||
|
||||
use crate::client::OnspringClient;
|
||||
use crate::error::Result;
|
||||
use crate::models::{
|
||||
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 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
|
||||
}
|
||||
|
||||
/// 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
|
||||
}
|
||||
|
||||
/// 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
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,10 @@ mod app;
|
||||
mod enums;
|
||||
mod field;
|
||||
mod paging;
|
||||
pub mod record;
|
||||
|
||||
pub use app::*;
|
||||
pub use enums::*;
|
||||
pub use field::*;
|
||||
pub use paging::*;
|
||||
pub use record::*;
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::enums::{DataFormat, ValueType};
|
||||
|
||||
/// Represents a record in an Onspring application.
|
||||
#[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>>,
|
||||
}
|
||||
|
||||
/// 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,
|
||||
}
|
||||
|
||||
/// 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>,
|
||||
}
|
||||
|
||||
/// Response from saving a record.
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SaveRecordResponse {
|
||||
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>,
|
||||
}
|
||||
|
||||
/// 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>,
|
||||
}
|
||||
|
||||
/// 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>,
|
||||
}
|
||||
Reference in New Issue
Block a user