diff --git a/projects/dekkku/src/bin/revert-main.rs b/projects/dekkku/src/bin/revert-main.rs deleted file mode 100644 index 745c5fd..0000000 --- a/projects/dekkku/src/bin/revert-main.rs +++ /dev/null @@ -1,315 +0,0 @@ -use async_graphql::dataloader::*; -use async_graphql::http::GraphiQLSource; -use async_graphql::*; -use async_graphql_poem::*; -use poem::{listener::TcpListener, web::Html, *}; -use rand::seq::SliceRandom; -use reqwest::Client; -use serde::{Deserialize, Serialize}; -use std::{ - collections::HashMap, - sync::{Arc, RwLock}, - time::Duration, -}; - -const BASE_URL: &str = "http://localhost:3000"; -const ALL_USERS: &str = "http://localhost:3000/users"; -const ALL_POSTS: &str = "http://localhost:3000/posts"; - -struct Store { - post: RwLock>, - users: RwLock>, - is_dirty: RwLock, -} - -impl Default for Store { - fn default() -> Self { - Self { - post: RwLock::new(HashMap::new()), - users: RwLock::new(HashMap::new()), - is_dirty: RwLock::new(false), - } - } -} - -struct Query; - -#[Object] -impl Query { - async fn posts( - &self, - ctx: &Context<'_>, - ) -> std::result::Result, async_graphql::Error> { - let client = ctx.data_unchecked::>(); - let response = client.get(ALL_POSTS).send().await?; - let posts: Vec = response.json().await?; - let store = ctx.data_unchecked::>(); - - let are_posts_same = { - let is_cache_empty = store.post.read().unwrap().is_empty(); - if is_cache_empty { - let mut rng = rand::thread_rng(); - let selected_posts: Vec<&Post> = posts.choose_multiple(&mut rng, 2).collect(); - if selected_posts.len() == 2 { - let mut posts_writer = store.post.write().unwrap(); - for post in selected_posts { - posts_writer.insert(post.id.unwrap(), post.clone()); - } - } - false - } else { - let cached_posts: Vec = - store.post.read().unwrap().values().cloned().collect(); - let mut posts_writer = store.post.write().unwrap(); - cached_posts.iter().all(|post| { - if let Some(new_post) = posts.iter().find(|p| p.id == post.id) { - if post != new_post { - posts_writer.insert(new_post.id.unwrap(), new_post.clone()); - false - } else { - true - } - } else { - false - } - }) - } - }; - - if !are_posts_same { - // clean up the users. - store.users.write().unwrap().clear(); - } - - *store.is_dirty.write().unwrap() = !are_posts_same; - Ok(posts) - } - - async fn post( - &self, - ctx: &Context<'_>, - id: i32, - ) -> std::result::Result, async_graphql::Error> { - let loader = ctx.data_unchecked::>(); - let post = loader.load_one(id).await?; - if let Some(actual_post) = post.as_ref() { - let store = ctx.data_unchecked::>(); - let mut are_posts_same = true; - if let Some(cached_post) = store.post.read().unwrap().get(&id) { - if actual_post != cached_post { - *store.is_dirty.write().unwrap() = true; - are_posts_same = false; - } - } - if !are_posts_same { - // clean up the users. - store.users.write().unwrap().clear(); - } - store.post.write().unwrap().insert(id, actual_post.clone()); - } - Ok(post) - } - - async fn users( - &self, - ctx: &Context<'_>, - ) -> std::result::Result, async_graphql::Error> { - let client = ctx.data_unchecked::>(); - let response = client.get(ALL_USERS).send().await?; - let users: Vec = response.json().await?; - - let store = ctx.data_unchecked::>(); - let mut rng = rand::thread_rng(); - let selected_users: Vec<&User> = users.choose_multiple(&mut rng, 2).collect(); - - if selected_users.len() == 2 { - let users_writer = store.users.read().unwrap(); - let are_users_same = selected_users.iter().all(|user| { - if let Some(id) = user.id { - users_writer - .get(&id) - .map_or(false, |cached_user| cached_user == *user) - } else { - false - } - }); - - if !are_users_same { - *store.is_dirty.write().unwrap() = true; - } - } - - Ok(users) - } - - async fn user( - &self, - ctx: &Context<'_>, - id: i32, - ) -> std::result::Result, async_graphql::Error> { - let loader = ctx.data_unchecked::>(); - let user = loader.load_one(id).await?; - if let Some(actual_user) = &user { - let store = ctx.data_unchecked::>(); - if let Some(cached_user) = store.users.read().unwrap().get(&id) { - if cached_user != actual_user { - *store.is_dirty.write().unwrap() = true; - } - } - store.users.write().unwrap().insert(id, actual_user.clone()); - } - Ok(user) - } -} - -#[derive(SimpleObject, Serialize, Deserialize, Debug, Clone, PartialEq, Default)] -#[serde(rename_all = "camelCase")] -#[graphql(complex)] -struct Post { - id: Option, - #[graphql(name = "userId")] - user_id: i32, - title: Option, - body: Option, -} - -#[ComplexObject] -impl Post { - async fn user( - &self, - ctx: &Context<'_>, - ) -> std::result::Result, async_graphql::Error> { - let store = ctx.data_unchecked::>(); - if !*store.is_dirty.read().unwrap() - && store.users.read().unwrap().contains_key(&self.user_id) - { - let user = store.users.read().unwrap().get(&self.user_id).cloned(); - Ok(user) - } else { - let loader = ctx.data_unchecked::>(); - let user = loader.load_one(self.user_id).await?; - - if let Some(actual_user) = user.as_ref() { - if let Some(cached_user) = store.users.read().unwrap().get(&self.user_id) { - if cached_user != actual_user { - *store.is_dirty.write().unwrap() = true; - } - } - store - .users - .write() - .unwrap() - .insert(self.user_id, actual_user.clone()); - } - Ok(user) - } - } -} - -#[derive(SimpleObject, Serialize, Deserialize, Debug, Clone, PartialEq)] -struct User { - id: Option, - name: Option, - username: Option, - email: Option, - address: Option
, - phone: Option, - website: Option, -} - -#[derive(SimpleObject, Serialize, Deserialize, Debug, Clone, PartialEq)] -struct Address { - zipcode: Option, - geo: Option, -} - -#[derive(SimpleObject, Serialize, Deserialize, Debug, Clone, PartialEq)] -struct Geo { - lat: Option, - lng: Option, -} - -struct PostLoader(Arc); -struct UserLoader(Arc); - -impl Loader for PostLoader { - type Value = Post; - type Error = async_graphql::Error; - - async fn load( - &self, - keys: &[i32], - ) -> std::result::Result, Self::Error> { - let mut result = std::collections::HashMap::new(); - for &id in keys { - let url = format!("{}/posts/{}", BASE_URL, id); - let response = self.0.get(url).send().await?; - if response.status().is_success() { - let post: Post = response.json().await?; - result.insert(id, post); - } - } - Ok(result) - } -} - -impl Loader for UserLoader { - type Value = User; - type Error = async_graphql::Error; - - async fn load( - &self, - keys: &[i32], - ) -> std::result::Result, Self::Error> { - let mut result = std::collections::HashMap::new(); - let qp = keys - .iter() - .map(|id| format!("id={}", id)) - .collect::>() - .join("&"); - let url = format!("{}/users?{}", BASE_URL, qp); - let response = self.0.get(url).send().await?; - if response.status().is_success() { - let users: Vec = response.json().await?; - for user in users { - if let Some(id) = user.id { - result.insert(id, user); - } - } - } - Ok(result) - } -} - -fn create_schema() -> Schema { - let client = Arc::new(Client::new()); - let user_loader = - DataLoader::new(UserLoader(client.clone()), tokio::spawn).delay(Duration::from_millis(1)); - let post_loader = - DataLoader::new(PostLoader(client.clone()), tokio::spawn).delay(Duration::from_millis(5)); - Schema::build(Query, EmptyMutation, EmptySubscription) - .data(client) - .data(user_loader) - .data(post_loader) - .data(Arc::new(Store::default())) - .finish() -} - -#[handler] -async fn graphiql() -> impl IntoResponse { - Html(GraphiQLSource::build().finish()) -} - -#[tokio::main] -async fn main() -> anyhow::Result<()> { - let schema = create_schema(); - - // start the http server - let app = Route::new() - .at("/graphql", get(graphiql).post(GraphQL::new(schema.clone()))) - .at("/", get(graphiql).post(GraphQL::new(schema.clone()))); - Server::new(TcpListener::bind("0.0.0.0:8000")) - .run(app) - .await?; - Ok(()) -} \ No newline at end of file