Skip to content

Commit

Permalink
feat!(api-finance): versioning and auth guard
Browse files Browse the repository at this point in the history
  • Loading branch information
spicyzboss committed May 23, 2024
1 parent da58735 commit e7d20b7
Show file tree
Hide file tree
Showing 11 changed files with 186 additions and 51 deletions.
25 changes: 19 additions & 6 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ codegen-units = 1
finance = { path = "libs/finance" }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
worker = { version = "0.2.0", features = ["d1"] }
worker = { version = "0.3.0", features = ["d1", "http", "axum"] }
axum = { version = "0.7.5", default-features = false }
2 changes: 2 additions & 0 deletions apps/api_finance/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@ crate-type = ["cdylib"]
axum = { workspace = true }
finance = { workspace = true }
worker = { workspace = true }
console_error_panic_hook = "0.1.7"
tower-service = "0.3.2"
65 changes: 50 additions & 15 deletions apps/api_finance/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,55 @@
use axum::{
body::Body,
extract::Request,
http::Response,
middleware::{from_fn, Next},
routing::{get, post},
Extension, Router,
};
use finance::api::*;
use worker::{event, Context, Env, Request, Response, Result, Router};
use tower_service::Service;
use worker::{event, Context, Env, HttpRequest, Result};

macro_rules! handler (
($name:path) => {
|Extension(env): Extension<Env>, req: Request| async {
let resp = $name(req.try_into().expect("convert request"), env).await.expect("handler result");
Into::<Response<Body>>::into(resp)
}
}
);

macro_rules! layer {
($name:path) => {
|Extension(env): Extension<Env>, req: Request, next: Next| async {
$name(req, env, next).await.expect("handler result")
}
};
}

fn api_v1_services() -> Router {
Router::new()
.route("/transactions", get(handler!(list_transactions)))
.route("/transactions/:id", get(handler!(get_transaction)))
.route("/transactions", post(handler!(create_transaction)))
.route("/tags", get(handler!(list_tags)))
.route("/tags/:id", get(handler!(get_tag)))
.route("/tags", post(handler!(create_tag)))
.route("/categories", get(handler!(list_categories)))
.route("/categories/:id", get(handler!(get_category)))
.route("/categories", post(handler!(create_category)))
}

