From b0d7f779838e6a0fc48e8ba499f9e2ccddc334c4 Mon Sep 17 00:00:00 2001 From: Jef Roosens Date: Sun, 26 Dec 2021 17:46:17 +0100 Subject: [PATCH] Added post versions; updated db boilerplate --- .../2021-09-13-143540_sections/down.sql | 2 - migrations/2021-09-13-143540_sections/up.sql | 15 +-- src/db/mod.rs | 3 + src/db/posts.rs | 16 +-- src/db/sections.rs | 8 +- src/db/versions.rs | 103 ++++++++++++++++++ src/main.rs | 11 ++ src/schema.rs | 12 +- src/v1/mod.rs | 1 + src/v1/versions.rs | 74 +++++++++++++ 10 files changed, 211 insertions(+), 34 deletions(-) create mode 100644 src/db/versions.rs create mode 100644 src/v1/versions.rs diff --git a/migrations/2021-09-13-143540_sections/down.sql b/migrations/2021-09-13-143540_sections/down.sql index c3b5ba5..bd985fd 100644 --- a/migrations/2021-09-13-143540_sections/down.sql +++ b/migrations/2021-09-13-143540_sections/down.sql @@ -2,9 +2,7 @@ drop trigger insert_enforce_version_titles on versions; drop trigger update_enforce_version_titles on versions; drop function enforce_version_titles; -drop index sections_shortname_index; drop table versions cascade; -drop table tags cascade; drop table posts cascade; drop table sections cascade; diff --git a/migrations/2021-09-13-143540_sections/up.sql b/migrations/2021-09-13-143540_sections/up.sql index ca30c71..93a9196 100644 --- a/migrations/2021-09-13-143540_sections/up.sql +++ b/migrations/2021-09-13-143540_sections/up.sql @@ -6,17 +6,17 @@ create table sections ( -- Name to use when routing (this just makes for prettier URLs) shortname varchar(32) UNIQUE NOT NULL, -- Optional description of the section - description text, + description text NOT NULL DEFAULT '', -- Wether to show the section in the default list on the homepage is_default boolean NOT NULL DEFAULT false, -- Wether the posts should contain titles or not has_titles boolean NOT NULL DEFAULT true, -- Wether posts in this section should be shown publicly - is_private boolean NOT NULL DEFAULT false + is_private boolean NOT NULL DEFAULT false, + -- Wether the section is archived + is_archived boolean NOT NULL DEFAULT false ); -create index sections_shortname_index on sections(shortname); - create table posts ( id uuid DEFAULT gen_random_uuid() PRIMARY KEY, @@ -51,13 +51,6 @@ create table versions ( CONSTRAINT no_null_published_date CHECK (is_draft OR publish_date IS NOT NULL) ); -create table tags ( - post_id uuid NOT NULL REFERENCES posts(id) ON DELETE CASCADE, - value varchar(64) NOT NULL, - - PRIMARY KEY (post_id, value) -); - create function enforce_version_titles() returns trigger as $$ begin -- Draft versions shouldn't be evaluated. diff --git a/src/db/mod.rs b/src/db/mod.rs index 2ce2174..8831437 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -3,9 +3,12 @@ pub mod posts; pub mod sections; +pub mod versions; pub use posts::{NewPost, PatchPost, Post}; pub use sections::{NewSection, PatchSection, Section}; +pub use versions::{NewVersion, PatchVersion, Version}; pub const MAX_POSTS: u32 = 64; pub const MAX_SECTIONS: u32 = 64; +pub const MAX_VERSIONS: u32 = 16; diff --git a/src/db/posts.rs b/src/db/posts.rs index e2d11d1..3952425 100644 --- a/src/db/posts.rs +++ b/src/db/posts.rs @@ -1,4 +1,3 @@ -use chrono::NaiveDate; use diesel::{insert_into, prelude::*, Insertable, PgConnection, Queryable}; use rb::errors::{RbError, RbOption, RbResult}; use serde::{Deserialize, Serialize}; @@ -12,9 +11,8 @@ pub struct Post { pub id: Uuid, pub section_id: Uuid, - pub title: Option, - pub publish_date: NaiveDate, - pub content: String, + pub is_private: bool, + pub is_archived: bool, } /// A new post to be added to the database. @@ -24,9 +22,8 @@ pub struct Post pub struct NewPost { pub section_id: Uuid, - pub title: Option, - pub publish_date: NaiveDate, - pub content: String, + pub is_private: Option, + pub is_archived: Option, } /// A patch to be applied to a row in the database. @@ -35,9 +32,8 @@ pub struct NewPost pub struct PatchPost { pub section_id: Option, - pub title: Option, - pub publish_date: Option, - pub content: Option, + pub is_private: Option, + pub is_archived: Option, } /// Get a list of posts, specified by the offset & a limit. The maximum for `limit_` is determined diff --git a/src/db/sections.rs b/src/db/sections.rs index c68b0a4..e78ea65 100644 --- a/src/db/sections.rs +++ b/src/db/sections.rs @@ -12,9 +12,11 @@ pub struct Section pub id: Uuid, pub title: String, pub shortname: String, - pub description: Option, + pub description: String, pub is_default: bool, pub has_titles: bool, + pub is_private: bool, + pub is_archived: bool, } /// A new section to add. Any `Option` values will get replaced by their default value in the @@ -29,6 +31,8 @@ pub struct NewSection pub description: Option, pub is_default: Option, pub has_titles: Option, + pub is_private: Option, + pub is_archived: Option, } /// A patch to apply to a section. @@ -42,6 +46,8 @@ pub struct PatchSection description: Option, is_default: Option, has_titles: Option, + pub is_private: Option, + pub is_archived: Option, } /// Get an amount of sections from the database, given an offset & limit. The maximum value for diff --git a/src/db/versions.rs b/src/db/versions.rs new file mode 100644 index 0000000..ae7e020 --- /dev/null +++ b/src/db/versions.rs @@ -0,0 +1,103 @@ +use chrono::NaiveDate; +use diesel::{insert_into, prelude::*, Insertable, PgConnection, Queryable}; +use rb::errors::{RbError, RbOption, RbResult}; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; + +use crate::schema::{versions, versions::dsl::*}; + +/// A version inside the database. +#[derive(Queryable, Serialize)] +pub struct Version +{ + pub id: Uuid, + pub post_id: Uuid, + pub title: Option, + pub publish_date: Option, + pub content: String, + pub is_draft: bool, +} + +#[derive(Deserialize, Insertable)] +#[table_name = "versions"] +#[serde(rename_all = "camelCase")] +pub struct NewVersion +{ + pub post_id: Uuid, + pub title: Option, + pub publish_date: Option, + pub content: Option, + pub is_draft: Option, +} + +#[derive(Deserialize, AsChangeset)] +#[table_name = "versions"] +pub struct PatchVersion +{ + pub id: Uuid, + pub post_id: Option, + pub title: Option, + pub publish_date: Option, + pub content: Option, + pub is_draft: Option, +} + +pub fn get(conn: &PgConnection, offset_: u32, limit_: u32) -> RbResult> +{ + Ok(versions + .offset(offset_.into()) + .limit(std::cmp::min(limit_, super::MAX_VERSIONS).into()) + .load(conn) + .map_err(|_| RbError::DbError("Couldn't query versions."))?) +} + +pub fn get_for_post( + conn: &PgConnection, + post_id_: &Uuid, + offset_: u32, + limit_: u32, +) -> RbResult> +{ + Ok(versions + .offset(offset_.into()) + .filter(post_id.eq(post_id_)) + .limit(std::cmp::min(limit_, super::MAX_VERSIONS).into()) + .load(conn) + .map_err(|_| RbError::DbError("Couldn't query versions."))?) +} + +pub fn find(conn: &PgConnection, id_: &Uuid) -> RbOption +{ + match versions.find(id_).first(conn) { + Ok(val) => Ok(Some(val)), + Err(diesel::NotFound) => Ok(None), + _ => Err(RbError::DbError("Couldn't find version.")), + } +} + +pub fn create(conn: &PgConnection, new: &NewVersion) -> RbResult +{ + Ok(insert_into(versions) + .values(new) + .get_result(conn) + .map_err(|_| RbError::DbError("Couldn't insert version."))?) + + // TODO check for conflict? +} + +pub fn update(conn: &PgConnection, id_: &Uuid, patch: &PatchVersion) -> RbResult +{ + Ok(diesel::update(versions.filter(id.eq(id_))) + .set(patch) + .get_result(conn) + .map_err(|_| RbError::DbError("Couldn't update version."))?) +} + +pub fn delete(conn: &PgConnection, id_: &Uuid) -> RbResult<()> +{ + diesel::delete(versions.filter(id.eq(id_))) + .execute(conn) + .map_err(|_| RbError::DbError("Couldn't delete version."))?; + + Ok(()) +} diff --git a/src/main.rs b/src/main.rs index 7ca6841..002115f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -95,6 +95,17 @@ fn rocket() -> _ v1::posts::patch, v1::posts::delete ], + ) + .mount( + "/v1/versions", + routes![ + v1::versions::get, + v1::versions::get_for_post, + v1::versions::create, + v1::versions::find, + v1::versions::patch, + v1::versions::delete + ], ); let new_figment = rocket.figment(); diff --git a/src/schema.rs b/src/schema.rs index b032c41..cadb6f0 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -12,17 +12,11 @@ table! { id -> Uuid, title -> Varchar, shortname -> Varchar, - description -> Nullable, + description -> Text, is_default -> Bool, has_titles -> Bool, is_private -> Bool, - } -} - -table! { - tags (post_id, value) { - post_id -> Uuid, - value -> Varchar, + is_archived -> Bool, } } @@ -38,12 +32,10 @@ table! { } joinable!(posts -> sections (section_id)); -joinable!(tags -> posts (post_id)); joinable!(versions -> posts (post_id)); allow_tables_to_appear_in_same_query!( posts, sections, - tags, versions, ); diff --git a/src/v1/mod.rs b/src/v1/mod.rs index ac3e36c..b5e6454 100644 --- a/src/v1/mod.rs +++ b/src/v1/mod.rs @@ -1,2 +1,3 @@ pub mod posts; pub mod sections; +pub mod versions; diff --git a/src/v1/versions.rs b/src/v1/versions.rs new file mode 100644 index 0000000..449583f --- /dev/null +++ b/src/v1/versions.rs @@ -0,0 +1,74 @@ +use rb::{ + errors::{RbOption, RbResult}, + guards::Admin, +}; +use rb_blog::db; +use rocket::serde::json::Json; +use uuid::Uuid; + +use crate::RbDbConn; + +#[get("/?&", rank = 1)] +pub async fn get(conn: RbDbConn, offset: u32, limit: u32) -> RbResult>> +{ + Ok(Json( + conn.run(move |c| db::versions::get(c, offset, limit)) + .await?, + )) +} + +#[get("/?&&", rank = 0)] +pub async fn get_for_post( + conn: RbDbConn, + post_id: Uuid, + offset: u32, + limit: u32, +) -> RbResult>> +{ + Ok(Json( + conn.run(move |c| db::versions::get_for_post(c, &post_id, offset, limit)) + .await?, + )) +} + +#[post("/", data = "")] +pub async fn create( + _admin: Admin, + conn: RbDbConn, + new: Json, +) -> RbResult> +{ + Ok(Json( + conn.run(move |c| db::versions::create(c, &new.into_inner())) + .await?, + )) +} + +#[get("/")] +pub async fn find(conn: RbDbConn, id: uuid::Uuid) -> RbOption> +{ + Ok(conn + .run(move |c| db::versions::find(c, &id)) + .await? + .and_then(|p| Some(Json(p)))) +} + +#[patch("/", data = "")] +pub async fn patch( + _admin: Admin, + conn: RbDbConn, + id: uuid::Uuid, + patch: Json, +) -> RbResult> +{ + Ok(Json( + conn.run(move |c| db::versions::update(c, &id, &patch.into_inner())) + .await?, + )) +} + +#[delete("/")] +pub async fn delete(_admin: Admin, conn: RbDbConn, id: uuid::Uuid) -> RbResult<()> +{ + Ok(conn.run(move |c| db::versions::delete(c, &id)).await?) +}