nostr_profile.rs (8079B)
1 use radroots_replica_db_schema::nostr_profile::{ 2 INostrProfileCreate, INostrProfileCreateResolve, INostrProfileDelete, 3 INostrProfileDeleteResolve, INostrProfileFieldsFilter, INostrProfileFindMany, 4 INostrProfileFindManyResolve, INostrProfileFindOne, INostrProfileFindOneResolve, 5 INostrProfileUpdate, INostrProfileUpdateResolve, NostrProfile, NostrProfileFindManyRel, 6 NostrProfileQueryBindValues, 7 }; 8 use radroots_sql_core::error::SqlError; 9 use radroots_sql_core::{SqlExecutor, utils}; 10 use radroots_types::types::{IError, IResult, IResultList}; 11 use serde_json::Value; 12 13 const TABLE_NAME: &str = "nostr_profile"; 14 15 pub fn create( 16 exec: &dyn SqlExecutor, 17 opts: &INostrProfileCreate, 18 ) -> Result<INostrProfileCreateResolve, IError<SqlError>> { 19 let field_map = utils::to_object_map(opts).expect("serialize object map"); 20 let id = utils::uuidv4(); 21 let now = utils::time_created_on(); 22 let meta: [(&str, Value); 3] = [ 23 ("id", Value::from(id.clone())), 24 ("created_at", Value::from(now.clone())), 25 ("updated_at", Value::from(now.clone())), 26 ]; 27 let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map); 28 let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); 29 let _ = exec.exec(&sql, ¶ms_json)?; 30 let on = NostrProfileQueryBindValues::Id { id: id.clone() }; 31 let result = find_one_by_on(exec, &on)?.ok_or(IError::from(SqlError::NotFound(id.clone())))?; 32 Ok(IResult { result }) 33 } 34 35 pub fn find_one( 36 exec: &dyn SqlExecutor, 37 opts: &INostrProfileFindOne, 38 ) -> Result<INostrProfileFindOneResolve, IError<SqlError>> { 39 let result = match opts { 40 INostrProfileFindOne::On(args) => find_one_by_on(exec, &args.on)?, 41 INostrProfileFindOne::Rel(args) => find_one_by_rel(exec, &args.rel)?, 42 }; 43 Ok(IResult { result }) 44 } 45 46 pub fn find_many( 47 exec: &dyn SqlExecutor, 48 opts: &INostrProfileFindMany, 49 ) -> Result<INostrProfileFindManyResolve, IError<SqlError>> { 50 let results = match opts { 51 INostrProfileFindMany::Filter { filter } => find_many_filter(exec, filter)?, 52 INostrProfileFindMany::Rel { rel } => find_many_by_rel(exec, rel)?, 53 }; 54 Ok(IResultList { results }) 55 } 56 57 fn find_many_filter( 58 exec: &dyn SqlExecutor, 59 filter: &Option<INostrProfileFieldsFilter>, 60 ) -> Result<Vec<NostrProfile>, IError<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<NostrProfile> = utils::parse_json(&json)?; 65 Ok(rows) 66 } 67 68 fn find_one_by_on( 69 exec: &dyn SqlExecutor, 70 on: &NostrProfileQueryBindValues, 71 ) -> Result<Option<NostrProfile>, IError<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<NostrProfile> = utils::parse_json(&json)?; 77 Ok(rows.pop()) 78 } 79 80 fn rel_query(rel: &NostrProfileFindManyRel) -> (&'static str, Vec<Value>) { 81 match rel { 82 NostrProfileFindManyRel::OnRelay(args) => ( 83 "SELECT pr.* FROM nostr_profile pr JOIN nostr_profile_relay pr_rl ON pr.id = pr_rl.tb_pr WHERE pr_rl.tb_rl = ?", 84 vec![Value::from(args.id.clone())], 85 ), 86 NostrProfileFindManyRel::OffRelay(args) => ( 87 "SELECT pr.* FROM nostr_profile pr WHERE NOT EXISTS (SELECT 1 FROM nostr_profile_relay pr_rl WHERE pr_rl.tb_pr = pr.id AND pr_rl.tb_rl = ?)", 88 vec![Value::from(args.id.clone())], 89 ), 90 } 91 } 92 93 fn find_one_by_rel( 94 exec: &dyn SqlExecutor, 95 rel: &NostrProfileFindManyRel, 96 ) -> Result<Option<NostrProfile>, IError<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<NostrProfile> = utils::parse_json(&json)?; 102 Ok(rows.pop()) 103 } 104 105 fn find_many_by_rel( 106 exec: &dyn SqlExecutor, 107 rel: &NostrProfileFindManyRel, 108 ) -> Result<Vec<NostrProfile>, IError<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<NostrProfile> = utils::parse_json(&json)?; 114 Ok(rows) 115 } 116 117 fn select_by_id(exec: &dyn SqlExecutor, id: &str) -> Result<NostrProfile, IError<SqlError>> { 118 let params_json = 119 utils::to_params_json(vec![Value::from(id.to_owned())]).expect("serialize bind params"); 120 let sql = format!("SELECT * FROM {TABLE_NAME} WHERE id = ?;"); 121 let json = exec.query_raw(&sql, ¶ms_json)?; 122 let mut rows: Vec<NostrProfile> = utils::parse_json(&json)?; 123 rows.pop() 124 .ok_or(IError::from(SqlError::NotFound(id.to_owned()))) 125 } 126 127 pub fn update( 128 exec: &dyn SqlExecutor, 129 opts: &INostrProfileUpdate, 130 ) -> Result<INostrProfileUpdateResolve, IError<SqlError>> { 131 let mut updates = 132 utils::to_partial_object_map(&opts.fields).expect("serialize partial object map"); 133 if updates.is_empty() { 134 return Err(IError::from(SqlError::InvalidArgument(String::from( 135 "no fields to update", 136 )))); 137 } 138 updates.insert( 139 String::from("updated_at"), 140 Value::from(utils::time_created_on()), 141 ); 142 let mut set_parts = Vec::with_capacity(updates.len()); 143 let mut bind_values = Vec::with_capacity(updates.len() + 1); 144 for (column, value) in updates { 145 set_parts.push(format!("{column} = ?")); 146 bind_values.push(utils::to_db_bind_value(&value)); 147 } 148 let id_for_lookup = match opts.on.primary_key() { 149 Some(id) => id, 150 None => { 151 let found = find_one_by_on(exec, &opts.on)?; 152 let model = found.ok_or(IError::from(SqlError::NotFound(opts.on.lookup_key())))?; 153 model.id 154 } 155 }; 156 bind_values.push(Value::from(id_for_lookup.clone())); 157 let sql = format!( 158 "UPDATE {TABLE_NAME} SET {} WHERE id = ?;", 159 set_parts.join(", ") 160 ); 161 let params_json = utils::to_params_json(bind_values).expect("serialize bind params"); 162 let _ = exec.exec(&sql, ¶ms_json)?; 163 let updated = select_by_id(exec, &id_for_lookup)?; 164 Ok(IResult { result: updated }) 165 } 166 167 pub fn delete( 168 exec: &dyn SqlExecutor, 169 opts: &INostrProfileDelete, 170 ) -> Result<INostrProfileDeleteResolve, IError<SqlError>> { 171 let id_for_lookup = match opts { 172 INostrProfileDelete::On(args) => match args.on.primary_key() { 173 Some(id) => id, 174 None => { 175 let found = find_one_by_on(exec, &args.on)?; 176 let model = found.ok_or(IError::from(SqlError::NotFound(args.on.lookup_key())))?; 177 model.id 178 } 179 }, 180 INostrProfileDelete::Rel(args) => { 181 let found = find_one_by_rel(exec, &args.rel)?; 182 let model = found.ok_or(IError::from(SqlError::NotFound(rel_lookup_key(&args.rel))))?; 183 model.id 184 } 185 }; 186 let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())]) 187 .expect("serialize bind params"); 188 let sql = format!("DELETE FROM {TABLE_NAME} WHERE id = ?;"); 189 let outcome = exec.exec(&sql, ¶ms_json)?; 190 if outcome.changes == 0 { 191 return Err(IError::from(SqlError::NotFound(id_for_lookup.clone()))); 192 } 193 Ok(IResult { 194 result: id_for_lookup, 195 }) 196 } 197 198 fn rel_lookup_key(rel: &NostrProfileFindManyRel) -> String { 199 match rel { 200 NostrProfileFindManyRel::OnRelay(args) => format!("on_relay:{}", args.id.as_str()), 201 NostrProfileFindManyRel::OffRelay(args) => format!("off_relay:{}", args.id.as_str()), 202 } 203 }