Added post versions; updated db boilerplate
parent
8af3ff6996
commit
b0d7f77983
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<String>,
|
||||
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<String>,
|
||||
pub publish_date: NaiveDate,
|
||||
pub content: String,
|
||||
pub is_private: Option<bool>,
|
||||
pub is_archived: Option<bool>,
|
||||
}
|
||||
|
||||
/// 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<Uuid>,
|
||||
pub title: Option<String>,
|
||||
pub publish_date: Option<NaiveDate>,
|
||||
pub content: Option<String>,
|
||||
pub is_private: Option<bool>,
|
||||
pub is_archived: Option<bool>,
|
||||
}
|
||||
|
||||
/// Get a list of posts, specified by the offset & a limit. The maximum for `limit_` is determined
|
||||
|
|
|
@ -12,9 +12,11 @@ pub struct Section
|
|||
pub id: Uuid,
|
||||
pub title: String,
|
||||
pub shortname: String,
|
||||
pub description: Option<String>,
|
||||
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<String>,
|
||||
pub is_default: Option<bool>,
|
||||
pub has_titles: Option<bool>,
|
||||
pub is_private: Option<bool>,
|
||||
pub is_archived: Option<bool>,
|
||||
}
|
||||
|
||||
/// A patch to apply to a section.
|
||||
|
@ -42,6 +46,8 @@ pub struct PatchSection
|
|||
description: Option<String>,
|
||||
is_default: Option<bool>,
|
||||
has_titles: Option<bool>,
|
||||
pub is_private: Option<bool>,
|
||||
pub is_archived: Option<bool>,
|
||||
}
|
||||
|
||||
/// Get an amount of sections from the database, given an offset & limit. The maximum value for
|
||||
|
|
|
@ -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<String>,
|
||||
pub publish_date: Option<NaiveDate>,
|
||||
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<String>,
|
||||
pub publish_date: Option<NaiveDate>,
|
||||
pub content: Option<String>,
|
||||
pub is_draft: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, AsChangeset)]
|
||||
#[table_name = "versions"]
|
||||
pub struct PatchVersion
|
||||
{
|
||||
pub id: Uuid,
|
||||
pub post_id: Option<Uuid>,
|
||||
pub title: Option<String>,
|
||||
pub publish_date: Option<NaiveDate>,
|
||||
pub content: Option<String>,
|
||||
pub is_draft: Option<bool>,
|
||||
}
|
||||
|
||||
pub fn get(conn: &PgConnection, offset_: u32, limit_: u32) -> RbResult<Vec<Version>>
|
||||
{
|
||||
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<Vec<Version>>
|
||||
{
|
||||
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<Version>
|
||||
{
|
||||
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<Version>
|
||||
{
|
||||
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<Version>
|
||||
{
|
||||
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(())
|
||||
}
|
11
src/main.rs
11
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();
|
||||
|
|
|
@ -12,17 +12,11 @@ table! {
|
|||
id -> Uuid,
|
||||
title -> Varchar,
|
||||
shortname -> Varchar,
|
||||
description -> Nullable<Text>,
|
||||
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,
|
||||
);
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
pub mod posts;
|
||||
pub mod sections;
|
||||
pub mod versions;
|
||||
|
|
|
@ -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("/?<offset>&<limit>", rank = 1)]
|
||||
pub async fn get(conn: RbDbConn, offset: u32, limit: u32) -> RbResult<Json<Vec<db::Version>>>
|
||||
{
|
||||
Ok(Json(
|
||||
conn.run(move |c| db::versions::get(c, offset, limit))
|
||||
.await?,
|
||||
))
|
||||
}
|
||||
|
||||
#[get("/?<post_id>&<offset>&<limit>", rank = 0)]
|
||||
pub async fn get_for_post(
|
||||
conn: RbDbConn,
|
||||
post_id: Uuid,
|
||||
offset: u32,
|
||||
limit: u32,
|
||||
) -> RbResult<Json<Vec<db::Version>>>
|
||||
{
|
||||
Ok(Json(
|
||||
conn.run(move |c| db::versions::get_for_post(c, &post_id, offset, limit))
|
||||
.await?,
|
||||
))
|
||||
}
|
||||
|
||||
#[post("/", data = "<new>")]
|
||||
pub async fn create(
|
||||
_admin: Admin,
|
||||
conn: RbDbConn,
|
||||
new: Json<db::NewVersion>,
|
||||
) -> RbResult<Json<db::Version>>
|
||||
{
|
||||
Ok(Json(
|
||||
conn.run(move |c| db::versions::create(c, &new.into_inner()))
|
||||
.await?,
|
||||
))
|
||||
}
|
||||
|
||||
#[get("/<id>")]
|
||||
pub async fn find(conn: RbDbConn, id: uuid::Uuid) -> RbOption<Json<db::Version>>
|
||||
{
|
||||
Ok(conn
|
||||
.run(move |c| db::versions::find(c, &id))
|
||||
.await?
|
||||
.and_then(|p| Some(Json(p))))
|
||||
}
|
||||
|
||||
#[patch("/<id>", data = "<patch>")]
|
||||
pub async fn patch(
|
||||
_admin: Admin,
|
||||
conn: RbDbConn,
|
||||
id: uuid::Uuid,
|
||||
patch: Json<db::PatchVersion>,
|
||||
) -> RbResult<Json<db::Version>>
|
||||
{
|
||||
Ok(Json(
|
||||
conn.run(move |c| db::versions::update(c, &id, &patch.into_inner()))
|
||||
.await?,
|
||||
))
|
||||
}
|
||||
|
||||
#[delete("/<id>")]
|
||||
pub async fn delete(_admin: Admin, conn: RbDbConn, id: uuid::Uuid) -> RbResult<()>
|
||||
{
|
||||
Ok(conn.run(move |c| db::versions::delete(c, &id)).await?)
|
||||
}
|
Reference in New Issue