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