Skip to content
Open
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
18 changes: 16 additions & 2 deletions Cargo.lock

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

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,5 @@ serde_json = "1.0.75"
derive_more = "0.99.17"
thiserror = "1.0.30"
anyhow = "1.0.53"
sqlx = { version = "0.5", features = [ "runtime-tokio-rustls", "json", "chrono", "uuid", "macros", "tls" ] }
sqlx = { version = "0.5", features = [ "runtime-tokio-rustls", "json", "chrono", "uuid", "macros", "tls" ] }
routerify = "3.0.0"
56 changes: 37 additions & 19 deletions src/app/server.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
use http::Request;
use hyper::{Server, Client, Body};
use hyper::service::{make_service_fn, service_fn};
use hyper::{Server, Client};
use hyper_tls::HttpsConnector;
use routerify::RouterService;
use std::{net::SocketAddr};
use dotenv::dotenv;
use redis::{Client as RedisClient};

use crate::helpers::request::HyperClient;
use crate::routes::server::routes;
use crate::routes::server::{router};
use crate::setup::variables::SettingsVars;

type GenericError = hyper::Error;
Expand All @@ -16,16 +15,16 @@ type GenericError = hyper::Error;
pub struct AppState {
pub redis: RedisClient,
pub hyper: HyperClient,
pub req: Request<Body>,
// pub req: Request<Body>,
pub env_vars: SettingsVars,
}

