Skip to content

Commit

Permalink
SSR for posts and authors (#72)
Browse files Browse the repository at this point in the history
* ssr-for-posts-and-authors

* fmt

* refactor

* fix super

* correct

* refactoring
  • Loading branch information
tikitko authored Dec 25, 2023
1 parent 7b18ee0 commit abdea46
Show file tree
Hide file tree
Showing 23 changed files with 214 additions and 97 deletions.
11 changes: 11 additions & 0 deletions blog-generic/src/author_slug_utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
pub const SPLIT_SYMBOL: char = '^';

pub fn clean(slug: &String) -> String {
slug.rsplit_once(SPLIT_SYMBOL)
.map(|r| r.0.to_owned())
.unwrap_or(slug.clone())
}

pub fn extend(slug: &String, suffix: &String) -> String {
format!("{slug}{SPLIT_SYMBOL}{suffix}")
}
14 changes: 3 additions & 11 deletions blog-generic/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@
pub mod author_slug_utils;
pub mod entities;
pub mod events;
mod page_processor;

const AUTHOR_SLUG_SPLIT_SYMBOL: char = '^';

pub fn clean_author_slug(slug: &String) -> String {
slug.rsplit_once(AUTHOR_SLUG_SPLIT_SYMBOL)
.map(|r| r.0.to_owned())
.unwrap_or(slug.clone())
}

pub fn extend_author_slug(slug: &String, suffix: &String) -> String {
format!("{slug}{AUTHOR_SLUG_SPLIT_SYMBOL}{suffix}")
}
pub use page_processor::*;
25 changes: 25 additions & 0 deletions blog-generic/src/page_processor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
pub trait PageProcessor {
fn create_for_page(page: &u64) -> Self;
fn limit(&self) -> u64;
fn offset(&self) -> u64;
}

pub struct DefaultPageProcessor<const LIMIT: u64 = 10> {
page: u64,
}

impl<const LIMIT: u64> PageProcessor for DefaultPageProcessor<LIMIT> {
fn create_for_page(page: &u64) -> Self {
Self { page: *page }
}
fn limit(&self) -> u64 {
LIMIT
}
fn offset(&self) -> u64 {
let Some(real_page) = self.page.checked_sub(1) else {
return 0;
};
let offset = real_page * LIMIT;
offset
}
}
18 changes: 18 additions & 0 deletions blog-server-api/src/endpoints/author/handler.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
use std::sync::Arc;

use blog_generic::entities::AuthorContainer;
use blog_server_services::traits::author_service::AuthorService;

use super::request_content::AuthorRequestContent;
use super::response_content_failure::AuthorResponseContentFailure;
use super::response_content_failure::AuthorResponseContentFailure::*;
Expand All @@ -23,3 +28,16 @@ pub async fn http_handler(

Ok(author.into())
}

pub async fn direct_handler(
slug: String,
author_service: Arc<Box<dyn AuthorService>>,
) -> Option<AuthorContainer> {
http_handler((AuthorRequestContent {
slug,
author_service,
},))
.await
.ok()
.map(|s| s.container)
}
3 changes: 0 additions & 3 deletions blog-server-api/src/endpoints/author/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,3 @@ mod response_content_failure;
mod response_content_success;

pub use handler::*;
pub use request_content::*;
pub use response_content_failure::*;
pub use response_content_success::*;
4 changes: 2 additions & 2 deletions blog-server-api/src/endpoints/author/request_content.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use screw_api::request::{ApiRequestContent, ApiRequestOriginContent};
use std::sync::Arc;

pub struct AuthorRequestContent {
pub slug: String,
pub author_service: Arc<Box<dyn AuthorService>>,
pub(super) slug: String,
pub(super) author_service: Arc<Box<dyn AuthorService>>,
}

impl<Extensions> ApiRequestContent<Extensions> for AuthorRequestContent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use screw_api::response::{ApiResponseContentBase, ApiResponseContentSuccess};

#[derive(Debug, Clone)]
pub struct AuthorResponseContentSuccess {
pub container: AuthorContainer,
pub(super) container: AuthorContainer,
}

impl Into<AuthorResponseContentSuccess> for ServiceAuthor {
Expand Down
19 changes: 19 additions & 0 deletions blog-server-api/src/endpoints/authors/handler.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use std::sync::Arc;

use blog_generic::entities::{AuthorsContainer, TotalOffsetLimitContainer};
use blog_server_services::traits::author_service::AuthorService;

use super::request_content::AuthorsRequestContent;
use super::response_content_failure::AuthorsResponseContentFailure;
Expand Down Expand Up @@ -50,3 +53,19 @@ pub async fn http_handler(
}
.into())
}

