Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SSR for posts and authors #72

Merged
merged 6 commits into from
Dec 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kind of)00

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