impl AppState {
fn new( req: Request<Body>, hyper: HyperClient, redis: RedisClient) -> Self {
fn new(hyper: HyperClient, redis: RedisClient) -> Self {
// is this expensive? Should this rather be done at the point of intializing the hyper_client e.t.c, and then implement
// clone (Iterator trait) for settingsVars?
let env_vars = SettingsVars::new();
Self { redis, hyper, req, env_vars}
Self { redis, hyper, env_vars}
}
}

Expand All @@ -35,23 +34,42 @@ pub async fn server() {
let addr = SocketAddr::from(([127, 0, 0, 1], 8080));
let https = HttpsConnector::new();
let hyper_pool = Client::builder().build::<_, hyper::Body>(https);
let hyper_client = hyper_pool.clone();
// let hyper_client = hyper_pool.clone();

let redis_client= RedisClient::open("redis://127.0.0.1/").expect("Redis connection failed");
// let hyper_client = redis_client.get_async_connection().await.un
// let hyper_client = redis_client.get_async_connection().await.unwrap();
// let mgr = ConnectionManager::new(redis_client).await.unwrap();

let service = make_service_fn(move|_| {
let redis = redis_client.clone();
let client = hyper_client.clone();
// let service = make_service_fn(move|_| {
// let redis = redis_client.clone();
// let client = hyper_client.clone();

async {
Ok::<_, GenericError>(service_fn(move |req| {
let state = AppState::new(req, client.to_owned(), redis.to_owned());
routes(state)
}))
}
});
// async {
// Ok::<_, GenericError>(service_fn(move |req| {
// let state = AppState::new(req, client.to_owned(), redis.to_owned());
// routes(state)
// }))
// }
// });

// let service = make_service_fn(move|_| {
// let redis = redis_client.clone();
// let client = hyper_client.clone();

// async {
// Ok::<_, GenericError>(service_fn(move |req| {
// routes(req, client.to_owned(), redis.to_owned())
// }))
// }
// });

let env_vars = SettingsVars::new();

let state = AppState::new(hyper_pool.to_owned(), redis_client.to_owned());

let router = router(state);

let service = RouterService::new(router).unwrap();

let server = Server::bind(&addr).serve(service);

Expand Down
2 changes: 2 additions & 0 deletions src/controllers/ Implement Iterator for SettingsVars t.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Implement Iterator for SettingsVars to allow clone
// Continue sqlx migration
4 changes: 3 additions & 1 deletion src/controllers/authorize_bot.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use hyper::{Method, Body, Response};
use hyper::{Method, Body, Response, Request};
use redis::{AsyncCommands};

use crate::app::server::AppState;
Expand All @@ -9,6 +9,8 @@ use crate::helpers::{
scope::Scope,
keyval::KeyVal,
};


use crate::setup::{variables::SettingsVars};
use crate::middlewares::request_builder::RequestBuilder;

Expand Down
11 changes: 8 additions & 3 deletions src/controllers/destroy.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::{collections::HashMap};
use hyper::{Body, Request, Method};
use routerify::prelude::*;
use futures::{stream, StreamExt};
use serde_json::Value;
use tokio;
Expand Down Expand Up @@ -67,6 +68,7 @@ impl PostIds {
if duplicates.is_some() || empty_string.is_some() || not_number.is_some() {
panic!("{} must be an array of ids or an empty array", key)
}
// Consider verifying that the string is a valid number when parsed - all twitter ids are numbers

let ids = s.get(&key.to_string()).unwrap()
.iter().map(|k| (k.clone(), key)).collect::<Vec<(String, TweetType)>>();
Expand All @@ -85,14 +87,17 @@ impl PostIds {


// rename this module to destory which then contains destory RTs and destory Posts
pub async fn handle_delete(app_state: AppState) -> TResult<ApiBody> {
let AppState {redis, req, hyper, ..} = app_state;
pub async fn handle_delete(req: Request<Body>) -> TResult<ApiBody> {

let AppState {redis, hyper, ..} = req.data::<AppState>().unwrap();

let mut con = redis.get_async_connection().await?;
let access_token: String = redis::cmd("GET").arg(&["access_token"]).query_async(&mut con).await?;

let req_body = &req.into_body();

// req body for the ids must be a vector of strings(id of tweets)
let req_body = req.into_body();
// let req_body = req.into_body();
let byte_body = hyper::body::to_bytes(req_body).await?.to_owned();
let body: Ids = serde_json::from_slice(&byte_body)?;

Expand Down
12 changes: 6 additions & 6 deletions src/controllers/handle_redirect.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use http::Method;
use hyper::{StatusCode};
use hyper::{StatusCode, Body, Method, Request};
use redis::{Client as RedisClient};
use routerify::prelude::*;
use crate::{helpers::{
response::{TResult, ApiBody, make_request, ResponseBuilder},
request::{HyperClient}, keyval::KeyVal, commons::GrantType},
Expand Down Expand Up @@ -40,9 +40,9 @@ async fn access_token(hyper_client: HyperClient, redis_client: RedisClient, auth


// req: Request<hyper::Body>, hyper_client: HyperClient, redis_client: RedisClient
pub async fn handle_redirect(app_state: AppState) -> TResult<ApiBody> {
pub async fn handle_redirect(req: Request<Body>) -> TResult<ApiBody> {

let AppState {redis, hyper, req, env_vars} = app_state;
let AppState {redis, hyper, env_vars} = req.data::<AppState>().unwrap();
let SettingsVars{state, api_key, twitter_v1, ..} = env_vars;

let mut con = redis.get_async_connection().await?;
Expand Down Expand Up @@ -88,9 +88,9 @@ pub async fn handle_redirect(app_state: AppState) -> TResult<ApiBody> {
let is_v2_callback = query_params.verify_present(vec!["code".into(), "state".into()]);

if let Some(dict) = is_v2_callback {
if query_params.validate("state".into(), state) {
if query_params.validate("state".into(), state.clone()) {
let code = dict.get("code").unwrap().to_string();
access_token(hyper.clone(), redis, code).await?;
access_token(hyper.clone(), redis.clone(), code).await?;

return ResponseBuilder::new("Access Granted".into(), Some(""), StatusCode::OK.as_u16()).reply();
}
Expand Down
8 changes: 5 additions & 3 deletions src/controllers/health_check.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use hyper::{StatusCode};
use hyper::{Body, StatusCode, Request};
use routerify::prelude::*;

use crate::helpers::response::{TResult, ApiBody, ResponseBuilder};
use crate::{helpers::response::{TResult, ApiBody, ResponseBuilder}, app::server::AppState};


pub fn health_check() -> TResult<ApiBody> {
pub async fn health_check(req: Request<Body>) -> TResult<ApiBody> {
let state = req.data::<AppState>().unwrap();
ResponseBuilder::new("Ok".into(), Some(""), StatusCode::OK.as_u16()).reply()
}
6 changes: 3 additions & 3 deletions src/controllers/not_found.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use hyper::{StatusCode};
use hyper::{StatusCode, Body, Request};

use crate::{helpers::response::{TResult, ApiBody, ResponseBuilder}, app::server::AppState};
use crate::{helpers::response::{TResult, ApiBody, ResponseBuilder}};


pub async fn not_found (_state: AppState) -> TResult<ApiBody> {
pub async fn not_found (_req: Request<Body>) -> TResult<ApiBody> {
ResponseBuilder::new("Resorce not found".into(), Some(""), StatusCode::NOT_FOUND.as_u16()).reply()
}
10 changes: 7 additions & 3 deletions src/controllers/user_lookup.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use hyper::{Method, StatusCode};
use http::Request;
use hyper::{Method, StatusCode, Body};
use routerify::prelude::*;

use crate::{helpers::{
response::{ResponseBuilder, TResult, ApiBody, make_request, TwitterResponseHashData}},
Expand All @@ -8,9 +10,11 @@ use crate::{helpers::{


// use this endpoint to verify the validity of the username when they want to request for their timeline when using OAuth2.0
pub async fn user_lookup(app_state: AppState) -> TResult<ApiBody> {
pub async fn user_lookup(req: Request<Body>) -> TResult<ApiBody> {

let app_state = req.data::<AppState>().unwrap();
// todo!() move this to params once route management is migrated to routerify
let AppState{redis, req, hyper, env_vars, ..} = app_state;
let AppState{redis, hyper, env_vars, ..} = app_state;
let SettingsVars {twitter_v2, ..} = env_vars;

let username = req.uri().query().unwrap().split("=").collect::<Vec<_>>()[1];
Expand Down
66 changes: 45 additions & 21 deletions src/routes/server.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
use std::convert::Infallible;
use hyper::{Request, Body, Method};

use crate::app::server::AppState;
use redis::{Client as RedisClient};
use routerify::{Router, Middleware};


use crate::errors::response::TError;
use crate::helpers::request::HyperClient;
use crate::helpers::response::{ApiBody};
use crate::{helpers::response::TResult};
use crate::controllers::{not_found, authorize_bot,
Expand All @@ -10,25 +17,42 @@ use crate::controllers::{not_found, authorize_bot,



pub async fn routes(
state: AppState
) -> TResult<ApiBody> {
// migrate this to [routerify](https://docs.rs/routerify/latest/routerify/) eventually
let req = &state.req;

match (req.method(), req.uri().path(), req.uri().query()) {
(&Method::GET, "/", _) => health_check(),
(&Method::GET, "/enable", _) => authorize_bot(state).await,
(&Method::GET, "/oauth/callback", x) => handle_redirect(state).await,
(&Method::POST, "/revoke", _) => revoke_token(state).await,
(&Method::GET, "/refresh", _) => refresh_token(state).await,
(&Method::GET, "/user", x) => user_lookup(state).await,
(&Method::GET, "/timeline", x) => get_timeline(state).await,
(&Method::POST, "/remove", _) => handle_delete(state).await,
(&Method::GET, "/oauth1/request", _) => request_token(state).await,
(&Method::GET, "/oauth1/", _) => request_token(state).await,
_ => {
not_found(state).await
}
}
// pub async fn routes(
// state: AppState
// ) -> TResult<ApiBody> {
// // migrate this to [routerify](https://docs.rs/routerify/latest/routerify/) eventually
// let req = &state.req;

// match (req.method(), req.uri().path(), req.uri().query()) {
// (&Method::GET, "/", _) => health_check(),
// (&Method::GET, "/enable", _) => authorize_bot(state).await,
// (&Method::GET, "/oauth/callback", x) => handle_redirect(state).await,
// (&Method::POST, "/revoke", _) => revoke_token(state).await,
// (&Method::GET, "/refresh", _) => refresh_token(state).await,
// (&Method::GET, "/user", x) => user_lookup(state).await,
// (&Method::GET, "/timeline", x) => get_timeline(state).await,
// (&Method::POST, "/remove", _) => handle_delete(state).await,
// (&Method::GET, "/oauth1/request", _) => request_token(state).await,
// (&Method::GET, "/oauth1/", _) => request_token(state).await,
// _ => {
// not_found(state).await
// }
// }
// }



pub fn router(state: AppState) -> Router<Body, TError> {
Router::builder()
// Specify the state data which will be available to every route handlers,
// error handler and middlewares.
.data(state)
// .middleware(Middleware::pre(logger))
.get("/", health_check)
.get("/oauth/callback", handle_redirect)
// .get("/enable", authorize_bot)
.any(not_found)
// .err_handler_with_info(error_handler)
.build()
.unwrap()
}