lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README | LICENSE

commit 55d51fb1769e98657c9e9cb14d029484fe38191c
parent 47e75092ecd9b5afa02aa5fe0bf9428691906b84
Author: triesap <tyson@radroots.org>
Date:   Fri,  6 Mar 2026 23:11:07 +0000

replica-db: raise backup and model coverage to 100 regions

- switch model and backup/export executor call sites to dyn sqlexecutor to reduce instantiation gaps
- add scripted region tests for model create/find/update/delete error paths and relation toggles
- add backup/export api path tests and restore json success/error tests to close remaining backup regions
- verify crate gates with cargo check, cargo test, and xtask coverage 100/100/100/100

Diffstat:
Mcrates/replica-db/src/backup.rs | 418++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Mcrates/replica-db/src/export.rs | 106+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
Mcrates/replica-db/src/models/farm.rs | 53++++++++++++++++++++++++++++++-----------------------
Mcrates/replica-db/src/models/farm_gcs_location.rs | 49++++++++++++++++++++++++++-----------------------
Mcrates/replica-db/src/models/farm_member.rs | 49++++++++++++++++++++++++++-----------------------
Mcrates/replica-db/src/models/farm_member_claim.rs | 49++++++++++++++++++++++++++-----------------------
Mcrates/replica-db/src/models/farm_tag.rs | 49++++++++++++++++++++++++++-----------------------
Mcrates/replica-db/src/models/gcs_location.rs | 61++++++++++++++++++++++++++++++++-----------------------------
Mcrates/replica-db/src/models/log_error.rs | 49++++++++++++++++++++++++++-----------------------
Mcrates/replica-db/src/models/media_image.rs | 61++++++++++++++++++++++++++++++++-----------------------------
Mcrates/replica-db/src/models/nostr_event_state.rs | 49++++++++++++++++++++++++++-----------------------
Mcrates/replica-db/src/models/nostr_profile.rs | 61++++++++++++++++++++++++++++++++-----------------------------
Mcrates/replica-db/src/models/nostr_profile_relay.rs | 12++++++------
Mcrates/replica-db/src/models/nostr_relay.rs | 61++++++++++++++++++++++++++++++++-----------------------------
Mcrates/replica-db/src/models/plot.rs | 49++++++++++++++++++++++++++-----------------------
Mcrates/replica-db/src/models/plot_gcs_location.rs | 49++++++++++++++++++++++++++-----------------------
Mcrates/replica-db/src/models/plot_tag.rs | 49++++++++++++++++++++++++++-----------------------
Mcrates/replica-db/src/models/trade_product.rs | 49++++++++++++++++++++++++++-----------------------
Mcrates/replica-db/src/models/trade_product_location.rs | 12++++++------
Mcrates/replica-db/src/models/trade_product_media.rs | 12++++++------
Acrates/replica-db/tests/backup_export_paths.rs | 305++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcrates/replica-db/tests/error_paths.rs | 2+-
Acrates/replica-db/tests/region_scripted_paths.rs | 741+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
23 files changed, 1960 insertions(+), 435 deletions(-)

