nostr_relay.rs (8574B)
1 use radroots_replica_schema::nostr_relay::{ 2 INostrRelayCreate, INostrRelayCreateResolve, INostrRelayDelete, INostrRelayDeleteResolve, 3 INostrRelayFieldsFilter, INostrRelayFindMany, INostrRelayFindManyResolve, INostrRelayFindOne, 4 INostrRelayFindOneResolve, INostrRelayUpdate, INostrRelayUpdateResolve, NostrRelay, 5 NostrRelayFindManyRel, NostrRelayQueryBindValues, 6 }; 7 use radroots_replica_schema::{ReplicaSchemaError, ReplicaSchemaResult, ReplicaSchemaResultList}; 8 use radroots_sql_core::error::SqlError; 9 use radroots_sql_core::{SqlExecutor, utils}; 10 use serde_json::Value; 11 12 const TABLE_NAME: &str = "nostr_relay"; 13 14 pub fn create( 15 exec: &dyn SqlExecutor, 16 opts: &INostrRelayCreate, 17 ) -> Result<INostrRelayCreateResolve, ReplicaSchemaError<SqlError>> { 18 let field_map = utils::to_object_map(opts).expect("serialize object map"); 19 let id = utils::uuidv4(); 20 let now = utils::time_created_on(); 21 let meta: [(&str, Value); 3] = [ 22 ("id", Value::from(id.clone())), 23 ("created_at", Value::from(now.clone())), 24 ("updated_at", Value::from(now.clone())), 25 ]; 26 let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map); 27 let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); 28 let _ = exec.exec(&sql, ¶ms_json)?; 29 let on = NostrRelayQueryBindValues::Id { id: id.clone() }; 30 let result = find_one_by_on(exec, &on)? 31 .ok_or(ReplicaSchemaError::from(SqlError::NotFound(id.clone())))?; 32 Ok(ReplicaSchemaResult { result }) 33 } 34 35 pub fn find_one( 36 exec: &dyn SqlExecutor, 37 opts: &INostrRelayFindOne, 38 ) -> Result<INostrRelayFindOneResolve, ReplicaSchemaError<SqlError>> { 39 let result = match opts { 40 INostrRelayFindOne::On(args) => find_one_by_on(exec, &args.on)?, 41 INostrRelayFindOne::Rel(args) => find_one_by_rel(exec, &args.rel)?, 42 }; 43 Ok(ReplicaSchemaResult { result }) 44 } 45 46 pub fn find_many( 47 exec: &dyn SqlExecutor, 48 opts: &INostrRelayFindMany, 49 ) -> Result<INostrRelayFindManyResolve, ReplicaSchemaError<SqlError>> { 50 let results = match opts { 51 INostrRelayFindMany::Filter { filter } => find_many_filter(exec, filter)?, 52 INostrRelayFindMany::Rel { rel } => find_many_by_rel(exec, rel)?, 53 }; 54 Ok(ReplicaSchemaResultList { results }) 55 } 56 57 fn find_many_filter( 58 exec: &dyn SqlExecutor, 59 filter: &Option<INostrRelayFieldsFilter>, 60 ) -> Result<Vec<NostrRelay>, ReplicaSchemaError<SqlError>> { 61 let (sql, bind_values) = utils::build_select_query_with_meta(TABLE_NAME, filter.as_ref()); 62 let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); 63 let json = exec.query_raw(&sql, ¶ms_json)?; 64 let rows: Vec<NostrRelay> = utils::parse_json(&json)?; 65 Ok(rows) 66 } 67 68 fn find_one_by_on( 69 exec: &dyn SqlExecutor, 70 on: &NostrRelayQueryBindValues, 71 ) -> Result<Option<NostrRelay>, ReplicaSchemaError<SqlError>> { 72 let (column, value) = on.to_filter_param(); 73 let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); 74 let params_json = utils::to_params_json(vec![value]).expect("serialize bind params"); 75 let json = exec.query_raw(&sql, ¶ms_json)?; 76 let mut rows: Vec<NostrRelay> = utils::parse_json(&json)?; 77 Ok(rows.pop()) 78 } 79 80 fn rel_query(rel: &NostrRelayFindManyRel) -> (&'static str, Vec<Value>) { 81 match rel { 82 NostrRelayFindManyRel::OnProfile(args) => ( 83 "SELECT rl.* FROM nostr_relay rl JOIN nostr_profile_relay pr_rl ON rl.id = pr_rl.tb_rl JOIN nostr_profile pr ON pr.id = pr_rl.tb_pr WHERE pr.public_key = ?", 84 vec![Value::from(args.public_key.clone())], 85 ), 86 NostrRelayFindManyRel::OffProfile(args) => ( 87 "SELECT rl.* FROM nostr_relay rl LEFT JOIN nostr_profile_relay pr_rl ON rl.id = pr_rl.tb_rl LEFT JOIN nostr_profile pr ON pr.id = pr_rl.tb_pr WHERE pr.public_key <> ?", 88 vec![Value::from(args.public_key.clone())], 89 ), 90 } 91 } 92 93 fn find_one_by_rel( 94 exec: &dyn SqlExecutor, 95 rel: &NostrRelayFindManyRel, 96 ) -> Result<Option<NostrRelay>, ReplicaSchemaError<SqlError>> { 97 let (sql, bind_values) = rel_query(rel); 98 let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); 99 let sql = format!("{sql} LIMIT 1;"); 100 let json = exec.query_raw(&sql, ¶ms_json)?; 101 let mut rows: Vec<NostrRelay> = utils::parse_json(&json)?; 102 Ok(rows.pop()) 103 } 104 105 fn find_many_by_rel( 106 exec: &dyn SqlExecutor, 107 rel: &NostrRelayFindManyRel, 108 ) -> Result<Vec<NostrRelay>, ReplicaSchemaError<SqlError>> { 109 let (sql, bind_values) = rel_query(rel); 110 let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); 111 let sql = format!("{sql};"); 112 let json = exec.query_raw(&sql, ¶ms_json)?; 113 let rows: Vec<NostrRelay> = utils::parse_json(&json)?; 114 Ok(rows) 115 } 116 117 fn select_by_id( 118 exec: &dyn SqlExecutor, 119 id: &str, 120 ) -> Result<NostrRelay, ReplicaSchemaError<SqlError>> { 121 let params_json = 122 utils::to_params_json(vec![Value::from(id.to_owned())]).expect("serialize bind params"); 123 let sql = format!("SELECT * FROM {TABLE_NAME} WHERE id = ?;"); 124 let json = exec.query_raw(&sql, ¶ms_json)?; 125 let mut rows: Vec<NostrRelay> = utils::parse_json(&json)?; 126 rows.pop() 127 .ok_or(ReplicaSchemaError::from(SqlError::NotFound(id.to_owned()))) 128 } 129 130 pub fn update( 131 exec: &dyn SqlExecutor, 132 opts: &INostrRelayUpdate, 133 ) -> Result<INostrRelayUpdateResolve, ReplicaSchemaError<SqlError>> { 134 let mut updates = 135 utils::to_partial_object_map(&opts.fields).expect("serialize partial object map"); 136 if updates.is_empty() { 137 return Err(ReplicaSchemaError::from(SqlError::InvalidArgument( 138 String::from("no fields to update"), 139 ))); 140 } 141 updates.insert( 142 String::from("updated_at"), 143 Value::from(utils::time_created_on()), 144 ); 145 let mut set_parts = Vec::with_capacity(updates.len()); 146 let mut bind_values = Vec::with_capacity(updates.len() + 1); 147 for (column, value) in updates { 148 set_parts.push(format!("{column} = ?")); 149 bind_values.push(utils::to_db_bind_value(&value)); 150 } 151 let id_for_lookup = match opts.on.primary_key() { 152 Some(id) => id, 153 None => { 154 let found = find_one_by_on(exec, &opts.on)?; 155 let model = found.ok_or(ReplicaSchemaError::from(SqlError::NotFound( 156 opts.on.lookup_key(), 157 )))?; 158 model.id 159 } 160 }; 161 bind_values.push(Value::from(id_for_lookup.clone())); 162 let sql = format!( 163 "UPDATE {TABLE_NAME} SET {} WHERE id = ?;", 164 set_parts.join(", ") 165 ); 166 let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); 167 let _ = exec.exec(&sql, ¶ms_json)?; 168 let updated = select_by_id(exec, &id_for_lookup)?; 169 Ok(ReplicaSchemaResult { result: updated }) 170 } 171 172 pub fn delete( 173 exec: &dyn SqlExecutor, 174 opts: &INostrRelayDelete, 175 ) -> Result<INostrRelayDeleteResolve, ReplicaSchemaError<SqlError>> { 176 let id_for_lookup = match opts { 177 INostrRelayDelete::On(args) => match args.on.primary_key() { 178 Some(id) => id, 179 None => { 180 let found = find_one_by_on(exec, &args.on)?; 181 let model = found.ok_or(ReplicaSchemaError::from(SqlError::NotFound( 182 args.on.lookup_key(), 183 )))?; 184 model.id 185 } 186 }, 187 INostrRelayDelete::Rel(args) => { 188 let found = find_one_by_rel(exec, &args.rel)?; 189 let model = found.ok_or(ReplicaSchemaError::from(SqlError::NotFound( 190 rel_lookup_key(&args.rel), 191 )))?; 192 model.id 193 } 194 }; 195 let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())]) 196 .expect("serialize bind params"); 197 let sql = format!("DELETE FROM {TABLE_NAME} WHERE id = ?;"); 198 let outcome = exec.exec(&sql, ¶ms_json)?; 199 if outcome.changes == 0 { 200 return Err(ReplicaSchemaError::from(SqlError::NotFound( 201 id_for_lookup.clone(), 202 ))); 203 } 204 Ok(ReplicaSchemaResult { 205 result: id_for_lookup, 206 }) 207 } 208 209 fn rel_lookup_key(rel: &NostrRelayFindManyRel) -> String { 210 match rel { 211 NostrRelayFindManyRel::OnProfile(args) => { 212 format!("on_profile:{}", args.public_key.as_str()) 213 } 214 NostrRelayFindManyRel::OffProfile(args) => { 215 format!("off_profile:{}", args.public_key.as_str()) 216 } 217 } 218 }