Browse Source

check for pre-existing decks before creating new ones

master
Inderjit Gill 2 months ago
parent
commit
1b6341753b
12 changed files with 157 additions and 46 deletions
  1. +1
    -1
      civil-server/src/db/deck_kind.rs
  2. +44
    -1
      civil-server/src/db/decks.rs
  3. +20
    -9
      civil-server/src/db/edges.rs
  4. +9
    -12
      civil-server/src/db/ideas.rs
  5. +8
    -4
      civil-server/src/db/people.rs
  6. +37
    -2
      civil-server/src/db/pg.rs
  7. +26
    -11
      civil-server/src/db/publications.rs
  8. +8
    -2
      civil-server/src/db/timelines.rs
  9. +1
    -1
      civil-server/src/handler/ideas.rs
  10. +1
    -1
      civil-server/src/handler/people.rs
  11. +1
    -1
      civil-server/src/handler/publications.rs
  12. +1
    -1
      civil-server/src/handler/timelines.rs

+ 1
- 1
civil-server/src/db/deck_kind.rs View File

@ -19,7 +19,7 @@ use crate::interop::decks as interop;
use postgres_types::{FromSql, ToSql};
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, ToSql, FromSql, Deserialize, Serialize, PartialEq)]
#[derive(Copy, Clone, Debug, ToSql, FromSql, Deserialize, Serialize, PartialEq)]
#[postgres(name = "deck_kind")]
pub enum DeckKind {
#[postgres(name = "publication")]


+ 44
- 1
civil-server/src/db/decks.rs View File

@ -30,6 +30,11 @@ use tokio_pg_mapper_derive::PostgresMapper;
#[allow(unused_imports)]
use tracing::info;
pub(crate) enum DeckBaseOrigin {
Created,
PreExisting,
}
// used when constructing a type derived from deck (Idea, Publication etc)
// gets the basic information from the decks table for use with additional data to construct the final struct
// e.g. DeckBase + IdeaExtra to create an interop::Idea
@ -107,7 +112,7 @@ impl From<DetailedRef> for interop::Ref {
name: d.name,
resource: interop::DeckResource::from(d.kind),
ref_kind: interop::RefKind::from(d.ref_kind),
annotation: d.annotation
annotation: d.annotation,
}
}
}
@ -342,6 +347,44 @@ pub(crate) async fn deckbase_get(
.await
}
// tuple where the second element is a bool indicating whether the deck was created (true) or
// we're returning a pre-existing deck (false)
//
pub(crate) async fn deckbase_get_or_create(
tx: &Transaction<'_>,
user_id: Key,
kind: DeckKind,
name: &str,
) -> Result<(DeckBase, DeckBaseOrigin)> {
let existing_deck_res = deckbase_get_by_name(&tx, user_id, kind, &name).await;
match existing_deck_res {
Ok(deck) => Ok((deck.into(), DeckBaseOrigin::PreExisting)),
Err(e) => match e {
Error::NotFound => {
let deck = deckbase_create(&tx, user_id, kind, &name).await?;
Ok((deck.into(), DeckBaseOrigin::Created))
}
_ => Err(e),
},
}
}
async fn deckbase_get_by_name(
tx: &Transaction<'_>,
user_id: Key,
kind: DeckKind,
name: &str,
) -> Result<DeckBase> {
pg::one_may_not_find::<DeckBase>(
&tx,
"select id, name, created_at, graph_terminator
from decks
where user_id = $1 and name = $2 and kind = $3",
&[&user_id, &name, &kind],
)
.await
}
pub(crate) async fn deckbase_create(
tx: &Transaction<'_>,
user_id: Key,


+ 20
- 9
civil-server/src/db/edges.rs View File

@ -17,7 +17,7 @@
use super::pg;
use crate::db::deck_kind::DeckKind;
use crate::db::ideas as ideas_db;
use crate::db::decks as decks_db;
use crate::db::ref_kind::RefKind;
use crate::error::{Error, Result};
use crate::interop::decks as interop_decks;
@ -113,6 +113,10 @@ pub(crate) async fn create_from_note_to_decks(
if existing.id == deck_reference.id {
let r = RefKind::from(deck_reference.ref_kind);
if existing.ref_kind != r || existing.annotation != deck_reference.annotation {
info!(
"updating properties of an existing reference {} {}",
existing.id, existing.note_id
);
pg::zero(
&tx,
&stmt_update_ref_kind,
@ -134,7 +138,10 @@ pub(crate) async fn create_from_note_to_decks(
VALUES ($1, $2, $3, $4)";
for deck_reference in &edge_connectivity.existing_deck_references {
if !is_deck_associated_with_note(deck_reference.id, &associated_decks) {
info!("creating {}, {}", &note_id, &deck_reference.id);
info!(
"creating new edge to pre-existing deck {}, {}",
&note_id, &deck_reference.id
);
let r = RefKind::from(deck_reference.ref_kind);
pg::zero(
&tx,
@ -148,13 +155,17 @@ pub(crate) async fn create_from_note_to_decks(
// create new tags and create edges from the note to them
//
for new_deck_reference in &edge_connectivity.new_deck_references {
// todo(<2020-03-30 Mon>): additional check to make sure that this tag doesn't already exist
// it's a stupid thing that could happen if:
// 1. a user has the same deck open in two windows
// 2. adds a new tag to a note in one window
// 3. adds the same new tag in the other window
//
let deck = ideas_db::create_idea_tx(&tx, user_id, &new_deck_reference.name).await?;
info!(
"create new idea: {} and a new edge",
new_deck_reference.name
);
let (deck, _created) = decks_db::deckbase_get_or_create(
&tx,
user_id,
DeckKind::Idea,
&new_deck_reference.name,
)
.await?;
let no_annotation: Option<String> = None;
let r = RefKind::from(new_deck_reference.ref_kind);
pg::zero(


+ 9
- 12
civil-server/src/db/ideas.rs View File

@ -21,7 +21,7 @@ use crate::db::decks;
use crate::error::{Error, Result};
use crate::interop::ideas as interop;
use crate::interop::Key;
use deadpool_postgres::{Client, Pool, Transaction};
use deadpool_postgres::{Client, Pool};
use serde::{Deserialize, Serialize};
use tokio_pg_mapper_derive::PostgresMapper;
@ -73,15 +73,6 @@ impl From<decks::DeckBase> for interop::Idea {
}
}
pub(crate) async fn create_idea_tx(
tx: &Transaction<'_>,
user_id: Key,
deck_name: &str,
) -> Result<interop::Idea> {
let deck = decks::deckbase_create(&tx, user_id, DeckKind::Idea, deck_name).await?;
Ok(deck.into())
}
pub(crate) async fn all(db_pool: &Pool, user_id: Key) -> Result<Vec<interop::Idea>> {
pg::many_from::<Idea, interop::Idea>(
db_pool,
@ -163,11 +154,17 @@ pub(crate) async fn get(db_pool: &Pool, user_id: Key, idea_id: Key) -> Result<in
Ok(deck.into())
}
pub(crate) async fn create(db_pool: &Pool, user_id: Key, title: &str) -> Result<interop::Idea> {
pub(crate) async fn get_or_create(
db_pool: &Pool,
user_id: Key,
title: &str,
) -> Result<interop::Idea> {
let mut client: Client = db_pool.get().await.map_err(Error::DeadPool)?;
let tx = client.transaction().await?;
let deck = decks::deckbase_create(&tx, user_id, DeckKind::Idea, &title).await?;
let (deck, _origin) =
decks::deckbase_get_or_create(&tx, user_id, DeckKind::Idea, &title).await?;
tx.commit().await?;


+ 8
- 4
civil-server/src/db/people.rs View File

@ -78,13 +78,17 @@ impl From<decks::DeckBase> for interop::Person {
}
}
pub(crate) async fn create(db_pool: &Pool, user_id: Key, title: &str) -> Result<interop::Person> {
info!("db::create_person");
pub(crate) async fn get_or_create(
db_pool: &Pool,
user_id: Key,
title: &str,
) -> Result<interop::Person> {
let mut client: Client = db_pool.get().await.map_err(Error::DeadPool)?;
let tx = client.transaction().await?;
let deck = decks::deckbase_create(&tx, user_id, DeckKind::Person, &title).await?;
let (deck, _origin) =
decks::deckbase_get_or_create(&tx, user_id, DeckKind::Person, &title).await?;
tx.commit().await?;


+ 37
- 2
civil-server/src/db/pg.rs View File

@ -53,6 +53,31 @@ pub async fn one<T>(
where
T: FromTokioPostgresRow,
{
one_(tx, sql_query, sql_params, true).await
}
// the same as one except that if the query returns NotFound an error isn't printed
//
pub async fn one_may_not_find<T>(
tx: &Transaction<'_>,
sql_query: &str,
sql_params: &[&(dyn tokio_postgres::types::ToSql + std::marker::Sync)],
) -> Result<T>
where
T: FromTokioPostgresRow,
{
one_(tx, sql_query, sql_params, false).await
}
async fn one_<T>(
tx: &Transaction<'_>,
sql_query: &str,
sql_params: &[&(dyn tokio_postgres::types::ToSql + std::marker::Sync)],
log_not_found_error: bool,
) -> Result<T>
where
T: FromTokioPostgresRow,
{
let _stmt = sql_query;
let _stmt = _stmt.replace("$table_fields", &T::sql_table_fields());
let stmt = match tx.prepare(&_stmt).await {
@ -76,8 +101,18 @@ where
match res {
Ok(_) => res,
Err(e) => {
error!("{}", e);
error!("QUERY: {}", &sql_query);
match e {
Error::NotFound => {
if log_not_found_error {
error!("{}", e);
error!("QUERY: {}", &sql_query);
}
}
_ => {
error!("{}", e);
error!("QUERY: {}", &sql_query);
}
}
Err(e)
}
}


+ 26
- 11
civil-server/src/db/publications.rs View File

@ -181,11 +181,10 @@ pub(crate) async fn get(
.await
}
pub(crate) async fn create(
pub(crate) async fn get_or_create(
db_pool: &Pool,
user_id: Key,
title: &str,
// publication: &interop::ProtoPublication,
) -> Result<interop::Publication> {
let source = "";
let author = "";
@ -193,17 +192,33 @@ pub(crate) async fn create(
let rating = 0;
let mut client: Client = db_pool.get().await.map_err(Error::DeadPool)?;
let tx = client.transaction().await?;
let deck = decks::deckbase_create(&tx, user_id, DeckKind::Publication, &title).await?;
let publication_extras = pg::one::<PublicationExtra>(
&tx,
"INSERT INTO publication_extras(deck_id, source, author, short_description, rating)
VALUES ($1, $2, $3, $4, $5)
RETURNING $table_fields",
&[&deck.id, &source, &author, &short_description, &rating],
)
.await?;
let (deck, origin) =
decks::deckbase_get_or_create(&tx, user_id, DeckKind::Publication, &title).await?;
let publication_extras =
match origin {
decks::DeckBaseOrigin::Created => pg::one::<PublicationExtra>(
&tx,
"INSERT INTO publication_extras(deck_id, source, author, short_description, rating)
VALUES ($1, $2, $3, $4, $5)
RETURNING $table_fields",
&[&deck.id, &source, &author, &short_description, &rating],
)
.await?,
decks::DeckBaseOrigin::PreExisting => {
pg::one::<PublicationExtra>(
&tx,
"select deck_id, source, author, short_description, rating
from publication_extras
where deck_id=$1",
&[&deck.id],
)
.await?
}
};
tx.commit().await?;


+ 8
- 2
civil-server/src/db/timelines.rs View File

@ -65,11 +65,17 @@ impl From<decks::DeckBase> for interop::Timeline {
}
}
pub(crate) async fn create(db_pool: &Pool, user_id: Key, title: &str) -> Result<interop::Timeline> {
pub(crate) async fn get_or_create(
db_pool: &Pool,
user_id: Key,
title: &str,
) -> Result<interop::Timeline> {
let mut client: Client = db_pool.get().await.map_err(Error::DeadPool)?;
let tx = client.transaction().await?;
let deck = decks::deckbase_create(&tx, user_id, DeckKind::Timeline, &title).await?;
let (deck, _origin) =
decks::deckbase_get_or_create(&tx, user_id, DeckKind::Timeline, &title).await?;
tx.commit().await?;


+ 1
- 1
civil-server/src/handler/ideas.rs View File

@ -40,7 +40,7 @@ pub async fn create(
let user_id = session::user_id(&session)?;
let proto_deck = proto_deck.into_inner();
let idea = db::create(&db_pool, user_id, &proto_deck.title).await?;
let idea = db::get_or_create(&db_pool, user_id, &proto_deck.title).await?;
Ok(HttpResponse::Ok().json(idea))
}


+ 1
- 1
civil-server/src/handler/people.rs View File

@ -42,7 +42,7 @@ pub async fn create(
let user_id = session::user_id(&session)?;
let proto_deck = proto_deck.into_inner();
let person = db::create(&db_pool, user_id, &proto_deck.title).await?;
let person = db::get_or_create(&db_pool, user_id, &proto_deck.title).await?;
Ok(HttpResponse::Ok().json(person))
}


+ 1
- 1
civil-server/src/handler/publications.rs View File

@ -39,7 +39,7 @@ pub async fn create(
let user_id = session::user_id(&session)?;
let proto_deck = proto_deck.into_inner();
let publication = db::create(&db_pool, user_id, &proto_deck.title).await?;
let publication = db::get_or_create(&db_pool, user_id, &proto_deck.title).await?;
Ok(HttpResponse::Ok().json(publication))
}


+ 1
- 1
civil-server/src/handler/timelines.rs View File

@ -41,7 +41,7 @@ pub async fn create(
let user_id = session::user_id(&session)?;
let proto_deck = proto_deck.into_inner();
let timeline = db::create(&db_pool, user_id, &proto_deck.title).await?;
let timeline = db::get_or_create(&db_pool, user_id, &proto_deck.title).await?;
Ok(HttpResponse::Ok().json(timeline))
}


Loading…
Cancel
Save