FEAT: Can save a new page. Automagically includes its root note.

This commit is contained in:
Elf M. Sternberg 2020-09-30 07:37:18 -07:00
parent 6bc8b0e911
commit 75809d821d
5 changed files with 70 additions and 18 deletions

View File

@ -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"] }

View File

@ -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);
}
} }

View File

@ -0,0 +1,8 @@
INSERT INTO pages (
slug,
title,
note_id,
creation_date,
updated_date,
lastview_date)
VALUES (?, ?, ?, ?, ?, ?);

View File

@ -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=?;

View File

@ -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(&notetype) .bind(&notetype)
.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(&note_id)
.bind(&now).bind(&now).bind(&now)
.execute(&mut tx).await?
.last_insert_rowid();
tx.commit().await?;
Ok(page_id)
}
} }