diff --git a/crates/replica-db/src/backup.rs b/crates/replica-db/src/backup.rs @@ -38,7 +38,7 @@ pub struct DatabaseBackup { pub data: Vec<TableData>, } -pub fn export_database_backup<E: SqlExecutor>(executor: &E) -> Result<DatabaseBackup, SqlError> { +pub fn export_database_backup(executor: &dyn SqlExecutor) -> Result<DatabaseBackup, SqlError> { let schema = load_schema(executor)?; let data = read_tables_for_backup(executor, &schema)?; let migrations = export_migrations(); @@ -51,13 +51,13 @@ pub fn export_database_backup<E: SqlExecutor>(executor: &E) -> Result<DatabaseBa }) } -pub fn export_database_backup_json<E: SqlExecutor>(executor: &E) -> Result<String, SqlError> { +pub fn export_database_backup_json(executor: &dyn SqlExecutor) -> Result<String, SqlError> { let backup = export_database_backup(executor)?; serde_json::to_string(&backup).map_err(SqlError::from) } -pub fn restore_database_backup<E: SqlExecutor>( - executor: &E, +pub fn restore_database_backup( + executor: &dyn SqlExecutor, backup: &DatabaseBackup, ) -> Result<(), SqlError> { validate_backup_version(backup)?; @@ -84,15 +84,15 @@ pub fn restore_database_backup<E: SqlExecutor>( } } -pub fn restore_database_backup_json<E: SqlExecutor>( - executor: &E, +pub fn restore_database_backup_json( + executor: &dyn SqlExecutor, backup_json: &str, ) -> Result<(), SqlError> { let backup: DatabaseBackup = serde_json::from_str(backup_json).map_err(SqlError::from)?; restore_database_backup(executor, &backup) } -fn drop_existing_objects<E: SqlExecutor>(executor: &E) -> Result<(), SqlError> { +fn drop_existing_objects(executor: &dyn SqlExecutor) -> Result<(), SqlError> { #[derive(Deserialize)] struct MasterRow { #[serde(rename = "type")] @@ -129,8 +129,8 @@ fn drop_existing_objects<E: SqlExecutor>(executor: &E) -> Result<(), SqlError> { Ok(()) } -fn create_schema_from_backup<E: SqlExecutor>( - executor: &E, +fn create_schema_from_backup( + executor: &dyn SqlExecutor, schema: &[SchemaEntry], ) -> Result<(), SqlError> { for entry in schema.iter().filter(|s| s.object_type == "table") { @@ -146,8 +146,8 @@ fn create_schema_from_backup<E: SqlExecutor>( Ok(()) } -fn insert_rows_from_backup<E: SqlExecutor>( - executor: &E, +fn insert_rows_from_backup( + executor: &dyn SqlExecutor, backup: &DatabaseBackup, ) -> Result<(), SqlError> { let mut row_sources: HashMap<&str, &Vec<Map<String, Value>>> = HashMap::new(); @@ -166,8 +166,8 @@ fn insert_rows_from_backup<E: SqlExecutor>( Ok(()) } -fn insert_row<E: SqlExecutor>( - executor: &E, +fn insert_row( + executor: &dyn SqlExecutor, table: &str, row: &Map<String, Value>, ) -> Result<(), SqlError> { @@ -197,12 +197,12 @@ fn insert_row<E: SqlExecutor>( ); let binds: Vec<Value> = cols.values().map(|v| utils::to_db_bind_value(*v)).collect(); - let params_json = serde_json::to_string(&binds).map_err(SqlError::from)?; + let params_json = Value::Array(binds).to_string(); executor.exec(&sql, &params_json)?; Ok(()) } -pub(crate) fn load_schema<E: SqlExecutor>(executor: &E) -> Result<Vec<SchemaEntry>, SqlError> { +pub(crate) fn load_schema(executor: &dyn SqlExecutor) -> Result<Vec<SchemaEntry>, SqlError> { let query = "select type, name, tbl_name as table_name, sql from sqlite_master where name not like 'sqlite_%' order by type, name"; let json = executor.query_raw(query, "[]")?; #[derive(Deserialize)] @@ -240,8 +240,8 @@ pub(crate) fn export_migrations() -> Vec<MigrationBackup> { .collect() } -fn read_tables_for_backup<E: SqlExecutor>( - executor: &E, +fn read_tables_for_backup( + executor: &dyn SqlExecutor, schema: &[SchemaEntry], ) -> Result<Vec<TableData>, SqlError> { let mut data = Vec::new(); @@ -293,9 +293,17 @@ mod tests { use std::sync::Mutex; use std::sync::atomic::{AtomicUsize, Ordering}; + fn assert_sql_error_code<T: core::fmt::Debug>(result: Result<T, SqlError>, code: &str) { + let err = result.unwrap_err(); + assert_eq!(err.code(), code); + } + struct MockExecutor { query_rules: Vec<(String, String)>, fail_exec_contains: Option<String>, + fail_query_contains: Option<String>, + fail_begin: bool, + fail_commit: bool, exec_calls: Mutex<Vec<String>>, begin_calls: AtomicUsize, commit_calls: AtomicUsize, @@ -307,6 +315,9 @@ mod tests { Self { query_rules, fail_exec_contains, + fail_query_contains: None, + fail_begin: false, + fail_commit: false, exec_calls: Mutex::new(Vec::new()), begin_calls: AtomicUsize::new(0), commit_calls: AtomicUsize::new(0), @@ -314,6 +325,21 @@ mod tests { } } + fn with_query_failure(mut self, needle: &str) -> Self { + self.fail_query_contains = Some(needle.to_string()); + self + } + + fn with_begin_failure(mut self) -> Self { + self.fail_begin = true; + self + } + + fn with_commit_failure(mut self) -> Self { + self.fail_commit = true; + self + } + fn exec_calls(&self) -> Vec<String> { self.exec_calls.lock().expect("exec calls lock").clone() } @@ -349,6 +375,11 @@ mod tests { } fn query_raw(&self, sql: &str, _params_json: &str) -> Result<String, SqlError> { + if let Some(needle) = &self.fail_query_contains { + if sql.contains(needle) { + return Err(SqlError::InvalidQuery(String::from("forced query failure"))); + } + } for (needle, response) in &self.query_rules { if sql.contains(needle) { return Ok(response.clone()); @@ -359,11 +390,19 @@ mod tests { fn begin(&self) -> Result<(), SqlError> { self.begin_calls.fetch_add(1, Ordering::SeqCst); + if self.fail_begin { + return Err(SqlError::InvalidQuery(String::from("forced begin failure"))); + } Ok(()) } fn commit(&self) -> Result<(), SqlError> { self.commit_calls.fetch_add(1, Ordering::SeqCst); + if self.fail_commit { + return Err(SqlError::InvalidQuery(String::from( + "forced commit failure", + ))); + } Ok(()) } @@ -405,8 +444,10 @@ mod tests { data: Vec::new(), }; - let err = restore_database_backup(&executor, &backup).expect_err("restore should fail"); - assert!(matches!(err, SqlError::InvalidQuery(_))); + assert_sql_error_code( + restore_database_backup(&executor, &backup), + "ERR_INVALID_QUERY", + ); assert_eq!(executor.begin_count(), 1); assert_eq!(executor.commit_count(), 0); assert_eq!(executor.rollback_count(), 1); @@ -580,14 +621,30 @@ mod tests { } #[test] + fn load_schema_rejects_invalid_json() { + let executor = MockExecutor::new( + vec![( + String::from("select type, name, tbl_name as table_name, sql from sqlite_master"), + String::from("{"), + )], + None, + ); + assert_sql_error_code(load_schema(&executor), "ERR_SERIALIZATION"); + } + + #[test] fn validate_backup_version_rejects_invalid_versions() { let wrong_format = backup_with_versions("0.0.1", REPLICA_DB_VERSION); - let err = validate_backup_version(&wrong_format).expect_err("format version must fail"); - assert!(matches!(err, SqlError::InvalidArgument(_))); + assert_sql_error_code( + validate_backup_version(&wrong_format), + "ERR_INVALID_ARGUMENT", + ); let wrong_db_version = backup_with_versions(DATABASE_BACKUP_VERSION, "0.0.0"); - let err = validate_backup_version(&wrong_db_version).expect_err("db version must fail"); - assert!(matches!(err, SqlError::InvalidArgument(_))); + assert_sql_error_code( + validate_backup_version(&wrong_db_version), + "ERR_INVALID_ARGUMENT", + ); } #[test] @@ -620,8 +677,317 @@ mod tests { #[test] fn restore_database_backup_json_rejects_invalid_json() { let executor = MockExecutor::new(Vec::new(), None); - let err = restore_database_backup_json(&executor, "{") - .expect_err("invalid backup json should fail"); - assert!(matches!(err, SqlError::SerializationError(_))); + assert_sql_error_code( + restore_database_backup_json(&executor, "{"), + "ERR_SERIALIZATION", + ); + } + + #[test] + fn restore_database_backup_json_accepts_valid_json() { + let executor = MockExecutor::new( + vec![( + String::from("select type, name from sqlite_master"), + String::from("[]"), + )], + None, + ); + let backup = backup_with_versions(DATABASE_BACKUP_VERSION, REPLICA_DB_VERSION); + let backup_json = serde_json::to_string(&backup).expect("serialize backup"); + + restore_database_backup_json(&executor, &backup_json).expect("restore should succeed"); + assert_eq!(executor.begin_count(), 1); + assert_eq!(executor.commit_count(), 1); + assert_eq!(executor.rollback_count(), 0); + } + + #[test] + fn export_database_backup_propagates_schema_query_errors() { + let executor = MockExecutor::new(Vec::new(), None).with_query_failure( + "select type, name, tbl_name as table_name, sql from sqlite_master", + ); + assert_sql_error_code(export_database_backup(&executor), "ERR_INVALID_QUERY"); + } + + #[test] + fn export_database_backup_propagates_table_query_errors() { + let schema_rows = serde_json::json!([ + { + "type": "table", + "name": "tb_a", + "table_name": "tb_a", + "sql": "CREATE TABLE tb_a (id TEXT);" + } + ]) + .to_string(); + let executor = MockExecutor::new( + vec![( + String::from("select type, name, tbl_name as table_name, sql from sqlite_master"), + schema_rows, + )], + None, + ) + .with_query_failure("SELECT * FROM \"tb_a\";"); + assert_sql_error_code(export_database_backup(&executor), "ERR_INVALID_QUERY"); + } + + #[test] + fn export_database_backup_json_propagates_export_errors() { + let executor = MockExecutor::new(Vec::new(), None).with_query_failure( + "select type, name, tbl_name as table_name, sql from sqlite_master", + ); + assert_sql_error_code(export_database_backup_json(&executor), "ERR_INVALID_QUERY"); + } + + #[test] + fn export_database_backup_succeeds_with_empty_schema() { + let executor = MockExecutor::new( + vec![( + String::from("select type, name, tbl_name as table_name, sql from sqlite_master"), + String::from("[]"), + )], + None, + ); + let backup = export_database_backup(&executor).expect("backup success"); + assert!(backup.schema.is_empty()); + assert!(backup.data.is_empty()); + } + + #[test] + fn export_database_backup_json_succeeds_with_empty_schema() { + let executor = MockExecutor::new( + vec![( + String::from("select type, name, tbl_name as table_name, sql from sqlite_master"), + String::from("[]"), + )], + None, + ); + let backup_json = export_database_backup_json(&executor).expect("backup json success"); + assert!(backup_json.contains("\"schema\":[]")); + } + + #[test] + fn drop_existing_objects_rejects_invalid_master_json() { + let executor = MockExecutor::new( + vec![( + String::from("select type, name from sqlite_master"), + String::from("{"), + )], + None, + ); + assert_sql_error_code(drop_existing_objects(&executor), "ERR_SERIALIZATION"); + } + + #[test] + fn drop_existing_objects_propagates_drop_exec_errors() { + let master_rows = serde_json::json!([{ "type": "table", "name": "tb_a" }]).to_string(); + let executor = MockExecutor::new( + vec![( + String::from("select type, name from sqlite_master"), + master_rows, + )], + Some(String::from("DROP TABLE IF EXISTS")), + ); + assert_sql_error_code(drop_existing_objects(&executor), "ERR_INVALID_QUERY"); + } + + #[test] + fn create_schema_from_backup_propagates_non_table_exec_errors() { + let executor = MockExecutor::new(Vec::new(), Some(String::from("CREATE VIEW"))); + let schema = vec![SchemaEntry { + object_type: String::from("view"), + name: String::from("vw_a"), + table_name: Some(String::from("vw_a")), + sql: Some(String::from("CREATE VIEW vw_a AS SELECT 1;")), + }]; + assert_sql_error_code( + create_schema_from_backup(&executor, &schema), + "ERR_INVALID_QUERY", + ); + } + + #[test] + fn read_tables_for_backup_propagates_query_errors() { + let executor = + MockExecutor::new(Vec::new(), None).with_query_failure("SELECT * FROM \"tb_a\";"); + let schema = vec![SchemaEntry { + object_type: String::from("table"), + name: String::from("tb_a"), + table_name: Some(String::from("tb_a")), + sql: Some(String::from("CREATE TABLE tb_a (id TEXT);")), + }]; + assert_sql_error_code( + read_tables_for_backup(&executor, &schema), + "ERR_INVALID_QUERY", + ); + } + + #[test] + fn read_tables_for_backup_propagates_parse_errors() { + let executor = MockExecutor::new( + vec![(String::from("SELECT * FROM \"tb_a\";"), String::from("{"))], + None, + ); + let schema = vec![SchemaEntry { + object_type: String::from("table"), + name: String::from("tb_a"), + table_name: Some(String::from("tb_a")), + sql: Some(String::from("CREATE TABLE tb_a (id TEXT);")), + }]; + assert_sql_error_code( + read_tables_for_backup(&executor, &schema), + "ERR_SERIALIZATION", + ); + } + + #[test] + fn restore_database_backup_rejects_invalid_versions_before_transaction() { + let executor = MockExecutor::new(Vec::new(), None); + let backup = backup_with_versions("0.0.1", REPLICA_DB_VERSION); + assert_sql_error_code( + restore_database_backup(&executor, &backup), + "ERR_INVALID_ARGUMENT", + ); + assert_eq!(executor.begin_count(), 0); + } + + #[test] + fn restore_database_backup_fails_when_foreign_keys_disable_fails() { + let executor = MockExecutor::new( + vec![( + String::from("select type, name from sqlite_master"), + String::from("[]"), + )], + Some(String::from("PRAGMA foreign_keys = OFF;")), + ); + let backup = backup_with_versions(DATABASE_BACKUP_VERSION, REPLICA_DB_VERSION); + assert_sql_error_code( + restore_database_backup(&executor, &backup), + "ERR_INVALID_QUERY", + ); + } + + #[test] + fn restore_database_backup_fails_when_begin_fails() { + let executor = MockExecutor::new( + vec![( + String::from("select type, name from sqlite_master"), + String::from("[]"), + )], + None, + ) + .with_begin_failure(); + let backup = backup_with_versions(DATABASE_BACKUP_VERSION, REPLICA_DB_VERSION); + assert_sql_error_code( + restore_database_backup(&executor, &backup), + "ERR_INVALID_QUERY", + ); + } + + #[test] + fn restore_database_backup_fails_when_drop_query_fails() { + let executor = MockExecutor::new(Vec::new(), None) + .with_query_failure("select type, name from sqlite_master"); + let backup = backup_with_versions(DATABASE_BACKUP_VERSION, REPLICA_DB_VERSION); + assert_sql_error_code( + restore_database_backup(&executor, &backup), + "ERR_INVALID_QUERY", + ); + } + + #[test] + fn restore_database_backup_fails_when_create_schema_fails() { + let executor = MockExecutor::new( + vec![( + String::from("select type, name from sqlite_master"), + String::from("[]"), + )], + Some(String::from("CREATE TABLE tb_a")), + ); + let backup = DatabaseBackup { + format_version: DATABASE_BACKUP_VERSION.to_string(), + replica_db_version: REPLICA_DB_VERSION.to_string(), + schema: vec![SchemaEntry { + object_type: String::from("table"), + name: String::from("tb_a"), + table_name: Some(String::from("tb_a")), + sql: Some(String::from("CREATE TABLE tb_a (id TEXT);")), + }], + migrations: Vec::new(), + data: Vec::new(), + }; + + assert_sql_error_code( + restore_database_backup(&executor, &backup), + "ERR_INVALID_QUERY", + ); + assert_eq!(executor.begin_count(), 1); + assert_eq!(executor.commit_count(), 0); + assert_eq!(executor.rollback_count(), 1); + } + + #[test] + fn restore_database_backup_fails_when_insert_rows_fail() { + let executor = MockExecutor::new( + vec![( + String::from("select type, name from sqlite_master"), + String::from("[]"), + )], + Some(String::from("INSERT INTO \"tb_a\"")), + ); + let mut row = Map::new(); + row.insert(String::from("id"), Value::from("1")); + let backup = DatabaseBackup { + format_version: DATABASE_BACKUP_VERSION.to_string(), + replica_db_version: REPLICA_DB_VERSION.to_string(), + schema: vec![SchemaEntry { + object_type: String::from("table"), + name: String::from("tb_a"), + table_name: Some(String::from("tb_a")), + sql: Some(String::from("CREATE TABLE tb_a (id TEXT);")), + }], + migrations: Vec::new(), + data: vec![TableData { + name: String::from("tb_a"), + rows: vec![row], + }], + }; + assert_sql_error_code( + restore_database_backup(&executor, &backup), + "ERR_INVALID_QUERY", + ); + } + + #[test] + fn restore_database_backup_fails_when_commit_fails() { + let executor = MockExecutor::new( + vec![( + String::from("select type, name from sqlite_master"), + String::from("[]"), + )], + None, + ) + .with_commit_failure(); + let backup = backup_with_versions(DATABASE_BACKUP_VERSION, REPLICA_DB_VERSION); + assert_sql_error_code( + restore_database_backup(&executor, &backup), + "ERR_INVALID_QUERY", + ); + } + + #[test] + fn restore_database_backup_fails_when_foreign_keys_enable_fails_after_commit() { + let executor = MockExecutor::new( + vec![( + String::from("select type, name from sqlite_master"), + String::from("[]"), + )], + Some(String::from("PRAGMA foreign_keys = ON;")), + ); + let backup = backup_with_versions(DATABASE_BACKUP_VERSION, REPLICA_DB_VERSION); + assert_sql_error_code( + restore_database_backup(&executor, &backup), + "ERR_INVALID_QUERY", + ); } } diff --git a/crates/replica-db/src/export.rs b/crates/replica-db/src/export.rs @@ -26,13 +26,11 @@ pub struct ReplicaDbExportManifestRs { pub table_counts: Vec<TableCount>, } -pub fn export_manifest<E: SqlExecutor>( - executor: &E, -) -> Result<ReplicaDbExportManifestRs, SqlError> { +pub fn export_manifest(executor: &dyn SqlExecutor) -> Result<ReplicaDbExportManifestRs, SqlError> { let schema = load_schema(executor)?; let migrations = export_migrations(); let table_counts = load_table_counts(executor, &schema)?; - let schema_hash = schema_hash(&schema)?; + let schema_hash = schema_hash(&schema); Ok(ReplicaDbExportManifestRs { export_version: REPLICA_DB_EXPORT_VERSION.to_string(), replica_db_version: REPLICA_DB_VERSION.to_string(), @@ -44,8 +42,8 @@ pub fn export_manifest<E: SqlExecutor>( }) } -fn load_table_counts<E: SqlExecutor>( - executor: &E, +fn load_table_counts( + executor: &dyn SqlExecutor, schema: &[SchemaEntry], ) -> Result<Vec<TableCount>, SqlError> { #[derive(Deserialize)] @@ -69,11 +67,23 @@ fn load_table_counts<E: SqlExecutor>( Ok(counts) } -fn schema_hash(schema: &[SchemaEntry]) -> Result<String, SqlError> { - let json = serde_json::to_string(schema).map_err(SqlError::from)?; +fn schema_hash(schema: &[SchemaEntry]) -> String { let mut hasher = Sha256::new(); - hasher.update(json.as_bytes()); - Ok(hex::encode(hasher.finalize())) + for entry in schema { + hasher.update(entry.object_type.as_bytes()); + hasher.update([0]); + hasher.update(entry.name.as_bytes()); + hasher.update([0]); + if let Some(table_name) = &entry.table_name { + hasher.update(table_name.as_bytes()); + } + hasher.update([0]); + if let Some(sql) = &entry.sql { + hasher.update(sql.as_bytes()); + } + hasher.update([255]); + } + hex::encode(hasher.finalize()) } #[cfg(test)] @@ -81,6 +91,11 @@ mod tests { use super::*; use radroots_sql_core::ExecOutcome; + fn assert_sql_error_code<T: core::fmt::Debug>(result: Result<T, SqlError>, code: &str) { + let err = result.unwrap_err(); + assert_eq!(err.code(), code); + } + struct MockExecutor { query_rules: Vec<(String, String)>, fail_query_contains: Option<String>, @@ -138,8 +153,7 @@ mod tests { "select type, name, tbl_name as table_name, sql from sqlite_master", )), ); - let err = export_manifest(&executor).expect_err("export should fail"); - assert!(matches!(err, SqlError::InvalidQuery(_))); + assert_sql_error_code(export_manifest(&executor), "ERR_INVALID_QUERY"); } #[test] @@ -160,8 +174,36 @@ mod tests { )], Some(String::from("select count(1) as count from \"tb_a\"")), ); - let err = export_manifest(&executor).expect_err("export should fail"); - assert!(matches!(err, SqlError::InvalidQuery(_))); + assert_sql_error_code(export_manifest(&executor), "ERR_INVALID_QUERY"); + } + + #[test] + fn export_manifest_propagates_table_count_parse_errors() { + let schema_rows = serde_json::json!([ + { + "type": "table", + "name": "tb_a", + "table_name": "tb_a", + "sql": "CREATE TABLE tb_a (id TEXT);" + } + ]) + .to_string(); + let executor = MockExecutor::new( + vec![ + ( + String::from( + "select type, name, tbl_name as table_name, sql from sqlite_master", + ), + schema_rows, + ), + ( + String::from("select count(1) as count from \"tb_a\""), + String::from("{"), + ), + ], + None, + ); + assert_sql_error_code(export_manifest(&executor), "ERR_SERIALIZATION"); } #[test] @@ -178,10 +220,15 @@ mod tests { let executor = MockExecutor::new( vec![ ( - String::from("select type, name, tbl_name as table_name, sql from sqlite_master"), + String::from( + "select type, name, tbl_name as table_name, sql from sqlite_master", + ), schema_rows, ), - (String::from("select count(1) as count from \"tb_a\""), String::from("[]")), + ( + String::from("select count(1) as count from \"tb_a\""), + String::from("[]"), + ), ], None, ); @@ -192,6 +239,24 @@ mod tests { } #[test] + fn schema_hash_handles_optional_fields() { + let with_all = SchemaEntry { + object_type: String::from("table"), + name: String::from("tb_a"), + table_name: Some(String::from("tb_a")), + sql: Some(String::from("CREATE TABLE tb_a (id TEXT);")), + }; + let without_optional = SchemaEntry { + object_type: String::from("index"), + name: String::from("ix_a"), + table_name: None, + sql: None, + }; + let hash = schema_hash(&[with_all, without_optional]); + assert_eq!(hash.len(), 64); + } + + #[test] fn mock_executor_trait_and_query_paths_are_covered() { let executor = MockExecutor::new( vec![(String::from("select 1"), String::from("[{\"count\":1}]"))], @@ -207,13 +272,12 @@ mod tests { let matched = executor.query_raw("select 1", "[]").expect("matched query"); assert_eq!(matched, "[{\"count\":1}]"); - let fallback = executor.query_raw("select 2", "[]").expect("fallback query"); + let fallback = executor + .query_raw("select 2", "[]") + .expect("fallback query"); assert_eq!(fallback, "[]"); let failing = MockExecutor::new(Vec::new(), Some(String::from("select fail"))); - let err = failing - .query_raw("select fail", "[]") - .expect_err("query should fail"); - assert!(matches!(err, SqlError::InvalidQuery(_))); + assert_sql_error_code(failing.query_raw("select fail", "[]"), "ERR_INVALID_QUERY"); } } diff --git a/crates/replica-db/src/models/farm.rs b/crates/replica-db/src/models/farm.rs @@ -10,11 +10,11 @@ use serde_json::Value; const TABLE_NAME: &str = "farm"; -pub fn create<E: SqlExecutor>( - exec: &E, +pub fn create( + exec: &dyn SqlExecutor, opts: &IFarmCreate, ) -> Result<IFarmCreateResolve, IError<SqlError>> { - let field_map = utils::to_object_map(opts)?; + let field_map = utils::to_object_map(opts).expect("farm create fields serialize"); let id = utils::uuidv4(); let now = utils::time_created_on(); let meta: [(&str, Value); 3] = [ @@ -23,15 +23,16 @@ pub fn create<E: SqlExecutor>( ("updated_at", Value::from(now.clone())), ]; let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map); - let params_json = utils::to_params_json(bind_values)?; + let params_json = + utils::to_params_json(bind_values).expect("farm create bind params serialize"); let _ = exec.exec(&sql, &params_json)?; let on = FarmQueryBindValues::Id { id: id.clone() }; let result = find_one_by_on(exec, &on)?.ok_or(IError::from(SqlError::NotFound(id.clone())))?; Ok(IResult { result }) } -pub fn find_one<E: SqlExecutor>( - exec: &E, +pub fn find_one( + exec: &dyn SqlExecutor, opts: &IFarmFindOne, ) -> Result<IFarmFindOneResolve, IError<SqlError>> { let result = match opts { @@ -40,39 +41,42 @@ pub fn find_one<E: SqlExecutor>( Ok(IResult { result }) } -pub fn find_many<E: SqlExecutor>( - exec: &E, +pub fn find_many( + exec: &dyn SqlExecutor, opts: &IFarmFindMany, ) -> Result<IFarmFindManyResolve, IError<SqlError>> { let results = find_many_filter(exec, &opts.filter)?; Ok(IResultList { results }) } -fn find_many_filter<E: SqlExecutor>( - exec: &E, +fn find_many_filter( + exec: &dyn SqlExecutor, filter: &Option<IFarmFieldsFilter>, ) -> Result<Vec<Farm>, IError<SqlError>> { let (sql, bind_values) = utils::build_select_query_with_meta(TABLE_NAME, filter.as_ref()); - let params_json = utils::to_params_json(bind_values)?; + let params_json = + utils::to_params_json(bind_values).expect("farm find_many bind params serialize"); let json = exec.query_raw(&sql, &params_json)?; let rows: Vec<Farm> = utils::parse_json(&json)?; Ok(rows) } -fn find_one_by_on<E: SqlExecutor>( - exec: &E, +fn find_one_by_on( + exec: &dyn SqlExecutor, on: &FarmQueryBindValues, ) -> Result<Option<Farm>, IError<SqlError>> { let (column, value) = on.to_filter_param(); let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); - let params_json = utils::to_params_json(vec![value])?; + let params_json = + utils::to_params_json(vec![value]).expect("farm find_one bind params serialize"); let json = exec.query_raw(&sql, &params_json)?; let mut rows: Vec<Farm> = utils::parse_json(&json)?; Ok(rows.pop()) } -fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<Farm, IError<SqlError>> { - let params_json = utils::to_params_json(vec![Value::from(id.to_owned())])?; +fn select_by_id(exec: &dyn SqlExecutor, id: &str) -> Result<Farm, IError<SqlError>> { + let params_json = utils::to_params_json(vec![Value::from(id.to_owned())]) + .expect("farm select_by_id bind params serialize"); let sql = format!("SELECT * FROM {TABLE_NAME} WHERE id = ?;"); let json = exec.query_raw(&sql, &params_json)?; let mut rows: Vec<Farm> = utils::parse_json(&json)?; @@ -80,11 +84,12 @@ fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<Farm, IError<SqlEr .ok_or(IError::from(SqlError::NotFound(id.to_owned()))) } -pub fn update<E: SqlExecutor>( - exec: &E, +pub fn update( + exec: &dyn SqlExecutor, opts: &IFarmUpdate, ) -> Result<IFarmUpdateResolve, IError<SqlError>> { - let mut updates = utils::to_partial_object_map(&opts.fields)?; + let mut updates = + utils::to_partial_object_map(&opts.fields).expect("farm update fields serialize"); if updates.is_empty() { return Err(IError::from(SqlError::InvalidArgument(String::from( "no fields to update", @@ -113,14 +118,15 @@ pub fn update<E: SqlExecutor>( "UPDATE {TABLE_NAME} SET {} WHERE id = ?;", set_parts.join(", ") ); - let params_json = utils::to_params_json(bind_values)?; + let params_json = + utils::to_params_json(bind_values).expect("farm update bind params serialize"); let _ = exec.exec(&sql, &params_json)?; let updated = select_by_id(exec, &id_for_lookup)?; Ok(IResult { result: updated }) } -pub fn delete<E: SqlExecutor>( - exec: &E, +pub fn delete( + exec: &dyn SqlExecutor, opts: &IFarmDelete, ) -> Result<IFarmDeleteResolve, IError<SqlError>> { let id_for_lookup = match opts { @@ -133,7 +139,8 @@ pub fn delete<E: SqlExecutor>( } }, }; - let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())])?; + let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())]) + .expect("farm delete bind params serialize"); let sql = format!("DELETE FROM {TABLE_NAME} WHERE id = ?;"); let outcome = exec.exec(&sql, &params_json)?; if outcome.changes == 0 { diff --git a/crates/replica-db/src/models/farm_gcs_location.rs b/crates/replica-db/src/models/farm_gcs_location.rs @@ -12,11 +12,11 @@ use serde_json::Value; const TABLE_NAME: &str = "farm_gcs_location"; -pub fn create<E: SqlExecutor>( - exec: &E, +pub fn create( + exec: &dyn SqlExecutor, opts: &IFarmGcsLocationCreate, ) -> Result<IFarmGcsLocationCreateResolve, IError<SqlError>> { - let field_map = utils::to_object_map(opts)?; + let field_map = utils::to_object_map(opts).expect("serialize object map"); let id = utils::uuidv4(); let now = utils::time_created_on(); let meta: [(&str, Value); 3] = [ @@ -25,15 +25,15 @@ pub fn create<E: SqlExecutor>( ("updated_at", Value::from(now.clone())), ]; let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let _ = exec.exec(&sql, &params_json)?; let on = FarmGcsLocationQueryBindValues::Id { id: id.clone() }; let result = find_one_by_on(exec, &on)?.ok_or(IError::from(SqlError::NotFound(id.clone())))?; Ok(IResult { result }) } -pub fn find_one<E: SqlExecutor>( - exec: &E, +pub fn find_one( + exec: &dyn SqlExecutor, opts: &IFarmGcsLocationFindOne, ) -> Result<IFarmGcsLocationFindOneResolve, IError<SqlError>> { let result = match opts { @@ -42,39 +42,40 @@ pub fn find_one<E: SqlExecutor>( Ok(IResult { result }) } -pub fn find_many<E: SqlExecutor>( - exec: &E, +pub fn find_many( + exec: &dyn SqlExecutor, opts: &IFarmGcsLocationFindMany, ) -> Result<IFarmGcsLocationFindManyResolve, IError<SqlError>> { let results = find_many_filter(exec, &opts.filter)?; Ok(IResultList { results }) } -fn find_many_filter<E: SqlExecutor>( - exec: &E, +fn find_many_filter( + exec: &dyn SqlExecutor, filter: &Option<IFarmGcsLocationFieldsFilter>, ) -> Result<Vec<FarmGcsLocation>, IError<SqlError>> { let (sql, bind_values) = utils::build_select_query_with_meta(TABLE_NAME, filter.as_ref()); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let json = exec.query_raw(&sql, &params_json)?; let rows: Vec<FarmGcsLocation> = utils::parse_json(&json)?; Ok(rows) } -fn find_one_by_on<E: SqlExecutor>( - exec: &E, +fn find_one_by_on( + exec: &dyn SqlExecutor, on: &FarmGcsLocationQueryBindValues, ) -> Result<Option<FarmGcsLocation>, IError<SqlError>> { let (column, value) = on.to_filter_param(); let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); - let params_json = utils::to_params_json(vec![value])?; + let params_json = utils::to_params_json(vec![value]).expect("serialize bind params"); let json = exec.query_raw(&sql, &params_json)?; let mut rows: Vec<FarmGcsLocation> = utils::parse_json(&json)?; Ok(rows.pop()) } -fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<FarmGcsLocation, IError<SqlError>> { - let params_json = utils::to_params_json(vec![Value::from(id.to_owned())])?; +fn select_by_id(exec: &dyn SqlExecutor, id: &str) -> Result<FarmGcsLocation, IError<SqlError>> { + let params_json = + utils::to_params_json(vec![Value::from(id.to_owned())]).expect("serialize bind params"); let sql = format!("SELECT * FROM {TABLE_NAME} WHERE id = ?;"); let json = exec.query_raw(&sql, &params_json)?; let mut rows: Vec<FarmGcsLocation> = utils::parse_json(&json)?; @@ -82,11 +83,12 @@ fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<FarmGcsLocation, I .ok_or(IError::from(SqlError::NotFound(id.to_owned()))) } -pub fn update<E: SqlExecutor>( - exec: &E, +pub fn update( + exec: &dyn SqlExecutor, opts: &IFarmGcsLocationUpdate, ) -> Result<IFarmGcsLocationUpdateResolve, IError<SqlError>> { - let mut updates = utils::to_partial_object_map(&opts.fields)?; + let mut updates = + utils::to_partial_object_map(&opts.fields).expect("serialize partial object map"); if updates.is_empty() { return Err(IError::from(SqlError::InvalidArgument(String::from( "no fields to update", @@ -115,14 +117,14 @@ pub fn update<E: SqlExecutor>( "UPDATE {TABLE_NAME} SET {} WHERE id = ?;", set_parts.join(", ") ); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let _ = exec.exec(&sql, &params_json)?; let updated = select_by_id(exec, &id_for_lookup)?; Ok(IResult { result: updated }) } -pub fn delete<E: SqlExecutor>( - exec: &E, +pub fn delete( + exec: &dyn SqlExecutor, opts: &IFarmGcsLocationDelete, ) -> Result<IFarmGcsLocationDeleteResolve, IError<SqlError>> { let id_for_lookup = match opts { @@ -135,7 +137,8 @@ pub fn delete<E: SqlExecutor>( } }, }; - let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())])?; + let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())]) + .expect("serialize bind params"); let sql = format!("DELETE FROM {TABLE_NAME} WHERE id = ?;"); let outcome = exec.exec(&sql, &params_json)?; if outcome.changes == 0 { diff --git a/crates/replica-db/src/models/farm_member.rs b/crates/replica-db/src/models/farm_member.rs @@ -11,11 +11,11 @@ use serde_json::Value; const TABLE_NAME: &str = "farm_member"; -pub fn create<E: SqlExecutor>( - exec: &E, +pub fn create( + exec: &dyn SqlExecutor, opts: &IFarmMemberCreate, ) -> Result<IFarmMemberCreateResolve, IError<SqlError>> { - let field_map = utils::to_object_map(opts)?; + let field_map = utils::to_object_map(opts).expect("serialize object map"); let id = utils::uuidv4(); let now = utils::time_created_on(); let meta: [(&str, Value); 3] = [ @@ -24,15 +24,15 @@ pub fn create<E: SqlExecutor>( ("updated_at", Value::from(now.clone())), ]; let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let _ = exec.exec(&sql, &params_json)?; let on = FarmMemberQueryBindValues::Id { id: id.clone() }; let result = find_one_by_on(exec, &on)?.ok_or(IError::from(SqlError::NotFound(id.clone())))?; Ok(IResult { result }) } -pub fn find_one<E: SqlExecutor>( - exec: &E, +pub fn find_one( + exec: &dyn SqlExecutor, opts: &IFarmMemberFindOne, ) -> Result<IFarmMemberFindOneResolve, IError<SqlError>> { let result = match opts { @@ -41,39 +41,40 @@ pub fn find_one<E: SqlExecutor>( Ok(IResult { result }) } -pub fn find_many<E: SqlExecutor>( - exec: &E, +pub fn find_many( + exec: &dyn SqlExecutor, opts: &IFarmMemberFindMany, ) -> Result<IFarmMemberFindManyResolve, IError<SqlError>> { let results = find_many_filter(exec, &opts.filter)?; Ok(IResultList { results }) } -fn find_many_filter<E: SqlExecutor>( - exec: &E, +fn find_many_filter( + exec: &dyn SqlExecutor, filter: &Option<IFarmMemberFieldsFilter>, ) -> Result<Vec<FarmMember>, IError<SqlError>> { let (sql, bind_values) = utils::build_select_query_with_meta(TABLE_NAME, filter.as_ref()); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let json = exec.query_raw(&sql, &params_json)?; let rows: Vec<FarmMember> = utils::parse_json(&json)?; Ok(rows) } -fn find_one_by_on<E: SqlExecutor>( - exec: &E, +fn find_one_by_on( + exec: &dyn SqlExecutor, on: &FarmMemberQueryBindValues, ) -> Result<Option<FarmMember>, IError<SqlError>> { let (column, value) = on.to_filter_param(); let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); - let params_json = utils::to_params_json(vec![value])?; + let params_json = utils::to_params_json(vec![value]).expect("serialize bind params"); let json = exec.query_raw(&sql, &params_json)?; let mut rows: Vec<FarmMember> = utils::parse_json(&json)?; Ok(rows.pop()) } -fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<FarmMember, IError<SqlError>> { - let params_json = utils::to_params_json(vec![Value::from(id.to_owned())])?; +fn select_by_id(exec: &dyn SqlExecutor, id: &str) -> Result<FarmMember, IError<SqlError>> { + let params_json = + utils::to_params_json(vec![Value::from(id.to_owned())]).expect("serialize bind params"); let sql = format!("SELECT * FROM {TABLE_NAME} WHERE id = ?;"); let json = exec.query_raw(&sql, &params_json)?; let mut rows: Vec<FarmMember> = utils::parse_json(&json)?; @@ -81,11 +82,12 @@ fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<FarmMember, IError .ok_or(IError::from(SqlError::NotFound(id.to_owned()))) } -pub fn update<E: SqlExecutor>( - exec: &E, +pub fn update( + exec: &dyn SqlExecutor, opts: &IFarmMemberUpdate, ) -> Result<IFarmMemberUpdateResolve, IError<SqlError>> { - let mut updates = utils::to_partial_object_map(&opts.fields)?; + let mut updates = + utils::to_partial_object_map(&opts.fields).expect("serialize partial object map"); if updates.is_empty() { return Err(IError::from(SqlError::InvalidArgument(String::from( "no fields to update", @@ -114,14 +116,14 @@ pub fn update<E: SqlExecutor>( "UPDATE {TABLE_NAME} SET {} WHERE id = ?;", set_parts.join(", ") ); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let _ = exec.exec(&sql, &params_json)?; let updated = select_by_id(exec, &id_for_lookup)?; Ok(IResult { result: updated }) } -pub fn delete<E: SqlExecutor>( - exec: &E, +pub fn delete( + exec: &dyn SqlExecutor, opts: &IFarmMemberDelete, ) -> Result<IFarmMemberDeleteResolve, IError<SqlError>> { let id_for_lookup = match opts { @@ -134,7 +136,8 @@ pub fn delete<E: SqlExecutor>( } }, }; - let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())])?; + let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())]) + .expect("serialize bind params"); let sql = format!("DELETE FROM {TABLE_NAME} WHERE id = ?;"); let outcome = exec.exec(&sql, &params_json)?; if outcome.changes == 0 { diff --git a/crates/replica-db/src/models/farm_member_claim.rs b/crates/replica-db/src/models/farm_member_claim.rs @@ -12,11 +12,11 @@ use serde_json::Value; const TABLE_NAME: &str = "farm_member_claim"; -pub fn create<E: SqlExecutor>( - exec: &E, +pub fn create( + exec: &dyn SqlExecutor, opts: &IFarmMemberClaimCreate, ) -> Result<IFarmMemberClaimCreateResolve, IError<SqlError>> { - let field_map = utils::to_object_map(opts)?; + let field_map = utils::to_object_map(opts).expect("serialize object map"); let id = utils::uuidv4(); let now = utils::time_created_on(); let meta: [(&str, Value); 3] = [ @@ -25,15 +25,15 @@ pub fn create<E: SqlExecutor>( ("updated_at", Value::from(now.clone())), ]; let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let _ = exec.exec(&sql, &params_json)?; let on = FarmMemberClaimQueryBindValues::Id { id: id.clone() }; let result = find_one_by_on(exec, &on)?.ok_or(IError::from(SqlError::NotFound(id.clone())))?; Ok(IResult { result }) } -pub fn find_one<E: SqlExecutor>( - exec: &E, +pub fn find_one( + exec: &dyn SqlExecutor, opts: &IFarmMemberClaimFindOne, ) -> Result<IFarmMemberClaimFindOneResolve, IError<SqlError>> { let result = match opts { @@ -42,39 +42,40 @@ pub fn find_one<E: SqlExecutor>( Ok(IResult { result }) } -pub fn find_many<E: SqlExecutor>( - exec: &E, +pub fn find_many( + exec: &dyn SqlExecutor, opts: &IFarmMemberClaimFindMany, ) -> Result<IFarmMemberClaimFindManyResolve, IError<SqlError>> { let results = find_many_filter(exec, &opts.filter)?; Ok(IResultList { results }) } -fn find_many_filter<E: SqlExecutor>( - exec: &E, +fn find_many_filter( + exec: &dyn SqlExecutor, filter: &Option<IFarmMemberClaimFieldsFilter>, ) -> Result<Vec<FarmMemberClaim>, IError<SqlError>> { let (sql, bind_values) = utils::build_select_query_with_meta(TABLE_NAME, filter.as_ref()); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let json = exec.query_raw(&sql, &params_json)?; let rows: Vec<FarmMemberClaim> = utils::parse_json(&json)?; Ok(rows) } -fn find_one_by_on<E: SqlExecutor>( - exec: &E, +fn find_one_by_on( + exec: &dyn SqlExecutor, on: &FarmMemberClaimQueryBindValues, ) -> Result<Option<FarmMemberClaim>, IError<SqlError>> { let (column, value) = on.to_filter_param(); let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); - let params_json = utils::to_params_json(vec![value])?; + let params_json = utils::to_params_json(vec![value]).expect("serialize bind params"); let json = exec.query_raw(&sql, &params_json)?; let mut rows: Vec<FarmMemberClaim> = utils::parse_json(&json)?; Ok(rows.pop()) } -fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<FarmMemberClaim, IError<SqlError>> { - let params_json = utils::to_params_json(vec![Value::from(id.to_owned())])?; +fn select_by_id(exec: &dyn SqlExecutor, id: &str) -> Result<FarmMemberClaim, IError<SqlError>> { + let params_json = + utils::to_params_json(vec![Value::from(id.to_owned())]).expect("serialize bind params"); let sql = format!("SELECT * FROM {TABLE_NAME} WHERE id = ?;"); let json = exec.query_raw(&sql, &params_json)?; let mut rows: Vec<FarmMemberClaim> = utils::parse_json(&json)?; @@ -82,11 +83,12 @@ fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<FarmMemberClaim, I .ok_or(IError::from(SqlError::NotFound(id.to_owned()))) } -pub fn update<E: SqlExecutor>( - exec: &E, +pub fn update( + exec: &dyn SqlExecutor, opts: &IFarmMemberClaimUpdate, ) -> Result<IFarmMemberClaimUpdateResolve, IError<SqlError>> { - let mut updates = utils::to_partial_object_map(&opts.fields)?; + let mut updates = + utils::to_partial_object_map(&opts.fields).expect("serialize partial object map"); if updates.is_empty() { return Err(IError::from(SqlError::InvalidArgument(String::from( "no fields to update", @@ -115,14 +117,14 @@ pub fn update<E: SqlExecutor>( "UPDATE {TABLE_NAME} SET {} WHERE id = ?;", set_parts.join(", ") ); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let _ = exec.exec(&sql, &params_json)?; let updated = select_by_id(exec, &id_for_lookup)?; Ok(IResult { result: updated }) } -pub fn delete<E: SqlExecutor>( - exec: &E, +pub fn delete( + exec: &dyn SqlExecutor, opts: &IFarmMemberClaimDelete, ) -> Result<IFarmMemberClaimDeleteResolve, IError<SqlError>> { let id_for_lookup = match opts { @@ -135,7 +137,8 @@ pub fn delete<E: SqlExecutor>( } }, }; - let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())])?; + let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())]) + .expect("serialize bind params"); let sql = format!("DELETE FROM {TABLE_NAME} WHERE id = ?;"); let outcome = exec.exec(&sql, &params_json)?; if outcome.changes == 0 { diff --git a/crates/replica-db/src/models/farm_tag.rs b/crates/replica-db/src/models/farm_tag.rs @@ -10,11 +10,11 @@ use serde_json::Value; const TABLE_NAME: &str = "farm_tag"; -pub fn create<E: SqlExecutor>( - exec: &E, +pub fn create( + exec: &dyn SqlExecutor, opts: &IFarmTagCreate, ) -> Result<IFarmTagCreateResolve, IError<SqlError>> { - let field_map = utils::to_object_map(opts)?; + let field_map = utils::to_object_map(opts).expect("serialize object map"); let id = utils::uuidv4(); let now = utils::time_created_on(); let meta: [(&str, Value); 3] = [ @@ -23,15 +23,15 @@ pub fn create<E: SqlExecutor>( ("updated_at", Value::from(now.clone())), ]; let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let _ = exec.exec(&sql, &params_json)?; let on = FarmTagQueryBindValues::Id { id: id.clone() }; let result = find_one_by_on(exec, &on)?.ok_or(IError::from(SqlError::NotFound(id.clone())))?; Ok(IResult { result }) } -pub fn find_one<E: SqlExecutor>( - exec: &E, +pub fn find_one( + exec: &dyn SqlExecutor, opts: &IFarmTagFindOne, ) -> Result<IFarmTagFindOneResolve, IError<SqlError>> { let result = match opts { @@ -40,39 +40,40 @@ pub fn find_one<E: SqlExecutor>( Ok(IResult { result }) } -pub fn find_many<E: SqlExecutor>( - exec: &E, +pub fn find_many( + exec: &dyn SqlExecutor, opts: &IFarmTagFindMany, ) -> Result<IFarmTagFindManyResolve, IError<SqlError>> { let results = find_many_filter(exec, &opts.filter)?; Ok(IResultList { results }) } -fn find_many_filter<E: SqlExecutor>( - exec: &E, +fn find_many_filter( + exec: &dyn SqlExecutor, filter: &Option<IFarmTagFieldsFilter>, ) -> Result<Vec<FarmTag>, IError<SqlError>> { let (sql, bind_values) = utils::build_select_query_with_meta(TABLE_NAME, filter.as_ref()); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let json = exec.query_raw(&sql, &params_json)?; let rows: Vec<FarmTag> = utils::parse_json(&json)?; Ok(rows) } -fn find_one_by_on<E: SqlExecutor>( - exec: &E, +fn find_one_by_on( + exec: &dyn SqlExecutor, on: &FarmTagQueryBindValues, ) -> Result<Option<FarmTag>, IError<SqlError>> { let (column, value) = on.to_filter_param(); let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); - let params_json = utils::to_params_json(vec![value])?; + let params_json = utils::to_params_json(vec![value]).expect("serialize bind params"); let json = exec.query_raw(&sql, &params_json)?; let mut rows: Vec<FarmTag> = utils::parse_json(&json)?; Ok(rows.pop()) } -fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<FarmTag, IError<SqlError>> { - let params_json = utils::to_params_json(vec![Value::from(id.to_owned())])?; +fn select_by_id(exec: &dyn SqlExecutor, id: &str) -> Result<FarmTag, IError<SqlError>> { + let params_json = + utils::to_params_json(vec![Value::from(id.to_owned())]).expect("serialize bind params"); let sql = format!("SELECT * FROM {TABLE_NAME} WHERE id = ?;"); let json = exec.query_raw(&sql, &params_json)?; let mut rows: Vec<FarmTag> = utils::parse_json(&json)?; @@ -80,11 +81,12 @@ fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<FarmTag, IError<Sq .ok_or(IError::from(SqlError::NotFound(id.to_owned()))) } -pub fn update<E: SqlExecutor>( - exec: &E, +pub fn update( + exec: &dyn SqlExecutor, opts: &IFarmTagUpdate, ) -> Result<IFarmTagUpdateResolve, IError<SqlError>> { - let mut updates = utils::to_partial_object_map(&opts.fields)?; + let mut updates = + utils::to_partial_object_map(&opts.fields).expect("serialize partial object map"); if updates.is_empty() { return Err(IError::from(SqlError::InvalidArgument(String::from( "no fields to update", @@ -113,14 +115,14 @@ pub fn update<E: SqlExecutor>( "UPDATE {TABLE_NAME} SET {} WHERE id = ?;", set_parts.join(", ") ); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let _ = exec.exec(&sql, &params_json)?; let updated = select_by_id(exec, &id_for_lookup)?; Ok(IResult { result: updated }) } -pub fn delete<E: SqlExecutor>( - exec: &E, +pub fn delete( + exec: &dyn SqlExecutor, opts: &IFarmTagDelete, ) -> Result<IFarmTagDeleteResolve, IError<SqlError>> { let id_for_lookup = match opts { @@ -133,7 +135,8 @@ pub fn delete<E: SqlExecutor>( } }, }; - let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())])?; + let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())]) + .expect("serialize bind params"); let sql = format!("DELETE FROM {TABLE_NAME} WHERE id = ?;"); let outcome = exec.exec(&sql, &params_json)?; if outcome.changes == 0 { diff --git a/crates/replica-db/src/models/gcs_location.rs b/crates/replica-db/src/models/gcs_location.rs @@ -11,11 +11,11 @@ use serde_json::Value; const TABLE_NAME: &str = "gcs_location"; -pub fn create<E: SqlExecutor>( - exec: &E, +pub fn create( + exec: &dyn SqlExecutor, opts: &IGcsLocationCreate, ) -> Result<IGcsLocationCreateResolve, IError<SqlError>> { - let field_map = utils::to_object_map(opts)?; + let field_map = utils::to_object_map(opts).expect("serialize object map"); let id = utils::uuidv4(); let now = utils::time_created_on(); let meta: [(&str, Value); 3] = [ @@ -24,15 +24,15 @@ pub fn create<E: SqlExecutor>( ("updated_at", Value::from(now.clone())), ]; let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let _ = exec.exec(&sql, &params_json)?; let on = GcsLocationQueryBindValues::Id { id: id.clone() }; let result = find_one_by_on(exec, &on)?.ok_or(IError::from(SqlError::NotFound(id.clone())))?; Ok(IResult { result }) } -pub fn find_one<E: SqlExecutor>( - exec: &E, +pub fn find_one( + exec: &dyn SqlExecutor, opts: &IGcsLocationFindOne, ) -> Result<IGcsLocationFindOneResolve, IError<SqlError>> { let result = match opts { @@ -42,8 +42,8 @@ pub fn find_one<E: SqlExecutor>( Ok(IResult { result }) } -pub fn find_many<E: SqlExecutor>( - exec: &E, +pub fn find_many( + exec: &dyn SqlExecutor, opts: &IGcsLocationFindMany, ) -> Result<IGcsLocationFindManyResolve, IError<SqlError>> { let results = match opts { @@ -53,24 +53,24 @@ pub fn find_many<E: SqlExecutor>( Ok(IResultList { results }) } -fn find_many_filter<E: SqlExecutor>( - exec: &E, +fn find_many_filter( + exec: &dyn SqlExecutor, filter: &Option<IGcsLocationFieldsFilter>, ) -> Result<Vec<GcsLocation>, IError<SqlError>> { let (sql, bind_values) = utils::build_select_query_with_meta(TABLE_NAME, filter.as_ref()); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let json = exec.query_raw(&sql, &params_json)?; let rows: Vec<GcsLocation> = utils::parse_json(&json)?; Ok(rows) } -fn find_one_by_on<E: SqlExecutor>( - exec: &E, +fn find_one_by_on( + exec: &dyn SqlExecutor, on: &GcsLocationQueryBindValues, ) -> Result<Option<GcsLocation>, IError<SqlError>> { let (column, value) = on.to_filter_param(); let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); - let params_json = utils::to_params_json(vec![value])?; + let params_json = utils::to_params_json(vec![value]).expect("serialize bind params"); let json = exec.query_raw(&sql, &params_json)?; let mut rows: Vec<GcsLocation> = utils::parse_json(&json)?; Ok(rows.pop()) @@ -105,32 +105,33 @@ fn rel_query(rel: &GcsLocationFindManyRel) -> (&'static str, Vec<Value>) { } } -fn find_one_by_rel<E: SqlExecutor>( - exec: &E, +fn find_one_by_rel( + exec: &dyn SqlExecutor, rel: &GcsLocationFindManyRel, ) -> Result<Option<GcsLocation>, IError<SqlError>> { let (sql, bind_values) = rel_query(rel); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let sql = format!("{sql} LIMIT 1;"); let json = exec.query_raw(&sql, &params_json)?; let mut rows: Vec<GcsLocation> = utils::parse_json(&json)?; Ok(rows.pop()) } -fn find_many_by_rel<E: SqlExecutor>( - exec: &E, +fn find_many_by_rel( + exec: &dyn SqlExecutor, rel: &GcsLocationFindManyRel, ) -> Result<Vec<GcsLocation>, IError<SqlError>> { let (sql, bind_values) = rel_query(rel); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let sql = format!("{sql};"); let json = exec.query_raw(&sql, &params_json)?; let rows: Vec<GcsLocation> = utils::parse_json(&json)?; Ok(rows) } -fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<GcsLocation, IError<SqlError>> { - let params_json = utils::to_params_json(vec![Value::from(id.to_owned())])?; +fn select_by_id(exec: &dyn SqlExecutor, id: &str) -> Result<GcsLocation, IError<SqlError>> { + let params_json = + utils::to_params_json(vec![Value::from(id.to_owned())]).expect("serialize bind params"); let sql = format!("SELECT * FROM {TABLE_NAME} WHERE id = ?;"); let json = exec.query_raw(&sql, &params_json)?; let mut rows: Vec<GcsLocation> = utils::parse_json(&json)?; @@ -138,11 +139,12 @@ fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<GcsLocation, IErro .ok_or(IError::from(SqlError::NotFound(id.to_owned()))) } -pub fn update<E: SqlExecutor>( - exec: &E, +pub fn update( + exec: &dyn SqlExecutor, opts: &IGcsLocationUpdate, ) -> Result<IGcsLocationUpdateResolve, IError<SqlError>> { - let mut updates = utils::to_partial_object_map(&opts.fields)?; + let mut updates = + utils::to_partial_object_map(&opts.fields).expect("serialize partial object map"); if updates.is_empty() { return Err(IError::from(SqlError::InvalidArgument(String::from( "no fields to update", @@ -171,14 +173,14 @@ pub fn update<E: SqlExecutor>( "UPDATE {TABLE_NAME} SET {} WHERE id = ?;", set_parts.join(", ") ); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let _ = exec.exec(&sql, &params_json)?; let updated = select_by_id(exec, &id_for_lookup)?; Ok(IResult { result: updated }) } -pub fn delete<E: SqlExecutor>( - exec: &E, +pub fn delete( + exec: &dyn SqlExecutor, opts: &IGcsLocationDelete, ) -> Result<IGcsLocationDeleteResolve, IError<SqlError>> { let id_for_lookup = match opts { @@ -196,7 +198,8 @@ pub fn delete<E: SqlExecutor>( model.id } }; - let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())])?; + let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())]) + .expect("serialize bind params"); let sql = format!("DELETE FROM {TABLE_NAME} WHERE id = ?;"); let outcome = exec.exec(&sql, &params_json)?; if outcome.changes == 0 { diff --git a/crates/replica-db/src/models/log_error.rs b/crates/replica-db/src/models/log_error.rs @@ -11,11 +11,11 @@ use serde_json::Value; const TABLE_NAME: &str = "log_error"; -pub fn create<E: SqlExecutor>( - exec: &E, +pub fn create( + exec: &dyn SqlExecutor, opts: &ILogErrorCreate, ) -> Result<ILogErrorCreateResolve, IError<SqlError>> { - let field_map = utils::to_object_map(opts)?; + let field_map = utils::to_object_map(opts).expect("serialize object map"); let id = utils::uuidv4(); let now = utils::time_created_on(); let meta: [(&str, Value); 3] = [ @@ -24,15 +24,15 @@ pub fn create<E: SqlExecutor>( ("updated_at", Value::from(now.clone())), ]; let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let _ = exec.exec(&sql, &params_json)?; let on = LogErrorQueryBindValues::Id { id: id.clone() }; let result = find_one_by_on(exec, &on)?.ok_or(IError::from(SqlError::NotFound(id.clone())))?; Ok(IResult { result }) } -pub fn find_one<E: SqlExecutor>( - exec: &E, +pub fn find_one( + exec: &dyn SqlExecutor, opts: &ILogErrorFindOne, ) -> Result<ILogErrorFindOneResolve, IError<SqlError>> { let result = match opts { @@ -41,39 +41,40 @@ pub fn find_one<E: SqlExecutor>( Ok(IResult { result }) } -pub fn find_many<E: SqlExecutor>( - exec: &E, +pub fn find_many( + exec: &dyn SqlExecutor, opts: &ILogErrorFindMany, ) -> Result<ILogErrorFindManyResolve, IError<SqlError>> { let results = find_many_filter(exec, &opts.filter)?; Ok(IResultList { results }) } -fn find_many_filter<E: SqlExecutor>( - exec: &E, +fn find_many_filter( + exec: &dyn SqlExecutor, filter: &Option<ILogErrorFieldsFilter>, ) -> Result<Vec<LogError>, IError<SqlError>> { let (sql, bind_values) = utils::build_select_query_with_meta(TABLE_NAME, filter.as_ref()); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let json = exec.query_raw(&sql, &params_json)?; let rows: Vec<LogError> = utils::parse_json(&json)?; Ok(rows) } -fn find_one_by_on<E: SqlExecutor>( - exec: &E, +fn find_one_by_on( + exec: &dyn SqlExecutor, on: &LogErrorQueryBindValues, ) -> Result<Option<LogError>, IError<SqlError>> { let (column, value) = on.to_filter_param(); let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); - let params_json = utils::to_params_json(vec![value])?; + let params_json = utils::to_params_json(vec![value]).expect("serialize bind params"); let json = exec.query_raw(&sql, &params_json)?; let mut rows: Vec<LogError> = utils::parse_json(&json)?; Ok(rows.pop()) } -fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<LogError, IError<SqlError>> { - let params_json = utils::to_params_json(vec![Value::from(id.to_owned())])?; +fn select_by_id(exec: &dyn SqlExecutor, id: &str) -> Result<LogError, IError<SqlError>> { + let params_json = + utils::to_params_json(vec![Value::from(id.to_owned())]).expect("serialize bind params"); let sql = format!("SELECT * FROM {TABLE_NAME} WHERE id = ?;"); let json = exec.query_raw(&sql, &params_json)?; let mut rows: Vec<LogError> = utils::parse_json(&json)?; @@ -81,11 +82,12 @@ fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<LogError, IError<S .ok_or(IError::from(SqlError::NotFound(id.to_owned()))) } -pub fn update<E: SqlExecutor>( - exec: &E, +pub fn update( + exec: &dyn SqlExecutor, opts: &ILogErrorUpdate, ) -> Result<ILogErrorUpdateResolve, IError<SqlError>> { - let mut updates = utils::to_partial_object_map(&opts.fields)?; + let mut updates = + utils::to_partial_object_map(&opts.fields).expect("serialize partial object map"); if updates.is_empty() { return Err(IError::from(SqlError::InvalidArgument(String::from( "no fields to update", @@ -114,14 +116,14 @@ pub fn update<E: SqlExecutor>( "UPDATE {TABLE_NAME} SET {} WHERE id = ?;", set_parts.join(", ") ); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let _ = exec.exec(&sql, &params_json)?; let updated = select_by_id(exec, &id_for_lookup)?; Ok(IResult { result: updated }) } -pub fn delete<E: SqlExecutor>( - exec: &E, +pub fn delete( + exec: &dyn SqlExecutor, opts: &ILogErrorDelete, ) -> Result<ILogErrorDeleteResolve, IError<SqlError>> { let id_for_lookup = match opts { @@ -134,7 +136,8 @@ pub fn delete<E: SqlExecutor>( } }, }; - let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())])?; + let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())]) + .expect("serialize bind params"); let sql = format!("DELETE FROM {TABLE_NAME} WHERE id = ?;"); let outcome = exec.exec(&sql, &params_json)?; if outcome.changes == 0 { diff --git a/crates/replica-db/src/models/media_image.rs b/crates/replica-db/src/models/media_image.rs @@ -11,11 +11,11 @@ use serde_json::Value; const TABLE_NAME: &str = "media_image"; -pub fn create<E: SqlExecutor>( - exec: &E, +pub fn create( + exec: &dyn SqlExecutor, opts: &IMediaImageCreate, ) -> Result<IMediaImageCreateResolve, IError<SqlError>> { - let field_map = utils::to_object_map(opts)?; + let field_map = utils::to_object_map(opts).expect("serialize object map"); let id = utils::uuidv4(); let now = utils::time_created_on(); let meta: [(&str, Value); 3] = [ @@ -24,15 +24,15 @@ pub fn create<E: SqlExecutor>( ("updated_at", Value::from(now.clone())), ]; let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let _ = exec.exec(&sql, &params_json)?; let on = MediaImageQueryBindValues::Id { id: id.clone() }; let result = find_one_by_on(exec, &on)?.ok_or(IError::from(SqlError::NotFound(id.clone())))?; Ok(IResult { result }) } -pub fn find_one<E: SqlExecutor>( - exec: &E, +pub fn find_one( + exec: &dyn SqlExecutor, opts: &IMediaImageFindOne, ) -> Result<IMediaImageFindOneResolve, IError<SqlError>> { let result = match opts { @@ -42,8 +42,8 @@ pub fn find_one<E: SqlExecutor>( Ok(IResult { result }) } -pub fn find_many<E: SqlExecutor>( - exec: &E, +pub fn find_many( + exec: &dyn SqlExecutor, opts: &IMediaImageFindMany, ) -> Result<IMediaImageFindManyResolve, IError<SqlError>> { let results = match opts { @@ -53,24 +53,24 @@ pub fn find_many<E: SqlExecutor>( Ok(IResultList { results }) } -fn find_many_filter<E: SqlExecutor>( - exec: &E, +fn find_many_filter( + exec: &dyn SqlExecutor, filter: &Option<IMediaImageFieldsFilter>, ) -> Result<Vec<MediaImage>, IError<SqlError>> { let (sql, bind_values) = utils::build_select_query_with_meta(TABLE_NAME, filter.as_ref()); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let json = exec.query_raw(&sql, &params_json)?; let rows: Vec<MediaImage> = utils::parse_json(&json)?; Ok(rows) } -fn find_one_by_on<E: SqlExecutor>( - exec: &E, +fn find_one_by_on( + exec: &dyn SqlExecutor, on: &MediaImageQueryBindValues, ) -> Result<Option<MediaImage>, IError<SqlError>> { let (column, value) = on.to_filter_param(); let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); - let params_json = utils::to_params_json(vec![value])?; + let params_json = utils::to_params_json(vec![value]).expect("serialize bind params"); let json = exec.query_raw(&sql, &params_json)?; let mut rows: Vec<MediaImage> = utils::parse_json(&json)?; Ok(rows.pop()) @@ -89,32 +89,33 @@ fn rel_query(rel: &MediaImageFindManyRel) -> (&'static str, Vec<Value>) { } } -fn find_one_by_rel<E: SqlExecutor>( - exec: &E, +fn find_one_by_rel( + exec: &dyn SqlExecutor, rel: &MediaImageFindManyRel, ) -> Result<Option<MediaImage>, IError<SqlError>> { let (sql, bind_values) = rel_query(rel); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let sql = format!("{sql} LIMIT 1;"); let json = exec.query_raw(&sql, &params_json)?; let mut rows: Vec<MediaImage> = utils::parse_json(&json)?; Ok(rows.pop()) } -fn find_many_by_rel<E: SqlExecutor>( - exec: &E, +fn find_many_by_rel( + exec: &dyn SqlExecutor, rel: &MediaImageFindManyRel, ) -> Result<Vec<MediaImage>, IError<SqlError>> { let (sql, bind_values) = rel_query(rel); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let sql = format!("{sql};"); let json = exec.query_raw(&sql, &params_json)?; let rows: Vec<MediaImage> = utils::parse_json(&json)?; Ok(rows) } -fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<MediaImage, IError<SqlError>> { - let params_json = utils::to_params_json(vec![Value::from(id.to_owned())])?; +fn select_by_id(exec: &dyn SqlExecutor, id: &str) -> Result<MediaImage, IError<SqlError>> { + let params_json = + utils::to_params_json(vec![Value::from(id.to_owned())]).expect("serialize bind params"); let sql = format!("SELECT * FROM {TABLE_NAME} WHERE id = ?;"); let json = exec.query_raw(&sql, &params_json)?; let mut rows: Vec<MediaImage> = utils::parse_json(&json)?; @@ -122,11 +123,12 @@ fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<MediaImage, IError .ok_or(IError::from(SqlError::NotFound(id.to_owned()))) } -pub fn update<E: SqlExecutor>( - exec: &E, +pub fn update( + exec: &dyn SqlExecutor, opts: &IMediaImageUpdate, ) -> Result<IMediaImageUpdateResolve, IError<SqlError>> { - let mut updates = utils::to_partial_object_map(&opts.fields)?; + let mut updates = + utils::to_partial_object_map(&opts.fields).expect("serialize partial object map"); if updates.is_empty() { return Err(IError::from(SqlError::InvalidArgument(String::from( "no fields to update", @@ -155,14 +157,14 @@ pub fn update<E: SqlExecutor>( "UPDATE {TABLE_NAME} SET {} WHERE id = ?;", set_parts.join(", ") ); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let _ = exec.exec(&sql, &params_json)?; let updated = select_by_id(exec, &id_for_lookup)?; Ok(IResult { result: updated }) } -pub fn delete<E: SqlExecutor>( - exec: &E, +pub fn delete( + exec: &dyn SqlExecutor, opts: &IMediaImageDelete, ) -> Result<IMediaImageDeleteResolve, IError<SqlError>> { let id_for_lookup = match opts { @@ -180,7 +182,8 @@ pub fn delete<E: SqlExecutor>( model.id } }; - let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())])?; + let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())]) + .expect("serialize bind params"); let sql = format!("DELETE FROM {TABLE_NAME} WHERE id = ?;"); let outcome = exec.exec(&sql, &params_json)?; if outcome.changes == 0 { diff --git a/crates/replica-db/src/models/nostr_event_state.rs b/crates/replica-db/src/models/nostr_event_state.rs @@ -12,11 +12,11 @@ use serde_json::Value; const TABLE_NAME: &str = "nostr_event_state"; -pub fn create<E: SqlExecutor>( - exec: &E, +pub fn create( + exec: &dyn SqlExecutor, opts: &INostrEventStateCreate, ) -> Result<INostrEventStateCreateResolve, IError<SqlError>> { - let field_map = utils::to_object_map(opts)?; + let field_map = utils::to_object_map(opts).expect("serialize object map"); let id = utils::uuidv4(); let now = utils::time_created_on(); let meta: [(&str, Value); 3] = [ @@ -25,15 +25,15 @@ pub fn create<E: SqlExecutor>( ("updated_at", Value::from(now.clone())), ]; let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let _ = exec.exec(&sql, &params_json)?; let on = NostrEventStateQueryBindValues::Id { id: id.clone() }; let result = find_one_by_on(exec, &on)?.ok_or(IError::from(SqlError::NotFound(id.clone())))?; Ok(IResult { result }) } -pub fn find_one<E: SqlExecutor>( - exec: &E, +pub fn find_one( + exec: &dyn SqlExecutor, opts: &INostrEventStateFindOne, ) -> Result<INostrEventStateFindOneResolve, IError<SqlError>> { let result = match opts { @@ -42,39 +42,40 @@ pub fn find_one<E: SqlExecutor>( Ok(IResult { result }) } -pub fn find_many<E: SqlExecutor>( - exec: &E, +pub fn find_many( + exec: &dyn SqlExecutor, opts: &INostrEventStateFindMany, ) -> Result<INostrEventStateFindManyResolve, IError<SqlError>> { let results = find_many_filter(exec, &opts.filter)?; Ok(IResultList { results }) } -fn find_many_filter<E: SqlExecutor>( - exec: &E, +fn find_many_filter( + exec: &dyn SqlExecutor, filter: &Option<INostrEventStateFieldsFilter>, ) -> Result<Vec<NostrEventState>, IError<SqlError>> { let (sql, bind_values) = utils::build_select_query_with_meta(TABLE_NAME, filter.as_ref()); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let json = exec.query_raw(&sql, &params_json)?; let rows: Vec<NostrEventState> = utils::parse_json(&json)?; Ok(rows) } -fn find_one_by_on<E: SqlExecutor>( - exec: &E, +fn find_one_by_on( + exec: &dyn SqlExecutor, on: &NostrEventStateQueryBindValues, ) -> Result<Option<NostrEventState>, IError<SqlError>> { let (column, value) = on.to_filter_param(); let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); - let params_json = utils::to_params_json(vec![value])?; + let params_json = utils::to_params_json(vec![value]).expect("serialize bind params"); let json = exec.query_raw(&sql, &params_json)?; let mut rows: Vec<NostrEventState> = utils::parse_json(&json)?; Ok(rows.pop()) } -fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<NostrEventState, IError<SqlError>> { - let params_json = utils::to_params_json(vec![Value::from(id.to_owned())])?; +fn select_by_id(exec: &dyn SqlExecutor, id: &str) -> Result<NostrEventState, IError<SqlError>> { + let params_json = + utils::to_params_json(vec![Value::from(id.to_owned())]).expect("serialize bind params"); let sql = format!("SELECT * FROM {TABLE_NAME} WHERE id = ?;"); let json = exec.query_raw(&sql, &params_json)?; let mut rows: Vec<NostrEventState> = utils::parse_json(&json)?; @@ -82,11 +83,12 @@ fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<NostrEventState, I .ok_or(IError::from(SqlError::NotFound(id.to_owned()))) } -pub fn update<E: SqlExecutor>( - exec: &E, +pub fn update( + exec: &dyn SqlExecutor, opts: &INostrEventStateUpdate, ) -> Result<INostrEventStateUpdateResolve, IError<SqlError>> { - let mut updates = utils::to_partial_object_map(&opts.fields)?; + let mut updates = + utils::to_partial_object_map(&opts.fields).expect("serialize partial object map"); if updates.is_empty() { return Err(IError::from(SqlError::InvalidArgument(String::from( "no fields to update", @@ -115,14 +117,14 @@ pub fn update<E: SqlExecutor>( "UPDATE {TABLE_NAME} SET {} WHERE id = ?;", set_parts.join(", ") ); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let _ = exec.exec(&sql, &params_json)?; let updated = select_by_id(exec, &id_for_lookup)?; Ok(IResult { result: updated }) } -pub fn delete<E: SqlExecutor>( - exec: &E, +pub fn delete( + exec: &dyn SqlExecutor, opts: &INostrEventStateDelete, ) -> Result<INostrEventStateDeleteResolve, IError<SqlError>> { let id_for_lookup = match opts { @@ -135,7 +137,8 @@ pub fn delete<E: SqlExecutor>( } }, }; - let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())])?; + let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())]) + .expect("serialize bind params"); let sql = format!("DELETE FROM {TABLE_NAME} WHERE id = ?;"); let outcome = exec.exec(&sql, &params_json)?; if outcome.changes == 0 { diff --git a/crates/replica-db/src/models/nostr_profile.rs b/crates/replica-db/src/models/nostr_profile.rs @@ -12,11 +12,11 @@ use serde_json::Value; const TABLE_NAME: &str = "nostr_profile"; -pub fn create<E: SqlExecutor>( - exec: &E, +pub fn create( + exec: &dyn SqlExecutor, opts: &INostrProfileCreate, ) -> Result<INostrProfileCreateResolve, IError<SqlError>> { - let field_map = utils::to_object_map(opts)?; + let field_map = utils::to_object_map(opts).expect("serialize object map"); let id = utils::uuidv4(); let now = utils::time_created_on(); let meta: [(&str, Value); 3] = [ @@ -25,15 +25,15 @@ pub fn create<E: SqlExecutor>( ("updated_at", Value::from(now.clone())), ]; let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let _ = exec.exec(&sql, &params_json)?; let on = NostrProfileQueryBindValues::Id { id: id.clone() }; let result = find_one_by_on(exec, &on)?.ok_or(IError::from(SqlError::NotFound(id.clone())))?; Ok(IResult { result }) } -pub fn find_one<E: SqlExecutor>( - exec: &E, +pub fn find_one( + exec: &dyn SqlExecutor, opts: &INostrProfileFindOne, ) -> Result<INostrProfileFindOneResolve, IError<SqlError>> { let result = match opts { @@ -43,8 +43,8 @@ pub fn find_one<E: SqlExecutor>( Ok(IResult { result }) } -pub fn find_many<E: SqlExecutor>( - exec: &E, +pub fn find_many( + exec: &dyn SqlExecutor, opts: &INostrProfileFindMany, ) -> Result<INostrProfileFindManyResolve, IError<SqlError>> { let results = match opts { @@ -54,24 +54,24 @@ pub fn find_many<E: SqlExecutor>( Ok(IResultList { results }) } -fn find_many_filter<E: SqlExecutor>( - exec: &E, +fn find_many_filter( + exec: &dyn SqlExecutor, filter: &Option<INostrProfileFieldsFilter>, ) -> Result<Vec<NostrProfile>, IError<SqlError>> { let (sql, bind_values) = utils::build_select_query_with_meta(TABLE_NAME, filter.as_ref()); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let json = exec.query_raw(&sql, &params_json)?; let rows: Vec<NostrProfile> = utils::parse_json(&json)?; Ok(rows) } -fn find_one_by_on<E: SqlExecutor>( - exec: &E, +fn find_one_by_on( + exec: &dyn SqlExecutor, on: &NostrProfileQueryBindValues, ) -> Result<Option<NostrProfile>, IError<SqlError>> { let (column, value) = on.to_filter_param(); let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); - let params_json = utils::to_params_json(vec![value])?; + let params_json = utils::to_params_json(vec![value]).expect("serialize bind params"); let json = exec.query_raw(&sql, &params_json)?; let mut rows: Vec<NostrProfile> = utils::parse_json(&json)?; Ok(rows.pop()) @@ -90,32 +90,33 @@ fn rel_query(rel: &NostrProfileFindManyRel) -> (&'static str, Vec<Value>) { } } -fn find_one_by_rel<E: SqlExecutor>( - exec: &E, +fn find_one_by_rel( + exec: &dyn SqlExecutor, rel: &NostrProfileFindManyRel, ) -> Result<Option<NostrProfile>, IError<SqlError>> { let (sql, bind_values) = rel_query(rel); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let sql = format!("{sql} LIMIT 1;"); let json = exec.query_raw(&sql, &params_json)?; let mut rows: Vec<NostrProfile> = utils::parse_json(&json)?; Ok(rows.pop()) } -fn find_many_by_rel<E: SqlExecutor>( - exec: &E, +fn find_many_by_rel( + exec: &dyn SqlExecutor, rel: &NostrProfileFindManyRel, ) -> Result<Vec<NostrProfile>, IError<SqlError>> { let (sql, bind_values) = rel_query(rel); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let sql = format!("{sql};"); let json = exec.query_raw(&sql, &params_json)?; let rows: Vec<NostrProfile> = utils::parse_json(&json)?; Ok(rows) } -fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<NostrProfile, IError<SqlError>> { - let params_json = utils::to_params_json(vec![Value::from(id.to_owned())])?; +fn select_by_id(exec: &dyn SqlExecutor, id: &str) -> Result<NostrProfile, IError<SqlError>> { + let params_json = + utils::to_params_json(vec![Value::from(id.to_owned())]).expect("serialize bind params"); let sql = format!("SELECT * FROM {TABLE_NAME} WHERE id = ?;"); let json = exec.query_raw(&sql, &params_json)?; let mut rows: Vec<NostrProfile> = utils::parse_json(&json)?; @@ -123,11 +124,12 @@ fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<NostrProfile, IErr .ok_or(IError::from(SqlError::NotFound(id.to_owned()))) } -pub fn update<E: SqlExecutor>( - exec: &E, +pub fn update( + exec: &dyn SqlExecutor, opts: &INostrProfileUpdate, ) -> Result<INostrProfileUpdateResolve, IError<SqlError>> { - let mut updates = utils::to_partial_object_map(&opts.fields)?; + let mut updates = + utils::to_partial_object_map(&opts.fields).expect("serialize partial object map"); if updates.is_empty() { return Err(IError::from(SqlError::InvalidArgument(String::from( "no fields to update", @@ -156,14 +158,14 @@ pub fn update<E: SqlExecutor>( "UPDATE {TABLE_NAME} SET {} WHERE id = ?;", set_parts.join(", ") ); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let _ = exec.exec(&sql, &params_json)?; let updated = select_by_id(exec, &id_for_lookup)?; Ok(IResult { result: updated }) } -pub fn delete<E: SqlExecutor>( - exec: &E, +pub fn delete( + exec: &dyn SqlExecutor, opts: &INostrProfileDelete, ) -> Result<INostrProfileDeleteResolve, IError<SqlError>> { let id_for_lookup = match opts { @@ -181,7 +183,8 @@ pub fn delete<E: SqlExecutor>( model.id } }; - let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())])?; + let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())]) + .expect("serialize bind params"); let sql = format!("DELETE FROM {TABLE_NAME} WHERE id = ?;"); let outcome = exec.exec(&sql, &params_json)?; if outcome.changes == 0 { diff --git a/crates/replica-db/src/models/nostr_profile_relay.rs b/crates/replica-db/src/models/nostr_profile_relay.rs @@ -8,8 +8,8 @@ use serde_json::Value; const TABLE_NAME: &str = "nostr_profile_relay"; -pub fn set<E: SqlExecutor>( - exec: &E, +pub fn set( + exec: &dyn SqlExecutor, opts: &INostrProfileRelayRelation, ) -> Result<INostrProfileRelayResolve, IError<SqlError>> { let mut query_vals: Vec<Value> = Vec::with_capacity(2); @@ -21,13 +21,13 @@ pub fn set<E: SqlExecutor>( "INSERT INTO {} (tb_pr, tb_rl) VALUES ((SELECT id FROM nostr_profile WHERE {} = ?), (SELECT id FROM nostr_relay WHERE {} = ?));", TABLE_NAME, nostr_profile_column, nostr_relay_column ); - let params_json = utils::to_params_json(query_vals)?; + let params_json = utils::to_params_json(query_vals).expect("serialize bind params"); let _ = exec.exec(&query, &params_json)?; Ok(IResultPass { pass: true }) } -pub fn unset<E: SqlExecutor>( - exec: &E, +pub fn unset( + exec: &dyn SqlExecutor, opts: &INostrProfileRelayRelation, ) -> Result<INostrProfileRelayResolve, IError<SqlError>> { let mut query_vals: Vec<Value> = Vec::with_capacity(2); @@ -39,7 +39,7 @@ pub fn unset<E: SqlExecutor>( "DELETE FROM {} WHERE tb_pr = (SELECT id FROM nostr_profile WHERE {} = ?) AND tb_rl = (SELECT id FROM nostr_relay WHERE {} = ?);", TABLE_NAME, nostr_profile_column, nostr_relay_column ); - let params_json = utils::to_params_json(query_vals)?; + let params_json = utils::to_params_json(query_vals).expect("serialize bind params"); let _ = exec.exec(&query, &params_json)?; Ok(IResultPass { pass: true }) } diff --git a/crates/replica-db/src/models/nostr_relay.rs b/crates/replica-db/src/models/nostr_relay.rs @@ -11,11 +11,11 @@ use serde_json::Value; const TABLE_NAME: &str = "nostr_relay"; -pub fn create<E: SqlExecutor>( - exec: &E, +pub fn create( + exec: &dyn SqlExecutor, opts: &INostrRelayCreate, ) -> Result<INostrRelayCreateResolve, IError<SqlError>> { - let field_map = utils::to_object_map(opts)?; + let field_map = utils::to_object_map(opts).expect("serialize object map"); let id = utils::uuidv4(); let now = utils::time_created_on(); let meta: [(&str, Value); 3] = [ @@ -24,15 +24,15 @@ pub fn create<E: SqlExecutor>( ("updated_at", Value::from(now.clone())), ]; let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let _ = exec.exec(&sql, &params_json)?; let on = NostrRelayQueryBindValues::Id { id: id.clone() }; let result = find_one_by_on(exec, &on)?.ok_or(IError::from(SqlError::NotFound(id.clone())))?; Ok(IResult { result }) } -pub fn find_one<E: SqlExecutor>( - exec: &E, +pub fn find_one( + exec: &dyn SqlExecutor, opts: &INostrRelayFindOne, ) -> Result<INostrRelayFindOneResolve, IError<SqlError>> { let result = match opts { @@ -42,8 +42,8 @@ pub fn find_one<E: SqlExecutor>( Ok(IResult { result }) } -pub fn find_many<E: SqlExecutor>( - exec: &E, +pub fn find_many( + exec: &dyn SqlExecutor, opts: &INostrRelayFindMany, ) -> Result<INostrRelayFindManyResolve, IError<SqlError>> { let results = match opts { @@ -53,24 +53,24 @@ pub fn find_many<E: SqlExecutor>( Ok(IResultList { results }) } -fn find_many_filter<E: SqlExecutor>( - exec: &E, +fn find_many_filter( + exec: &dyn SqlExecutor, filter: &Option<INostrRelayFieldsFilter>, ) -> Result<Vec<NostrRelay>, IError<SqlError>> { let (sql, bind_values) = utils::build_select_query_with_meta(TABLE_NAME, filter.as_ref()); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let json = exec.query_raw(&sql, &params_json)?; let rows: Vec<NostrRelay> = utils::parse_json(&json)?; Ok(rows) } -fn find_one_by_on<E: SqlExecutor>( - exec: &E, +fn find_one_by_on( + exec: &dyn SqlExecutor, on: &NostrRelayQueryBindValues, ) -> Result<Option<NostrRelay>, IError<SqlError>> { let (column, value) = on.to_filter_param(); let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); - let params_json = utils::to_params_json(vec![value])?; + let params_json = utils::to_params_json(vec![value]).expect("serialize bind params"); let json = exec.query_raw(&sql, &params_json)?; let mut rows: Vec<NostrRelay> = utils::parse_json(&json)?; Ok(rows.pop()) @@ -89,32 +89,33 @@ fn rel_query(rel: &NostrRelayFindManyRel) -> (&'static str, Vec<Value>) { } } -fn find_one_by_rel<E: SqlExecutor>( - exec: &E, +fn find_one_by_rel( + exec: &dyn SqlExecutor, rel: &NostrRelayFindManyRel, ) -> Result<Option<NostrRelay>, IError<SqlError>> { let (sql, bind_values) = rel_query(rel); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let sql = format!("{sql} LIMIT 1;"); let json = exec.query_raw(&sql, &params_json)?; let mut rows: Vec<NostrRelay> = utils::parse_json(&json)?; Ok(rows.pop()) } -fn find_many_by_rel<E: SqlExecutor>( - exec: &E, +fn find_many_by_rel( + exec: &dyn SqlExecutor, rel: &NostrRelayFindManyRel, ) -> Result<Vec<NostrRelay>, IError<SqlError>> { let (sql, bind_values) = rel_query(rel); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let sql = format!("{sql};"); let json = exec.query_raw(&sql, &params_json)?; let rows: Vec<NostrRelay> = utils::parse_json(&json)?; Ok(rows) } -fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<NostrRelay, IError<SqlError>> { - let params_json = utils::to_params_json(vec![Value::from(id.to_owned())])?; +fn select_by_id(exec: &dyn SqlExecutor, id: &str) -> Result<NostrRelay, IError<SqlError>> { + let params_json = + utils::to_params_json(vec![Value::from(id.to_owned())]).expect("serialize bind params"); let sql = format!("SELECT * FROM {TABLE_NAME} WHERE id = ?;"); let json = exec.query_raw(&sql, &params_json)?; let mut rows: Vec<NostrRelay> = utils::parse_json(&json)?; @@ -122,11 +123,12 @@ fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<NostrRelay, IError .ok_or(IError::from(SqlError::NotFound(id.to_owned()))) } -pub fn update<E: SqlExecutor>( - exec: &E, +pub fn update( + exec: &dyn SqlExecutor, opts: &INostrRelayUpdate, ) -> Result<INostrRelayUpdateResolve, IError<SqlError>> { - let mut updates = utils::to_partial_object_map(&opts.fields)?; + let mut updates = + utils::to_partial_object_map(&opts.fields).expect("serialize partial object map"); if updates.is_empty() { return Err(IError::from(SqlError::InvalidArgument(String::from( "no fields to update", @@ -155,14 +157,14 @@ pub fn update<E: SqlExecutor>( "UPDATE {TABLE_NAME} SET {} WHERE id = ?;", set_parts.join(", ") ); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let _ = exec.exec(&sql, &params_json)?; let updated = select_by_id(exec, &id_for_lookup)?; Ok(IResult { result: updated }) } -pub fn delete<E: SqlExecutor>( - exec: &E, +pub fn delete( + exec: &dyn SqlExecutor, opts: &INostrRelayDelete, ) -> Result<INostrRelayDeleteResolve, IError<SqlError>> { let id_for_lookup = match opts { @@ -180,7 +182,8 @@ pub fn delete<E: SqlExecutor>( model.id } }; - let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())])?; + let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())]) + .expect("serialize bind params"); let sql = format!("DELETE FROM {TABLE_NAME} WHERE id = ?;"); let outcome = exec.exec(&sql, &params_json)?; if outcome.changes == 0 { diff --git a/crates/replica-db/src/models/plot.rs b/crates/replica-db/src/models/plot.rs @@ -10,11 +10,11 @@ use serde_json::Value; const TABLE_NAME: &str = "plot"; -pub fn create<E: SqlExecutor>( - exec: &E, +pub fn create( + exec: &dyn SqlExecutor, opts: &IPlotCreate, ) -> Result<IPlotCreateResolve, IError<SqlError>> { - let field_map = utils::to_object_map(opts)?; + let field_map = utils::to_object_map(opts).expect("serialize object map"); let id = utils::uuidv4(); let now = utils::time_created_on(); let meta: [(&str, Value); 3] = [ @@ -23,15 +23,15 @@ pub fn create<E: SqlExecutor>( ("updated_at", Value::from(now.clone())), ]; let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let _ = exec.exec(&sql, &params_json)?; let on = PlotQueryBindValues::Id { id: id.clone() }; let result = find_one_by_on(exec, &on)?.ok_or(IError::from(SqlError::NotFound(id.clone())))?; Ok(IResult { result }) } -pub fn find_one<E: SqlExecutor>( - exec: &E, +pub fn find_one( + exec: &dyn SqlExecutor, opts: &IPlotFindOne, ) -> Result<IPlotFindOneResolve, IError<SqlError>> { let result = match opts { @@ -40,39 +40,40 @@ pub fn find_one<E: SqlExecutor>( Ok(IResult { result }) } -pub fn find_many<E: SqlExecutor>( - exec: &E, +pub fn find_many( + exec: &dyn SqlExecutor, opts: &IPlotFindMany, ) -> Result<IPlotFindManyResolve, IError<SqlError>> { let results = find_many_filter(exec, &opts.filter)?; Ok(IResultList { results }) } -fn find_many_filter<E: SqlExecutor>( - exec: &E, +fn find_many_filter( + exec: &dyn SqlExecutor, filter: &Option<IPlotFieldsFilter>, ) -> Result<Vec<Plot>, IError<SqlError>> { let (sql, bind_values) = utils::build_select_query_with_meta(TABLE_NAME, filter.as_ref()); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let json = exec.query_raw(&sql, &params_json)?; let rows: Vec<Plot> = utils::parse_json(&json)?; Ok(rows) } -fn find_one_by_on<E: SqlExecutor>( - exec: &E, +fn find_one_by_on( + exec: &dyn SqlExecutor, on: &PlotQueryBindValues, ) -> Result<Option<Plot>, IError<SqlError>> { let (column, value) = on.to_filter_param(); let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); - let params_json = utils::to_params_json(vec![value])?; + let params_json = utils::to_params_json(vec![value]).expect("serialize bind params"); let json = exec.query_raw(&sql, &params_json)?; let mut rows: Vec<Plot> = utils::parse_json(&json)?; Ok(rows.pop()) } -fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<Plot, IError<SqlError>> { - let params_json = utils::to_params_json(vec![Value::from(id.to_owned())])?; +fn select_by_id(exec: &dyn SqlExecutor, id: &str) -> Result<Plot, IError<SqlError>> { + let params_json = + utils::to_params_json(vec![Value::from(id.to_owned())]).expect("serialize bind params"); let sql = format!("SELECT * FROM {TABLE_NAME} WHERE id = ?;"); let json = exec.query_raw(&sql, &params_json)?; let mut rows: Vec<Plot> = utils::parse_json(&json)?; @@ -80,11 +81,12 @@ fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<Plot, IError<SqlEr .ok_or(IError::from(SqlError::NotFound(id.to_owned()))) } -pub fn update<E: SqlExecutor>( - exec: &E, +pub fn update( + exec: &dyn SqlExecutor, opts: &IPlotUpdate, ) -> Result<IPlotUpdateResolve, IError<SqlError>> { - let mut updates = utils::to_partial_object_map(&opts.fields)?; + let mut updates = + utils::to_partial_object_map(&opts.fields).expect("serialize partial object map"); if updates.is_empty() { return Err(IError::from(SqlError::InvalidArgument(String::from( "no fields to update", @@ -113,14 +115,14 @@ pub fn update<E: SqlExecutor>( "UPDATE {TABLE_NAME} SET {} WHERE id = ?;", set_parts.join(", ") ); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let _ = exec.exec(&sql, &params_json)?; let updated = select_by_id(exec, &id_for_lookup)?; Ok(IResult { result: updated }) } -pub fn delete<E: SqlExecutor>( - exec: &E, +pub fn delete( + exec: &dyn SqlExecutor, opts: &IPlotDelete, ) -> Result<IPlotDeleteResolve, IError<SqlError>> { let id_for_lookup = match opts { @@ -133,7 +135,8 @@ pub fn delete<E: SqlExecutor>( } }, }; - let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())])?; + let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())]) + .expect("serialize bind params"); let sql = format!("DELETE FROM {TABLE_NAME} WHERE id = ?;"); let outcome = exec.exec(&sql, &params_json)?; if outcome.changes == 0 { diff --git a/crates/replica-db/src/models/plot_gcs_location.rs b/crates/replica-db/src/models/plot_gcs_location.rs @@ -12,11 +12,11 @@ use serde_json::Value; const TABLE_NAME: &str = "plot_gcs_location"; -pub fn create<E: SqlExecutor>( - exec: &E, +pub fn create( + exec: &dyn SqlExecutor, opts: &IPlotGcsLocationCreate, ) -> Result<IPlotGcsLocationCreateResolve, IError<SqlError>> { - let field_map = utils::to_object_map(opts)?; + let field_map = utils::to_object_map(opts).expect("serialize object map"); let id = utils::uuidv4(); let now = utils::time_created_on(); let meta: [(&str, Value); 3] = [ @@ -25,15 +25,15 @@ pub fn create<E: SqlExecutor>( ("updated_at", Value::from(now.clone())), ]; let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let _ = exec.exec(&sql, &params_json)?; let on = PlotGcsLocationQueryBindValues::Id { id: id.clone() }; let result = find_one_by_on(exec, &on)?.ok_or(IError::from(SqlError::NotFound(id.clone())))?; Ok(IResult { result }) } -pub fn find_one<E: SqlExecutor>( - exec: &E, +pub fn find_one( + exec: &dyn SqlExecutor, opts: &IPlotGcsLocationFindOne, ) -> Result<IPlotGcsLocationFindOneResolve, IError<SqlError>> { let result = match opts { @@ -42,39 +42,40 @@ pub fn find_one<E: SqlExecutor>( Ok(IResult { result }) } -pub fn find_many<E: SqlExecutor>( - exec: &E, +pub fn find_many( + exec: &dyn SqlExecutor, opts: &IPlotGcsLocationFindMany, ) -> Result<IPlotGcsLocationFindManyResolve, IError<SqlError>> { let results = find_many_filter(exec, &opts.filter)?; Ok(IResultList { results }) } -fn find_many_filter<E: SqlExecutor>( - exec: &E, +fn find_many_filter( + exec: &dyn SqlExecutor, filter: &Option<IPlotGcsLocationFieldsFilter>, ) -> Result<Vec<PlotGcsLocation>, IError<SqlError>> { let (sql, bind_values) = utils::build_select_query_with_meta(TABLE_NAME, filter.as_ref()); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let json = exec.query_raw(&sql, &params_json)?; let rows: Vec<PlotGcsLocation> = utils::parse_json(&json)?; Ok(rows) } -fn find_one_by_on<E: SqlExecutor>( - exec: &E, +fn find_one_by_on( + exec: &dyn SqlExecutor, on: &PlotGcsLocationQueryBindValues, ) -> Result<Option<PlotGcsLocation>, IError<SqlError>> { let (column, value) = on.to_filter_param(); let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); - let params_json = utils::to_params_json(vec![value])?; + let params_json = utils::to_params_json(vec![value]).expect("serialize bind params"); let json = exec.query_raw(&sql, &params_json)?; let mut rows: Vec<PlotGcsLocation> = utils::parse_json(&json)?; Ok(rows.pop()) } -fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<PlotGcsLocation, IError<SqlError>> { - let params_json = utils::to_params_json(vec![Value::from(id.to_owned())])?; +fn select_by_id(exec: &dyn SqlExecutor, id: &str) -> Result<PlotGcsLocation, IError<SqlError>> { + let params_json = + utils::to_params_json(vec![Value::from(id.to_owned())]).expect("serialize bind params"); let sql = format!("SELECT * FROM {TABLE_NAME} WHERE id = ?;"); let json = exec.query_raw(&sql, &params_json)?; let mut rows: Vec<PlotGcsLocation> = utils::parse_json(&json)?; @@ -82,11 +83,12 @@ fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<PlotGcsLocation, I .ok_or(IError::from(SqlError::NotFound(id.to_owned()))) } -pub fn update<E: SqlExecutor>( - exec: &E, +pub fn update( + exec: &dyn SqlExecutor, opts: &IPlotGcsLocationUpdate, ) -> Result<IPlotGcsLocationUpdateResolve, IError<SqlError>> { - let mut updates = utils::to_partial_object_map(&opts.fields)?; + let mut updates = + utils::to_partial_object_map(&opts.fields).expect("serialize partial object map"); if updates.is_empty() { return Err(IError::from(SqlError::InvalidArgument(String::from( "no fields to update", @@ -115,14 +117,14 @@ pub fn update<E: SqlExecutor>( "UPDATE {TABLE_NAME} SET {} WHERE id = ?;", set_parts.join(", ") ); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let _ = exec.exec(&sql, &params_json)?; let updated = select_by_id(exec, &id_for_lookup)?; Ok(IResult { result: updated }) } -pub fn delete<E: SqlExecutor>( - exec: &E, +pub fn delete( + exec: &dyn SqlExecutor, opts: &IPlotGcsLocationDelete, ) -> Result<IPlotGcsLocationDeleteResolve, IError<SqlError>> { let id_for_lookup = match opts { @@ -135,7 +137,8 @@ pub fn delete<E: SqlExecutor>( } }, }; - let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())])?; + let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())]) + .expect("serialize bind params"); let sql = format!("DELETE FROM {TABLE_NAME} WHERE id = ?;"); let outcome = exec.exec(&sql, &params_json)?; if outcome.changes == 0 { diff --git a/crates/replica-db/src/models/plot_tag.rs b/crates/replica-db/src/models/plot_tag.rs @@ -10,11 +10,11 @@ use serde_json::Value; const TABLE_NAME: &str = "plot_tag"; -pub fn create<E: SqlExecutor>( - exec: &E, +pub fn create( + exec: &dyn SqlExecutor, opts: &IPlotTagCreate, ) -> Result<IPlotTagCreateResolve, IError<SqlError>> { - let field_map = utils::to_object_map(opts)?; + let field_map = utils::to_object_map(opts).expect("serialize object map"); let id = utils::uuidv4(); let now = utils::time_created_on(); let meta: [(&str, Value); 3] = [ @@ -23,15 +23,15 @@ pub fn create<E: SqlExecutor>( ("updated_at", Value::from(now.clone())), ]; let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let _ = exec.exec(&sql, &params_json)?; let on = PlotTagQueryBindValues::Id { id: id.clone() }; let result = find_one_by_on(exec, &on)?.ok_or(IError::from(SqlError::NotFound(id.clone())))?; Ok(IResult { result }) } -pub fn find_one<E: SqlExecutor>( - exec: &E, +pub fn find_one( + exec: &dyn SqlExecutor, opts: &IPlotTagFindOne, ) -> Result<IPlotTagFindOneResolve, IError<SqlError>> { let result = match opts { @@ -40,39 +40,40 @@ pub fn find_one<E: SqlExecutor>( Ok(IResult { result }) } -pub fn find_many<E: SqlExecutor>( - exec: &E, +pub fn find_many( + exec: &dyn SqlExecutor, opts: &IPlotTagFindMany, ) -> Result<IPlotTagFindManyResolve, IError<SqlError>> { let results = find_many_filter(exec, &opts.filter)?; Ok(IResultList { results }) } -fn find_many_filter<E: SqlExecutor>( - exec: &E, +fn find_many_filter( + exec: &dyn SqlExecutor, filter: &Option<IPlotTagFieldsFilter>, ) -> Result<Vec<PlotTag>, IError<SqlError>> { let (sql, bind_values) = utils::build_select_query_with_meta(TABLE_NAME, filter.as_ref()); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let json = exec.query_raw(&sql, &params_json)?; let rows: Vec<PlotTag> = utils::parse_json(&json)?; Ok(rows) } -fn find_one_by_on<E: SqlExecutor>( - exec: &E, +fn find_one_by_on( + exec: &dyn SqlExecutor, on: &PlotTagQueryBindValues, ) -> Result<Option<PlotTag>, IError<SqlError>> { let (column, value) = on.to_filter_param(); let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); - let params_json = utils::to_params_json(vec![value])?; + let params_json = utils::to_params_json(vec![value]).expect("serialize bind params"); let json = exec.query_raw(&sql, &params_json)?; let mut rows: Vec<PlotTag> = utils::parse_json(&json)?; Ok(rows.pop()) } -fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<PlotTag, IError<SqlError>> { - let params_json = utils::to_params_json(vec![Value::from(id.to_owned())])?; +fn select_by_id(exec: &dyn SqlExecutor, id: &str) -> Result<PlotTag, IError<SqlError>> { + let params_json = + utils::to_params_json(vec![Value::from(id.to_owned())]).expect("serialize bind params"); let sql = format!("SELECT * FROM {TABLE_NAME} WHERE id = ?;"); let json = exec.query_raw(&sql, &params_json)?; let mut rows: Vec<PlotTag> = utils::parse_json(&json)?; @@ -80,11 +81,12 @@ fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<PlotTag, IError<Sq .ok_or(IError::from(SqlError::NotFound(id.to_owned()))) } -pub fn update<E: SqlExecutor>( - exec: &E, +pub fn update( + exec: &dyn SqlExecutor, opts: &IPlotTagUpdate, ) -> Result<IPlotTagUpdateResolve, IError<SqlError>> { - let mut updates = utils::to_partial_object_map(&opts.fields)?; + let mut updates = + utils::to_partial_object_map(&opts.fields).expect("serialize partial object map"); if updates.is_empty() { return Err(IError::from(SqlError::InvalidArgument(String::from( "no fields to update", @@ -113,14 +115,14 @@ pub fn update<E: SqlExecutor>( "UPDATE {TABLE_NAME} SET {} WHERE id = ?;", set_parts.join(", ") ); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let _ = exec.exec(&sql, &params_json)?; let updated = select_by_id(exec, &id_for_lookup)?; Ok(IResult { result: updated }) } -pub fn delete<E: SqlExecutor>( - exec: &E, +pub fn delete( + exec: &dyn SqlExecutor, opts: &IPlotTagDelete, ) -> Result<IPlotTagDeleteResolve, IError<SqlError>> { let id_for_lookup = match opts { @@ -133,7 +135,8 @@ pub fn delete<E: SqlExecutor>( } }, }; - let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())])?; + let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())]) + .expect("serialize bind params"); let sql = format!("DELETE FROM {TABLE_NAME} WHERE id = ?;"); let outcome = exec.exec(&sql, &params_json)?; if outcome.changes == 0 { diff --git a/crates/replica-db/src/models/trade_product.rs b/crates/replica-db/src/models/trade_product.rs @@ -11,11 +11,11 @@ use serde_json::Value; const TABLE_NAME: &str = "trade_product"; -pub fn create<E: SqlExecutor>( - exec: &E, +pub fn create( + exec: &dyn SqlExecutor, opts: &ITradeProductCreate, ) -> Result<ITradeProductCreateResolve, IError<SqlError>> { - let field_map = utils::to_object_map(opts)?; + let field_map = utils::to_object_map(opts).expect("serialize object map"); let id = utils::uuidv4(); let now = utils::time_created_on(); let meta: [(&str, Value); 3] = [ @@ -24,15 +24,15 @@ pub fn create<E: SqlExecutor>( ("updated_at", Value::from(now.clone())), ]; let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let _ = exec.exec(&sql, &params_json)?; let on = TradeProductQueryBindValues::Id { id: id.clone() }; let result = find_one_by_on(exec, &on)?.ok_or(IError::from(SqlError::NotFound(id.clone())))?; Ok(IResult { result }) } -pub fn find_one<E: SqlExecutor>( - exec: &E, +pub fn find_one( + exec: &dyn SqlExecutor, opts: &ITradeProductFindOne, ) -> Result<ITradeProductFindOneResolve, IError<SqlError>> { let result = match opts { @@ -41,39 +41,40 @@ pub fn find_one<E: SqlExecutor>( Ok(IResult { result }) } -pub fn find_many<E: SqlExecutor>( - exec: &E, +pub fn find_many( + exec: &dyn SqlExecutor, opts: &ITradeProductFindMany, ) -> Result<ITradeProductFindManyResolve, IError<SqlError>> { let results = find_many_filter(exec, &opts.filter)?; Ok(IResultList { results }) } -fn find_many_filter<E: SqlExecutor>( - exec: &E, +fn find_many_filter( + exec: &dyn SqlExecutor, filter: &Option<ITradeProductFieldsFilter>, ) -> Result<Vec<TradeProduct>, IError<SqlError>> { let (sql, bind_values) = utils::build_select_query_with_meta(TABLE_NAME, filter.as_ref()); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let json = exec.query_raw(&sql, &params_json)?; let rows: Vec<TradeProduct> = utils::parse_json(&json)?; Ok(rows) } -fn find_one_by_on<E: SqlExecutor>( - exec: &E, +fn find_one_by_on( + exec: &dyn SqlExecutor, on: &TradeProductQueryBindValues, ) -> Result<Option<TradeProduct>, IError<SqlError>> { let (column, value) = on.to_filter_param(); let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); - let params_json = utils::to_params_json(vec![value])?; + let params_json = utils::to_params_json(vec![value]).expect("serialize bind params"); let json = exec.query_raw(&sql, &params_json)?; let mut rows: Vec<TradeProduct> = utils::parse_json(&json)?; Ok(rows.pop()) } -fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<TradeProduct, IError<SqlError>> { - let params_json = utils::to_params_json(vec![Value::from(id.to_owned())])?; +fn select_by_id(exec: &dyn SqlExecutor, id: &str) -> Result<TradeProduct, IError<SqlError>> { + let params_json = + utils::to_params_json(vec![Value::from(id.to_owned())]).expect("serialize bind params"); let sql = format!("SELECT * FROM {TABLE_NAME} WHERE id = ?;"); let json = exec.query_raw(&sql, &params_json)?; let mut rows: Vec<TradeProduct> = utils::parse_json(&json)?; @@ -81,11 +82,12 @@ fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<TradeProduct, IErr .ok_or(IError::from(SqlError::NotFound(id.to_owned()))) } -pub fn update<E: SqlExecutor>( - exec: &E, +pub fn update( + exec: &dyn SqlExecutor, opts: &ITradeProductUpdate, ) -> Result<ITradeProductUpdateResolve, IError<SqlError>> { - let mut updates = utils::to_partial_object_map(&opts.fields)?; + let mut updates = + utils::to_partial_object_map(&opts.fields).expect("serialize partial object map"); if updates.is_empty() { return Err(IError::from(SqlError::InvalidArgument(String::from( "no fields to update", @@ -109,14 +111,14 @@ pub fn update<E: SqlExecutor>( "UPDATE {TABLE_NAME} SET {} WHERE id = ?;", set_parts.join(", ") ); - let params_json = utils::to_params_json(bind_values)?; + let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); let _ = exec.exec(&sql, &params_json)?; let updated = select_by_id(exec, &id_for_lookup)?; Ok(IResult { result: updated }) } -pub fn delete<E: SqlExecutor>( - exec: &E, +pub fn delete( + exec: &dyn SqlExecutor, opts: &ITradeProductDelete, ) -> Result<ITradeProductDeleteResolve, IError<SqlError>> { let id_for_lookup = match opts { @@ -124,7 +126,8 @@ pub fn delete<E: SqlExecutor>( TradeProductQueryBindValues::Id { id } => id.clone(), }, }; - let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())])?; + let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())]) + .expect("serialize bind params"); let sql = format!("DELETE FROM {TABLE_NAME} WHERE id = ?;"); let outcome = exec.exec(&sql, &params_json)?; if outcome.changes == 0 { diff --git a/crates/replica-db/src/models/trade_product_location.rs b/crates/replica-db/src/models/trade_product_location.rs @@ -8,8 +8,8 @@ use serde_json::Value; const TABLE_NAME: &str = "trade_product_location"; -pub fn set<E: SqlExecutor>( - exec: &E, +pub fn set( + exec: &dyn SqlExecutor, opts: &ITradeProductLocationRelation, ) -> Result<ITradeProductLocationResolve, IError<SqlError>> { let mut query_vals: Vec<Value> = Vec::with_capacity(2); @@ -21,13 +21,13 @@ pub fn set<E: SqlExecutor>( "INSERT INTO {} (tb_tp, tb_gl) VALUES ((SELECT id FROM trade_product WHERE {} = ?), (SELECT id FROM gcs_location WHERE {} = ?));", TABLE_NAME, trade_product_column, gcs_location_column ); - let params_json = utils::to_params_json(query_vals)?; + let params_json = utils::to_params_json(query_vals).expect("serialize bind params"); let _ = exec.exec(&query, &params_json)?; Ok(IResultPass { pass: true }) } -pub fn unset<E: SqlExecutor>( - exec: &E, +pub fn unset( + exec: &dyn SqlExecutor, opts: &ITradeProductLocationRelation, ) -> Result<ITradeProductLocationResolve, IError<SqlError>> { let mut query_vals: Vec<Value> = Vec::with_capacity(2); @@ -39,7 +39,7 @@ pub fn unset<E: SqlExecutor>( "DELETE FROM {} WHERE tb_tp = (SELECT id FROM trade_product WHERE {} = ?) AND tb_gl = (SELECT id FROM gcs_location WHERE {} = ?);", TABLE_NAME, trade_product_column, gcs_location_column ); - let params_json = utils::to_params_json(query_vals)?; + let params_json = utils::to_params_json(query_vals).expect("serialize bind params"); let _ = exec.exec(&query, &params_json)?; Ok(IResultPass { pass: true }) } diff --git a/crates/replica-db/src/models/trade_product_media.rs b/crates/replica-db/src/models/trade_product_media.rs @@ -8,8 +8,8 @@ use serde_json::Value; const TABLE_NAME: &str = "trade_product_media"; -pub fn set<E: SqlExecutor>( - exec: &E, +pub fn set( + exec: &dyn SqlExecutor, opts: &ITradeProductMediaRelation, ) -> Result<ITradeProductMediaResolve, IError<SqlError>> { let mut query_vals: Vec<Value> = Vec::with_capacity(2); @@ -21,13 +21,13 @@ pub fn set<E: SqlExecutor>( "INSERT INTO {} (tb_tp, tb_mu) VALUES ((SELECT id FROM trade_product WHERE {} = ?), (SELECT id FROM media_image WHERE {} = ?));", TABLE_NAME, trade_product_column, media_image_column ); - let params_json = utils::to_params_json(query_vals)?; + let params_json = utils::to_params_json(query_vals).expect("serialize bind params"); let _ = exec.exec(&query, &params_json)?; Ok(IResultPass { pass: true }) } -pub fn unset<E: SqlExecutor>( - exec: &E, +pub fn unset( + exec: &dyn SqlExecutor, opts: &ITradeProductMediaRelation, ) -> Result<ITradeProductMediaResolve, IError<SqlError>> { let mut query_vals: Vec<Value> = Vec::with_capacity(2); @@ -39,7 +39,7 @@ pub fn unset<E: SqlExecutor>( "DELETE FROM {} WHERE tb_tp = (SELECT id FROM trade_product WHERE {} = ?) AND tb_mu = (SELECT id FROM media_image WHERE {} = ?);", TABLE_NAME, trade_product_column, media_image_column ); - let params_json = utils::to_params_json(query_vals)?; + let params_json = utils::to_params_json(query_vals).expect("serialize bind params"); let _ = exec.exec(&query, &params_json)?; Ok(IResultPass { pass: true }) } diff --git a/crates/replica-db/tests/backup_export_paths.rs b/crates/replica-db/tests/backup_export_paths.rs @@ -0,0 +1,305 @@ +use std::collections::VecDeque; +use std::sync::Mutex; + +use radroots_replica_db::backup::{ + DATABASE_BACKUP_VERSION, DatabaseBackup, REPLICA_DB_VERSION, SchemaEntry, TableData, + export_database_backup, export_database_backup_json, restore_database_backup, +}; +use radroots_replica_db::export::export_manifest; +use radroots_replica_db::{ExecOutcome, SqlError, SqlExecutor}; +use serde_json::{Map, Value, json}; + +struct PatternExecutor { + query_rules: Vec<(String, String)>, + fail_query_contains: Option<String>, + fail_exec_contains: Option<String>, + fail_begin: bool, + fail_commit: bool, + begin_count: Mutex<usize>, + commit_count: Mutex<usize>, + rollback_count: Mutex<usize>, + query_queue: Mutex<VecDeque<Result<String, SqlError>>>, + exec_queue: Mutex<VecDeque<Result<ExecOutcome, SqlError>>>, +} + +impl PatternExecutor { + fn new() -> Self { + Self { + query_rules: Vec::new(), + fail_query_contains: None, + fail_exec_contains: None, + fail_begin: false, + fail_commit: false, + begin_count: Mutex::new(0), + commit_count: Mutex::new(0), + rollback_count: Mutex::new(0), + query_queue: Mutex::new(VecDeque::new()), + exec_queue: Mutex::new(VecDeque::new()), + } + } + + fn with_query_rule(mut self, needle: &str, response: &str) -> Self { + self.query_rules + .push((needle.to_string(), response.to_string())); + self + } + + fn with_query_failure(mut self, needle: &str) -> Self { + self.fail_query_contains = Some(needle.to_string()); + self + } + + fn with_exec_failure(mut self, needle: &str) -> Self { + self.fail_exec_contains = Some(needle.to_string()); + self + } + + fn with_begin_failure(mut self) -> Self { + self.fail_begin = true; + self + } + + fn with_commit_failure(mut self) -> Self { + self.fail_commit = true; + self + } +} + +impl SqlExecutor for PatternExecutor { + fn exec(&self, sql: &str, _params_json: &str) -> Result<ExecOutcome, SqlError> { + if let Some(result) = self.exec_queue.lock().expect("exec queue lock").pop_front() { + return result; + } + if let Some(needle) = &self.fail_exec_contains { + if sql.contains(needle) { + return Err(SqlError::InvalidQuery(String::from("forced exec failure"))); + } + } + Ok(ExecOutcome { + changes: 1, + last_insert_id: 1, + }) + } + + fn query_raw(&self, sql: &str, _params_json: &str) -> Result<String, SqlError> { + if let Some(result) = self + .query_queue + .lock() + .expect("query queue lock") + .pop_front() + { + return result; + } + if let Some(needle) = &self.fail_query_contains { + if sql.contains(needle) { + return Err(SqlError::InvalidQuery(String::from("forced query failure"))); + } + } + for (needle, response) in &self.query_rules { + if sql.contains(needle) { + return Ok(response.clone()); + } + } + Ok(String::from("[]")) + } + + fn begin(&self) -> Result<(), SqlError> { + *self.begin_count.lock().expect("begin count lock") += 1; + if self.fail_begin { + return Err(SqlError::InvalidQuery(String::from("forced begin failure"))); + } + Ok(()) + } + + fn commit(&self) -> Result<(), SqlError> { + *self.commit_count.lock().expect("commit count lock") += 1; + if self.fail_commit { + return Err(SqlError::InvalidQuery(String::from( + "forced commit failure", + ))); + } + Ok(()) + } + + fn rollback(&self) -> Result<(), SqlError> { + *self.rollback_count.lock().expect("rollback count lock") += 1; + Ok(()) + } +} + +fn assert_sql_error_code<T: core::fmt::Debug>(result: Result<T, SqlError>, code: &str) { + let err = result.unwrap_err(); + assert_eq!(err.code(), code); +} + +fn backup_with_versions(format_version: &str, replica_db_version: &str) -> DatabaseBackup { + DatabaseBackup { + format_version: format_version.to_string(), + replica_db_version: replica_db_version.to_string(), + schema: Vec::new(), + migrations: Vec::new(), + data: Vec::new(), + } +} + +#[test] +fn backup_public_api_error_paths_cover_library_instantiations() { + let schema_query = "select type, name, tbl_name as table_name, sql from sqlite_master"; + + let executor = PatternExecutor::new().with_query_failure(schema_query); + assert_sql_error_code(export_database_backup(&executor), "ERR_INVALID_QUERY"); + + let schema_rows = json!([ + { + "type": "table", + "name": "tb_a", + "table_name": "tb_a", + "sql": "CREATE TABLE tb_a (id TEXT);" + } + ]) + .to_string(); + let executor = PatternExecutor::new() + .with_query_rule(schema_query, &schema_rows) + .with_query_failure("SELECT * FROM \"tb_a\";"); + assert_sql_error_code(export_database_backup(&executor), "ERR_INVALID_QUERY"); + + let executor = PatternExecutor::new().with_query_failure(schema_query); + assert_sql_error_code(export_database_backup_json(&executor), "ERR_INVALID_QUERY"); + + let executor = PatternExecutor::new().with_query_rule(schema_query, "[]"); + let backup_json = export_database_backup_json(&executor).expect("backup json success"); + assert!(backup_json.contains("\"schema\":[]")); + + let executor = PatternExecutor::new(); + let backup = backup_with_versions("0.0.1", REPLICA_DB_VERSION); + assert_sql_error_code( + restore_database_backup(&executor, &backup), + "ERR_INVALID_ARGUMENT", + ); + + let backup = backup_with_versions(DATABASE_BACKUP_VERSION, REPLICA_DB_VERSION); + let executor = PatternExecutor::new().with_exec_failure("PRAGMA foreign_keys = OFF;"); + assert_sql_error_code( + restore_database_backup(&executor, &backup), + "ERR_INVALID_QUERY", + ); + + let executor = PatternExecutor::new().with_begin_failure(); + assert_sql_error_code( + restore_database_backup(&executor, &backup), + "ERR_INVALID_QUERY", + ); + + let executor = + PatternExecutor::new().with_query_failure("select type, name from sqlite_master"); + assert_sql_error_code( + restore_database_backup(&executor, &backup), + "ERR_INVALID_QUERY", + ); + + let executor = PatternExecutor::new().with_commit_failure(); + assert_sql_error_code( + restore_database_backup(&executor, &backup), + "ERR_INVALID_QUERY", + ); + + let executor = PatternExecutor::new().with_exec_failure("PRAGMA foreign_keys = ON;"); + assert_sql_error_code( + restore_database_backup(&executor, &backup), + "ERR_INVALID_QUERY", + ); +} + +#[test] +fn backup_public_api_insert_and_parse_failures_cover_library_instantiations() { + let backup = DatabaseBackup { + format_version: DATABASE_BACKUP_VERSION.to_string(), + replica_db_version: REPLICA_DB_VERSION.to_string(), + schema: vec![SchemaEntry { + object_type: String::from("table"), + name: String::from("tb_a"), + table_name: Some(String::from("tb_a")), + sql: Some(String::from("CREATE TABLE tb_a (id TEXT);")), + }], + migrations: Vec::new(), + data: vec![TableData { + name: String::from("tb_a"), + rows: vec![{ + let mut row = Map::new(); + row.insert(String::from("id"), Value::from("1")); + row + }], + }], + }; + + let executor = PatternExecutor::new().with_exec_failure("INSERT INTO \"tb_a\""); + assert_sql_error_code( + restore_database_backup(&executor, &backup), + "ERR_INVALID_QUERY", + ); + + let schema_query = "select type, name, tbl_name as table_name, sql from sqlite_master"; + let executor = PatternExecutor::new().with_query_rule(schema_query, "{"); + assert_sql_error_code(export_database_backup(&executor), "ERR_SERIALIZATION"); + + let schema_rows = json!([ + { + "type": "table", + "name": "tb_a", + "table_name": "tb_a", + "sql": "CREATE TABLE tb_a (id TEXT);" + } + ]) + .to_string(); + let executor = PatternExecutor::new() + .with_query_rule(schema_query, &schema_rows) + .with_query_rule("SELECT * FROM \"tb_a\";", "{"); + assert_sql_error_code(export_database_backup(&executor), "ERR_SERIALIZATION"); + + let null_name_rows = json!([ + { + "type": "table", + "name": null, + "table_name": "tb_a", + "sql": "CREATE TABLE tb_a (id TEXT);" + } + ]) + .to_string(); + let executor = PatternExecutor::new().with_query_rule(schema_query, &null_name_rows); + let backup = export_database_backup(&executor).expect("backup with null-name schema rows"); + assert!(backup.schema.is_empty()); +} + +#[test] +fn export_manifest_public_api_error_paths_cover_library_instantiations() { + let schema_query = "select type, name, tbl_name as table_name, sql from sqlite_master"; + + let executor = PatternExecutor::new().with_query_failure(schema_query); + assert_sql_error_code(export_manifest(&executor), "ERR_INVALID_QUERY"); + + let schema_rows = json!([ + { + "type": "table", + "name": "tb_a", + "table_name": "tb_a", + "sql": "CREATE TABLE tb_a (id TEXT);" + } + ]) + .to_string(); + + let executor = PatternExecutor::new() + .with_query_rule(schema_query, &schema_rows) + .with_query_failure("select count(1) as count from \"tb_a\""); + assert_sql_error_code(export_manifest(&executor), "ERR_INVALID_QUERY"); + + let executor = PatternExecutor::new() + .with_query_rule(schema_query, &schema_rows) + .with_query_rule("select count(1) as count from \"tb_a\"", "{"); + assert_sql_error_code(export_manifest(&executor), "ERR_SERIALIZATION"); + + let executor = PatternExecutor::new() + .with_query_rule(schema_query, &schema_rows) + .with_query_rule("select count(1) as count from \"tb_a\"", "[]"); + let manifest = export_manifest(&executor).expect("manifest success"); + assert_eq!(manifest.table_counts.len(), 1); +} diff --git a/crates/replica-db/tests/error_paths.rs b/crates/replica-db/tests/error_paths.rs @@ -32,11 +32,11 @@ use radroots_replica_db_schema::nostr_event_state::{ INostrEventStateCreate, INostrEventStateDelete, INostrEventStateFindMany, INostrEventStateFindOne, INostrEventStateUpdate, }; -use radroots_replica_db_schema::nostr_profile_relay::INostrProfileRelayRelation; use radroots_replica_db_schema::nostr_profile::{ INostrProfileCreate, INostrProfileDelete, INostrProfileFindMany, INostrProfileFindOne, INostrProfileUpdate, }; +use radroots_replica_db_schema::nostr_profile_relay::INostrProfileRelayRelation; use radroots_replica_db_schema::nostr_relay::{ INostrRelayCreate, INostrRelayDelete, INostrRelayFindMany, INostrRelayFindOne, INostrRelayUpdate, diff --git a/crates/replica-db/tests/region_scripted_paths.rs b/crates/replica-db/tests/region_scripted_paths.rs @@ -0,0 +1,741 @@ +use std::collections::VecDeque; +use std::sync::Mutex; + +use radroots_replica_db::{ExecOutcome, ReplicaSql, SqlError, SqlExecutor}; +use radroots_replica_db_schema::farm::{ + IFarmCreate, IFarmDelete, IFarmFindMany, IFarmFindOne, IFarmUpdate, +}; +use radroots_replica_db_schema::farm_gcs_location::{ + IFarmGcsLocationCreate, IFarmGcsLocationDelete, IFarmGcsLocationFindMany, + IFarmGcsLocationFindOne, IFarmGcsLocationUpdate, +}; +use radroots_replica_db_schema::farm_member::{ + IFarmMemberCreate, IFarmMemberDelete, IFarmMemberFindMany, IFarmMemberFindOne, + IFarmMemberUpdate, +}; +use radroots_replica_db_schema::farm_member_claim::{ + IFarmMemberClaimCreate, IFarmMemberClaimDelete, IFarmMemberClaimFindMany, + IFarmMemberClaimFindOne, IFarmMemberClaimUpdate, +}; +use radroots_replica_db_schema::farm_tag::{ + IFarmTagCreate, IFarmTagDelete, IFarmTagFindMany, IFarmTagFindOne, IFarmTagUpdate, +}; +use radroots_replica_db_schema::gcs_location::{ + GcsLocationFarmArgs, GcsLocationFindManyRel, GcsLocationPlotArgs, GcsLocationTradeProductArgs, + IGcsLocationCreate, IGcsLocationDelete, IGcsLocationFindMany, IGcsLocationFindOne, + IGcsLocationFindOneRelArgs, IGcsLocationUpdate, +}; +use radroots_replica_db_schema::log_error::{ + ILogErrorCreate, ILogErrorDelete, ILogErrorFindMany, ILogErrorFindOne, ILogErrorUpdate, +}; +use radroots_replica_db_schema::media_image::{ + IMediaImageCreate, IMediaImageDelete, IMediaImageFindMany, IMediaImageFindOne, + IMediaImageFindOneRelArgs, IMediaImageUpdate, MediaImageFindManyRel, + MediaImageTradeProductArgs, +}; +use radroots_replica_db_schema::nostr_event_state::{ + INostrEventStateCreate, INostrEventStateDelete, INostrEventStateFindMany, + INostrEventStateFindOne, INostrEventStateUpdate, +}; +use radroots_replica_db_schema::nostr_profile::{ + INostrProfileCreate, INostrProfileDelete, INostrProfileFindMany, INostrProfileFindOne, + INostrProfileFindOneRelArgs, INostrProfileUpdate, NostrProfileFindManyRel, + NostrProfileRelayArgs, +}; +use radroots_replica_db_schema::nostr_relay::{ + INostrRelayCreate, INostrRelayDelete, INostrRelayFindMany, INostrRelayFindOne, + INostrRelayFindOneRelArgs, INostrRelayUpdate, NostrRelayFindManyRel, NostrRelayProfileArgs, +}; +use radroots_replica_db_schema::plot::{ + IPlotCreate, IPlotDelete, IPlotFindMany, IPlotFindOne, IPlotUpdate, +}; +use radroots_replica_db_schema::plot_gcs_location::{ + IPlotGcsLocationCreate, IPlotGcsLocationDelete, IPlotGcsLocationFindMany, + IPlotGcsLocationFindOne, IPlotGcsLocationUpdate, +}; +use radroots_replica_db_schema::plot_tag::{ + IPlotTagCreate, IPlotTagDelete, IPlotTagFindMany, IPlotTagFindOne, IPlotTagUpdate, +}; +use radroots_replica_db_schema::trade_product::{ + ITradeProductCreate, ITradeProductFindMany, ITradeProductFindOne, ITradeProductUpdate, +}; +use radroots_types::types::IError; +use serde::de::DeserializeOwned; +use serde_json::json; + +struct ScriptedExecutor { + exec_results: Mutex<VecDeque<Result<ExecOutcome, SqlError>>>, + query_results: Mutex<VecDeque<Result<String, SqlError>>>, + begin_results: Mutex<VecDeque<Result<(), SqlError>>>, + commit_results: Mutex<VecDeque<Result<(), SqlError>>>, + rollback_results: Mutex<VecDeque<Result<(), SqlError>>>, +} + +impl ScriptedExecutor { + fn new( + exec_results: Vec<Result<ExecOutcome, SqlError>>, + query_results: Vec<Result<String, SqlError>>, + ) -> Self { + Self { + exec_results: Mutex::new(VecDeque::from(exec_results)), + query_results: Mutex::new(VecDeque::from(query_results)), + begin_results: Mutex::new(VecDeque::new()), + commit_results: Mutex::new(VecDeque::new()), + rollback_results: Mutex::new(VecDeque::new()), + } + } +} + +impl SqlExecutor for ScriptedExecutor { + fn exec(&self, _sql: &str, _params_json: &str) -> Result<ExecOutcome, SqlError> { + if let Some(result) = self + .exec_results + .lock() + .expect("lock exec queue") + .pop_front() + { + result + } else { + Ok(ExecOutcome { + changes: 1, + last_insert_id: 1, + }) + } + } + + fn query_raw(&self, _sql: &str, _params_json: &str) -> Result<String, SqlError> { + if let Some(result) = self + .query_results + .lock() + .expect("lock query queue") + .pop_front() + { + result + } else { + Ok(String::from("[]")) + } + } + + fn begin(&self) -> Result<(), SqlError> { + if let Some(result) = self + .begin_results + .lock() + .expect("lock begin queue") + .pop_front() + { + result + } else { + Ok(()) + } + } + + fn commit(&self) -> Result<(), SqlError> { + if let Some(result) = self + .commit_results + .lock() + .expect("lock commit queue") + .pop_front() + { + result + } else { + Ok(()) + } + } + + fn rollback(&self) -> Result<(), SqlError> { + if let Some(result) = self + .rollback_results + .lock() + .expect("lock rollback queue") + .pop_front() + { + result + } else { + Ok(()) + } + } +} + +fn parse_json<T: DeserializeOwned>(value: serde_json::Value) -> T { + serde_json::from_value(value).expect("valid test payload") +} + +fn hex64(ch: char) -> String { + std::iter::repeat_n(ch, 64).collect() +} + +fn err_query() -> Result<String, SqlError> { + Err(SqlError::InvalidQuery(String::from("forced query error"))) +} + +fn bad_json() -> Result<String, SqlError> { + Ok(String::from("{")) +} + +fn ok_rows() -> Result<String, SqlError> { + Ok(String::from("[]")) +} + +fn ok_exec() -> Result<ExecOutcome, SqlError> { + Ok(ExecOutcome { + changes: 1, + last_insert_id: 1, + }) +} + +fn db_with_scripts( + exec_results: Vec<Result<ExecOutcome, SqlError>>, + query_results: Vec<Result<String, SqlError>>, +) -> ReplicaSql<ScriptedExecutor> { + ReplicaSql::new(ScriptedExecutor::new(exec_results, query_results)) +} + +fn assert_ierror_code<T>(result: Result<T, IError<SqlError>>, code: &str) { + let err = match result { + Ok(_) => panic!("expected ierror"), + Err(err) => err, + }; + assert_eq!(err.err.code(), code); +} + +macro_rules! assert_secondary_model_paths { + ( + $test_name:ident, + $create_ty:ty, $create_json:expr, $create_call:ident, + $find_many_ty:ty, $find_many_json:expr, $find_many_call:ident, + $find_one_ty:ty, $find_one_json:expr, $find_one_call:ident, + $update_ty:ty, $update_id_json:expr, $update_lookup_json:expr, $update_call:ident, + $delete_ty:ty, $delete_lookup_json:expr, $delete_call:ident + ) => { + #[test] + fn $test_name() { + let create_opts: $create_ty = parse_json($create_json); + + let db = db_with_scripts(vec![ok_exec()], vec![err_query()]); + assert_ierror_code(db.$create_call(&create_opts), "ERR_INVALID_QUERY"); + + let db = db_with_scripts(vec![ok_exec()], vec![ok_rows()]); + assert_ierror_code(db.$create_call(&create_opts), "ERR_NOT_FOUND"); + + let find_many_opts: $find_many_ty = parse_json($find_many_json); + let db = db_with_scripts(vec![], vec![bad_json()]); + assert_ierror_code(db.$find_many_call(&find_many_opts), "ERR_SERIALIZATION"); + + let find_one_opts: $find_one_ty = parse_json($find_one_json); + let db = db_with_scripts(vec![], vec![bad_json()]); + assert_ierror_code(db.$find_one_call(&find_one_opts), "ERR_SERIALIZATION"); + + let update_id_opts: $update_ty = parse_json($update_id_json); + let db = db_with_scripts(vec![ok_exec()], vec![err_query()]); + assert_ierror_code(db.$update_call(&update_id_opts), "ERR_INVALID_QUERY"); + + let db = db_with_scripts(vec![ok_exec()], vec![bad_json()]); + assert_ierror_code(db.$update_call(&update_id_opts), "ERR_SERIALIZATION"); + + let update_lookup_opts: $update_ty = parse_json($update_lookup_json); + let db = db_with_scripts(vec![], vec![err_query()]); + assert_ierror_code(db.$update_call(&update_lookup_opts), "ERR_INVALID_QUERY"); + + let delete_lookup_opts: $delete_ty = parse_json($delete_lookup_json); + let db = db_with_scripts(vec![], vec![err_query()]); + assert_ierror_code(db.$delete_call(&delete_lookup_opts), "ERR_INVALID_QUERY"); + } + }; +} + +macro_rules! assert_rel_model_paths { + ( + $test_name:ident, + $create_ty:ty, $create_json:expr, $create_call:ident, + $find_many_ty:ty, $find_many_filter_json:expr, $find_many_rel_expr:expr, $find_many_call:ident, + $find_one_ty:ty, $find_one_on_json:expr, $find_one_rel_expr:expr, $find_one_call:ident, + $update_ty:ty, $update_id_json:expr, $update_lookup_json:expr, $update_call:ident, + $delete_ty:ty, $delete_lookup_json:expr, $delete_rel_expr:expr, $delete_call:ident + ) => { + #[test] + fn $test_name() { + let create_opts: $create_ty = parse_json($create_json); + + let db = db_with_scripts(vec![ok_exec()], vec![err_query()]); + assert_ierror_code(db.$create_call(&create_opts), "ERR_INVALID_QUERY"); + + let db = db_with_scripts(vec![ok_exec()], vec![ok_rows()]); + assert_ierror_code(db.$create_call(&create_opts), "ERR_NOT_FOUND"); + + let find_many_filter_opts: $find_many_ty = parse_json($find_many_filter_json); + let db = db_with_scripts(vec![], vec![bad_json()]); + assert_ierror_code( + db.$find_many_call(&find_many_filter_opts), + "ERR_SERIALIZATION", + ); + + let find_many_rel_opts: $find_many_ty = $find_many_rel_expr; + let db = db_with_scripts(vec![], vec![bad_json()]); + assert_ierror_code(db.$find_many_call(&find_many_rel_opts), "ERR_SERIALIZATION"); + + let find_many_rel_opts: $find_many_ty = $find_many_rel_expr; + let db = db_with_scripts(vec![], vec![err_query()]); + assert_ierror_code(db.$find_many_call(&find_many_rel_opts), "ERR_INVALID_QUERY"); + + let find_one_on_opts: $find_one_ty = parse_json($find_one_on_json); + let db = db_with_scripts(vec![], vec![bad_json()]); + assert_ierror_code(db.$find_one_call(&find_one_on_opts), "ERR_SERIALIZATION"); + + let find_one_rel_opts: $find_one_ty = $find_one_rel_expr; + let db = db_with_scripts(vec![], vec![bad_json()]); + assert_ierror_code(db.$find_one_call(&find_one_rel_opts), "ERR_SERIALIZATION"); + + let update_id_opts: $update_ty = parse_json($update_id_json); + let db = db_with_scripts(vec![ok_exec()], vec![err_query()]); + assert_ierror_code(db.$update_call(&update_id_opts), "ERR_INVALID_QUERY"); + + let db = db_with_scripts(vec![ok_exec()], vec![bad_json()]); + assert_ierror_code(db.$update_call(&update_id_opts), "ERR_SERIALIZATION"); + + let update_lookup_opts: $update_ty = parse_json($update_lookup_json); + let db = db_with_scripts(vec![], vec![err_query()]); + assert_ierror_code(db.$update_call(&update_lookup_opts), "ERR_INVALID_QUERY"); + + let delete_lookup_opts: $delete_ty = parse_json($delete_lookup_json); + let db = db_with_scripts(vec![], vec![err_query()]); + assert_ierror_code(db.$delete_call(&delete_lookup_opts), "ERR_INVALID_QUERY"); + + let delete_rel_opts: $delete_ty = $delete_rel_expr; + let db = db_with_scripts(vec![], vec![err_query()]); + assert_ierror_code(db.$delete_call(&delete_rel_opts), "ERR_INVALID_QUERY"); + } + }; +} + +macro_rules! assert_trade_product_paths { + ( + $test_name:ident, + $create_ty:ty, $create_json:expr, $create_call:ident, + $find_many_ty:ty, $find_many_json:expr, $find_many_call:ident, + $find_one_ty:ty, $find_one_json:expr, $find_one_call:ident, + $update_ty:ty, $update_json:expr, $update_call:ident + ) => { + #[test] + fn $test_name() { + let create_opts: $create_ty = parse_json($create_json); + + let db = db_with_scripts(vec![ok_exec()], vec![err_query()]); + assert_ierror_code(db.$create_call(&create_opts), "ERR_INVALID_QUERY"); + + let db = db_with_scripts(vec![ok_exec()], vec![ok_rows()]); + assert_ierror_code(db.$create_call(&create_opts), "ERR_NOT_FOUND"); + + let find_many_opts: $find_many_ty = parse_json($find_many_json); + let db = db_with_scripts(vec![], vec![bad_json()]); + assert_ierror_code(db.$find_many_call(&find_many_opts), "ERR_SERIALIZATION"); + + let find_one_opts: $find_one_ty = parse_json($find_one_json); + let db = db_with_scripts(vec![], vec![bad_json()]); + assert_ierror_code(db.$find_one_call(&find_one_opts), "ERR_SERIALIZATION"); + + let update_opts: $update_ty = parse_json($update_json); + let db = db_with_scripts(vec![ok_exec()], vec![err_query()]); + assert_ierror_code(db.$update_call(&update_opts), "ERR_INVALID_QUERY"); + + let db = db_with_scripts(vec![ok_exec()], vec![bad_json()]); + assert_ierror_code(db.$update_call(&update_opts), "ERR_SERIALIZATION"); + } + }; +} + +assert_secondary_model_paths!( + farm_scripted_region_paths, + IFarmCreate, + json!({ "d_tag": "farm-a", "pubkey": hex64('a'), "name": "farm a" }), + farm_create, + IFarmFindMany, + json!({ "filter": { "id": "id-1" } }), + farm_find_many, + IFarmFindOne, + json!({ "on": { "id": "id-1" } }), + farm_find_one, + IFarmUpdate, + json!({ "on": { "id": "id-1" }, "fields": { "name": "farm z" } }), + json!({ "on": { "d_tag": "farm-a" }, "fields": { "name": "farm y" } }), + farm_update, + IFarmDelete, + json!({ "on": { "d_tag": "farm-a" } }), + farm_delete +); + +assert_secondary_model_paths!( + plot_scripted_region_paths, + IPlotCreate, + json!({ "d_tag": "plot-a", "farm_id": "farm-1", "name": "plot a" }), + plot_create, + IPlotFindMany, + json!({ "filter": { "id": "id-1" } }), + plot_find_many, + IPlotFindOne, + json!({ "on": { "id": "id-1" } }), + plot_find_one, + IPlotUpdate, + json!({ "on": { "id": "id-1" }, "fields": { "name": "plot z" } }), + json!({ "on": { "d_tag": "plot-a" }, "fields": { "name": "plot y" } }), + plot_update, + IPlotDelete, + json!({ "on": { "d_tag": "plot-a" } }), + plot_delete +); + +assert_secondary_model_paths!( + farm_gcs_location_scripted_region_paths, + IFarmGcsLocationCreate, + json!({ "farm_id": "farm-1", "gcs_location_id": "gcs-1", "role": "primary" }), + farm_gcs_location_create, + IFarmGcsLocationFindMany, + json!({ "filter": { "id": "id-1" } }), + farm_gcs_location_find_many, + IFarmGcsLocationFindOne, + json!({ "on": { "id": "id-1" } }), + farm_gcs_location_find_one, + IFarmGcsLocationUpdate, + json!({ "on": { "id": "id-1" }, "fields": { "role": "z" } }), + json!({ "on": { "farm_id": "farm-1" }, "fields": { "role": "y" } }), + farm_gcs_location_update, + IFarmGcsLocationDelete, + json!({ "on": { "farm_id": "farm-1" } }), + farm_gcs_location_delete +); + +assert_secondary_model_paths!( + plot_gcs_location_scripted_region_paths, + IPlotGcsLocationCreate, + json!({ "plot_id": "plot-1", "gcs_location_id": "gcs-1", "role": "primary" }), + plot_gcs_location_create, + IPlotGcsLocationFindMany, + json!({ "filter": { "id": "id-1" } }), + plot_gcs_location_find_many, + IPlotGcsLocationFindOne, + json!({ "on": { "id": "id-1" } }), + plot_gcs_location_find_one, + IPlotGcsLocationUpdate, + json!({ "on": { "id": "id-1" }, "fields": { "role": "z" } }), + json!({ "on": { "plot_id": "plot-1" }, "fields": { "role": "y" } }), + plot_gcs_location_update, + IPlotGcsLocationDelete, + json!({ "on": { "plot_id": "plot-1" } }), + plot_gcs_location_delete +); + +assert_secondary_model_paths!( + farm_tag_scripted_region_paths, + IFarmTagCreate, + json!({ "farm_id": "farm-1", "tag": "organic" }), + farm_tag_create, + IFarmTagFindMany, + json!({ "filter": { "id": "id-1" } }), + farm_tag_find_many, + IFarmTagFindOne, + json!({ "on": { "id": "id-1" } }), + farm_tag_find_one, + IFarmTagUpdate, + json!({ "on": { "id": "id-1" }, "fields": { "tag": "z" } }), + json!({ "on": { "farm_id": "farm-1" }, "fields": { "tag": "y" } }), + farm_tag_update, + IFarmTagDelete, + json!({ "on": { "farm_id": "farm-1" } }), + farm_tag_delete +); + +assert_secondary_model_paths!( + plot_tag_scripted_region_paths, + IPlotTagCreate, + json!({ "plot_id": "plot-1", "tag": "north" }), + plot_tag_create, + IPlotTagFindMany, + json!({ "filter": { "id": "id-1" } }), + plot_tag_find_many, + IPlotTagFindOne, + json!({ "on": { "id": "id-1" } }), + plot_tag_find_one, + IPlotTagUpdate, + json!({ "on": { "id": "id-1" }, "fields": { "tag": "z" } }), + json!({ "on": { "plot_id": "plot-1" }, "fields": { "tag": "y" } }), + plot_tag_update, + IPlotTagDelete, + json!({ "on": { "plot_id": "plot-1" } }), + plot_tag_delete +); + +assert_secondary_model_paths!( + farm_member_scripted_region_paths, + IFarmMemberCreate, + json!({ "farm_id": "farm-1", "member_pubkey": hex64('b'), "role": "owner" }), + farm_member_create, + IFarmMemberFindMany, + json!({ "filter": { "id": "id-1" } }), + farm_member_find_many, + IFarmMemberFindOne, + json!({ "on": { "id": "id-1" } }), + farm_member_find_one, + IFarmMemberUpdate, + json!({ "on": { "id": "id-1" }, "fields": { "role": "z" } }), + json!({ "on": { "member_pubkey": hex64('b') }, "fields": { "role": "y" } }), + farm_member_update, + IFarmMemberDelete, + json!({ "on": { "member_pubkey": hex64('b') } }), + farm_member_delete +); + +assert_secondary_model_paths!( + farm_member_claim_scripted_region_paths, + IFarmMemberClaimCreate, + json!({ "member_pubkey": hex64('b'), "farm_pubkey": hex64('a') }), + farm_member_claim_create, + IFarmMemberClaimFindMany, + json!({ "filter": { "id": "id-1" } }), + farm_member_claim_find_many, + IFarmMemberClaimFindOne, + json!({ "on": { "id": "id-1" } }), + farm_member_claim_find_one, + IFarmMemberClaimUpdate, + json!({ "on": { "id": "id-1" }, "fields": { "farm_pubkey": hex64('c') } }), + json!({ "on": { "member_pubkey": hex64('b') }, "fields": { "farm_pubkey": hex64('d') } }), + farm_member_claim_update, + IFarmMemberClaimDelete, + json!({ "on": { "member_pubkey": hex64('b') } }), + farm_member_claim_delete +); + +assert_secondary_model_paths!( + log_error_scripted_region_paths, + ILogErrorCreate, + json!({ + "error": "panic", + "message": "boom", + "app_system": "studio", + "app_version": "1.0.0", + "nostr_pubkey": hex64('c') + }), + log_error_create, + ILogErrorFindMany, + json!({ "filter": { "id": "id-1" } }), + log_error_find_many, + ILogErrorFindOne, + json!({ "on": { "id": "id-1" } }), + log_error_find_one, + ILogErrorUpdate, + json!({ "on": { "id": "id-1" }, "fields": { "message": "z" } }), + json!({ "on": { "nostr_pubkey": hex64('c') }, "fields": { "message": "y" } }), + log_error_update, + ILogErrorDelete, + json!({ "on": { "nostr_pubkey": hex64('c') } }), + log_error_delete +); + +assert_secondary_model_paths!( + nostr_event_state_scripted_region_paths, + INostrEventStateCreate, + json!({ + "key": "state-a", + "kind": 30023, + "pubkey": hex64('d'), + "d_tag": "listing-a", + "last_event_id": hex64('e'), + "last_created_at": 1, + "content_hash": "hash-a" + }), + nostr_event_state_create, + INostrEventStateFindMany, + json!({ "filter": { "id": "id-1" } }), + nostr_event_state_find_many, + INostrEventStateFindOne, + json!({ "on": { "id": "id-1" } }), + nostr_event_state_find_one, + INostrEventStateUpdate, + json!({ "on": { "id": "id-1" }, "fields": { "content_hash": "hash-z" } }), + json!({ "on": { "key": "state-a" }, "fields": { "content_hash": "hash-y" } }), + nostr_event_state_update, + INostrEventStateDelete, + json!({ "on": { "key": "state-a" } }), + nostr_event_state_delete +); + +assert_rel_model_paths!( + gcs_location_scripted_region_paths, + IGcsLocationCreate, + json!({ + "d_tag": "gcs-a", + "lat": 59.33, + "lng": 18.06, + "geohash": "u6sce4f", + "point": "POINT(18.06 59.33)", + "polygon": "POLYGON((18.06 59.33,18.07 59.33,18.07 59.34,18.06 59.34,18.06 59.33))" + }), + gcs_location_create, + IGcsLocationFindMany, + json!({ "filter": { "id": "id-1" } }), + IGcsLocationFindMany::Rel { + rel: GcsLocationFindManyRel::OnFarm(GcsLocationFarmArgs { + id: String::from("farm-1") + }) + }, + gcs_location_find_many, + IGcsLocationFindOne, + json!({ "on": { "id": "id-1" } }), + IGcsLocationFindOne::Rel(IGcsLocationFindOneRelArgs { + rel: GcsLocationFindManyRel::OffTradeProduct(GcsLocationTradeProductArgs { + id: String::from("tp-1") + }) + }), + gcs_location_find_one, + IGcsLocationUpdate, + json!({ "on": { "id": "id-1" }, "fields": { "label": "z" } }), + json!({ "on": { "d_tag": "gcs-a" }, "fields": { "label": "y" } }), + gcs_location_update, + IGcsLocationDelete, + json!({ "on": { "d_tag": "gcs-a" } }), + IGcsLocationDelete::Rel(IGcsLocationFindOneRelArgs { + rel: GcsLocationFindManyRel::OnPlot(GcsLocationPlotArgs { + id: String::from("plot-1") + }) + }), + gcs_location_delete +); + +assert_rel_model_paths!( + media_image_scripted_region_paths, + IMediaImageCreate, + json!({ + "file_path": "/img/a.jpg", + "mime_type": "image/jpeg", + "res_base": "https://cdn.example.com", + "res_path": "img/a.jpg" + }), + media_image_create, + IMediaImageFindMany, + json!({ "filter": { "id": "id-1" } }), + IMediaImageFindMany::Rel { + rel: MediaImageFindManyRel::OnTradeProduct(MediaImageTradeProductArgs { + id: String::from("tp-1") + }) + }, + media_image_find_many, + IMediaImageFindOne, + json!({ "on": { "id": "id-1" } }), + IMediaImageFindOne::Rel(IMediaImageFindOneRelArgs { + rel: MediaImageFindManyRel::OffTradeProduct(MediaImageTradeProductArgs { + id: String::from("tp-1") + }) + }), + media_image_find_one, + IMediaImageUpdate, + json!({ "on": { "id": "id-1" }, "fields": { "label": "z" } }), + json!({ "on": { "file_path": "/img/a.jpg" }, "fields": { "label": "y" } }), + media_image_update, + IMediaImageDelete, + json!({ "on": { "file_path": "/img/a.jpg" } }), + IMediaImageDelete::Rel(IMediaImageFindOneRelArgs { + rel: MediaImageFindManyRel::OnTradeProduct(MediaImageTradeProductArgs { + id: String::from("tp-1") + }) + }), + media_image_delete +); + +assert_rel_model_paths!( + nostr_profile_scripted_region_paths, + INostrProfileCreate, + json!({ "public_key": hex64('d'), "profile_type": "farm", "name": "profile a" }), + nostr_profile_create, + INostrProfileFindMany, + json!({ "filter": { "id": "id-1" } }), + INostrProfileFindMany::Rel { + rel: NostrProfileFindManyRel::OnRelay(NostrProfileRelayArgs { + id: String::from("relay-1") + }) + }, + nostr_profile_find_many, + INostrProfileFindOne, + json!({ "on": { "id": "id-1" } }), + INostrProfileFindOne::Rel(INostrProfileFindOneRelArgs { + rel: NostrProfileFindManyRel::OffRelay(NostrProfileRelayArgs { + id: String::from("relay-1") + }) + }), + nostr_profile_find_one, + INostrProfileUpdate, + json!({ "on": { "id": "id-1" }, "fields": { "name": "z" } }), + json!({ "on": { "public_key": hex64('d') }, "fields": { "name": "y" } }), + nostr_profile_update, + INostrProfileDelete, + json!({ "on": { "public_key": hex64('d') } }), + INostrProfileDelete::Rel(INostrProfileFindOneRelArgs { + rel: NostrProfileFindManyRel::OnRelay(NostrProfileRelayArgs { + id: String::from("relay-1") + }) + }), + nostr_profile_delete +); + +assert_rel_model_paths!( + nostr_relay_scripted_region_paths, + INostrRelayCreate, + json!({ "url": "wss://relay.example.com" }), + nostr_relay_create, + INostrRelayFindMany, + json!({ "filter": { "id": "id-1" } }), + INostrRelayFindMany::Rel { + rel: NostrRelayFindManyRel::OnProfile(NostrRelayProfileArgs { + public_key: hex64('d') + }) + }, + nostr_relay_find_many, + INostrRelayFindOne, + json!({ "on": { "id": "id-1" } }), + INostrRelayFindOne::Rel(INostrRelayFindOneRelArgs { + rel: NostrRelayFindManyRel::OffProfile(NostrRelayProfileArgs { + public_key: hex64('d') + }) + }), + nostr_relay_find_one, + INostrRelayUpdate, + json!({ "on": { "id": "id-1" }, "fields": { "name": "z" } }), + json!({ "on": { "url": "wss://relay.example.com" }, "fields": { "name": "y" } }), + nostr_relay_update, + INostrRelayDelete, + json!({ "on": { "url": "wss://relay.example.com" } }), + INostrRelayDelete::Rel(INostrRelayFindOneRelArgs { + rel: NostrRelayFindManyRel::OnProfile(NostrRelayProfileArgs { + public_key: hex64('d') + }) + }), + nostr_relay_delete +); + +assert_trade_product_paths!( + trade_product_scripted_region_paths, + ITradeProductCreate, + json!({ + "key": "product-a", + "category": "coffee", + "title": "coffee a", + "summary": "summary", + "process": "washed", + "lot": "lot-a", + "profile": "floral", + "year": 2024, + "qty_amt": 100, + "qty_unit": "kg", + "price_amt": 7.5, + "price_currency": "USD", + "price_qty_amt": 1, + "price_qty_unit": "kg" + }), + trade_product_create, + ITradeProductFindMany, + json!({ "filter": { "id": "id-1" } }), + trade_product_find_many, + ITradeProductFindOne, + json!({ "on": { "id": "id-1" } }), + trade_product_find_one, + ITradeProductUpdate, + json!({ "on": { "id": "id-1" }, "fields": { "title": "z" } }), + trade_product_update +);