diff --git a/Cargo.lock b/Cargo.lock index 48f9341..65c2a78 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -115,6 +115,56 @@ dependencies = [ "libc", ] +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" +dependencies = [ + "anstyle", + "once_cell", + "windows-sys 0.59.0", +] + [[package]] name = "any_ascii" version = "0.3.2" @@ -796,6 +846,12 @@ dependencies = [ "inout", ] +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -1097,6 +1153,29 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" +[[package]] +name = "env_filter" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "jiff", + "log", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -1854,8 +1933,10 @@ dependencies = [ "axum", "axum-test", "chrono", + "env_logger", "imphnen-entities", "imphnen-gateway", + "imphnen-iam", "imphnen-libs", "imphnen-utils", "lazy_static", @@ -2076,6 +2157,12 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "itertools" version = "0.10.5" @@ -2109,6 +2196,30 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +[[package]] +name = "jiff" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f02000660d30638906021176af16b17498bd0d12813dbfe7b276d8bc7f3c0806" +dependencies = [ + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde", +] + +[[package]] +name = "jiff-static" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c30758ddd7188629c6713fc45d1188af4f44c90582311d0c8d8c9907f60c48" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + [[package]] name = "js-sys" version = "0.3.77" @@ -2845,6 +2956,21 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +[[package]] +name = "portable-atomic" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" + +[[package]] +name = "portable-atomic-util" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -4741,6 +4867,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "utoipa" version = "5.3.1" diff --git a/Cargo.toml b/Cargo.toml index 58e6c7e..8d676d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,6 +38,7 @@ axum-test = "17.2.0" fancy-regex = "0.14.0" futures = "0.3.31" tower = "0.5.2" +env_logger = "0.11.8" [profile.release] lto = "fat" diff --git a/imphnen-backend/Cargo.toml b/imphnen-backend/Cargo.toml index 0409d39..6d4ff86 100644 --- a/imphnen-backend/Cargo.toml +++ b/imphnen-backend/Cargo.toml @@ -8,6 +8,7 @@ imphnen-libs = { version = "0.1.0", path = "../imphnen-libs" } imphnen-utils = { version = "0.1.0", path = "../imphnen-utils" } imphnen-gateway = { version = "0.1.0", path = "../imphnen-gateway" } imphnen-entities = { version = "0.1.0", path = "../imphnen-entities" } +imphnen-iam = { version = "0.1.0", path = "../imphnen-iam" } axum.workspace = true serde.workspace = true serde_json.workspace = true @@ -23,3 +24,4 @@ chrono.workspace = true anyhow.workspace = true tower-http.workspace = true utoipa-swagger-ui.workspace = true +env_logger.workspace = true diff --git a/imphnen-backend/src/bin/api.rs b/imphnen-backend/src/bin/api.rs index a261ef9..b6b3c3a 100644 --- a/imphnen-backend/src/bin/api.rs +++ b/imphnen-backend/src/bin/api.rs @@ -1,8 +1,10 @@ +use env_logger; use imphnen_gateway::gateway_service; use imphnen_libs::axum_init; #[tokio::main] async fn main() { + env_logger::init(); axum_init(|surrealdb_ws, surrealdb_mem| async { gateway_service(surrealdb_ws, surrealdb_mem).await }) diff --git a/imphnen-backend/src/bin/seed_roles_permissions.rs b/imphnen-backend/src/bin/seed_roles_permissions.rs index 71466d5..90f5484 100644 --- a/imphnen-backend/src/bin/seed_roles_permissions.rs +++ b/imphnen-backend/src/bin/seed_roles_permissions.rs @@ -1,4 +1,4 @@ -use imphnen_utils::{get_iso_date, make_thing, Env}; +use imphnen_iam::{get_iso_date, make_thing, Env}; use std::error::Error; use surrealdb::{engine::remote::ws::Ws, opt::auth::Root, Surreal}; @@ -17,43 +17,41 @@ async fn main() -> Result<(), Box> { .use_db(env.surrealdb_dbname) .await?; - let role_permissions = vec![ - ( - "50133429-f4b1-4249-9f97-7b86e6ee9d86", - vec![ - "7c15e31d-36e2-49f9-97db-138c03fb0cf6", - "319ee593-ff0a-4f29-bbaf-9feb3174a3a6", - ], - ), - ( - "f6b03f25-e416-4893-ac88-caaa690afb07", - vec![ - "023e2dfe-93c3-4008-94a8-b5dff403f73b", - "96df0689-2ae9-4894-bf00-837c19415e5c", - "98b3dc4c-0124-461f-afcd-166637c5e6e8", - "319ee593-ff0a-4f29-bbaf-9feb3174a3a6", - "7c15e31d-36e2-49f9-97db-138c03fb0cf6", - "9164ca6e-c7e3-4238-a15f-f36ab9577e7e", - "319ee593-ff0a-4f29-bbaf-9feb3174a3a2", - "a00d5608-4c48-4542-845c-dfe004687022", - "35b0d992-65c8-4b62-b030-e6e0320e4048", - ], - ), + let admin_permissions = vec![ + "023e2dfe-93c3-4008-94a8-b5dff403f73b", + "0269ed71-0ae0-4c43-ad29-e3d861d8f9a0", + "299cb4d5-6556-4cc9-b6c1-32e6d31e0f9b", + "319ee593-ff0a-4f29-bbaf-9feb3174a3a2", + "319ee593-ff0a-4f29-bbaf-9feb3174a3a6", + "35b0d992-65c8-4b62-b030-e6e0320e4048", + "4da8b434-89f9-4d91-85ae-eebd63cdbeda", + "73888d18-b3e9-4f62-95a5-ba2c0d69fccb", + "7c15e31d-36e2-49f9-97db-138c03fb0cf6", + "7d4b1379-4960-416a-b045-98cd82c0cac9", + "8195eeb8-e64f-4172-aa57-596492c84a72", + "81eba91d-b8ab-44b9-bbfe-4e6da2f98952", + "9164ca6e-c7e3-4238-a15f-f36ab9577e7e", + "96df0689-2ae9-4894-bf00-837c19415e5c", + "98b3dc4c-0124-461f-afcd-166637c5e6e8", + "a00d5608-4c48-4542-845c-dfe004687022", + "b2dc3928-86ba-4c59-a03d-0b57d5183ebc", + "dad435cf-042c-41bd-a946-cea61ed2ffbc", ]; - for (role_id, permission_ids) in role_permissions { - let permission_refs: Vec<_> = permission_ids - .into_iter() - .map(|perm_id| make_thing("app_permissions", perm_id)) - .collect(); - db.query("UPDATE type::thing('app_roles', $role_id) SET permissions = $permissions, updated_at = $updated_at") - .bind(("role_id", role_id)) - .bind(("permissions", permission_refs)) - .bind(("updated_at", get_iso_date())) - .await?; - println!("✅ Updated permissions for role ID: {}", role_id); - } - println!("✅ All Roles permissions seeded"); + let admin_role_id = "f6b03f25-e416-4893-ac88-caaa690afb07"; + + let permission_refs_admin: Vec<_> = admin_permissions + .into_iter() + .map(|perm_id| make_thing("app_permissions", perm_id)) + .collect(); + + db.query("UPDATE type::thing('app_roles', $role_id) SET permissions = $permissions, updated_at = $updated_at WHERE is_deleted = false") + .bind(("role_id", admin_role_id)) + .bind(("permissions", permission_refs_admin)) + .bind(("updated_at", get_iso_date())) + .await?; + + println!("✅ All permissions successfully added to each role"); Ok(()) } diff --git a/imphnen-backend/src/bin/seed_users.rs b/imphnen-backend/src/bin/seed_users.rs index 01426bc..b5791b2 100644 --- a/imphnen-backend/src/bin/seed_users.rs +++ b/imphnen-backend/src/bin/seed_users.rs @@ -1,31 +1,8 @@ +use imphnen_iam::UsersSchema; use imphnen_utils::{get_iso_date, hash_password, Env}; -use serde::{Deserialize, Serialize}; use std::error::Error; use surrealdb::{engine::remote::ws::Ws, opt::auth::Root, sql::Thing, Surreal}; -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct UsersSchema { - pub id: Thing, - pub fullname: String, - pub email: String, - pub password: String, - pub avatar: Option, - pub phone_number: String, - pub referral_code: Option, - pub referred_by: Option, - pub identity_number: Option, - pub is_active: bool, - pub is_deleted: bool, - pub student_type: String, - pub religion: Option, - pub gender: Option, - pub birthdate: Option, - pub is_profile_completed: bool, - pub role: Thing, - pub created_at: String, - pub updated_at: String, -} - #[tokio::main] async fn main() -> Result<(), Box> { let env = Env::new(); @@ -68,16 +45,10 @@ async fn main() -> Result<(), Box> { password: hash_password("password").unwrap(), avatar: None, phone_number: "081234567890".into(), - referral_code: None, - referred_by: None, - identity_number: None, is_active: true, is_deleted: false, - student_type: "TNI".into(), - religion: None, gender: None, birthdate: None, - is_profile_completed: false, role: Thing::from(("app_roles", role_id)), created_at: get_iso_date(), updated_at: get_iso_date(), diff --git a/imphnen-gacha/src/v1/gacha_item/gacha_item_repository.rs b/imphnen-gacha/src/v1/gacha_item/gacha_item_repository.rs index 436a899..3f405a9 100644 --- a/imphnen-gacha/src/v1/gacha_item/gacha_item_repository.rs +++ b/imphnen-gacha/src/v1/gacha_item/gacha_item_repository.rs @@ -30,6 +30,7 @@ impl<'a> GachaItemRepository<'a> { None, "name", None, + None, ) .await } diff --git a/imphnen-iam/src/v1/permissions/permissions_controller.rs b/imphnen-iam/src/v1/permissions/permissions_controller.rs index 6cac43c..60708ec 100644 --- a/imphnen-iam/src/v1/permissions/permissions_controller.rs +++ b/imphnen-iam/src/v1/permissions/permissions_controller.rs @@ -1,23 +1,26 @@ use axum::{ + Extension, Json, extract::{Path, Query}, response::IntoResponse, - Extension, Json, }; use crate::{ + AppState, MessageResponseDto, MetaRequestDto, ResponseListSuccessDto, + ResponseSuccessDto, v1::{ permissions_dto::{PermissionsItemDto, PermissionsRequestDto}, permissions_service::PermissionsService, }, - AppState, MessageResponseDto, MetaRequestDto, ResponseListSuccessDto, - ResponseSuccessDto, }; -use super::{permissions_guard, PermissionsEnum}; +use super::{PermissionsEnum, permissions_guard}; #[utoipa::path( get, path = "/v1/permissions", + security( + ("Bearer" = []) + ), params( ("page" = Option, Query, description = "Page number"), ("per_page" = Option, Query, description = "Items per page"), diff --git a/imphnen-iam/src/v1/permissions/permissions_repository.rs b/imphnen-iam/src/v1/permissions/permissions_repository.rs index 1d1b0cf..66ba5b7 100644 --- a/imphnen-iam/src/v1/permissions/permissions_repository.rs +++ b/imphnen-iam/src/v1/permissions/permissions_repository.rs @@ -36,17 +36,13 @@ impl<'a> PermissionsRepository<'a> { None, "name", None, + None, ) .await?; let transformed_data = raw_result .data .into_iter() - .map(|permission| PermissionsItemDto { - id: extract_id(&permission.id), - name: permission.name, - created_at: permission.created_at, - updated_at: permission.updated_at, - }) + .map(|permission| PermissionsSchema::list(&permission)) .collect(); Ok(ResponseListSuccessDto { data: transformed_data, diff --git a/imphnen-iam/src/v1/permissions/permissions_schema.rs b/imphnen-iam/src/v1/permissions/permissions_schema.rs index 274743a..ef86d95 100644 --- a/imphnen-iam/src/v1/permissions/permissions_schema.rs +++ b/imphnen-iam/src/v1/permissions/permissions_schema.rs @@ -2,6 +2,8 @@ use crate::{ResourceEnum, make_thing}; use serde::{Deserialize, Serialize}; use surrealdb::{Uuid, sql::Thing}; +use super::PermissionsItemDto; + #[derive(Clone, Debug, Serialize, Deserialize)] pub struct PermissionsSchema { pub id: Thing, @@ -25,3 +27,14 @@ impl Default for PermissionsSchema { } } } + +impl PermissionsSchema { + pub fn list(&self) -> PermissionsItemDto { + PermissionsItemDto { + id: self.id.id.to_raw(), + name: self.name.clone(), + created_at: self.created_at.clone(), + updated_at: self.updated_at.clone(), + } + } +} diff --git a/imphnen-iam/src/v1/roles/roles_repository.rs b/imphnen-iam/src/v1/roles/roles_repository.rs index fcae8da..77c20d8 100644 --- a/imphnen-iam/src/v1/roles/roles_repository.rs +++ b/imphnen-iam/src/v1/roles/roles_repository.rs @@ -35,27 +35,21 @@ impl<'a> RolesRepository<'a> { conditions.push(format!("{} = $filter", filter_by)); } } - let raw_result: ResponseListSuccessDto> = - query_list_with_meta( - &self.state.surrealdb_ws, - &ResourceEnum::Roles.to_string(), - &meta, - conditions, - None, - "name", - None, - ) - .await?; + let raw_result: ResponseListSuccessDto> = query_list_with_meta( + &self.state.surrealdb_ws, + &ResourceEnum::Roles.to_string(), + &meta, + conditions, + None, + "name", + None, + None, + ) + .await?; let data = raw_result .data .into_iter() - .map(|role| RolesListItemDto { - id: extract_id(&role.id), - name: role.name, - created_at: role.created_at, - updated_at: role.updated_at, - permissions_count: role.permissions.len(), - }) + .map(|role| RolesSchema::list(&role)) .collect(); Ok(ResponseListSuccessDto { data, diff --git a/imphnen-iam/src/v1/roles/roles_schema.rs b/imphnen-iam/src/v1/roles/roles_schema.rs index 4898257..16e8ec9 100644 --- a/imphnen-iam/src/v1/roles/roles_schema.rs +++ b/imphnen-iam/src/v1/roles/roles_schema.rs @@ -1,5 +1,5 @@ use super::{ - RolesDetailItemDto, RolesDetailQueryDto, RolesRequestCreateDto, + RolesDetailItemDto, RolesDetailQueryDto, RolesListItemDto, RolesRequestCreateDto, RolesRequestUpdateDto, }; use crate::{ResourceEnum, make_thing}; @@ -112,4 +112,14 @@ impl RolesSchema { updated_at: Some(get_iso_date()), } } + + pub fn list(&self) -> RolesListItemDto { + RolesListItemDto { + id: self.id.id.to_raw(), + name: self.name.clone(), + permissions_count: self.permissions.len(), + created_at: self.created_at.clone(), + updated_at: self.updated_at.clone(), + } + } } diff --git a/imphnen-iam/src/v1/users/users_dto.rs b/imphnen-iam/src/v1/users/users_dto.rs index 4e3d8b0..3fd6a31 100644 --- a/imphnen-iam/src/v1/users/users_dto.rs +++ b/imphnen-iam/src/v1/users/users_dto.rs @@ -139,10 +139,10 @@ pub struct UsersListQueryDto { } impl UsersListQueryDto { - pub fn from(&self, role: String) -> UsersListItemDto { + pub fn from(&self) -> UsersListItemDto { UsersListItemDto { id: self.id.id.to_raw(), - role, + role: self.role.name.clone(), fullname: self.fullname.clone(), email: self.email.clone(), avatar: self.avatar.clone(), @@ -170,3 +170,23 @@ pub struct UsersDetailQueryDto { pub created_at: String, pub updated_at: String, } + +impl UsersDetailQueryDto { + pub fn from(&self) -> Self { + Self { + id: self.id.clone(), + role: RolesDetailQueryDto::from(self.role.clone()), + fullname: self.fullname.clone(), + email: self.email.clone(), + avatar: self.avatar.clone(), + phone_number: self.phone_number.clone(), + is_active: self.is_active, + gender: self.gender.clone(), + is_deleted: self.is_deleted, + password: self.password.clone(), + birthdate: self.birthdate.clone(), + created_at: self.created_at.clone(), + updated_at: self.updated_at.clone(), + } + } +} diff --git a/imphnen-iam/src/v1/users/users_repository.rs b/imphnen-iam/src/v1/users/users_repository.rs index feab5df..d0efd33 100644 --- a/imphnen-iam/src/v1/users/users_repository.rs +++ b/imphnen-iam/src/v1/users/users_repository.rs @@ -1,8 +1,7 @@ use super::{UsersDetailQueryDto, UsersListItemDto, UsersListQueryDto, UsersSchema}; use crate::{ - AppState, MetaRequestDto, PermissionsQueryDto, ResourceEnum, - ResponseListSuccessDto, RolesDetailQueryDto, get_id, make_thing, - query_list_with_meta, + AppState, MetaRequestDto, ResourceEnum, ResponseListSuccessDto, get_id, + make_thing, query_list_with_meta, }; use anyhow::{Result, bail}; use imphnen_utils::DetailQueryBuilder; @@ -45,25 +44,15 @@ impl<'a> UsersRepository<'a> { vec!["is_deleted = false".into()], None, "fullname", - Some(vec![ - "email", - "fullname", - "id", - "is_active", - "is_deleted", - "role", - "role.permissions", - ]), + Some(vec!["*"]), + Some(vec!["role", "role.permissions"]), ) .await?; let data = raw_result .data .into_iter() - .map(|schema| { - let role = schema.clone().role.name; - schema.from(role) - }) + .map(|schema| UsersListQueryDto::from(&schema)) .collect::>(); Ok(ResponseListSuccessDto { @@ -81,21 +70,7 @@ impl<'a> UsersRepository<'a> { let builder = DetailQueryBuilder::new(ResourceEnum::Users.to_string()) .with_where("email") .where_value(email.clone()) - .with_select_fields(vec![ - "id", - "fullname", - "email", - "avatar", - "phone_number", - "is_active", - "is_deleted", - "gender", - "birthdate", - "password", - "created_at", - "updated_at", - "role", - ]) + .with_select_fields(vec!["*"]) .with_fetch("role") .with_fetch("role.permissions"); @@ -112,40 +87,7 @@ impl<'a> UsersRepository<'a> { bail!("User not found"); } - let permissions = user - .role - .permissions - .into_iter() - .map(|perm| PermissionsQueryDto { - id: perm.id, - name: perm.name, - created_at: perm.created_at, - updated_at: perm.updated_at, - }) - .collect(); - - Ok(UsersDetailQueryDto { - id: user.id, - fullname: user.fullname, - email: user.email, - avatar: user.avatar, - phone_number: user.phone_number, - is_active: user.is_active, - is_deleted: user.is_deleted, - gender: user.gender, - birthdate: user.birthdate, - password: user.password, - created_at: user.created_at, - updated_at: user.updated_at, - role: RolesDetailQueryDto { - id: user.role.id, - name: user.role.name, - created_at: user.role.created_at, - updated_at: user.role.updated_at, - is_deleted: user.role.is_deleted, - permissions, - }, - }) + Ok(UsersDetailQueryDto::from(&user)) } pub async fn query_user_by_id(&self, id: String) -> Result { @@ -153,21 +95,7 @@ impl<'a> UsersRepository<'a> { let builder = DetailQueryBuilder::new(ResourceEnum::Users.to_string()) .with_id(&id) - .with_select_fields(vec![ - "id", - "fullname", - "email", - "avatar", - "phone_number", - "is_active", - "is_deleted", - "gender", - "birthdate", - "password", - "created_at", - "updated_at", - "role", - ]) + .with_select_fields(vec!["*"]) .with_fetch("role") .with_fetch("role.permissions"); @@ -184,40 +112,7 @@ impl<'a> UsersRepository<'a> { bail!("User not found"); } - let permissions = user - .role - .permissions - .into_iter() - .map(|perm| PermissionsQueryDto { - id: perm.id, - name: perm.name, - created_at: perm.created_at, - updated_at: perm.updated_at, - }) - .collect(); - - Ok(UsersDetailQueryDto { - id: user.id, - fullname: user.fullname, - email: user.email, - avatar: user.avatar, - phone_number: user.phone_number, - is_active: user.is_active, - is_deleted: user.is_deleted, - gender: user.gender, - birthdate: user.birthdate, - password: user.password, - created_at: user.created_at, - updated_at: user.updated_at, - role: RolesDetailQueryDto { - id: user.role.id, - name: user.role.name, - created_at: user.role.created_at, - updated_at: user.role.updated_at, - is_deleted: user.role.is_deleted, - permissions, - }, - }) + Ok(UsersDetailQueryDto::from(&user)) } pub async fn query_create_user(&self, data: UsersSchema) -> Result { diff --git a/imphnen-utils/src/bind_filter.rs b/imphnen-utils/src/bind_filter.rs index c922765..0c3de18 100644 --- a/imphnen-utils/src/bind_filter.rs +++ b/imphnen-utils/src/bind_filter.rs @@ -4,11 +4,5 @@ pub fn bind_filter_value( query: Query<'_, Client>, val: String, ) -> Query<'_, Client> { - if let Ok(b) = val.parse::() { - query.bind(("filter", b)) - } else if let Ok(i) = val.parse::() { - query.bind(("filter", i)) - } else { - query.bind(("filter", val)) - } + query.bind(("filter", val)) // langsung string aja, udah cukup } diff --git a/imphnen-utils/src/crud.rs b/imphnen-utils/src/crud.rs deleted file mode 100644 index d95669b..0000000 --- a/imphnen-utils/src/crud.rs +++ /dev/null @@ -1,8 +0,0 @@ -pub trait Crud { - fn list(&self, _args: Args) -> T { - unimplemented!("list() is not implemented for this type") - } - fn detail(&self, _args: Args) -> T { - unimplemented!("detail() is not implemented for this type") - } -} diff --git a/imphnen-utils/src/lib.rs b/imphnen-utils/src/lib.rs index 08ddcb6..a400ee9 100644 --- a/imphnen-utils/src/lib.rs +++ b/imphnen-utils/src/lib.rs @@ -2,7 +2,6 @@ use imphnen_entities::*; use imphnen_libs::*; pub mod bind_filter; -pub mod crud; pub mod extract_email; pub mod generate_date; pub mod generate_otp; @@ -15,7 +14,6 @@ pub mod response_format; pub mod validator; pub use bind_filter::*; -pub use crud::*; pub use extract_email::*; pub use generate_date::*; pub use generate_otp::*; diff --git a/imphnen-utils/src/query_builder.rs b/imphnen-utils/src/query_builder.rs index df6eab0..65e1d77 100644 --- a/imphnen-utils/src/query_builder.rs +++ b/imphnen-utils/src/query_builder.rs @@ -20,11 +20,13 @@ impl ListQueryBuilder { meta: &MetaRequestDto, search_field: impl Into, select_fields: Option>, + fetch_fields: Option>, ) -> Self { let mut builder = Self::new(resource) .with_search(meta.search.as_deref(), &search_field.into()) .with_filter(meta.filter_by.as_deref(), meta.filter.as_deref()) .with_sorting(meta.sort_by.as_deref(), meta.order.as_deref()) + .with_fetch(fetch_fields) .with_pagination(meta.page, meta.per_page); if let Some(fields) = select_fields { @@ -54,9 +56,10 @@ impl ListQueryBuilder { pub fn with_search(mut self, search: Option<&str>, field: &str) -> Self { if let Some(search) = search { if !search.is_empty() { - self - .conditions - .push(format!("string::contains({} ?? '', $search)", field)); + self.conditions.push(format!( + "string::contains(string::lowercase({} ?? ''), string::lowercase($search))", + field + )); } } self @@ -65,7 +68,10 @@ impl ListQueryBuilder { pub fn with_filter(mut self, field: Option<&str>, value: Option<&str>) -> Self { if let (Some(f), Some(v)) = (field, value) { if !v.is_empty() { - self.conditions.push(format!("{} = $filter", f)); + self.conditions.push(format!( + "string::contains(string::join('', [{}]), $filter)", + f + )); } } self @@ -89,8 +95,10 @@ impl ListQueryBuilder { self } - pub fn with_fetch(mut self, fetch: impl Into) -> Self { - self.fetch.push(fetch.into()); + pub fn with_fetch(mut self, fetches: Option>) -> Self { + if let Some(items) = fetches { + self.fetch.extend(items.into_iter().map(String::from)); + } self } diff --git a/imphnen-utils/src/query_list.rs b/imphnen-utils/src/query_list.rs index bb27db8..aa65645 100644 --- a/imphnen-utils/src/query_list.rs +++ b/imphnen-utils/src/query_list.rs @@ -12,6 +12,7 @@ pub async fn query_list_with_meta( custom_select: Option, search_field: &str, select_fields: Option>, + fetch_fields: Option>, ) -> Result>> where T: DeserializeOwned + Serialize, @@ -21,13 +22,20 @@ where let start = (page - 1) * per_page; let sql = custom_select.unwrap_or_else(|| { - ListQueryBuilder::from_meta(table, meta, search_field, select_fields).build() + ListQueryBuilder::from_meta( + table, + meta, + search_field, + select_fields, + fetch_fields, + ) + .build() }); let mut query_exec = db.query(sql); if let Some(search) = &meta.search { if !search.is_empty() { - query_exec = query_exec.bind(("search", search.clone())); + query_exec = query_exec.bind(("search", search.to_lowercase().clone())); } } if let Some(filter_val) = &meta.filter {