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