add author info to GETs
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
Hamcha 2023-07-11 12:56:30 +02:00
parent 5f76de0a9a
commit f5c7570d9d
Signed by: hamcha
GPG key ID: 1669C533B8CF6D89
4 changed files with 106 additions and 39 deletions

View file

@ -93,10 +93,25 @@ pub struct UpdatePostData {
pub blocks: Option<Vec<PostBlock>>,
}
/// Post with site and author dereferenced for convenience
#[derive(Serialize)]
pub struct PostWithAuthor {
pub author_display_name: Option<String>,
pub author_username: String,
pub slug: String,
pub title: String,
pub description: Option<String>,
pub tags: Vec<String>,
pub blocks: Json<Vec<PostBlock>>,
pub created_at: NaiveDateTime,
pub modified_at: Option<NaiveDateTime>,
pub deleted_at: Option<NaiveDateTime>,
}
#[async_trait]
pub trait PostRepository {
/// Get a post from its slug
async fn get_post_from_url(&self, site: &str, slug: &str) -> Result<Post, Error>;
async fn get_post_from_url(&self, site: &str, slug: &str) -> Result<PostWithAuthor, Error>;
/// Create a new post
async fn create_post(

View file

@ -31,9 +31,10 @@ pub struct Site {
/// More useful version of Site for showing to users
#[derive(Serialize)]
pub struct APISite {
pub struct SiteWithAuthor {
pub name: String,
pub owner: String,
pub owner_display_name: Option<String>,
pub title: String,
pub description: Option<String>,
pub created_at: NaiveDateTime,
@ -58,7 +59,7 @@ pub struct UpdateSiteData {
#[async_trait]
pub trait SiteRepository {
/// Retrieve site info from site name/slug
async fn get_site_by_name(&self, name: &str) -> Result<APISite, Error>;
async fn get_site_by_name(&self, name: &str) -> Result<SiteWithAuthor, Error>;
/// Create a new instance of a site and store it
async fn create_site(&self, owner: &Uuid, options: CreateSiteData) -> Result<(), Error>;

View file

@ -1,10 +1,12 @@
use async_trait::async_trait;
use content::post::PostBlock;
use serde_json::json;
use sqlx::types::Json;
use uuid::Uuid;
use crate::content::{
self,
post::{CreatePostData, Post, PostRepository, UpdatePostData},
post::{CreatePostData, PostRepository, PostWithAuthor, UpdatePostData},
Error,
};
@ -12,18 +14,43 @@ use super::Database;
#[async_trait]
impl PostRepository for Database {
async fn get_post_from_url(&self, site: &str, slug: &str) -> Result<Post, content::Error> {
let post_query = sqlx::query_as(
"SELECT pages.* FROM pages JOIN sites ON site = sites.id WHERE slug = $1 AND name = $2 AND pages.deleted_at IS NULL AND sites.deleted_at IS NULL")
.bind(slug)
.bind(site);
let post: Post = post_query
.fetch_one(&self.pool)
.await
.map_err(|e| match e {
sqlx::Error::RowNotFound => Error::NotFound,
_ => e.into(),
})?;
async fn get_post_from_url(
&self,
site: &str,
slug: &str,
) -> Result<PostWithAuthor, content::Error> {
let post = sqlx::query_as!(
PostWithAuthor,
r#"SELECT
COALESCE(users.display_name,users.name) as author_display_name,
users.name as author_username,
pages.slug,
pages.title,
pages.description,
pages.tags,
pages.blocks as "blocks!: Json<Vec<PostBlock>>",
pages.created_at,
pages.modified_at,
pages.deleted_at
FROM pages
JOIN
sites ON site = sites.id
JOIN
users ON author = users.id
WHERE
slug = $1
AND sites.name = $2
AND pages.deleted_at IS NULL
AND sites.deleted_at IS NULL"#,
slug,
site
)
.fetch_one(&self.pool)
.await
.map_err(|e| match e {
sqlx::Error::RowNotFound => Error::NotFound,
_ => e.into(),
})?;
Ok(post)
}

View file

@ -2,7 +2,7 @@ use async_trait::async_trait;
use uuid::Uuid;
use crate::content::{
site::{APISite, CreateSiteData, SiteRepository, UpdateSiteData},
site::{CreateSiteData, SiteRepository, SiteWithAuthor, UpdateSiteData},
Error,
};
@ -10,17 +10,30 @@ use super::Database;
#[async_trait]
impl SiteRepository for Database {
async fn get_site_by_name(&self, name: &str) -> Result<APISite, Error> {
async fn get_site_by_name(&self, name: &str) -> Result<SiteWithAuthor, Error> {
let site = sqlx::query_as!(
APISite,
"SELECT sites.name, users.name as owner, title, description, sites.created_at FROM sites JOIN users ON sites.owner = users.id WHERE sites.name = $1 AND sites.deleted_at IS NULL",
name
).fetch_one(&self.pool)
.await
.map_err(|e| match e {
sqlx::Error::RowNotFound => Error::NotFound,
_ => e.into(),
})?;
SiteWithAuthor,
r#"SELECT
sites.name,
users.name as owner,
COALESCE(users.display_name,users.name) as owner_display_name,
title,
description,
sites.created_at
FROM sites
JOIN
users ON sites.owner = users.id
WHERE
sites.name = $1
AND sites.deleted_at IS NULL"#,
name
)
.fetch_one(&self.pool)
.await
.map_err(|e| match e {
sqlx::Error::RowNotFound => Error::NotFound,
_ => e.into(),
})?;
Ok(site)
}
@ -50,18 +63,29 @@ impl SiteRepository for Database {
options: UpdateSiteData,
) -> Result<(), Error> {
let result = sqlx::query!(
"UPDATE sites SET name = COALESCE($1, name), title = COALESCE($2, title), description = COALESCE($3, description), modified_at = NOW() WHERE name = $4 AND owner = $5 AND deleted_at IS NULL",
options.name,
options.title,
options.description,
name,
owner,
).execute(&self.pool).await.map_err(|err| match err {
sqlx::Error::Database(dberr) if dberr.constraint() == Some("sites_name_key") => {
Error::IdentifierNotAvailable
}
_ => err.into(),
})?;
r#"UPDATE sites SET
name = COALESCE($1, name),
title = COALESCE($2, title),
description = COALESCE($3, description),
modified_at = NOW()
WHERE
name = $4
AND owner = $5
AND deleted_at IS NULL"#,
options.name,
options.title,
options.description,
name,
owner,
)
.execute(&self.pool)
.await
.map_err(|err| match err {
sqlx::Error::Database(dberr) if dberr.constraint() == Some("sites_name_key") => {
Error::IdentifierNotAvailable
}
_ => err.into(),
})?;
if result.rows_affected() == 0 {
return Err(Error::NotFound);