pub async fn direct_handler(
offset: u64,
limit: u64,
author_service: Arc<Box<dyn AuthorService>>,
) -> Option<AuthorsContainer> {
http_handler((AuthorsRequestContent {
query: None,
offset: Some(offset),
limit: Some(limit),
author_service,
},))
.await
.ok()
.map(|s| s.container)
}
2 changes: 1 addition & 1 deletion blog-server-api/src/endpoints/authors/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ mod request_content;
mod response_content_failure;
mod response_content_success;

pub use handler::http_handler;
pub use handler::*;
103 changes: 46 additions & 57 deletions blog-server-api/src/endpoints/client_handler.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::endpoints::*;
use crate::extensions::Resolve;
use crate::utils::auth;
use blog_server_services::traits::author_service::*;
use blog_server_services::traits::entity_post_service::*;
use blog_server_services::traits::post_service::*;
Expand All @@ -8,6 +8,7 @@ use screw_core::request::*;
use screw_core::response::*;
use screw_core::routing::*;

use blog_generic::*;
use blog_ui::*;

const INDEX_HTML: &str = include_str!("../../../index.html");
Expand Down Expand Up @@ -83,7 +84,7 @@ pub async fn client_handler<
};

let status = status(&request).await;
let app_content = app_content(&request).await;
let app_content = app_content::<_, DefaultPageProcessor>(&request).await;

let rendered = server_renderer(
request.path.as_str().to_string(),
Expand Down Expand Up @@ -164,72 +165,60 @@ async fn status<Extensions>(
}
}

