feat: microservice ready

This commit is contained in:
Maulana Sodiqin
2025-04-06 22:13:46 +07:00
parent 07a93ff0b7
commit 31f9b6cfea
80 changed files with 477 additions and 240 deletions
Generated
+114
View File
@@ -1849,6 +1849,31 @@ checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed"
[[package]] [[package]]
name = "imphnen-backend-service" name = "imphnen-backend-service"
version = "0.1.0" version = "0.1.0"
dependencies = [
"anyhow",
"axum",
"axum-test",
"chrono",
"imphnen-entities",
"imphnen-gateway-service",
"imphnen-libs",
"imphnen-utils",
"lazy_static",
"rand 0.9.0",
"regex",
"serde",
"serde_json",
"surrealdb",
"tokio",
"tower-http",
"utoipa",
"utoipa-swagger-ui",
"validator",
]
[[package]]
name = "imphnen-dimentorin-service"
version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"axum", "axum",
@@ -1881,6 +1906,79 @@ dependencies = [
"utoipa", "utoipa",
] ]
[[package]]
name = "imphnen-gacha-service"
version = "0.1.0"
dependencies = [
"anyhow",
"axum",
"axum-test",
"chrono",
"imphnen-entities",
"imphnen-libs",
"imphnen-utils",
"lazy_static",
"rand 0.9.0",
"regex",
"serde",
"serde_json",
"surrealdb",
"tokio",
"tower-http",
"utoipa",
"utoipa-swagger-ui",
"validator",
]
[[package]]
name = "imphnen-gateway-service"
version = "0.1.0"
dependencies = [
"anyhow",
"axum",
"axum-test",
"chrono",
"imphnen-entities",
"imphnen-iam-service",
"imphnen-libs",
"imphnen-utils",
"lazy_static",
"rand 0.9.0",
"regex",
"serde",
"serde_json",
"surrealdb",
"tokio",
"tower-http",
"utoipa",
"utoipa-swagger-ui",
"validator",
]
[[package]]
name = "imphnen-iam-service"
version = "0.1.0"
dependencies = [
"anyhow",
"axum",
"axum-test",
"chrono",
"imphnen-entities",
"imphnen-libs",
"imphnen-utils",
"lazy_static",
"rand 0.9.0",
"regex",
"serde",
"serde_json",
"surrealdb",
"tokio",
"tower-http",
"utoipa",
"utoipa-swagger-ui",
"validator",
]
[[package]] [[package]]
name = "imphnen-libs" name = "imphnen-libs"
version = "0.1.0" version = "0.1.0"
@@ -4159,6 +4257,22 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "tests"
version = "0.1.0"
dependencies = [
"axum",
"axum-test",
"chrono",
"imphnen-entities",
"imphnen-iam-service",
"imphnen-libs",
"imphnen-utils",
"serde_json",
"surrealdb",
"tokio",
]
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.69" version = "1.0.69"
+2 -4
View File
@@ -1,10 +1,8 @@
[workspace] [workspace]
resolver = "2" resolver = "2"
members = [ members = [
"imphnen-apis", "imphnen-*",
"imphnen-utils", "tests",
"imphnen-libs",
"imphnen-entities",
] ]
[workspace.dependencies] [workspace.dependencies]
-55
View File
@@ -1,55 +0,0 @@
use crate::{AppState, Env, SurrealMemClient, SurrealWsClient};
use axum::{
http::{header, HeaderValue, Method},
response::Redirect,
routing::get,
Extension, Router,
};
use tower_http::cors::CorsLayer;
use utoipa_swagger_ui::SwaggerUi;
pub mod v1;
pub mod v2;
pub use v1::*;
pub async fn apps(
surrealdb_ws: SurrealWsClient,
surrealdb_mem: SurrealMemClient,
) -> Router {
let state = AppState {
surrealdb_ws,
surrealdb_mem,
};
let env = Env::new();
let cors_origins = match env.rust_env.as_str() {
"development" => vec!["http://localhost:3000"],
"production" => {
vec!["https://gacha.imphnen.dev", "https://imphnen.dev"]
}
_ => vec![
"http://localhost:3000",
"https://gacha.imphnen.dev",
"https://imphnen.dev",
],
};
let allowed_origins: Vec<HeaderValue> = cors_origins
.into_iter()
.filter_map(|origin| origin.parse::<HeaderValue>().ok())
.collect();
let cors_middleware = CorsLayer::new()
.allow_origin(allowed_origins)
.allow_methods([Method::GET, Method::POST, Method::PUT, Method::DELETE])
.allow_headers([header::AUTHORIZATION, header::CONTENT_TYPE])
.allow_credentials(true);
Router::new()
.route("/", get(Redirect::to("/docs")))
.nest("/v1", v1::routes().await)
.nest("/v2", v2::routes().await)
.merge(SwaggerUi::new("/docs").url("/openapi.json", v1::docs_router()))
.layer(cors_middleware)
.layer(Extension(state))
}
@@ -1,50 +0,0 @@
use crate::{
common_response, extract_email, AppState, UsersItemDtoRaw, UsersRepository,
};
use axum::{
extract::Request, http::StatusCode, middleware::Next, response::Response,
Extension,
};
use std::convert::Infallible;
pub async fn auth_middleware(
Extension(state): Extension<AppState>,
mut req: Request,
next: Next,
) -> Result<Response, Infallible> {
let headers = req.headers();
let email = match extract_email(headers) {
Some(email) => email,
None => {
return Ok(common_response(
StatusCode::UNAUTHORIZED,
"Invalid or expired token",
));
}
};
let repository = UsersRepository::new(&state);
let user: Option<UsersItemDtoRaw> =
match repository.query_user_by_email(email).await {
Ok(user) => Some(user),
Err(err) => {
return Ok(common_response(
StatusCode::INTERNAL_SERVER_ERROR,
&err.to_string(),
))
}
};
if user.is_none() {
return Ok(common_response(
StatusCode::UNAUTHORIZED,
"Unauthorized user",
));
}
req.extensions_mut().insert(user.unwrap());
Ok(next.run(req).await)
}
-8
View File
@@ -1,8 +0,0 @@
use utoipa::OpenApi;
pub mod docs_controller;
pub use docs_controller::*;
pub fn docs_router() -> utoipa::openapi::OpenApi {
ApiDoc::openapi()
}
-23
View File
@@ -1,23 +0,0 @@
use axum::{middleware::from_fn, Router};
pub mod auth;
pub mod docs;
pub mod gacha;
pub mod permissions;
pub mod roles;
pub mod users;
pub use auth::*;
pub use docs::*;
pub use permissions::*;
pub use roles::*;
pub use users::*;
pub async fn routes() -> Router {
let public_routes = Router::new().nest("/auth", auth_router());
let protected_routes = Router::new()
.nest("/users", users_router())
.nest("/roles", roles_router())
.nest("/permissions", permissions_router())
.layer(from_fn(auth::auth_middleware::auth_middleware));
Router::new().merge(public_routes).merge(protected_routes)
}
-9
View File
@@ -1,9 +0,0 @@
use axum::{response::IntoResponse, routing::post, Router};
async fn dummy_login() -> impl IntoResponse {
"Logged in successfully"
}
pub fn auth_router() -> Router {
Router::new().route("/login", post(dummy_login))
}
-9
View File
@@ -1,9 +0,0 @@
use axum::Router;
pub mod auth;
pub use auth::*;
pub async fn routes() -> Router {
let public_routes = Router::new().nest("/auth", auth::auth_router());
Router::new().merge(public_routes)
}
-9
View File
@@ -1,9 +0,0 @@
use imphnen_backend_service::{apps, axum_init};
#[tokio::main]
async fn main() {
axum_init(|surrealdb_ws, surrealdb_mem| async {
apps(surrealdb_ws, surrealdb_mem).await
})
.await;
}
-9
View File
@@ -1,9 +0,0 @@
use imphnen_backend_service::{apps, axum_init};
#[tokio::main]
async fn main() {
axum_init(|surrealdb_ws, surrealdb_mem| async {
apps(surrealdb_ws, surrealdb_mem).await
})
.await;
}
+25
View File
@@ -0,0 +1,25 @@
[package]
name = "imphnen-backend-service"
version = "0.1.0"
edition = "2021"
[dependencies]
imphnen-entities = { version = "0.1.0", path = "../imphnen-entities" }
imphnen-libs = { version = "0.1.0", path = "../imphnen-libs" }
imphnen-utils = { version = "0.1.0", path = "../imphnen-utils" }
axum.workspace = true
serde.workspace = true
serde_json.workspace = true
utoipa.workspace = true
lazy_static.workspace = true
regex.workspace = true
validator.workspace = true
axum-test.workspace = true
surrealdb.workspace = true
rand.workspace = true
tokio.workspace = true
chrono.workspace = true
anyhow.workspace = true
tower-http.workspace = true
utoipa-swagger-ui.workspace = true
imphnen-gateway-service = { version = "0.1.0", path = "../imphnen-gateway-service" }
+10
View File
@@ -0,0 +1,10 @@
use imphnen_gateway_service::gateway_service;
use imphnen_libs::axum_init;
#[tokio::main]
async fn main() {
axum_init(|surrealdb_ws, surrealdb_mem| async {
gateway_service(surrealdb_ws, surrealdb_mem).await
})
.await;
}
+10
View File
@@ -0,0 +1,10 @@
use imphnen_gateway_service::gateway_service;
use imphnen_libs::axum_init;
#[tokio::main]
async fn main() {
axum_init(|surrealdb_ws, surrealdb_mem| async {
gateway_service(surrealdb_ws, surrealdb_mem).await
})
.await;
}
+24
View File
@@ -0,0 +1,24 @@
[package]
name = "imphnen-dimentorin-service"
version = "0.1.0"
edition = "2024"
[dependencies]
imphnen-entities = { version = "0.1.0", path = "../imphnen-entities" }
imphnen-libs = { version = "0.1.0", path = "../imphnen-libs" }
imphnen-utils = { version = "0.1.0", path = "../imphnen-utils" }
axum.workspace = true
serde.workspace = true
serde_json.workspace = true
utoipa.workspace = true
lazy_static.workspace = true
regex.workspace = true
validator.workspace = true
axum-test.workspace = true
surrealdb.workspace = true
rand.workspace = true
tokio.workspace = true
chrono.workspace = true
anyhow.workspace = true
tower-http.workspace = true
utoipa-swagger-ui.workspace = true
+14
View File
@@ -0,0 +1,14 @@
pub fn add(left: u64, right: u64) -> u64 {
left + right
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}
@@ -1,7 +1,7 @@
[package] [package]
name = "imphnen-backend-service" name = "imphnen-gacha-service"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2024"
[dependencies] [dependencies]
imphnen-entities = { version = "0.1.0", path = "../imphnen-entities" } imphnen-entities = { version = "0.1.0", path = "../imphnen-entities" }
+9
View File
@@ -0,0 +1,9 @@
use imphnen_entities::*;
use imphnen_libs::*;
use imphnen_utils::*;
pub mod v1;
pub use imphnen_entities::*;
pub use imphnen_libs::*;
pub use imphnen_utils::*;
pub use v1::*;
@@ -1,9 +1,9 @@
use super::{GachaClaimSchema, GachaItemSchema, GachaRollSchema}; use super::{GachaClaimSchema, GachaItemSchema, GachaRollSchema};
use crate::{ use crate::{
get_id, make_thing, query_list_with_meta, AppState, MetaRequestDto, ResourceEnum, AppState, MetaRequestDto, ResourceEnum, ResponseListSuccessDto, get_id,
ResponseListSuccessDto, make_thing, query_list_with_meta,
}; };
use anyhow::{bail, Result}; use anyhow::{Result, bail};
pub struct GachaRepository<'a> { pub struct GachaRepository<'a> {
state: &'a AppState, state: &'a AppState,
+2
View File
@@ -0,0 +1,2 @@
pub mod gacha;
pub use gacha::*;
+25
View File
@@ -0,0 +1,25 @@
[package]
name = "imphnen-gateway-service"
version = "0.1.0"
edition = "2024"
[dependencies]
imphnen-iam-service = { version = "0.1.0", path = "../imphnen-iam-service" }
imphnen-entities = { version = "0.1.0", path = "../imphnen-entities" }
imphnen-libs = { version = "0.1.0", path = "../imphnen-libs" }
imphnen-utils = { version = "0.1.0", path = "../imphnen-utils" }
axum.workspace = true
serde.workspace = true
serde_json.workspace = true
utoipa.workspace = true
lazy_static.workspace = true
regex.workspace = true
validator.workspace = true
axum-test.workspace = true
surrealdb.workspace = true
rand.workspace = true
tokio.workspace = true
chrono.workspace = true
anyhow.workspace = true
tower-http.workspace = true
utoipa-swagger-ui.workspace = true
@@ -1,9 +1,7 @@
use crate::{ use imphnen_iam_service::{
v1::{ auth, permissions, roles, users, AuthLoginRequestDto, AuthLoginResponsetDto, AuthResendOtpRequestDto, AuthVerifyEmailRequestDto,
auth, permissions, roles, users, AuthLoginRequestDto, AuthLoginResponsetDto, AuthResendOtpRequestDto, AuthVerifyEmailRequestDto AuthNewPasswordRequestDto, AuthRefreshTokenRequestDto, MessageResponseDto, MetaRequestDto, MetaResponseDto, PermissionsItemDto, PermissionsRequestDto, ResponseListSuccessDto, ResponseSuccessDto, RolesItemDto, RolesRequestCreateDto, RolesRequestUpdateDto, TokenDto, UsersCreateRequestDto, UsersDetailItemDto, UsersItemDto, UsersListItemDto, UsersUpdateRequestDto
}, AuthNewPasswordRequestDto, AuthRefreshTokenRequestDto, MessageResponseDto, MetaRequestDto, MetaResponseDto, PermissionsItemDto, PermissionsRequestDto, ResponseListSuccessDto, ResponseSuccessDto, RolesItemDto, RolesRequestCreateDto, RolesRequestUpdateDto, TokenDto, UsersCreateRequestDto, UsersDetailItemDto, UsersItemDto, UsersListItemDto, UsersUpdateRequestDto
}; };
use utoipa::{ use utoipa::{
openapi::security::{Http, HttpAuthScheme, SecurityScheme}, openapi::security::{Http, HttpAuthScheme, SecurityScheme},
Modify, OpenApi, Modify, OpenApi,
@@ -101,3 +99,7 @@ impl Modify for SecurityAddon {
} }
} }
} }
pub fn docs_router() -> utoipa::openapi::OpenApi {
ApiDoc::openapi()
}
+32
View File
@@ -0,0 +1,32 @@
use axum::{
Extension, Router, middleware::from_fn, response::Redirect, routing::get,
};
use imphnen_entities::{AppState, SurrealMemClient, SurrealWsClient};
use imphnen_iam_service::{iam_protected_routes, iam_public_routes};
pub mod docs;
pub mod middleware;
pub use docs::*;
pub use middleware::*;
use utoipa_swagger_ui::SwaggerUi;
pub async fn gateway_service(
surrealdb_ws: SurrealWsClient,
surrealdb_mem: SurrealMemClient,
) -> Router {
let state = AppState {
surrealdb_ws,
surrealdb_mem,
};
let public_routes = iam_public_routes();
let protected_routes = iam_protected_routes().layer(from_fn(auth_middleware));
Router::new()
.route("/", get(Redirect::to("/docs")))
.nest("/v1", public_routes.merge(protected_routes))
.merge(SwaggerUi::new("/docs").url("/openapi.json", docs_router()))
.layer(cors_middleware())
.layer(Extension(state.clone()))
}
+73
View File
@@ -0,0 +1,73 @@
use axum::{
Extension,
extract::Request,
http::{HeaderValue, Method, StatusCode, header},
middleware::Next,
response::Response,
};
use imphnen_iam_service::{UsersItemDtoRaw, UsersRepository};
use imphnen_libs::{AppState, Env};
use imphnen_utils::{common_response, extract_email};
use std::convert::Infallible;
use tower_http::cors::CorsLayer;
pub async fn auth_middleware(
Extension(state): Extension<AppState>,
mut req: Request,
next: Next,
) -> Result<Response, Infallible> {
let headers = req.headers();
let email = match extract_email(headers) {
Some(email) => email,
None => {
return Ok(common_response(
StatusCode::UNAUTHORIZED,
"Invalid or expired token",
));
}
};
let repository = UsersRepository::new(&state);
let user: Option<UsersItemDtoRaw> =
match repository.query_user_by_email(email).await {
Ok(user) => Some(user),
Err(err) => {
return Ok(common_response(
StatusCode::INTERNAL_SERVER_ERROR,
&err.to_string(),
));
}
};
if user.is_none() {
return Ok(common_response(
StatusCode::UNAUTHORIZED,
"Unauthorized user",
));
}
req.extensions_mut().insert(user.unwrap());
Ok(next.run(req).await)
}
pub fn cors_middleware() -> CorsLayer {
let env = Env::new();
let cors_origins = match env.rust_env.as_str() {
"development" => vec!["http://localhost:3000"],
"production" => {
vec!["https://gacha.imphnen.dev", "https://imphnen.dev"]
}
_ => vec![
"http://localhost:3000",
"https://gacha.imphnen.dev",
"https://imphnen.dev",
],
};
let allowed_origins: Vec<HeaderValue> = cors_origins
.into_iter()
.filter_map(|origin| origin.parse::<HeaderValue>().ok())
.collect();
CorsLayer::new()
.allow_origin(allowed_origins)
.allow_methods([Method::GET, Method::POST, Method::PUT, Method::DELETE])
.allow_headers([header::AUTHORIZATION, header::CONTENT_TYPE])
.allow_credentials(true)
}
+24
View File
@@ -0,0 +1,24 @@
[package]
name = "imphnen-iam-service"
version = "0.1.0"
edition = "2024"
[dependencies]
imphnen-entities = { version = "0.1.0", path = "../imphnen-entities" }
imphnen-libs = { version = "0.1.0", path = "../imphnen-libs" }
imphnen-utils = { version = "0.1.0", path = "../imphnen-utils" }
axum.workspace = true
serde.workspace = true
serde_json.workspace = true
utoipa.workspace = true
lazy_static.workspace = true
regex.workspace = true
validator.workspace = true
axum-test.workspace = true
surrealdb.workspace = true
rand.workspace = true
tokio.workspace = true
chrono.workspace = true
anyhow.workspace = true
tower-http.workspace = true
utoipa-swagger-ui.workspace = true
@@ -3,11 +3,11 @@ use imphnen_entities::*;
use imphnen_libs::*; use imphnen_libs::*;
use imphnen_utils::*; use imphnen_utils::*;
pub mod apps; pub mod v1;
pub use apps::*;
pub use imphnen_entities::*; pub use imphnen_entities::*;
pub use imphnen_libs::*; pub use imphnen_libs::*;
pub use v1::*;
pub fn create_test_user( pub fn create_test_user(
email: &str, email: &str,
@@ -1,17 +1,11 @@
use axum::{routing::post, Router}; use axum::{Router, routing::post};
pub mod auth_controller; pub mod auth_controller;
pub mod auth_dto; pub mod auth_dto;
pub mod auth_middleware;
pub mod auth_repository; pub mod auth_repository;
pub mod auth_schema; pub mod auth_schema;
pub mod auth_service; pub mod auth_service;
#[cfg(test)]
pub mod auth_controller_test;
#[cfg(test)]
pub mod auth_repository_test;
pub use auth_dto::*; pub use auth_dto::*;
pub use auth_repository::*; pub use auth_repository::*;
pub use auth_schema::*; pub use auth_schema::*;
+22
View File
@@ -0,0 +1,22 @@
use axum::Router;
pub mod auth;
pub mod permissions;
pub mod roles;
pub mod users;
pub use auth::*;
pub use permissions::*;
pub use roles::*;
pub use users::*;
pub fn iam_public_routes() -> Router {
Router::new().nest("/auth", auth_router())
}
pub fn iam_protected_routes() -> Router {
Router::new()
.nest("/users", users_router())
.nest("/roles", roles_router())
.nest("/permissions", permissions_router())
}
@@ -1,6 +1,6 @@
use axum::{ use axum::{
routing::{delete, get, post, put},
Router, Router,
routing::{delete, get, post, put},
}; };
pub mod permissions_controller; pub mod permissions_controller;
pub mod permissions_dto; pub mod permissions_dto;
@@ -10,11 +10,6 @@ pub mod permissions_repository;
pub mod permissions_schema; pub mod permissions_schema;
pub mod permissions_service; pub mod permissions_service;
#[cfg(test)]
pub mod permissions_controller_test;
#[cfg(test)]
pub mod permissions_repository_test;
pub use permissions_controller::*; pub use permissions_controller::*;
pub use permissions_dto::*; pub use permissions_dto::*;
pub use permissions_enum::*; pub use permissions_enum::*;
@@ -1,6 +1,6 @@
use axum::{ use axum::{
routing::{delete, get, post, put},
Router, Router,
routing::{delete, get, post, put},
}; };
pub mod roles_controller; pub mod roles_controller;
@@ -10,11 +10,6 @@ pub mod roles_repository;
pub mod roles_schema; pub mod roles_schema;
pub mod roles_service; pub mod roles_service;
#[cfg(test)]
pub mod roles_controller_test;
#[cfg(test)]
pub mod roles_repository_test;
pub use roles_controller::*; pub use roles_controller::*;
pub use roles_dto::*; pub use roles_dto::*;
pub use roles_enum::*; pub use roles_enum::*;
+16
View File
@@ -0,0 +1,16 @@
[package]
name = "tests"
version = "0.1.0"
edition = "2024"
[dependencies]
imphnen-libs = { version = "0.1.0", path = "../imphnen-libs" }
imphnen-utils = { version = "0.1.0", path = "../imphnen-utils" }
imphnen-entities = { version = "0.1.0", path = "../imphnen-entities" }
imphnen-iam-service = { version = "0.1.0", path = "../imphnen-iam-service" }
axum.workspace = true
axum-test.workspace = true
chrono.workspace = true
surrealdb.workspace = true
tokio.workspace = true
serde_json.workspace = true
@@ -1,8 +1,9 @@
use crate::{ use crate::{
apps::v1::auth::auth_router, create_mock_app_state, create_test_user, AuthOtpSchema, AuthRepository, OtpManager, ResourceEnum, RolesRepository,
encode_refresh_token, AuthOtpSchema, OtpManager, ResourceEnum, RolesRepository, UsersRepository, auth::auth_router, create_mock_app_state, create_test_user,
encode_refresh_token,
}; };
use axum::{http::StatusCode, Extension}; use axum::{Extension, http::StatusCode};
use axum_test::TestServer; use axum_test::TestServer;
use serde_json::json; use serde_json::json;
use surrealdb::Uuid; use surrealdb::Uuid;
@@ -31,7 +32,7 @@ async fn test_login_should_fail_with_wrong_password() {
.nest("/v1/auth", auth_router()) .nest("/v1/auth", auth_router())
.layer(Extension(state.clone())); .layer(Extension(state.clone()));
let server = TestServer::new(app).unwrap(); let server = TestServer::new(app).unwrap();
let repo = crate::apps::v1::users::UsersRepository::new(&state); let repo = UsersRepository::new(&state);
let role_repo = RolesRepository::new(&state); let role_repo = RolesRepository::new(&state);
let role_id = role_repo let role_id = role_repo
.query_role_by_name("Student".to_string()) .query_role_by_name("Student".to_string())
@@ -58,7 +59,7 @@ async fn test_login_should_fail_if_user_not_active() {
.nest("/v1/auth", auth_router()) .nest("/v1/auth", auth_router())
.layer(Extension(state.clone())); .layer(Extension(state.clone()));
let server = TestServer::new(app).unwrap(); let server = TestServer::new(app).unwrap();
let repo = crate::apps::v1::users::UsersRepository::new(&state); let repo = UsersRepository::new(&state);
let role_repo = RolesRepository::new(&state); let role_repo = RolesRepository::new(&state);
let role_id = role_repo let role_id = role_repo
.query_role_by_name("Student".to_string()) .query_role_by_name("Student".to_string())
@@ -91,7 +92,7 @@ async fn test_login_should_succeed() {
.nest("/v1/auth", auth_router()) .nest("/v1/auth", auth_router())
.layer(Extension(state.clone())); .layer(Extension(state.clone()));
let server = TestServer::new(app).unwrap(); let server = TestServer::new(app).unwrap();
let repo = crate::apps::v1::users::UsersRepository::new(&state); let repo = UsersRepository::new(&state);
let role_repo = RolesRepository::new(&state); let role_repo = RolesRepository::new(&state);
let role_id = role_repo let role_id = role_repo
.query_role_by_name("Student".to_string()) .query_role_by_name("Student".to_string())
@@ -183,7 +184,7 @@ async fn test_register_should_fail_if_email_already_taken() {
.nest("/v1/auth", auth_router()) .nest("/v1/auth", auth_router())
.layer(Extension(state.clone())); .layer(Extension(state.clone()));
let server = TestServer::new(app).unwrap(); let server = TestServer::new(app).unwrap();
let repo = crate::apps::v1::users::UsersRepository::new(&state); let repo = UsersRepository::new(&state);
let role_repo = RolesRepository::new(&state); let role_repo = RolesRepository::new(&state);
let role_id = role_repo let role_id = role_repo
.query_role_by_name("Student".to_string()) .query_role_by_name("Student".to_string())
@@ -353,7 +354,7 @@ async fn test_forgot_password_should_succeed() {
let mut user = let mut user =
create_test_user("forgot@example.com", "Forgot User", true, &role_id); create_test_user("forgot@example.com", "Forgot User", true, &role_id);
user.password = crate::hash_password("secret").unwrap(); user.password = crate::hash_password("secret").unwrap();
let user_repo = crate::apps::v1::users::UsersRepository::new(&state); let user_repo = UsersRepository::new(&state);
user_repo.query_create_user(user).await.unwrap(); user_repo.query_create_user(user).await.unwrap();
let payload = json!({ "email": "forgot@example.com" }); let payload = json!({ "email": "forgot@example.com" });
let res = server.post("/v1/auth/forgot").json(&payload).await; let res = server.post("/v1/auth/forgot").json(&payload).await;
@@ -463,8 +464,8 @@ async fn test_verify_email_should_succeed() {
.id; .id;
let user = create_test_user("verify@example.com", "Verify User", false, &role_id); let user = create_test_user("verify@example.com", "Verify User", false, &role_id);
let otp = OtpManager::generate_otp(); let otp = OtpManager::generate_otp();
let user_repo = crate::apps::v1::users::UsersRepository::new(&state); let user_repo = UsersRepository::new(&state);
let auth_repo = crate::apps::v1::auth::AuthRepository::new(&state); let auth_repo = AuthRepository::new(&state);
user_repo.query_create_user(user.clone()).await.unwrap(); user_repo.query_create_user(user.clone()).await.unwrap();
auth_repo auth_repo
.query_store_otp(user.email.clone(), otp) .query_store_otp(user.email.clone(), otp)
@@ -497,8 +498,8 @@ async fn test_verify_email_should_fail_with_wrong_otp() {
.id; .id;
let user = create_test_user("wrongotp@example.com", "Wrong OTP", false, &role_id); let user = create_test_user("wrongotp@example.com", "Wrong OTP", false, &role_id);
let otp = OtpManager::generate_otp(); let otp = OtpManager::generate_otp();
let user_repo = crate::apps::v1::users::UsersRepository::new(&state); let user_repo = UsersRepository::new(&state);
let auth_repo = crate::apps::v1::auth::AuthRepository::new(&state); let auth_repo = AuthRepository::new(&state);
user_repo.query_create_user(user.clone()).await.unwrap(); user_repo.query_create_user(user.clone()).await.unwrap();
auth_repo auth_repo
.query_store_otp(user.email.clone(), otp) .query_store_otp(user.email.clone(), otp)
@@ -530,10 +531,10 @@ async fn test_verify_email_should_fail_if_otp_expired() {
.unwrap() .unwrap()
.id; .id;
let user = create_test_user("expired@example.com", "Expired OTP", false, &role_id); let user = create_test_user("expired@example.com", "Expired OTP", false, &role_id);
let user_repo = crate::apps::v1::users::UsersRepository::new(&state); let user_repo = UsersRepository::new(&state);
user_repo.query_create_user(user.clone()).await.unwrap(); user_repo.query_create_user(user.clone()).await.unwrap();
use chrono::{Duration, Utc}; use chrono::{Duration, Utc};
let expired_otp = crate::apps::v1::auth::AuthOtpSchema { let expired_otp = AuthOtpSchema {
otp: 123456, otp: 123456,
expires_at: Utc::now() - Duration::minutes(1), expires_at: Utc::now() - Duration::minutes(1),
}; };
@@ -576,7 +577,7 @@ async fn test_new_password_should_succeed() {
) )
.unwrap(); .unwrap();
let email = "resetme@example.com"; let email = "resetme@example.com";
let repo = crate::apps::v1::users::UsersRepository::new(&state); let repo = UsersRepository::new(&state);
let role_repo = RolesRepository::new(&state); let role_repo = RolesRepository::new(&state);
let role_id = role_repo let role_id = role_repo
.query_role_by_name("Student".to_string()) .query_role_by_name("Student".to_string())
+4
View File
@@ -0,0 +1,4 @@
#[cfg(test)]
pub mod auth_controller_test;
#[cfg(test)]
pub mod auth_repository_test;
+4
View File
@@ -0,0 +1,4 @@
pub mod auth;
pub mod permissions;
pub mod roles;
pub mod users;
+4
View File
@@ -0,0 +1,4 @@
#[cfg(test)]
pub mod permissions_controller_test;
#[cfg(test)]
pub mod permissions_repository_test;
@@ -1,6 +1,6 @@
use crate::{ use crate::{
apps::v1::permissions::{PermissionsRepository, PermissionsSchema},
create_mock_app_state, create_mock_app_state,
permissions::{PermissionsRepository, PermissionsSchema},
}; };
use chrono::Utc; use chrono::Utc;
+4
View File
@@ -0,0 +1,4 @@
#[cfg(test)]
pub mod roles_controller_test;
#[cfg(test)]
pub mod roles_repository_test;
@@ -1,18 +1,16 @@
use crate::{ use crate::{
apps::v1::{ ResourceEnum, create_mock_app_state, get_iso_date, make_thing,
permissions::{ permissions::{
permissions_repository::PermissionsRepository, permissions_repository::PermissionsRepository,
permissions_schema::PermissionsSchema, permissions_schema::PermissionsSchema,
}, },
roles::{ roles::{
roles_dto::{RolesRequestCreateDto, RolesRequestUpdateDto}, roles_dto::{RolesRequestCreateDto, RolesRequestUpdateDto},
roles_repository::RolesRepository, roles_repository::RolesRepository,
},
}, },
create_mock_app_state, get_iso_date, make_thing, ResourceEnum,
}; };
use surrealdb::sql::Thing;
use surrealdb::Uuid; use surrealdb::Uuid;
use surrealdb::sql::Thing;
#[tokio::test] #[tokio::test]
async fn test_query_create_role_should_succeed() { async fn test_query_create_role_should_succeed() {
@@ -133,8 +131,8 @@ async fn test_query_delete_role_should_soft_delete() {
assert!(deleted.is_err()); assert!(deleted.is_err());
} }
#[tokio::test] #[tokio::test]
async fn test_query_update_role_should_fallback_to_existing_permissions_if_none_provided( async fn test_query_update_role_should_fallback_to_existing_permissions_if_none_provided()
) { {
let state = create_mock_app_state().await; let state = create_mock_app_state().await;
let repo = RolesRepository::new(&state); let repo = RolesRepository::new(&state);
let perm_repo = PermissionsRepository::new(&state); let perm_repo = PermissionsRepository::new(&state);
+4
View File
@@ -0,0 +1,4 @@
#[cfg(test)]
pub mod users_controller_test;
#[cfg(test)]
pub mod users_repository_test;
@@ -1,10 +1,10 @@
use crate::{ use crate::{
apps::v1::users_router, create_mock_app_state, RolesEnum, RolesRepository, RolesEnum, RolesRepository, UsersActiveInactiveRequestDto, UsersCreateRequestDto,
UsersActiveInactiveRequestDto, UsersCreateRequestDto, UsersRepository, UsersRepository, UsersUpdateRequestDto, users_router,
UsersUpdateRequestDto,
}; };
use axum::{http::StatusCode, Extension}; use axum::{Extension, http::StatusCode};
use axum_test::TestServer; use axum_test::TestServer;
use imphnen_utils::create_mock_app_state;
use surrealdb::Uuid; use surrealdb::Uuid;
#[tokio::test] #[tokio::test]
+6
View File
@@ -0,0 +1,6 @@
pub use imphnen_entities::*;
pub use imphnen_iam_service::*;
pub use imphnen_utils::*;
#[cfg(test)]
pub mod iam_service;