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