async fn app_content<Extensions>(
// TODO: to think, if it's not a cringe
async fn app_content<Extensions, PP>(
request: &router::RoutedRequest<Request<Extensions>>,
) -> Option<AppContent>
where
Extensions: Resolve<std::sync::Arc<Box<dyn AuthorService>>>
+ Resolve<std::sync::Arc<Box<dyn PostService>>>
+ Resolve<std::sync::Arc<Box<dyn EntityPostService>>>,
PP: PageProcessor,
{
let page_processor = PP::create_for_page(
&request
.query
.get("page")
.map(|v| v.parse().ok())
.flatten()
.unwrap_or(1),
);
match Route::recognize_path(request.path.as_str())? {
Route::Post { slug: _, id } | Route::EditPost { id } => {
use crate::endpoints::post;
let post_service: std::sync::Arc<Box<dyn PostService>> =
request.origin.extensions.resolve();
let entity_post_service: std::sync::Arc<Box<dyn EntityPostService>> =
request.origin.extensions.resolve();

let Ok(post::PostResponseContentSuccess { container }) =
post::http_handler((post::PostRequestContent {
id: id.to_string(),
post_service,
entity_post_service,
auth_author_future: Box::pin(std::future::ready(Err(
auth::Error::TokenMissing,
))),
},))
.await
else {
return None;
};

app_content_encode(&container.post)
}
Route::Author { slug } => {
use crate::endpoints::author;
let author_service: std::sync::Arc<Box<dyn AuthorService>> =
request.origin.extensions.resolve();

let Ok(author::AuthorResponseContentSuccess { container }) =
author::http_handler((author::AuthorRequestContent {
slug,
author_service,
},))
.await
else {
return None;
};

app_content_encode(&container.author)
}
Route::Post { slug: _, id } | Route::EditPost { id } => post::direct_handler(
id.to_string(),
request.origin.extensions.resolve(),
request.origin.extensions.resolve(),
)
.await
.map(|v| app_content_encode(&v.post))
.flatten(),
Route::Author { slug } => author::direct_handler(slug, request.origin.extensions.resolve())
.await
.map(|v| app_content_encode(&v.author))
.flatten(),
Route::Tag { slug: _, id } => {
use crate::endpoints::tag;
let post_service: std::sync::Arc<Box<dyn PostService>> =
request.origin.extensions.resolve();

let Ok(tag::TagResponseContentSuccess { container }) =
tag::http_handler((tag::TagRequestContent {
id: id.to_string(),
post_service,
},))
tag::direct_handler(id.to_string(), request.origin.extensions.resolve())
.await
else {
return None;
};

app_content_encode(&container.tag)
.map(|v| app_content_encode(&v.tag))
.flatten()
}
Route::Posts => posts::direct_handler(
page_processor.offset(),
page_processor.limit(),
request.origin.extensions.resolve(),
request.origin.extensions.resolve(),
)
.await
.map(|v| app_content_encode(&v))
.flatten(),
Route::Authors => authors::direct_handler(
page_processor.offset(),
page_processor.limit(),
request.origin.extensions.resolve(),
)
.await
.map(|v| app_content_encode(&v))
.flatten(),
_ => None,
}
}
24 changes: 24 additions & 0 deletions blog-server-api/src/endpoints/post/handler.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
use std::sync::Arc;

use blog_generic::entities::PostContainer;
use blog_server_services::traits::entity_post_service::EntityPostService;
use blog_server_services::traits::post_service::PostService;

use crate::utils::auth;

use super::request_content::PostRequestContent;
use super::response_content_failure::PostResponseContentFailure;
use super::response_content_failure::PostResponseContentFailure::*;
Expand Down Expand Up @@ -44,3 +52,19 @@ pub async fn http_handler(

Ok(post_entity.into())
}

pub async fn direct_handler(
id: String,
post_service: Arc<Box<dyn PostService>>,
entity_post_service: Arc<Box<dyn EntityPostService>>,
) -> Option<PostContainer> {
http_handler((PostRequestContent {
id,
post_service,
entity_post_service,
auth_author_future: Box::pin(std::future::ready(Err(auth::Error::TokenMissing))),
},))
.await
.ok()
.map(|s| s.container)
}
3 changes: 0 additions & 3 deletions blog-server-api/src/endpoints/post/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,3 @@ mod response_content_failure;
mod response_content_success;

pub use handler::*;
pub use request_content::*;
pub use response_content_failure::*;
pub use response_content_success::*;
8 changes: 4 additions & 4 deletions blog-server-api/src/endpoints/post/request_content.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ use screw_components::dyn_fn::DFuture;
use std::sync::Arc;

pub struct PostRequestContent {
pub id: String,
pub post_service: Arc<Box<dyn PostService>>,
pub entity_post_service: Arc<Box<dyn EntityPostService>>,
pub auth_author_future: DFuture<Result<Author, auth::Error>>,
pub(super) id: String,
pub(super) post_service: Arc<Box<dyn PostService>>,
pub(super) entity_post_service: Arc<Box<dyn EntityPostService>>,
pub(super) auth_author_future: DFuture<Result<Author, auth::Error>>,
}

impl<Extensions> ApiRequestContent<Extensions> for PostRequestContent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use screw_api::response::{ApiResponseContentBase, ApiResponseContentSuccess};

#[derive(Debug, Clone)]
pub struct PostResponseContentSuccess {
pub container: PostContainer,
pub(super) container: PostContainer,
}

impl Into<PostResponseContentSuccess> for Post {
Expand Down
22 changes: 22 additions & 0 deletions blog-server-api/src/endpoints/posts/handler.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
use std::sync::Arc;

use crate::utils::auth;
use blog_generic::entities::{PostsContainer, TotalOffsetLimitContainer};
use blog_server_services::traits::author_service::Author;
use blog_server_services::traits::entity_post_service::EntityPostService;
use blog_server_services::traits::post_service::PostService;
use screw_components::dyn_fn::DFuture;

use super::request_content::{PostsRequestContentFilter as Filter, *};
Expand Down Expand Up @@ -124,3 +128,21 @@ async fn handler(
}
.into())
}

pub async fn direct_handler(
offset: u64,
limit: u64,
post_service: Arc<Box<dyn PostService>>,
entity_post_service: Arc<Box<dyn EntityPostService>>,
) -> Option<PostsContainer> {
http_handler((PostsRequestContent {
filter: None,
offset: Some(offset),
limit: Some(limit),
post_service,
entity_post_service,
},))
.await
.ok()
.map(|s| s.container)
}
2 changes: 1 addition & 1 deletion blog-server-api/src/endpoints/posts/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ mod request_content;
mod response_content_failure;
mod response_content_success;

pub use handler::{http_handler, http_handler_unpublished};
pub use handler::*;
Loading

0 comments on commit abdea46

Please sign in to comment.