#[event(fetch)]
async fn main(req: Request, env: Env, _ctx: Context) -> Result<Response> {
let router = Router::new();
async fn main(req: HttpRequest, env: Env, _ctx: Context) -> Result<Response<Body>> {
console_error_panic_hook::set_once();

let router = Router::new()
.nest("/v1", api_v1_services())
.layer(from_fn(layer!(authentication_guard)))
.layer(Extension(env))
.call(req)
.await?;

router
.get_async("/transactions", list_transactions)
.get_async("/transactions/:id", retrieve_transaction)
.post_async("/transactions", create_transaction)
.get_async("/tags", list_tags)
.get_async("/tags/:id", retrieve_tag)
.post_async("/tags", create_tag)
.get_async("/categories", list_categories)
.get_async("/categories/:id", retrieve_category)
.post_async("/categories", create_category)
.run(req, env)
.await
Ok(router)
}
3 changes: 3 additions & 0 deletions apps/api_finance/wrangler.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ routes = [
[build]
command = "worker-build --release"

[vars]
AUTH_KEY = "AUTH_KEY"

[placement]
mode = "smart"

Expand Down
49 changes: 49 additions & 0 deletions libs/finance/src/api/controllers/authentication.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use axum::{
body::Body,
extract::Request,
http::{Response, StatusCode},
middleware::Next,
};
use worker::{Env, Result};

#[worker::send]
pub async fn authentication_guard(req: Request, env: Env, next: Next) -> Result<Response<Body>> {
let auth_key_env = env.var("AUTH_KEY");

let auth_key: String;

match auth_key_env {
Ok(key) => auth_key = key.to_string(),
_ => {
return Ok(
Response::builder()
.status(StatusCode::INTERNAL_SERVER_ERROR)
.body(Body::empty())
.unwrap(),
);
}
};

let auth_header_key_param = req.headers().get("x-auth-key");

match auth_header_key_param {
Some(key) => {
if auth_key.ne(key) {
return Ok(
Response::builder()
.status(StatusCode::UNAUTHORIZED)
.body(Body::empty())
.unwrap(),
);
}

return Ok(next.run(req).await);
}
None => Ok(
Response::builder()
.status(StatusCode::FORBIDDEN)
.body(Body::empty())
.unwrap(),
),
}
}
26 changes: 18 additions & 8 deletions libs/finance/src/api/controllers/category.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::{collections::HashMap, sync::Arc};

use worker::{Request, Response, Result, RouteContext};
use worker::{Env, Request, Response, Result};

use crate::{
api::CategoryCreateParamsDTO,
Expand All @@ -9,8 +9,9 @@ use crate::{
usecases::CategoryUsecase,
};

pub async fn list_categories(req: Request, ctx: RouteContext<()>) -> Result<Response> {
let db = Arc::new(ctx.env.d1("DB").expect("no d1 binding"));
#[worker::send]
pub async fn list_categories(req: Request, env: Env) -> Result<Response> {
let db = Arc::new(env.d1("DB").expect("no d1 binding"));
let repo = Arc::new(CategoryD1Repository::new(db.clone()));
let usecase = Arc::new(CategoryUsecase::new(repo.clone()));

Expand Down Expand Up @@ -42,12 +43,20 @@ pub async fn list_categories(req: Request, ctx: RouteContext<()>) -> Result<Resp
}
}

pub async fn retrieve_category(_req: Request, ctx: RouteContext<()>) -> Result<Response> {
let db = Arc::new(ctx.env.d1("DB").expect("no d1 binding"));
#[worker::send]
pub async fn get_category(req: Request, env: Env) -> Result<Response> {
let db = Arc::new(env.d1("DB").expect("no d1 binding"));
let repo = Arc::new(CategoryD1Repository::new(db.clone()));
let usecase = Arc::new(CategoryUsecase::new(repo.clone()));

let category_id = ctx.param("id").unwrap().to_string();
let url = req.url()?;

let category_id = url
.path_segments()
.unwrap()
.nth(1)
.unwrap()
.parse::<String>()?;
let result = usecase.repository.retrieve(category_id).await;

match result {
Expand All @@ -60,8 +69,9 @@ pub async fn retrieve_category(_req: Request, ctx: RouteContext<()>) -> Result<R
}
}

pub async fn create_category(mut req: Request, ctx: RouteContext<()>) -> Result<Response> {
let db = Arc::new(ctx.env.d1("DB").expect("no d1 binding"));
#[worker::send]
pub async fn create_category(mut req: Request, env: Env) -> Result<Response> {
let db = Arc::new(env.d1("DB").expect("no d1 binding"));
let repo = Arc::new(CategoryD1Repository::new(db.clone()));
let usecase = Arc::new(CategoryUsecase::new(repo.clone()));

Expand Down
8 changes: 5 additions & 3 deletions libs/finance/src/api/controllers/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
mod authentication;
mod category;
mod tag;
mod transaction;

pub use category::{create_category, list_categories, retrieve_category};
pub use tag::{create_tag, list_tags, retrieve_tag};
pub use transaction::{create_transaction, list_transactions, retrieve_transaction};
pub use authentication::authentication_guard;
pub use category::{create_category, get_category, list_categories};
pub use tag::{create_tag, get_tag, list_tags};
pub use transaction::{create_transaction, get_transaction, list_transactions};
26 changes: 18 additions & 8 deletions libs/finance/src/api/controllers/tag.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::{collections::HashMap, sync::Arc};

use worker::{Request, Response, Result, RouteContext};
use worker::{Env, Request, Response, Result};

use crate::{
api::TagCreateParamsDTO,
Expand All @@ -9,8 +9,9 @@ use crate::{
usecases::TagUsecase,
};

pub async fn list_tags(req: Request, ctx: RouteContext<()>) -> Result<Response> {
let db = Arc::new(ctx.env.d1("DB").expect("no d1 binding"));
#[worker::send]
pub async fn list_tags(req: Request, env: Env) -> Result<Response> {
let db = Arc::new(env.d1("DB").expect("no d1 binding"));
let repo = Arc::new(TagD1Repository::new(db.clone()));
let usecase = Arc::new(TagUsecase::new(repo.clone()));

Expand Down Expand Up @@ -42,12 +43,20 @@ pub async fn list_tags(req: Request, ctx: RouteContext<()>) -> Result<Response>
}
}

pub async fn retrieve_tag(_req: Request, ctx: RouteContext<()>) -> Result<Response> {
let db = Arc::new(ctx.env.d1("DB").expect("no d1 binding"));
#[worker::send]
pub async fn get_tag(req: Request, env: Env) -> Result<Response> {
let db = Arc::new(env.d1("DB").expect("no d1 binding"));
let repo = Arc::new(TagD1Repository::new(db.clone()));
let usecase = Arc::new(TagUsecase::new(repo.clone()));

let tag_id = ctx.param("id").unwrap().to_string();
let url = req.url()?;

let tag_id = url
.path_segments()
.unwrap()
.nth(1)
.unwrap()
.parse::<String>()?;
let result = usecase.repository.retrieve(tag_id).await;

match result {
Expand All @@ -60,8 +69,9 @@ pub async fn retrieve_tag(_req: Request, ctx: RouteContext<()>) -> Result<Respon
}
}

pub async fn create_tag(mut req: Request, ctx: RouteContext<()>) -> Result<Response> {
let db = Arc::new(ctx.env.d1("DB").expect("no d1 binding"));
#[worker::send]
pub async fn create_tag(mut req: Request, env: Env) -> Result<Response> {
let db = Arc::new(env.d1("DB").expect("no d1 binding"));
let repo = Arc::new(TagD1Repository::new(db.clone()));
let usecase = Arc::new(TagUsecase::new(repo.clone()));

Expand Down
27 changes: 19 additions & 8 deletions libs/finance/src/api/controllers/transaction.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::{collections::HashMap, sync::Arc};

use worker::{Request, Response, Result, RouteContext};
use worker::{Env, Request, Response, Result};

use crate::{
api::TransactionCreateParamsDTO,
Expand All @@ -9,8 +9,9 @@ use crate::{
usecases::TransactionUsecase,
};

pub async fn list_transactions(req: Request, ctx: RouteContext<()>) -> Result<Response> {
let db = Arc::new(ctx.env.d1("DB").expect("no d1 binding"));
#[worker::send]
pub async fn list_transactions(req: Request, env: Env) -> Result<Response> {
let db = Arc::new(env.d1("DB").expect("no d1 binding"));
let repo = Arc::new(TransactionD1Repository::new(db.clone()));
let usecase = Arc::new(TransactionUsecase::new(repo.clone()));

Expand Down Expand Up @@ -51,12 +52,21 @@ pub async fn list_transactions(req: Request, ctx: RouteContext<()>) -> Result<Re
}
}

pub async fn retrieve_transaction(_req: Request, ctx: RouteContext<()>) -> Result<Response> {
let db = Arc::new(ctx.env.d1("DB").expect("no d1 binding"));
#[worker::send]
pub async fn get_transaction(req: Request, env: Env) -> Result<Response> {
let db = Arc::new(env.d1("DB").expect("no d1 binding"));
let repo = Arc::new(TransactionD1Repository::new(db.clone()));
let usecase = Arc::new(TransactionUsecase::new(repo.clone()));

let transaction_id = ctx.param("id").unwrap().to_string();
let url = req.url()?;

let transaction_id = url
.path_segments()
.unwrap()
.nth(1)
.unwrap()
.parse::<String>()?;

let result = usecase.repository.retrieve(transaction_id).await;

match result {
Expand All @@ -69,8 +79,9 @@ pub async fn retrieve_transaction(_req: Request, ctx: RouteContext<()>) -> Resul
}
}

pub async fn create_transaction(mut req: Request, ctx: RouteContext<()>) -> Result<Response> {
let db = Arc::new(ctx.env.d1("DB").expect("no d1 binding"));
#[worker::send]
pub async fn create_transaction(mut req: Request, env: Env) -> Result<Response> {
let db = Arc::new(env.d1("DB").expect("no d1 binding"));
let repo = Arc::new(TransactionD1Repository::new(db.clone()));
let usecase = Arc::new(TransactionUsecase::new(repo.clone()));

Expand Down
Loading

0 comments on commit e7d20b7

Please sign in to comment.