Skip to content

Commit

Permalink
twiq: Add impl HasUserStore for App
Browse files Browse the repository at this point in the history
  • Loading branch information
bouzuya committed Oct 15, 2022
1 parent c44f7cd commit 9aa83fb
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 12 deletions.
1 change: 1 addition & 0 deletions twiq/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions twiq/crates/web/Cargo.toml
Expand Up @@ -10,6 +10,7 @@ anyhow = { version = "1.0.60", features = ["backtrace"] }
axum = "0.5.15"
db = { path = "../db" }
domain = { path = "../domain" }
query_handler = { path = "../query_handler" }
tokio = { version = "1.20.1", features = ["full"] }
tower = "0.4.13"
tower-http = { version = "0.3.4", features = ["trace"] }
Expand Down
12 changes: 12 additions & 0 deletions twiq/crates/web/src/main.rs
Expand Up @@ -3,6 +3,7 @@ mod router;
use std::{env, sync::Arc};

use axum::{Extension, Server};
use query_handler::{in_memory_user_store::InMemoryUserStore, user_store::HasUserStore};
use tower::ServiceBuilder;
use tower_http::trace::{DefaultMakeSpan, DefaultOnRequest, DefaultOnResponse, TraceLayer};
use tracing::{info, Level};
Expand All @@ -22,6 +23,7 @@ struct App {
event_store: InMemoryEventStore,
user_repository: InMemoryUserRepository,
user_request_repository: InMemoryUserRequestRepository,
user_store: InMemoryUserStore,
worker_repository: InMemoryWorkerRepository,
}

Expand All @@ -30,10 +32,12 @@ impl Default for App {
let event_store = InMemoryEventStore::default();
let user_repository = InMemoryUserRepository::new(event_store.clone());
let user_request_repository = InMemoryUserRequestRepository::new(event_store.clone());
let user_store = InMemoryUserStore::default();
Self {
event_store,
user_repository,
user_request_repository,
user_store,
worker_repository: Default::default(),
}
}
Expand Down Expand Up @@ -76,6 +80,14 @@ impl create_user_request::Has for App {}
impl send_user_request::Has for App {}
impl update_user::Has for App {}

impl HasUserStore for App {
type UserStore = InMemoryUserStore;

fn user_store(&self) -> &Self::UserStore {
&self.user_store
}
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt::init();
Expand Down
2 changes: 2 additions & 0 deletions twiq/crates/web/src/router.rs
Expand Up @@ -3,6 +3,7 @@ mod users_show;
mod worker;

use axum::Router;
use query_handler::user_store::HasUserStore;
use use_case::command::{create_user_request, request_user, send_user_request, update_user};

pub(crate) fn router<T>() -> Router
Expand All @@ -11,6 +12,7 @@ where
+ request_user::Has
+ send_user_request::Has
+ update_user::Has
+ HasUserStore
+ Send
+ Sync
+ 'static,
Expand Down
74 changes: 62 additions & 12 deletions twiq/crates/web/src/router/users_show.rs
@@ -1,11 +1,13 @@
use std::sync::Arc;
use std::{str::FromStr, sync::Arc};

use axum::{extract::Path, http::StatusCode, response::IntoResponse, routing, Extension, Router};
use domain::aggregate::user::TwitterUserId;
use query_handler::user_store::{HasUserStore, UserStore};
use use_case::command::request_user;

pub(crate) fn router<T>() -> Router
where
T: request_user::Has + Send + Sync + 'static,
T: request_user::Has + HasUserStore + Send + Sync + 'static,
{
Router::new().route("/users/:id", routing::get(users_show::<T>))
}
Expand All @@ -15,23 +17,35 @@ async fn users_show<T>(
Path(id): Path<String>,
) -> impl IntoResponse
where
T: request_user::Has + Send + Sync,
T: request_user::Has + HasUserStore + Send + Sync,
{
// TODO: if the user is cached, return it; otherwise, enqueue the ID.
// TODO: error handling
// ignore errors
let _ = application
.request_user(request_user::Command {
twitter_user_id: id.parse().unwrap(),
})
.await;
(StatusCode::ACCEPTED, id)
let twitter_user_id = TwitterUserId::from_str(id.as_str()).unwrap();
let user = application
.user_store()
.find_by_twitter_user_id(&id)
.await
.unwrap();
match user {
None => {
// TODO: error handling
// ignore errors
let _ = application
.request_user(request_user::Command { twitter_user_id })
.await;
(StatusCode::ACCEPTED, id)
}
Some(cached) => {
// TODO: check cache date
(StatusCode::OK, cached.twitter_user_name)
}
}
}

#[cfg(test)]
mod tests {
use axum::async_trait;
use hyper::StatusCode;
use query_handler::in_memory_user_store::InMemoryUserStore;
use use_case::{
in_memory_user_repository::InMemoryUserRepository, user_repository::HasUserRepository,
};
Expand All @@ -42,6 +56,7 @@ mod tests {

struct MockApp {
user_repository: InMemoryUserRepository,
user_store: InMemoryUserStore,
}

impl HasUserRepository for MockApp {
Expand All @@ -55,11 +70,20 @@ mod tests {
#[async_trait]
impl request_user::Has for MockApp {}

impl HasUserStore for MockApp {
type UserStore = InMemoryUserStore;

fn user_store(&self) -> &Self::UserStore {
&self.user_store
}
}

#[tokio::test]
async fn test() -> anyhow::Result<()> {
let router = router::<MockApp>();
let application = MockApp {
user_repository: InMemoryUserRepository::default(),
user_store: InMemoryUserStore::default(),
};
let application = Arc::new(application);
let router = router.layer(Extension(application));
Expand All @@ -68,4 +92,30 @@ mod tests {
assert_eq!(body, r#"125962981"#);
Ok(())
}

#[tokio::test]
async fn test_cached_user_exists() -> anyhow::Result<()> {
let router = router::<MockApp>();
let application = MockApp {
user_repository: InMemoryUserRepository::default(),
user_store: InMemoryUserStore::default(),
};
application
.user_store
.store(
None,
query_handler::user::User {
user_id: "40141a64-8236-48d0-958a-3cf812396ffe".to_owned(),
twitter_user_id: "125962981".to_owned(),
twitter_user_name: "bouzuya".to_owned(),
},
)
.await?;
let application = Arc::new(application);
let router = router.layer(Extension(application));
let (status, body) = test_get_request(router, "/users/125962981").await?;
assert_eq!(status, StatusCode::OK);
assert_eq!(body, r#"bouzuya"#);
Ok(())
}
}

0 comments on commit 9aa83fb

Please sign in to comment.