FEAT: Can save a new page. Automagically includes its root note.
This commit is contained in:
		
							parent
							
								
									6bc8b0e911
								
							
						
					
					
						commit
						75809d821d
					
				| 
						 | 
					@ -11,6 +11,7 @@ readme = "./README.org"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
 | 
					friendly_id = "0.3.0"
 | 
				
			||||||
thiserror = "1.0.20"
 | 
					thiserror = "1.0.20"
 | 
				
			||||||
tokio = { version = "0.2.22", features = ["rt-threaded", "blocking"] }
 | 
					tokio = { version = "0.2.22", features = ["rt-threaded", "blocking"] }
 | 
				
			||||||
serde = { version = "1.0.116", features = ["derive"] }
 | 
					serde = { version = "1.0.116", features = ["derive"] }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,23 +12,24 @@ mod tests {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async fn fresh_inmemory_database() -> NoteStore {
 | 
					    async fn fresh_inmemory_database() -> NoteStore {
 | 
				
			||||||
        let storagepool = NoteStore::new("sqlite://:memory:").await;
 | 
					        let storagepool = NoteStore::new("sqlite://:memory:").await;
 | 
				
			||||||
        assert!(storagepool.is_ok());
 | 
							assert!(storagepool.is_ok(), "{:?}", storagepool);
 | 
				
			||||||
        let storagepool = storagepool.unwrap();
 | 
					        let storagepool = storagepool.unwrap();
 | 
				
			||||||
        assert!(storagepool.reset_database().await.is_ok());
 | 
							let reset = storagepool.reset_database().await;
 | 
				
			||||||
 | 
					        assert!(reset.is_ok(), "{:?}", reset);
 | 
				
			||||||
        storagepool
 | 
					        storagepool
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[tokio::test(threaded_scheduler)]
 | 
					    #[tokio::test(threaded_scheduler)]
 | 
				
			||||||
    async fn fetching_unfound_page_works() {
 | 
					    async fn fetching_unfound_page_works() {
 | 
				
			||||||
        let storagepool = fresh_inmemory_database().await;
 | 
					        let storagepool = fresh_inmemory_database().await;
 | 
				
			||||||
        let unfoundpage = storagepool.fetch_page("nonexistent-page").await;
 | 
					        let unfoundpage = storagepool.fetch_raw_page("nonexistent-page").await;
 | 
				
			||||||
        assert!(unfoundpage.is_err());
 | 
					        assert!(unfoundpage.is_err());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[tokio::test(threaded_scheduler)]
 | 
					    #[tokio::test(threaded_scheduler)]
 | 
				
			||||||
    async fn fetching_unfound_note_works() {
 | 
					    async fn fetching_unfound_note_works() {
 | 
				
			||||||
        let storagepool = fresh_inmemory_database().await;
 | 
					        let storagepool = fresh_inmemory_database().await;
 | 
				
			||||||
        let unfoundnote = storagepool.fetch_note("nonexistent-note").await;
 | 
					        let unfoundnote = storagepool.fetch_raw_note("nonexistent-note").await;
 | 
				
			||||||
        assert!(unfoundnote.is_err());
 | 
					        assert!(unfoundnote.is_err());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -36,9 +37,9 @@ mod tests {
 | 
				
			||||||
    async fn cloning_storagepool_is_ok() {
 | 
					    async fn cloning_storagepool_is_ok() {
 | 
				
			||||||
        let storagepool = fresh_inmemory_database().await;
 | 
					        let storagepool = fresh_inmemory_database().await;
 | 
				
			||||||
        let storagepool2 = storagepool.clone();
 | 
					        let storagepool2 = storagepool.clone();
 | 
				
			||||||
        let unfoundnote = storagepool2.fetch_note("nonexistent-note").await;
 | 
					        let unfoundnote = storagepool2.fetch_raw_note("nonexistent-note").await;
 | 
				
			||||||
        assert!(unfoundnote.is_err());
 | 
					        assert!(unfoundnote.is_err());
 | 
				
			||||||
        let unfoundnote = storagepool.fetch_note("nonexistent-note").await;
 | 
					        let unfoundnote = storagepool.fetch_raw_note("nonexistent-note").await;
 | 
				
			||||||
        assert!(unfoundnote.is_err());
 | 
					        assert!(unfoundnote.is_err());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -46,16 +47,27 @@ mod tests {
 | 
				
			||||||
    async fn can_save_a_note() {
 | 
					    async fn can_save_a_note() {
 | 
				
			||||||
		let storagepool = fresh_inmemory_database().await;
 | 
							let storagepool = fresh_inmemory_database().await;
 | 
				
			||||||
		let note_id = storagepool.insert_note("noteid", "notecontent", "note").await;
 | 
							let note_id = storagepool.insert_note("noteid", "notecontent", "note").await;
 | 
				
			||||||
		assert!(note_id.is_ok(), format!("note is not ok: {:?}", note_id));
 | 
							assert!(note_id.is_ok(), "{:?}", note_id);
 | 
				
			||||||
		let note_id = note_id.unwrap();
 | 
							let note_id = note_id.unwrap();
 | 
				
			||||||
		assert!(note_id > 0);
 | 
							assert!(note_id > 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		let foundnote = storagepool.fetch_note("noteid").await;
 | 
							let foundnote = storagepool.fetch_raw_note("noteid").await;
 | 
				
			||||||
		assert!(foundnote.is_ok(), format!("foundnote is not ok: {:?}", foundnote));
 | 
							assert!(foundnote.is_ok(), "{:?}", foundnote);
 | 
				
			||||||
		let foundnote = foundnote.unwrap();
 | 
							let foundnote = foundnote.unwrap();
 | 
				
			||||||
		assert_eq!(foundnote.content, "notecontent");
 | 
							assert_eq!(foundnote.content, "notecontent");
 | 
				
			||||||
		assert_eq!(foundnote.notetype, "note");
 | 
							assert_eq!(foundnote.notetype, "note");
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[tokio::test(threaded_scheduler)]
 | 
				
			||||||
 | 
					    async fn can_save_a_page() {
 | 
				
			||||||
 | 
							let storagepool = fresh_inmemory_database().await;
 | 
				
			||||||
 | 
							let page_id = storagepool.insert_page("pageid", "Test page").await;
 | 
				
			||||||
 | 
							assert!(page_id.is_ok(), "{:?}", page_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							let page = storagepool.fetch_raw_page("pageid").await;
 | 
				
			||||||
 | 
							assert!(page.is_ok(), "{:?}", page);
 | 
				
			||||||
 | 
							let page = page.unwrap();
 | 
				
			||||||
 | 
							assert_eq!(page.title, "Test page");
 | 
				
			||||||
 | 
							assert!(page.note_id > 0);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,8 @@
 | 
				
			||||||
 | 
					INSERT INTO pages (
 | 
				
			||||||
 | 
						   slug,
 | 
				
			||||||
 | 
						   title,
 | 
				
			||||||
 | 
						   note_id,
 | 
				
			||||||
 | 
						   creation_date,
 | 
				
			||||||
 | 
						   updated_date,
 | 
				
			||||||
 | 
						   lastview_date)
 | 
				
			||||||
 | 
					VALUES (?, ?, ?, ?, ?, ?);
 | 
				
			||||||
| 
						 | 
					@ -1 +1 @@
 | 
				
			||||||
SELECT id, title, slug, note_id FROM pages WHERE slug=?;
 | 
					SELECT id, title, slug, note_id, creation_date, updated_date, lastview_date, deleted_date FROM pages WHERE slug=?;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,12 +1,13 @@
 | 
				
			||||||
use crate::errors::NoteStoreError;
 | 
					use crate::errors::NoteStoreError;
 | 
				
			||||||
use crate::structs::{RawNote, RawPage};
 | 
					use crate::structs::{RawNote, RawPage};
 | 
				
			||||||
 | 
					use friendly_id;
 | 
				
			||||||
use chrono;
 | 
					use chrono;
 | 
				
			||||||
use sqlx;
 | 
					use sqlx;
 | 
				
			||||||
use sqlx::sqlite::SqlitePool;
 | 
					use sqlx::sqlite::SqlitePool;
 | 
				
			||||||
use std::sync::Arc;
 | 
					use std::sync::Arc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// A handle to our Sqlite database.
 | 
					/// A handle to our Sqlite database.
 | 
				
			||||||
#[derive(Clone)]
 | 
					#[derive(Clone, Debug)]
 | 
				
			||||||
pub struct NoteStore(Arc<SqlitePool>);
 | 
					pub struct NoteStore(Arc<SqlitePool>);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type NoteResult<T> = core::result::Result<T, NoteStoreError>;
 | 
					type NoteResult<T> = core::result::Result<T, NoteStoreError>;
 | 
				
			||||||
| 
						 | 
					@ -26,12 +27,12 @@ impl NoteStore {
 | 
				
			||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub async fn fetch_page(&self, id: &str) -> SqlResult<RawPage> {
 | 
					    pub async fn fetch_raw_page(&self, id: &str) -> SqlResult<RawPage> {
 | 
				
			||||||
        let select_one_page_sql = include_str!("sql/select_one_page.sql");
 | 
					        let select_one_page_sql = include_str!("sql/select_one_page.sql");
 | 
				
			||||||
        sqlx::query_as(select_one_page_sql).bind(&id).fetch_one(&*self.0).await
 | 
					        sqlx::query_as(select_one_page_sql).bind(&id).fetch_one(&*self.0).await
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub async fn fetch_note(&self, id: &str) -> SqlResult<RawNote> {
 | 
					    pub async fn fetch_raw_note(&self, id: &str) -> SqlResult<RawNote> {
 | 
				
			||||||
        let select_one_note_sql = include_str!("sql/select_one_note.sql");
 | 
					        let select_one_note_sql = include_str!("sql/select_one_note.sql");
 | 
				
			||||||
        sqlx::query_as(select_one_note_sql).bind(&id).fetch_one(&*self.0).await
 | 
					        sqlx::query_as(select_one_note_sql).bind(&id).fetch_one(&*self.0).await
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -43,10 +44,40 @@ impl NoteStore {
 | 
				
			||||||
		   .bind(&id)
 | 
							   .bind(&id)
 | 
				
			||||||
		   .bind(&content)
 | 
							   .bind(&content)
 | 
				
			||||||
		   .bind(¬etype)
 | 
							   .bind(¬etype)
 | 
				
			||||||
		   .bind(&now)
 | 
							   .bind(&now).bind(&now).bind(&now)
 | 
				
			||||||
		   .bind(&now)
 | 
					 | 
				
			||||||
		   .bind(&now)
 | 
					 | 
				
			||||||
		   .execute(&*self.0).await?
 | 
							   .execute(&*self.0).await?
 | 
				
			||||||
		   .last_insert_rowid())
 | 
							   .last_insert_rowid())
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// TODO: We're returning the raw page with the raw note id, note
 | 
				
			||||||
 | 
						// the friendly ID.  Is there a disconnect there?  It's making me
 | 
				
			||||||
 | 
						// furiously to think.
 | 
				
			||||||
 | 
						pub async fn insert_page(&self, id: &str, title: &str) -> SqlResult<i64> {
 | 
				
			||||||
 | 
							let insert_one_note_sql = include_str!("sql/insert_one_note.sql");
 | 
				
			||||||
 | 
							let insert_one_page_sql = include_str!("sql/insert_one_page.sql");
 | 
				
			||||||
 | 
					        let new_note_id = friendly_id::create();
 | 
				
			||||||
 | 
							let now = chrono::Utc::now();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let mut tx = self.0.begin().await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							let note_id = sqlx::query(insert_one_note_sql)
 | 
				
			||||||
 | 
							   .bind(&new_note_id)
 | 
				
			||||||
 | 
							   .bind(&"")
 | 
				
			||||||
 | 
							   .bind(&"page")
 | 
				
			||||||
 | 
							   .bind(&now).bind(&now).bind(&now)
 | 
				
			||||||
 | 
							   .execute(&mut tx).await?
 | 
				
			||||||
 | 
							   .last_insert_rowid();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							let page_id = sqlx::query(insert_one_page_sql)
 | 
				
			||||||
 | 
								.bind(&id)
 | 
				
			||||||
 | 
								.bind(&title)
 | 
				
			||||||
 | 
								.bind(¬e_id)
 | 
				
			||||||
 | 
								.bind(&now).bind(&now).bind(&now)
 | 
				
			||||||
 | 
							    .execute(&mut tx).await?
 | 
				
			||||||
 | 
								.last_insert_rowid();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							tx.commit().await?;
 | 
				
			||||||
 | 
							Ok(page_id)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue