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>>,
|
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]
|
#[async_trait]
|
||||||
pub trait PostRepository {
|
pub trait PostRepository {
|
||||||
/// Get a post from its slug
|
/// 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
|
/// Create a new post
|
||||||
async fn create_post(
|
async fn create_post(
|
||||||
|
|
|
@ -31,9 +31,10 @@ pub struct Site {
|
||||||
|
|
||||||
/// More useful version of Site for showing to users
|
/// More useful version of Site for showing to users
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
pub struct APISite {
|
pub struct SiteWithAuthor {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub owner: String,
|
pub owner: String,
|
||||||
|
pub owner_display_name: Option<String>,
|
||||||
pub title: String,
|
pub title: String,
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
pub created_at: NaiveDateTime,
|
pub created_at: NaiveDateTime,
|
||||||
|
@ -58,7 +59,7 @@ pub struct UpdateSiteData {
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait SiteRepository {
|
pub trait SiteRepository {
|
||||||
/// Retrieve site info from site name/slug
|
/// 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
|
/// Create a new instance of a site and store it
|
||||||
async fn create_site(&self, owner: &Uuid, options: CreateSiteData) -> Result<(), Error>;
|
async fn create_site(&self, owner: &Uuid, options: CreateSiteData) -> Result<(), Error>;
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use content::post::PostBlock;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
use sqlx::types::Json;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::content::{
|
use crate::content::{
|
||||||
self,
|
self,
|
||||||
post::{CreatePostData, Post, PostRepository, UpdatePostData},
|
post::{CreatePostData, PostRepository, PostWithAuthor, UpdatePostData},
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -12,18 +14,43 @@ use super::Database;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl PostRepository for Database {
|
impl PostRepository for Database {
|
||||||
async fn get_post_from_url(&self, site: &str, slug: &str) -> Result<Post, content::Error> {
|
async fn get_post_from_url(
|
||||||
let post_query = sqlx::query_as(
|
&self,
|
||||||
"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")
|
site: &str,
|
||||||
.bind(slug)
|
slug: &str,
|
||||||
.bind(site);
|
) -> Result<PostWithAuthor, content::Error> {
|
||||||
let post: Post = post_query
|
let post = sqlx::query_as!(
|
||||||
.fetch_one(&self.pool)
|
PostWithAuthor,
|
||||||
.await
|
r#"SELECT
|
||||||
.map_err(|e| match e {
|
COALESCE(users.display_name,users.name) as author_display_name,
|
||||||
sqlx::Error::RowNotFound => Error::NotFound,
|
users.name as author_username,
|
||||||
_ => e.into(),
|
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)
|
Ok(post)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ use async_trait::async_trait;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::content::{
|
use crate::content::{
|
||||||
site::{APISite, CreateSiteData, SiteRepository, UpdateSiteData},
|
site::{CreateSiteData, SiteRepository, SiteWithAuthor, UpdateSiteData},
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -10,17 +10,30 @@ use super::Database;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl SiteRepository for Database {
|
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!(
|
let site = sqlx::query_as!(
|
||||||
APISite,
|
SiteWithAuthor,
|
||||||
"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",
|
r#"SELECT
|
||||||
name
|
sites.name,
|
||||||
).fetch_one(&self.pool)
|
users.name as owner,
|
||||||
.await
|
COALESCE(users.display_name,users.name) as owner_display_name,
|
||||||
.map_err(|e| match e {
|
title,
|
||||||
sqlx::Error::RowNotFound => Error::NotFound,
|
description,
|
||||||
_ => e.into(),
|
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)
|
Ok(site)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,18 +63,29 @@ impl SiteRepository for Database {
|
||||||
options: UpdateSiteData,
|
options: UpdateSiteData,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let result = sqlx::query!(
|
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",
|
r#"UPDATE sites SET
|
||||||
options.name,
|
name = COALESCE($1, name),
|
||||||
options.title,
|
title = COALESCE($2, title),
|
||||||
options.description,
|
description = COALESCE($3, description),
|
||||||
name,
|
modified_at = NOW()
|
||||||
owner,
|
WHERE
|
||||||
).execute(&self.pool).await.map_err(|err| match err {
|
name = $4
|
||||||
sqlx::Error::Database(dberr) if dberr.constraint() == Some("sites_name_key") => {
|
AND owner = $5
|
||||||
Error::IdentifierNotAvailable
|
AND deleted_at IS NULL"#,
|
||||||
}
|
options.name,
|
||||||
_ => err.into(),
|
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 {
|
if result.rows_affected() == 0 {
|
||||||
return Err(Error::NotFound);
|
return Err(Error::NotFound);
|
||||||
|
|
Loading…
Reference in a new issue