use onspring::{ App, BatchDeleteRecordsRequest, BatchGetRecordsRequest, CollectionResponse, CreatedWithIdResponse, DataFormat, Field, FileInfo, FileResponse, OnspringClient, OnspringError, PagedResponse, PagingRequest, QueryRecordsRequest, Record, SaveFileRequest, SaveListItemRequest, SaveListItemResponse, SaveRecordRequest, SaveRecordResponse, }; use uuid::Uuid; pub trait OnspringRunner { fn ping(&self) -> impl std::future::Future> + Send; fn list_apps( &self, paging: Option, ) -> impl std::future::Future, OnspringError>> + Send; fn get_app( &self, app_id: i32, ) -> impl std::future::Future> + Send; fn batch_get_apps( &self, ids: &[i32], ) -> impl std::future::Future, OnspringError>> + Send; fn list_fields( &self, app_id: i32, paging: Option, ) -> impl std::future::Future, OnspringError>> + Send; fn get_field( &self, field_id: i32, ) -> impl std::future::Future> + Send; fn batch_get_fields( &self, ids: &[i32], ) -> impl std::future::Future, OnspringError>> + Send; fn list_records( &self, app_id: i32, paging: Option, field_ids: Option<&[i32]>, data_format: Option, ) -> impl std::future::Future, OnspringError>> + Send; fn get_record( &self, app_id: i32, record_id: i32, field_ids: Option<&[i32]>, data_format: Option, ) -> impl std::future::Future> + Send; fn save_record( &self, request: SaveRecordRequest, ) -> impl std::future::Future> + Send; fn delete_record( &self, app_id: i32, record_id: i32, ) -> impl std::future::Future> + Send; fn batch_get_records( &self, request: BatchGetRecordsRequest, ) -> impl std::future::Future, OnspringError>> + Send; fn query_records( &self, request: QueryRecordsRequest, paging: Option, ) -> impl std::future::Future, OnspringError>> + Send; fn batch_delete_records( &self, request: BatchDeleteRecordsRequest, ) -> impl std::future::Future> + Send; fn get_file_info( &self, record_id: i32, field_id: i32, file_id: i32, ) -> impl std::future::Future> + Send; fn get_file( &self, record_id: i32, field_id: i32, file_id: i32, ) -> impl std::future::Future> + Send; fn upload_file( &self, request: SaveFileRequest, ) -> impl std::future::Future> + Send; fn delete_file( &self, record_id: i32, field_id: i32, file_id: i32, ) -> impl std::future::Future> + Send; fn save_list_item( &self, list_id: i32, request: SaveListItemRequest, ) -> impl std::future::Future> + Send; fn delete_list_item( &self, list_id: i32, item_id: Uuid, ) -> impl std::future::Future> + Send; } impl OnspringRunner for OnspringClient { async fn ping(&self) -> Result<(), OnspringError> { self.ping().await } async fn list_apps( &self, paging: Option, ) -> Result, OnspringError> { self.list_apps(paging).await } async fn get_app(&self, app_id: i32) -> Result { self.get_app(app_id).await } async fn batch_get_apps(&self, ids: &[i32]) -> Result, OnspringError> { self.batch_get_apps(ids).await } async fn list_fields( &self, app_id: i32, paging: Option, ) -> Result, OnspringError> { self.list_fields(app_id, paging).await } async fn get_field(&self, field_id: i32) -> Result { self.get_field(field_id).await } async fn batch_get_fields(&self, ids: &[i32]) -> Result, OnspringError> { self.batch_get_fields(ids).await } async fn list_records( &self, app_id: i32, paging: Option, field_ids: Option<&[i32]>, data_format: Option, ) -> Result, OnspringError> { self.list_records(app_id, paging, field_ids, data_format).await } async fn get_record( &self, app_id: i32, record_id: i32, field_ids: Option<&[i32]>, data_format: Option, ) -> Result { self.get_record(app_id, record_id, field_ids, data_format).await } async fn save_record( &self, request: SaveRecordRequest, ) -> Result { self.save_record(request).await } async fn delete_record(&self, app_id: i32, record_id: i32) -> Result<(), OnspringError> { self.delete_record(app_id, record_id).await } async fn batch_get_records( &self, request: BatchGetRecordsRequest, ) -> Result, OnspringError> { self.batch_get_records(request).await } async fn query_records( &self, request: QueryRecordsRequest, paging: Option, ) -> Result, OnspringError> { self.query_records(request, paging).await } async fn batch_delete_records( &self, request: BatchDeleteRecordsRequest, ) -> Result<(), OnspringError> { self.batch_delete_records(request).await } async fn get_file_info( &self, record_id: i32, field_id: i32, file_id: i32, ) -> Result { self.get_file_info(record_id, field_id, file_id).await } async fn get_file( &self, record_id: i32, field_id: i32, file_id: i32, ) -> Result { self.get_file(record_id, field_id, file_id).await } async fn upload_file( &self, request: SaveFileRequest, ) -> Result { self.upload_file(request).await } async fn delete_file( &self, record_id: i32, field_id: i32, file_id: i32, ) -> Result<(), OnspringError> { self.delete_file(record_id, field_id, file_id).await } async fn save_list_item( &self, list_id: i32, request: SaveListItemRequest, ) -> Result { self.save_list_item(list_id, request).await } async fn delete_list_item(&self, list_id: i32, item_id: Uuid) -> Result<(), OnspringError> { self.delete_list_item(list_id, item_id).await } } #[cfg(test)] pub mod testing { use super::*; use std::sync::Mutex; pub struct MockClient { pub ping_result: Result<(), OnspringError>, pub list_apps_result: Result, OnspringError>, pub get_app_result: Result, pub batch_get_apps_result: Result, OnspringError>, pub list_apps_paging: Mutex>>, pub get_app_id: Mutex>, pub batch_get_apps_ids: Mutex>>, pub list_fields_result: Result, OnspringError>, pub get_field_result: Result, pub batch_get_fields_result: Result, OnspringError>, pub list_fields_app_id: Mutex>, pub list_fields_paging: Mutex>>, pub get_field_id: Mutex>, pub batch_get_fields_ids: Mutex>>, pub list_records_result: Result, OnspringError>, pub get_record_result: Result, pub save_record_result: Result, pub delete_record_result: Result<(), OnspringError>, pub batch_get_records_result: Result, OnspringError>, pub query_records_result: Result, OnspringError>, pub batch_delete_records_result: Result<(), OnspringError>, pub list_records_app_id: Mutex>, pub list_records_paging: Mutex>>, pub list_records_field_ids: Mutex>>>, pub list_records_data_format: Mutex>>, pub get_record_app_id: Mutex>, pub get_record_record_id: Mutex>, pub get_record_field_ids: Mutex>>>, pub get_record_data_format: Mutex>>, pub save_record_request: Mutex>, pub delete_record_app_id: Mutex>, pub delete_record_record_id: Mutex>, pub batch_get_records_request: Mutex>, pub query_records_request: Mutex>, pub query_records_paging: Mutex>>, pub batch_delete_records_request: Mutex>, pub get_file_info_result: Result, pub get_file_result: Result, pub upload_file_result: Result, pub delete_file_result: Result<(), OnspringError>, pub get_file_info_record_id: Mutex>, pub get_file_info_field_id: Mutex>, pub get_file_info_file_id: Mutex>, pub get_file_record_id: Mutex>, pub get_file_field_id: Mutex>, pub get_file_file_id: Mutex>, pub upload_file_request: Mutex>, pub delete_file_record_id: Mutex>, pub delete_file_field_id: Mutex>, pub delete_file_file_id: Mutex>, pub save_list_item_result: Result, pub delete_list_item_result: Result<(), OnspringError>, pub save_list_item_list_id: Mutex>, pub save_list_item_request: Mutex>, pub delete_list_item_list_id: Mutex>, pub delete_list_item_item_id: Mutex>, } impl Default for MockClient { fn default() -> Self { Self { ping_result: Ok(()), list_apps_result: Ok(PagedResponse { page_number: None, page_size: None, total_pages: None, total_records: None, items: None, }), get_app_result: Ok(App { href: None, id: 1, name: None, }), batch_get_apps_result: Ok(CollectionResponse { count: None, items: None, }), list_apps_paging: Mutex::new(None), get_app_id: Mutex::new(None), batch_get_apps_ids: Mutex::new(None), list_fields_result: Ok(PagedResponse { page_number: None, page_size: None, total_pages: None, total_records: None, items: None, }), get_field_result: Ok(Field { id: 1, app_id: 1, name: None, field_type: None, status: None, is_required: false, is_unique: false, multiplicity: None, list_id: None, values: None, output_type: None, referenced_app_id: None, }), batch_get_fields_result: Ok(CollectionResponse { count: None, items: None, }), list_fields_app_id: Mutex::new(None), list_fields_paging: Mutex::new(None), get_field_id: Mutex::new(None), batch_get_fields_ids: Mutex::new(None), list_records_result: Ok(PagedResponse { page_number: None, page_size: None, total_pages: None, total_records: None, items: None, }), get_record_result: Ok(Record { app_id: 1, record_id: 1, field_data: None, }), save_record_result: Ok(SaveRecordResponse { id: 1, warnings: None, }), delete_record_result: Ok(()), batch_get_records_result: Ok(CollectionResponse { count: None, items: None, }), query_records_result: Ok(PagedResponse { page_number: None, page_size: None, total_pages: None, total_records: None, items: None, }), batch_delete_records_result: Ok(()), list_records_app_id: Mutex::new(None), list_records_paging: Mutex::new(None), list_records_field_ids: Mutex::new(None), list_records_data_format: Mutex::new(None), get_record_app_id: Mutex::new(None), get_record_record_id: Mutex::new(None), get_record_field_ids: Mutex::new(None), get_record_data_format: Mutex::new(None), save_record_request: Mutex::new(None), delete_record_app_id: Mutex::new(None), delete_record_record_id: Mutex::new(None), batch_get_records_request: Mutex::new(None), query_records_request: Mutex::new(None), query_records_paging: Mutex::new(None), batch_delete_records_request: Mutex::new(None), get_file_info_result: Ok(FileInfo { file_type: None, content_type: None, name: None, created_date: None, modified_date: None, owner: None, notes: None, file_href: None, }), get_file_result: Ok(FileResponse { content_type: None, file_name: None, data: bytes::Bytes::new(), }), upload_file_result: Ok(CreatedWithIdResponse { id: 1 }), delete_file_result: Ok(()), get_file_info_record_id: Mutex::new(None), get_file_info_field_id: Mutex::new(None), get_file_info_file_id: Mutex::new(None), get_file_record_id: Mutex::new(None), get_file_field_id: Mutex::new(None), get_file_file_id: Mutex::new(None), upload_file_request: Mutex::new(None), delete_file_record_id: Mutex::new(None), delete_file_field_id: Mutex::new(None), delete_file_file_id: Mutex::new(None), save_list_item_result: Ok(SaveListItemResponse { id: Uuid::nil(), }), delete_list_item_result: Ok(()), save_list_item_list_id: Mutex::new(None), save_list_item_request: Mutex::new(None), delete_list_item_list_id: Mutex::new(None), delete_list_item_item_id: Mutex::new(None), } } } fn clone_onspring_error(err: &OnspringError) -> OnspringError { match err { OnspringError::InvalidArgument(msg) => OnspringError::InvalidArgument(msg.clone()), OnspringError::Api { status_code, message, } => OnspringError::Api { status_code: *status_code, message: message.clone(), }, OnspringError::Serialization(_) => { let serde_err = serde_json::from_str::("invalid").unwrap_err(); OnspringError::Serialization(serde_err) } OnspringError::Http(_) => OnspringError::InvalidArgument("http error".to_string()), } } impl OnspringRunner for MockClient { async fn ping(&self) -> Result<(), OnspringError> { match &self.ping_result { Ok(()) => Ok(()), Err(err) => Err(clone_onspring_error(err)), } } async fn list_apps( &self, paging: Option, ) -> Result, OnspringError> { *self.list_apps_paging.lock().unwrap() = Some(paging); match &self.list_apps_result { Ok(res) => Ok(res.clone()), Err(err) => Err(clone_onspring_error(err)), } } async fn get_app(&self, app_id: i32) -> Result { *self.get_app_id.lock().unwrap() = Some(app_id); match &self.get_app_result { Ok(res) => Ok(res.clone()), Err(err) => Err(clone_onspring_error(err)), } } async fn batch_get_apps(&self, ids: &[i32]) -> Result, OnspringError> { *self.batch_get_apps_ids.lock().unwrap() = Some(ids.to_vec()); match &self.batch_get_apps_result { Ok(res) => Ok(res.clone()), Err(err) => Err(clone_onspring_error(err)), } } async fn list_fields( &self, app_id: i32, paging: Option, ) -> Result, OnspringError> { *self.list_fields_app_id.lock().unwrap() = Some(app_id); *self.list_fields_paging.lock().unwrap() = Some(paging); match &self.list_fields_result { Ok(res) => Ok(res.clone()), Err(err) => Err(clone_onspring_error(err)), } } async fn get_field(&self, field_id: i32) -> Result { *self.get_field_id.lock().unwrap() = Some(field_id); match &self.get_field_result { Ok(res) => Ok(res.clone()), Err(err) => Err(clone_onspring_error(err)), } } async fn batch_get_fields(&self, ids: &[i32]) -> Result, OnspringError> { *self.batch_get_fields_ids.lock().unwrap() = Some(ids.to_vec()); match &self.batch_get_fields_result { Ok(res) => Ok(res.clone()), Err(err) => Err(clone_onspring_error(err)), } } async fn list_records( &self, app_id: i32, paging: Option, field_ids: Option<&[i32]>, data_format: Option, ) -> Result, OnspringError> { *self.list_records_app_id.lock().unwrap() = Some(app_id); *self.list_records_paging.lock().unwrap() = Some(paging); *self.list_records_field_ids.lock().unwrap() = Some(field_ids.map(|ids| ids.to_vec())); *self.list_records_data_format.lock().unwrap() = Some(data_format); match &self.list_records_result { Ok(res) => Ok(res.clone()), Err(err) => Err(clone_onspring_error(err)), } } async fn get_record( &self, app_id: i32, record_id: i32, field_ids: Option<&[i32]>, data_format: Option, ) -> Result { *self.get_record_app_id.lock().unwrap() = Some(app_id); *self.get_record_record_id.lock().unwrap() = Some(record_id); *self.get_record_field_ids.lock().unwrap() = Some(field_ids.map(|ids| ids.to_vec())); *self.get_record_data_format.lock().unwrap() = Some(data_format); match &self.get_record_result { Ok(res) => Ok(res.clone()), Err(err) => Err(clone_onspring_error(err)), } } async fn save_record( &self, request: SaveRecordRequest, ) -> Result { *self.save_record_request.lock().unwrap() = Some(request); match &self.save_record_result { Ok(res) => Ok(res.clone()), Err(err) => Err(clone_onspring_error(err)), } } async fn delete_record(&self, app_id: i32, record_id: i32) -> Result<(), OnspringError> { *self.delete_record_app_id.lock().unwrap() = Some(app_id); *self.delete_record_record_id.lock().unwrap() = Some(record_id); match &self.delete_record_result { Ok(()) => Ok(()), Err(err) => Err(clone_onspring_error(err)), } } async fn batch_get_records( &self, request: BatchGetRecordsRequest, ) -> Result, OnspringError> { *self.batch_get_records_request.lock().unwrap() = Some(request); match &self.batch_get_records_result { Ok(res) => Ok(res.clone()), Err(err) => Err(clone_onspring_error(err)), } } async fn query_records( &self, request: QueryRecordsRequest, paging: Option, ) -> Result, OnspringError> { *self.query_records_request.lock().unwrap() = Some(request); *self.query_records_paging.lock().unwrap() = Some(paging); match &self.query_records_result { Ok(res) => Ok(res.clone()), Err(err) => Err(clone_onspring_error(err)), } } async fn batch_delete_records( &self, request: BatchDeleteRecordsRequest, ) -> Result<(), OnspringError> { *self.batch_delete_records_request.lock().unwrap() = Some(request); match &self.batch_delete_records_result { Ok(()) => Ok(()), Err(err) => Err(clone_onspring_error(err)), } } async fn get_file_info( &self, record_id: i32, field_id: i32, file_id: i32, ) -> Result { *self.get_file_info_record_id.lock().unwrap() = Some(record_id); *self.get_file_info_field_id.lock().unwrap() = Some(field_id); *self.get_file_info_file_id.lock().unwrap() = Some(file_id); match &self.get_file_info_result { Ok(res) => Ok(res.clone()), Err(err) => Err(clone_onspring_error(err)), } } async fn get_file( &self, record_id: i32, field_id: i32, file_id: i32, ) -> Result { *self.get_file_record_id.lock().unwrap() = Some(record_id); *self.get_file_field_id.lock().unwrap() = Some(field_id); *self.get_file_file_id.lock().unwrap() = Some(file_id); match &self.get_file_result { Ok(res) => Ok(res.clone()), Err(err) => Err(clone_onspring_error(err)), } } async fn upload_file( &self, request: SaveFileRequest, ) -> Result { *self.upload_file_request.lock().unwrap() = Some(request); match &self.upload_file_result { Ok(res) => Ok(res.clone()), Err(err) => Err(clone_onspring_error(err)), } } async fn delete_file( &self, record_id: i32, field_id: i32, file_id: i32, ) -> Result<(), OnspringError> { *self.delete_file_record_id.lock().unwrap() = Some(record_id); *self.delete_file_field_id.lock().unwrap() = Some(field_id); *self.delete_file_file_id.lock().unwrap() = Some(file_id); match &self.delete_file_result { Ok(()) => Ok(()), Err(err) => Err(clone_onspring_error(err)), } } async fn save_list_item( &self, list_id: i32, request: SaveListItemRequest, ) -> Result { *self.save_list_item_list_id.lock().unwrap() = Some(list_id); *self.save_list_item_request.lock().unwrap() = Some(request); match &self.save_list_item_result { Ok(res) => Ok(res.clone()), Err(err) => Err(clone_onspring_error(err)), } } async fn delete_list_item(&self, list_id: i32, item_id: Uuid) -> Result<(), OnspringError> { *self.delete_list_item_list_id.lock().unwrap() = Some(list_id); *self.delete_list_item_item_id.lock().unwrap() = Some(item_id); match &self.delete_list_item_result { Ok(()) => Ok(()), Err(err) => Err(clone_onspring_error(err)), } } } }