use oiseau::cache::Cache; use crate::model::{auth::User, journals::Note, permissions::FinePermission, Error, Result}; use crate::{auto_method, DataManager}; use oiseau::{PostgresRow, execute, get, query_rows, params}; impl DataManager { /// Get a [`Note`] from an SQL row. pub(crate) fn get_note_from_row(x: &PostgresRow) -> Note { Note { id: get!(x->0(i64)) as usize, created: get!(x->1(i64)) as usize, owner: get!(x->2(i64)) as usize, title: get!(x->3(String)), journal: get!(x->4(i64)) as usize, content: get!(x->5(String)), edited: get!(x->6(i64)) as usize, } } auto_method!(get_note_by_id(usize as i64)@get_note_from_row -> "SELECT * FROM notes WHERE id = $1" --name="note" --returns=Note --cache-key-tmpl="atto.note:{}"); /// Get all notes by journal. /// /// # Arguments /// * `id` - the ID of the journal to fetch notes for pub async fn get_notes_by_journal(&self, id: usize) -> Result> { let conn = match self.0.connect().await { Ok(c) => c, Err(e) => return Err(Error::DatabaseConnection(e.to_string())), }; let res = query_rows!( &conn, "SELECT * FROM notes WHERE journal = $1 ORDER BY edited", &[&(id as i64)], |x| { Self::get_note_from_row(x) } ); if res.is_err() { return Err(Error::GeneralNotFound("note".to_string())); } Ok(res.unwrap()) } /// Create a new note in the database. /// /// # Arguments /// * `data` - a mock [`Note`] object to insert pub async fn create_note(&self, data: Note) -> Result { // check values if data.title.len() < 2 { return Err(Error::DataTooShort("title".to_string())); } else if data.title.len() > 64 { return Err(Error::DataTooLong("title".to_string())); } if data.content.len() < 2 { return Err(Error::DataTooShort("content".to_string())); } else if data.content.len() > 16384 { return Err(Error::DataTooLong("content".to_string())); } // ... let conn = match self.0.connect().await { Ok(c) => c, Err(e) => return Err(Error::DatabaseConnection(e.to_string())), }; let res = execute!( &conn, "INSERT INTO notes VALUES ($1, $2, $3, $4, $5, $6, $7)", params![ &(data.id as i64), &(data.created as i64), &(data.owner as i64), &data.title, &(data.journal as i64), &data.content, &(data.edited as i64), ] ); if let Err(e) = res { return Err(Error::DatabaseError(e.to_string())); } Ok(data) } pub async fn delete_note(&self, id: usize, user: &User) -> Result<()> { let note = self.get_note_by_id(id).await?; // check user permission if user.id != note.owner && !user.permissions.check(FinePermission::MANAGE_NOTES) { return Err(Error::NotAllowed); } // ... let conn = match self.0.connect().await { Ok(c) => c, Err(e) => return Err(Error::DatabaseConnection(e.to_string())), }; let res = execute!(&conn, "DELETE FROM notes WHERE id = $1", &[&(id as i64)]); if let Err(e) = res { return Err(Error::DatabaseError(e.to_string())); } // delete notes let res = execute!(&conn, "DELETE FROM notes WHERE note = $1", &[&(id as i64)]); if let Err(e) = res { return Err(Error::DatabaseError(e.to_string())); } // ... self.0.1.remove(format!("atto.note:{}", id)).await; Ok(()) } auto_method!(update_note_title(&str)@get_note_by_id:MANAGE_NOTES -> "UPDATE notes SET title = $1 WHERE id = $2" --cache-key-tmpl="atto.note:{}"); }