Added some much-needed comments

pull/3/head
Jef Roosens 2021-11-28 21:45:29 +01:00
parent 9835638762
commit b83ea9796b
Signed by: Jef Roosens
GPG Key ID: 955C0660072F691F
8 changed files with 48 additions and 9 deletions

View File

@ -1,8 +1,9 @@
default: default:
address: "0.0.0.0" address: "0.0.0.0"
ports: 8000 port: 8000
debug: debug:
port: 8001
keep_alive: 5 keep_alive: 5
read_timeout: 5 read_timeout: 5
write_timeout: 5 write_timeout: 5

View File

@ -6,3 +6,6 @@ pub mod sections;
pub use posts::{NewPost, PatchPost, Post}; pub use posts::{NewPost, PatchPost, Post};
pub use sections::{NewSection, PatchSection, Section}; pub use sections::{NewSection, PatchSection, Section};
pub const MAX_POSTS: u32 = 64;
pub const MAX_SECTIONS: u32 = 64;

View File

@ -6,6 +6,7 @@ use uuid::Uuid;
use crate::schema::{posts, posts::dsl::*}; use crate::schema::{posts, posts::dsl::*};
/// A post inside the database.
#[derive(Queryable, Serialize)] #[derive(Queryable, Serialize)]
pub struct Post pub struct Post
{ {
@ -16,6 +17,7 @@ pub struct Post
pub content: String, pub content: String,
} }
/// A new post to be added to the database.
#[derive(Deserialize, Insertable)] #[derive(Deserialize, Insertable)]
#[table_name = "posts"] #[table_name = "posts"]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
@ -27,6 +29,7 @@ pub struct NewPost
pub content: String, pub content: String,
} }
/// A patch to be applied to a row in the database.
#[derive(Deserialize, AsChangeset)] #[derive(Deserialize, AsChangeset)]
#[table_name = "posts"] #[table_name = "posts"]
pub struct PatchPost pub struct PatchPost
@ -37,15 +40,18 @@ pub struct PatchPost
pub content: Option<String>, pub content: Option<String>,
} }
/// Get a list of posts, specified by the offset & a limit. The maximum for `limit_` is determined
/// by `super::MAX_POSTS`.
pub fn get(conn: &PgConnection, offset_: u32, limit_: u32) -> RbResult<Vec<Post>> pub fn get(conn: &PgConnection, offset_: u32, limit_: u32) -> RbResult<Vec<Post>>
{ {
Ok(posts Ok(posts
.offset(offset_.into()) .offset(offset_.into())
.limit(limit_.into()) .limit(std::cmp::min(limit_, super::MAX_POSTS).into())
.load(conn) .load(conn)
.map_err(|_| RbError::DbError("Couldn't query posts."))?) .map_err(|_| RbError::DbError("Couldn't query posts."))?)
} }
/// Try to find a post given its id (primary key).
pub fn find(conn: &PgConnection, id_: &Uuid) -> RbOption<Post> pub fn find(conn: &PgConnection, id_: &Uuid) -> RbOption<Post>
{ {
match posts.find(id_).first(conn) { match posts.find(id_).first(conn) {
@ -55,6 +61,7 @@ pub fn find(conn: &PgConnection, id_: &Uuid) -> RbOption<Post>
} }
} }
/// Create a new post & store it in the database.
pub fn create(conn: &PgConnection, new_post: &NewPost) -> RbResult<Post> pub fn create(conn: &PgConnection, new_post: &NewPost) -> RbResult<Post>
{ {
Ok(insert_into(posts) Ok(insert_into(posts)
@ -65,6 +72,7 @@ pub fn create(conn: &PgConnection, new_post: &NewPost) -> RbResult<Post>
// TODO check for conflict? // TODO check for conflict?
} }
/// Update a post in the database with a given ID, returning the updated row.
pub fn update(conn: &PgConnection, post_id: &Uuid, patch_post: &PatchPost) -> RbResult<Post> pub fn update(conn: &PgConnection, post_id: &Uuid, patch_post: &PatchPost) -> RbResult<Post>
{ {
Ok(diesel::update(posts.filter(id.eq(post_id))) Ok(diesel::update(posts.filter(id.eq(post_id)))
@ -73,6 +81,7 @@ pub fn update(conn: &PgConnection, post_id: &Uuid, patch_post: &PatchPost) -> Rb
.map_err(|_| RbError::DbError("Couldn't update post."))?) .map_err(|_| RbError::DbError("Couldn't update post."))?)
} }
/// Delete a post with a given ID.
pub fn delete(conn: &PgConnection, post_id: &Uuid) -> RbResult<()> pub fn delete(conn: &PgConnection, post_id: &Uuid) -> RbResult<()>
{ {
diesel::delete(posts.filter(id.eq(post_id))) diesel::delete(posts.filter(id.eq(post_id)))

View File

@ -5,6 +5,7 @@ use uuid::Uuid;
use crate::schema::{sections, sections::dsl::*}; use crate::schema::{sections, sections::dsl::*};
/// A section inside the database.
#[derive(Queryable, Serialize)] #[derive(Queryable, Serialize)]
pub struct Section pub struct Section
{ {
@ -16,6 +17,8 @@ pub struct Section
pub has_titles: bool, pub has_titles: bool,
} }
/// A new section to add. Any `Option` values will get replaced by their default value in the
/// database.
#[derive(Serialize, Deserialize, Insertable)] #[derive(Serialize, Deserialize, Insertable)]
#[table_name = "sections"] #[table_name = "sections"]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
@ -28,6 +31,7 @@ pub struct NewSection
pub has_titles: Option<bool>, pub has_titles: Option<bool>,
} }
/// A patch to apply to a section.
#[derive(Deserialize, AsChangeset)] #[derive(Deserialize, AsChangeset)]
#[table_name = "sections"] #[table_name = "sections"]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
@ -40,15 +44,18 @@ pub struct PatchSection
has_titles: Option<bool>, has_titles: Option<bool>,
} }
/// Get an amount of sections from the database, given an offset & limit. The maximum value for
/// `limit_` is determined by `super::MAX_SECTIONS`.
pub fn get(conn: &PgConnection, offset_: u32, limit_: u32) -> RbResult<Vec<Section>> pub fn get(conn: &PgConnection, offset_: u32, limit_: u32) -> RbResult<Vec<Section>>
{ {
Ok(sections Ok(sections
.offset(offset_.into()) .offset(offset_.into())
.limit(limit_.into()) .limit(std::cmp::min(limit_, super::MAX_SECTIONS).into())
.load(conn) .load(conn)
.map_err(|_| RbError::DbError("Couldn't query sections."))?) .map_err(|_| RbError::DbError("Couldn't query sections."))?)
} }
/// Try to find a section given its shortname.
pub fn find_with_shortname(conn: &PgConnection, shortname_: &str) -> RbOption<Section> pub fn find_with_shortname(conn: &PgConnection, shortname_: &str) -> RbOption<Section>
{ {
match sections.filter(shortname.eq(shortname_)).first(conn) { match sections.filter(shortname.eq(shortname_)).first(conn) {
@ -58,6 +65,7 @@ pub fn find_with_shortname(conn: &PgConnection, shortname_: &str) -> RbOption<Se
} }
} }
/// Create a new section.
pub fn create(conn: &PgConnection, new_section: &NewSection) -> RbResult<Section> pub fn create(conn: &PgConnection, new_section: &NewSection) -> RbResult<Section>
{ {
Ok(insert_into(sections) Ok(insert_into(sections)
@ -68,6 +76,7 @@ pub fn create(conn: &PgConnection, new_section: &NewSection) -> RbResult<Section
// TODO check for conflict? // TODO check for conflict?
} }
/// Update a section given its ID.
pub fn update(conn: &PgConnection, post_id: &Uuid, patch_post: &PatchSection) -> RbResult<Section> pub fn update(conn: &PgConnection, post_id: &Uuid, patch_post: &PatchSection) -> RbResult<Section>
{ {
Ok(diesel::update(sections.filter(id.eq(post_id))) Ok(diesel::update(sections.filter(id.eq(post_id)))
@ -76,6 +85,7 @@ pub fn update(conn: &PgConnection, post_id: &Uuid, patch_post: &PatchSection) ->
.map_err(|_| RbError::DbError("Couldn't update section."))?) .map_err(|_| RbError::DbError("Couldn't update section."))?)
} }
// Update a section given its shortname.
pub fn update_with_shortname( pub fn update_with_shortname(
conn: &PgConnection, conn: &PgConnection,
shortname_: &str, shortname_: &str,
@ -88,6 +98,7 @@ pub fn update_with_shortname(
.map_err(|_| RbError::DbError("Couldn't update section with shortname."))?) .map_err(|_| RbError::DbError("Couldn't update section with shortname."))?)
} }
/// Delete a section given its ID.
pub fn delete(conn: &PgConnection, post_id: &Uuid) -> RbResult<()> pub fn delete(conn: &PgConnection, post_id: &Uuid) -> RbResult<()>
{ {
diesel::delete(sections.filter(id.eq(post_id))) diesel::delete(sections.filter(id.eq(post_id)))

View File

@ -27,19 +27,22 @@ pub fn auth_header() -> rocket::http::Header<'static>
return rocket::http::Header::new("Authorization", "Bearer eyJhbGciOiJIUzI1NiJ9.eyJpZCI6IjVjMjM2OTI0NjY4ZDQzZWFiNGNmNDczYjk1YWZiNzgzIiwidXNlcm5hbWUiOiJKb2huIERvZSIsImFkbWluIjp0cnVlLCJleHAiOjE1MTYyMzkwMjIwfQ.if939L9le8LP-dtXnQs-mHPkb-VieRAvAfSu20755jY"); return rocket::http::Header::new("Authorization", "Bearer eyJhbGciOiJIUzI1NiJ9.eyJpZCI6IjVjMjM2OTI0NjY4ZDQzZWFiNGNmNDczYjk1YWZiNzgzIiwidXNlcm5hbWUiOiJKb2huIERvZSIsImFkbWluIjp0cnVlLCJleHAiOjE1MTYyMzkwMjIwfQ.if939L9le8LP-dtXnQs-mHPkb-VieRAvAfSu20755jY");
} }
/// Used by Rocket to store database connections.
#[database("postgres_rb")] #[database("postgres_rb")]
pub struct RbDbConn(diesel::PgConnection); pub struct RbDbConn(diesel::PgConnection);
/// Handles all error status codes.
#[catch(default)] #[catch(default)]
fn default_catcher(status: Status, _: &Request) -> Value fn default_catcher(status: Status, _: &Request) -> Value
{ {
json!({"status": status.code, "message": ""}) json!({"status": status.code, "message": ""})
} }
embed_migrations!(); /// Rocket fairing that executes the necessary migrations in our database.
async fn run_db_migrations(rocket: Rocket<Build>) -> Result<Rocket<Build>, Rocket<Build>> async fn run_db_migrations(rocket: Rocket<Build>) -> Result<Rocket<Build>, Rocket<Build>>
{ {
embed_migrations!();
let conn = RbDbConn::get_one(&rocket) let conn = RbDbConn::get_one(&rocket)
.await .await
.expect("database connection"); .expect("database connection");
@ -50,12 +53,15 @@ async fn run_db_migrations(rocket: Rocket<Build>) -> Result<Rocket<Build>, Rocke
.await .await
} }
/// Struct to deserialize from the config file. It contains any custom configuration our
/// application might need besides the default Rocket variables.
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize)]
pub struct RbConfig pub struct RbConfig
{ {
jwt: JwtConf, jwt: JwtConf,
} }
/// The main entrypoint of our program. It launches the Rocket instance.
#[launch] #[launch]
fn rocket() -> _ fn rocket() -> _
{ {
@ -69,8 +75,6 @@ fn rocket() -> _
"Run database migrations", "Run database migrations",
run_db_migrations, run_db_migrations,
)) ))
// .attach(AdHoc::try_on_ignite("Create admin user", create_admin_user))
// .attach(AdHoc::config::<JwtConf>())
.register("/", catchers![default_catcher]) .register("/", catchers![default_catcher])
.mount( .mount(
"/v1/sections", "/v1/sections",

View File

@ -7,6 +7,7 @@ use rocket::serde::json::Json;
use crate::RbDbConn; use crate::RbDbConn;
/// Get one or more posts.
#[get("/?<offset>&<limit>")] #[get("/?<offset>&<limit>")]
pub async fn get(conn: RbDbConn, offset: u32, limit: u32) -> RbResult<Json<Vec<db::Post>>> pub async fn get(conn: RbDbConn, offset: u32, limit: u32) -> RbResult<Json<Vec<db::Post>>>
{ {
@ -15,6 +16,7 @@ pub async fn get(conn: RbDbConn, offset: u32, limit: u32) -> RbResult<Json<Vec<d
)) ))
} }
/// Create a new post.
#[post("/", data = "<new_post>")] #[post("/", data = "<new_post>")]
pub async fn create( pub async fn create(
_admin: Admin, _admin: Admin,
@ -28,6 +30,7 @@ pub async fn create(
)) ))
} }
/// Get a post given its ID.
#[get("/<id>")] #[get("/<id>")]
pub async fn find(conn: RbDbConn, id: uuid::Uuid) -> RbOption<Json<db::Post>> pub async fn find(conn: RbDbConn, id: uuid::Uuid) -> RbOption<Json<db::Post>>
{ {
@ -37,6 +40,7 @@ pub async fn find(conn: RbDbConn, id: uuid::Uuid) -> RbOption<Json<db::Post>>
.and_then(|p| Some(Json(p)))) .and_then(|p| Some(Json(p))))
} }
/// Patch a post given its ID.
#[patch("/<id>", data = "<patch_post>")] #[patch("/<id>", data = "<patch_post>")]
pub async fn patch( pub async fn patch(
_admin: Admin, _admin: Admin,
@ -51,6 +55,7 @@ pub async fn patch(
)) ))
} }
/// Delete a post given its ID..
#[delete("/<id>")] #[delete("/<id>")]
pub async fn delete(_admin: Admin, conn: RbDbConn, id: uuid::Uuid) -> RbResult<()> pub async fn delete(_admin: Admin, conn: RbDbConn, id: uuid::Uuid) -> RbResult<()>
{ {

View File

@ -9,6 +9,8 @@ use rocket::serde::json::Json;
use crate::RbDbConn; use crate::RbDbConn;
/// Get multiple sections given an offset & a limit. The limit is bound by
/// `rb_blog::db::MAX_SECTIONS`.
#[get("/?<offset>&<limit>")] #[get("/?<offset>&<limit>")]
pub async fn get(conn: RbDbConn, offset: u32, limit: u32) -> RbResult<Json<Vec<db::Section>>> pub async fn get(conn: RbDbConn, offset: u32, limit: u32) -> RbResult<Json<Vec<db::Section>>>
{ {
@ -18,6 +20,7 @@ pub async fn get(conn: RbDbConn, offset: u32, limit: u32) -> RbResult<Json<Vec<d
)) ))
} }
/// Create a new section.
#[post("/", data = "<new_section>")] #[post("/", data = "<new_section>")]
pub async fn create( pub async fn create(
_admin: Admin, _admin: Admin,
@ -31,6 +34,7 @@ pub async fn create(
)) ))
} }
/// Get a section by its shortname.
#[get("/<shortname>")] #[get("/<shortname>")]
pub async fn find(conn: RbDbConn, shortname: String) -> RbOption<Json<db::Section>> pub async fn find(conn: RbDbConn, shortname: String) -> RbOption<Json<db::Section>>
{ {
@ -40,6 +44,7 @@ pub async fn find(conn: RbDbConn, shortname: String) -> RbOption<Json<db::Sectio
.and_then(|p| Some(Json(p)))) .and_then(|p| Some(Json(p))))
} }
/// Patch a section given its shortname.
#[patch("/<shortname>", data = "<patch_section>")] #[patch("/<shortname>", data = "<patch_section>")]
pub async fn patch( pub async fn patch(
_admin: Admin, _admin: Admin,
@ -56,6 +61,7 @@ pub async fn patch(
)) ))
} }
/// Delete a section given its ID.
#[delete("/<id>")] #[delete("/<id>")]
pub async fn delete(_admin: Admin, conn: RbDbConn, id: uuid::Uuid) -> RbResult<()> pub async fn delete(_admin: Admin, conn: RbDbConn, id: uuid::Uuid) -> RbResult<()>
{ {

View File

@ -12,8 +12,8 @@ data = {
"shortname": "short", "shortname": "short",
} }
r = requests.post("http://localhost:8000/v1/sections", headers=headers, json=data) r = requests.post("http://localhost:8000/api/v1/sections", headers=headers, json=data)
print(r.content) print(r.content)
print(r.status_code) print(r.status_code)
r = requests.get("http://localhost:8000/v1/sections?offset=0&limit=100") r = requests.get("http://localhost:8000/api/v1/sections?offset=0&limit=100")
print(r.json()) print(r.json())