This commit is contained in:
parent
5f76de0a9a
commit
f5c7570d9d
4 changed files with 106 additions and 39 deletions
|
@ -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(
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue