From 330383b85e8359545c3efbf1883a10cf515a8b30 Mon Sep 17 00:00:00 2001 From: Rafael Ruiz Date: Sun, 28 Apr 2024 22:28:44 +0200 Subject: [PATCH 01/22] [FEATURE] Access control resources for services created: - JWT authenticatication logic implemented. --- Cargo.lock | 75 +++++++++- Cargo.toml | 4 + src/domain/builder_db_service.rs | 9 +- src/infrastructure/controller.rs | 75 +++++++++- .../dto/db_service/dto_db_service.rs | 1 + .../dto/db_service/dto_db_service_suscribe.rs | 8 ++ src/infrastructure/services_jwt.rs | 131 ++++++++++++++++++ src/lib.rs | 2 + 8 files changed, 295 insertions(+), 10 deletions(-) create mode 100644 src/infrastructure/dto/db_service/dto_db_service_suscribe.rs create mode 100644 src/infrastructure/services_jwt.rs diff --git a/Cargo.lock b/Cargo.lock index 0a54d86..6372db5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -45,6 +45,18 @@ dependencies = [ "libc", ] +[[package]] +name = "argon2" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072" +dependencies = [ + "base64ct", + "blake2", + "cpufeatures", + "password-hash", +] + [[package]] name = "async-trait" version = "0.1.80" @@ -144,6 +156,21 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "base64-compat" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a8d4d2746f89841e49230dd26917df1876050f95abafafbe34f47cb534b88d7" +dependencies = [ + "byteorder", +] + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + [[package]] name = "bitflags" version = "1.3.2" @@ -168,6 +195,15 @@ dependencies = [ "wyz", ] +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -204,6 +240,12 @@ version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "bytes" version = "1.6.0" @@ -768,6 +810,21 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "jwt" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6204285f77fe7d9784db3fdc449ecce1a0114927a51d5a41c4c7a292011c015f" +dependencies = [ + "base64 0.13.1", + "crypto-common", + "digest", + "hmac", + "serde", + "serde_json", + "sha2", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -982,6 +1039,17 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "password-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" +dependencies = [ + "base64ct", + "rand_core", + "subtle", +] + [[package]] name = "pbkdf2" version = "0.11.0" @@ -1141,12 +1209,16 @@ version = "0.1.0" dependencies = [ "async-trait", "axum", + "base64-compat", "cargo_metadata", "crossterm", + "hmac", + "jwt", "lazy_static", "rust_db_manager_core", "serde", "serde_json", + "sha2", "tokio", "tower-http", "uuid", @@ -1155,8 +1227,9 @@ dependencies = [ [[package]] name = "rust_db_manager_core" version = "0.1.1" -source = "git+https://github.com/Rafael24595/rust-db-manager.git?branch=dev#403dba2c663bac7272a28f4d23aa2ad5f50e84cb" +source = "git+https://github.com/Rafael24595/rust-db-manager.git?branch=dev#c3332de183f5fc9eb20d1f1c102de4a8c9a185c1" dependencies = [ + "argon2", "async-trait", "cargo_metadata", "crossterm", diff --git a/Cargo.toml b/Cargo.toml index 8f0e0a5..3ce50e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,4 +16,8 @@ serde_json = "1.0" crossterm = "0.27.0" uuid = "1.8.0" cargo_metadata = "0.18.1" +base64-compat = "1.0.0" +hmac = "0.12.1" +sha2 = "0.10.8" +jwt = "0.16.0" rust_db_manager_core = { git = "https://github.com/Rafael24595/rust-db-manager.git", branch = "dev" } \ No newline at end of file diff --git a/src/domain/builder_db_service.rs b/src/domain/builder_db_service.rs index f23d05c..d5cd225 100644 --- a/src/domain/builder_db_service.rs +++ b/src/domain/builder_db_service.rs @@ -11,8 +11,13 @@ impl BuilderDBService { pub fn make(dto: DTODBService) -> Result { let connection_data = BuilderConnectionData::make(dto.connection_data)?; - let service = DBService::new(dto.name, dto.owner, connection_data); - Ok(service) + + let service = DBService::new(dto.name, dto.owner, dto.password, connection_data); + if service.is_err() { + return Err(ApiException::from(500, service.err().unwrap())); + } + + Ok(service.unwrap()) } } \ No newline at end of file diff --git a/src/infrastructure/controller.rs b/src/infrastructure/controller.rs index 8fbb1f3..8f73466 100644 --- a/src/infrastructure/controller.rs +++ b/src/infrastructure/controller.rs @@ -1,10 +1,10 @@ -use axum::{body::Body, extract::{Path, Query}, http::{Response, StatusCode}, response::IntoResponse, routing::{get, post}, Json, Router}; +use axum::{body::Body, extract::{Path, Query}, http::{HeaderMap, Response, StatusCode}, response::IntoResponse, routing::{get, post}, Json, Router}; -use rust_db_manager_core::commons::configuration::configuration::Configuration; +use rust_db_manager_core::{commons::configuration::configuration::Configuration, infrastructure::db_service::DBService}; use crate::{commons::{configuration::web_configuration::WebConfiguration, exception::api_exception::ApiException}, domain::builder_db_service::BuilderDBService}; -use super::{dto::{db_service::{dto_db_service::DTODBService, dto_db_service_lite::DTODBServiceLite}, dto_server_status::DTOServerStatus, pagination::{dto_paginated_collection::DTOPaginatedCollection, dto_query_pagination::DTOQueryPagination}}, pagination::Pagination}; +use super::{dto::{db_service::{dto_db_service::DTODBService, dto_db_service_lite::DTODBServiceLite, dto_db_service_suscribe::DTODBServiceSuscribe}, dto_server_status::DTOServerStatus, pagination::{dto_paginated_collection::DTOPaginatedCollection, dto_query_pagination::DTOQueryPagination}}, pagination::Pagination, services_jwt::ServicesJWT}; pub struct Controller{ } @@ -15,8 +15,9 @@ impl Controller { router .route("/status", get(Controller::status)) .route("/services", get(Controller::services)) + .route("/publish", post(Controller::service_publish)) + .route("/suscribe", post(Controller::service_suscribe)) .route("/:service/status", get(Controller::service_status)) - .route("/:service", post(Controller::insert_service)) } async fn status() -> (StatusCode, Json) { @@ -31,21 +32,60 @@ impl Controller { (StatusCode::ACCEPTED, Json(result)) } - async fn insert_service(Json(dto): Json) -> Result<(StatusCode, String), impl IntoResponse> { + async fn service_publish(headers: HeaderMap, Json(dto): Json) -> impl IntoResponse { let o_service = BuilderDBService::make(dto); if let Err(error) = o_service { return Err(error.into_response()); } - + let service = o_service.unwrap(); + let token = Controller::make_token(headers, service.clone()); + if token.is_err() { + return Err(token.unwrap_err().into_response()); + } + let db_service = Configuration::push_service(service.clone()); if let Err(error) = db_service { let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); return Err(exception.into_response()); } - Ok((StatusCode::ACCEPTED, service.name())) + let response = Response::builder() + .header("db-token", token.unwrap()) + .status(StatusCode::ACCEPTED) + .body(Body::from(service.name())) + .unwrap(); + + Ok(response) + } + + async fn service_suscribe(headers: HeaderMap, Json(dto): Json) -> impl IntoResponse { + let o_service = Configuration::find_service(dto.name); + if o_service.is_none() { + let exception = ApiException::new(StatusCode::NOT_FOUND.as_u16(), String::from("Service not found.")); + return Err(exception.into_response()); + } + + let service = o_service.unwrap(); + + if service.is_authorized(dto.password).is_err() { + let exception = ApiException::new(StatusCode::UNAUTHORIZED.as_u16(), String::from("Authentication error.")); + return Err(exception.into_response()); + } + + let token = Controller::make_token(headers, service.clone()); + if token.is_err() { + return Err(token.unwrap_err().into_response()); + } + + let response = Response::builder() + .header("db-token", token.unwrap()) + .status(StatusCode::ACCEPTED) + .body(Body::from(service.name())) + .unwrap(); + + Ok(response) } async fn service_status(Path(service): Path) -> Result<(StatusCode, String), impl IntoResponse> { @@ -69,6 +109,27 @@ impl Controller { Ok((StatusCode::ACCEPTED, String::from("Service up."))) } + fn make_token(headers: HeaderMap, service: DBService) -> Result { + match headers.get("db-token") { + Some(header) => { + let result = ServicesJWT::update(header.to_str().unwrap().to_owned(), service.clone()); + if result.is_err() { + return Err(result.unwrap_err()); + } + + Ok(result.unwrap()) + }, + None => { + let result = ServicesJWT::sign(service.clone()); + if result.is_err() { + return Err(result.unwrap_err()); + } + + Ok(result.unwrap()) + }, + } + } + fn not_found() -> Response { let error = ApiException::new( StatusCode::NOT_FOUND.as_u16(), diff --git a/src/infrastructure/dto/db_service/dto_db_service.rs b/src/infrastructure/dto/db_service/dto_db_service.rs index e752d4e..af57dc5 100644 --- a/src/infrastructure/dto/db_service/dto_db_service.rs +++ b/src/infrastructure/dto/db_service/dto_db_service.rs @@ -5,6 +5,7 @@ use super::dto_db_connection_data::DTOConnectionData; #[derive(Clone, Deserialize)] pub struct DTODBService { pub name: String, + pub password: String, pub owner: String, pub connection_data: DTOConnectionData } \ No newline at end of file diff --git a/src/infrastructure/dto/db_service/dto_db_service_suscribe.rs b/src/infrastructure/dto/db_service/dto_db_service_suscribe.rs new file mode 100644 index 0000000..df6f1e2 --- /dev/null +++ b/src/infrastructure/dto/db_service/dto_db_service_suscribe.rs @@ -0,0 +1,8 @@ +use serde::Deserialize; + +#[derive(Clone, Deserialize)] +pub struct DTODBServiceSuscribe { + pub name: String, + pub password: String, + pub owner: String, +} \ No newline at end of file diff --git a/src/infrastructure/services_jwt.rs b/src/infrastructure/services_jwt.rs new file mode 100644 index 0000000..f9f2612 --- /dev/null +++ b/src/infrastructure/services_jwt.rs @@ -0,0 +1,131 @@ +use hmac::{Hmac, Mac}; +use jwt::{SignWithKey, VerifyWithKey}; +use sha2::Sha256; +use std::collections::BTreeMap; + +use rust_db_manager_core::{commons::configuration::configuration::Configuration, infrastructure::db_service::DBService}; + +use crate::commons::exception::api_exception::ApiException; + +pub struct ServicesJWT { + +} + +impl ServicesJWT { + + pub fn sign(service: DBService) -> Result { + ServicesJWT::sign_services(Vec::from(vec![service])) + } + + pub fn sign_services(services: Vec) -> Result { + let s_key = services.iter() + .map(|s| s.salt()) + .collect::>() + .join("#"); + + let key: Result, hmac::digest::InvalidLength> = Hmac::new_from_slice(s_key.as_bytes()); + if key.is_err() { + let exception = ApiException::new(500, key.unwrap_err().to_string()); + return Err(exception); + } + + let collection = services.iter() + .map(|s| s.name()) + .collect::>() + .join("-"); + + let mut claims = BTreeMap::new(); + claims.insert("sub", collection); + + let token_str = claims.sign_with_key(&key.unwrap()); + if token_str.is_err() { + let exception = ApiException::new(500, token_str.unwrap_err().to_string()); + return Err(exception); + } + + Ok(token_str.unwrap()) + } + + pub fn update(token: String, service: DBService) -> Result { + let _ = ServicesJWT::verify(token.clone())?; + + let mut services = ServicesJWT::find_services(token.clone())?; + if services.iter().find(|s| s.name() == service.name()).is_some() { + let exception = ApiException::new(500, String::from("This token is already subscribed to the service.")); + return Err(exception); + } + + services.push(service); + + ServicesJWT::sign_services(services) + } + + pub fn verify(token: String) -> Result<(), ApiException> { + let salt = ServicesJWT::find_services(token.clone())?.iter() + .map(|s| s.salt()) + .collect::>().join("#"); + + let key: Result, hmac::digest::InvalidLength> = Hmac::new_from_slice(salt.as_bytes()); + if key.is_err() { + let exception = ApiException::new(500, key.unwrap_err().to_string()); + return Err(exception); + } + + let result: Result, jwt::Error> = token.verify_with_key(&key.unwrap()); + if result.is_err() { + let exception = ApiException::new(500, result.unwrap_err().to_string()); + return Err(exception); + } + + Ok(()) + } + + fn find_services(token: String) -> Result, ApiException> { + let fragments = token.split(".").collect::>(); + if fragments.len() != 3 { + let exception = ApiException::new(401, String::from("Invalid token.")); + return Err(exception); + } + + let claims = fragments.get(1).unwrap().trim(); + let b_services = base64::decode_config(claims, base64::URL_SAFE_NO_PAD); + if b_services.is_err() { + let exception = ApiException::new(500, b_services.unwrap_err().to_string()); + return Err(exception); + } + + let s_services = String::from_utf8(b_services.unwrap()); + if s_services.is_err() { + let exception = ApiException::new(500, s_services.unwrap_err().to_string()); + return Err(exception); + } + + let m_services: Result, serde_json::Error> = serde_json::from_str(&s_services.unwrap()); + if m_services.is_err() { + let exception = ApiException::new(500, m_services.unwrap_err().to_string()); + return Err(exception); + } + + let b_m_services = m_services.unwrap(); + let v_services = b_m_services.get("sub"); + + if v_services.is_none() { + let exception = ApiException::new(500, String::from("No services found.")); + return Err(exception); + } + + let mut collection = Vec::new(); + + for s_service in v_services.unwrap().split("-").collect::>() { + let service = Configuration::find_service(String::from(s_service)); + if service.is_none() { + let exception = ApiException::new(500, String::from("Unknown service.")); + return Err(exception); + } + collection.push(service.unwrap()); + } + + Ok(collection) + } + +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 92a1758..7707a03 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,6 +17,7 @@ pub mod infrastructure { pub mod dto_db_resources; pub mod dto_db_service_web_category; pub mod dto_db_service_lite; + pub mod dto_db_service_suscribe; pub mod dto_db_service; } pub mod pagination { @@ -28,5 +29,6 @@ pub mod infrastructure { pub mod controller; pub mod db_assets; pub mod pagination; + pub mod services_jwt; pub mod utils; } \ No newline at end of file From e58b6b64b8002f297f91eb13d4554944670fce68 Mon Sep 17 00:00:00 2001 From: Rafael Ruiz Date: Tue, 30 Apr 2024 19:21:21 +0200 Subject: [PATCH 02/22] [FEATURE] Supported repositories end-point implemented. --- src/infrastructure/controller.rs | 10 ++++++++-- src/infrastructure/db_assets.rs | 9 ++++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/infrastructure/controller.rs b/src/infrastructure/controller.rs index 8f73466..563e6fe 100644 --- a/src/infrastructure/controller.rs +++ b/src/infrastructure/controller.rs @@ -1,10 +1,10 @@ use axum::{body::Body, extract::{Path, Query}, http::{HeaderMap, Response, StatusCode}, response::IntoResponse, routing::{get, post}, Json, Router}; -use rust_db_manager_core::{commons::configuration::configuration::Configuration, infrastructure::db_service::DBService}; +use rust_db_manager_core::{commons::configuration::configuration::Configuration, infrastructure::{db_service::DBService, repository::e_db_repository::EDBRepository}}; use crate::{commons::{configuration::web_configuration::WebConfiguration, exception::api_exception::ApiException}, domain::builder_db_service::BuilderDBService}; -use super::{dto::{db_service::{dto_db_service::DTODBService, dto_db_service_lite::DTODBServiceLite, dto_db_service_suscribe::DTODBServiceSuscribe}, dto_server_status::DTOServerStatus, pagination::{dto_paginated_collection::DTOPaginatedCollection, dto_query_pagination::DTOQueryPagination}}, pagination::Pagination, services_jwt::ServicesJWT}; +use super::{db_assets::WebEDBRepository, dto::{db_service::{dto_db_service::DTODBService, dto_db_service_lite::DTODBServiceLite, dto_db_service_suscribe::DTODBServiceSuscribe, dto_db_service_web_category::DTODBServiceWebCategory}, dto_server_status::DTOServerStatus, pagination::{dto_paginated_collection::DTOPaginatedCollection, dto_query_pagination::DTOQueryPagination}}, pagination::Pagination, services_jwt::ServicesJWT}; pub struct Controller{ } @@ -14,6 +14,7 @@ impl Controller { pub fn route(router: Router) -> Router { router .route("/status", get(Controller::status)) + .route("/support", get(Controller::support)) .route("/services", get(Controller::services)) .route("/publish", post(Controller::service_publish)) .route("/suscribe", post(Controller::service_suscribe)) @@ -25,6 +26,11 @@ impl Controller { (StatusCode::ACCEPTED, Json(result)) } + async fn support() -> (StatusCode, Json>) { + let dto = EDBRepository::supported(); + (StatusCode::ACCEPTED, Json(dto)) + } + async fn services(Query(params): Query) -> (StatusCode, Json>) { let services = Configuration::find_services(); let dto = DTODBServiceLite::from_vec(services); diff --git a/src/infrastructure/db_assets.rs b/src/infrastructure/db_assets.rs index c661b41..e6303bf 100644 --- a/src/infrastructure/db_assets.rs +++ b/src/infrastructure/db_assets.rs @@ -1,15 +1,22 @@ use rust_db_manager_core::infrastructure::repository::e_db_repository::EDBRepository; -use super::dto::db_service::dto_db_resources::DTODBResources; +use super::dto::db_service::{dto_db_resources::DTODBResources, dto_db_service_web_category::DTODBServiceWebCategory}; pub trait WebEDBRepository { + fn supported() -> Vec; fn resources(&self) -> DTODBResources; } impl WebEDBRepository for EDBRepository { + fn supported() -> Vec { + EDBRepository::items().iter() + .map(|e| DTODBServiceWebCategory::from(e.clone())) + .collect() + } + fn resources(&self) -> DTODBResources { match self { EDBRepository::MongoDB => DTODBResources::new( From af7b87abd3a1b87ffd32c69ee0109a6dae67949b Mon Sep 17 00:00:00 2001 From: Rafael Ruiz Date: Wed, 1 May 2024 17:36:05 +0200 Subject: [PATCH 03/22] [FEATURE] Autentication handler created: - Remove service end-point created. --- .../configuration/web_configuration.rs | 2 + src/domain/builder_db_service.rs | 2 +- src/infrastructure/controller.rs | 84 +++++++++++++------ .../dto/db_service/dto_db_service.rs | 3 +- src/infrastructure/handler.rs | 40 +++++++++ src/infrastructure/services_jwt.rs | 10 ++- src/lib.rs | 1 + 7 files changed, 112 insertions(+), 30 deletions(-) create mode 100644 src/infrastructure/handler.rs diff --git a/src/commons/configuration/web_configuration.rs b/src/commons/configuration/web_configuration.rs index 2a86f9f..5aa4513 100644 --- a/src/commons/configuration/web_configuration.rs +++ b/src/commons/configuration/web_configuration.rs @@ -17,6 +17,8 @@ pub struct WebConfiguration { } impl WebConfiguration { + + pub const COOKIE_NAME: &'static str = "db-token"; pub fn initialize() -> WebConfiguration { let _ = Configuration::initialize(); diff --git a/src/domain/builder_db_service.rs b/src/domain/builder_db_service.rs index d5cd225..150c609 100644 --- a/src/domain/builder_db_service.rs +++ b/src/domain/builder_db_service.rs @@ -12,7 +12,7 @@ impl BuilderDBService { pub fn make(dto: DTODBService) -> Result { let connection_data = BuilderConnectionData::make(dto.connection_data)?; - let service = DBService::new(dto.name, dto.owner, dto.password, connection_data); + let service = DBService::new(dto.name, dto.owner, dto.protected, dto.password, connection_data); if service.is_err() { return Err(ApiException::from(500, service.err().unwrap())); } diff --git a/src/infrastructure/controller.rs b/src/infrastructure/controller.rs index 563e6fe..fcf5372 100644 --- a/src/infrastructure/controller.rs +++ b/src/infrastructure/controller.rs @@ -1,10 +1,10 @@ -use axum::{body::Body, extract::{Path, Query}, http::{HeaderMap, Response, StatusCode}, response::IntoResponse, routing::{get, post}, Json, Router}; +use axum::{body::Body, extract::{Path, Query}, http::{HeaderMap, Response, StatusCode}, middleware, response::IntoResponse, routing::{delete, get, post}, Json, Router}; use rust_db_manager_core::{commons::configuration::configuration::Configuration, infrastructure::{db_service::DBService, repository::e_db_repository::EDBRepository}}; use crate::{commons::{configuration::web_configuration::WebConfiguration, exception::api_exception::ApiException}, domain::builder_db_service::BuilderDBService}; -use super::{db_assets::WebEDBRepository, dto::{db_service::{dto_db_service::DTODBService, dto_db_service_lite::DTODBServiceLite, dto_db_service_suscribe::DTODBServiceSuscribe, dto_db_service_web_category::DTODBServiceWebCategory}, dto_server_status::DTOServerStatus, pagination::{dto_paginated_collection::DTOPaginatedCollection, dto_query_pagination::DTOQueryPagination}}, pagination::Pagination, services_jwt::ServicesJWT}; +use super::{db_assets::WebEDBRepository, dto::{db_service::{dto_db_service::DTODBService, dto_db_service_lite::DTODBServiceLite, dto_db_service_suscribe::DTODBServiceSuscribe, dto_db_service_web_category::DTODBServiceWebCategory}, dto_server_status::DTOServerStatus, pagination::{dto_paginated_collection::DTOPaginatedCollection, dto_query_pagination::DTOQueryPagination}}, handler, pagination::Pagination, services_jwt::ServicesJWT}; pub struct Controller{ } @@ -13,12 +13,14 @@ impl Controller { pub fn route(router: Router) -> Router { router + .route("/:service/status", get(Controller::service_status)) + .route("/:service", delete(Controller::service_remove)) + .route_layer(middleware::from_fn(handler::autentication_handler)) .route("/status", get(Controller::status)) .route("/support", get(Controller::support)) .route("/services", get(Controller::services)) - .route("/publish", post(Controller::service_publish)) - .route("/suscribe", post(Controller::service_suscribe)) - .route("/:service/status", get(Controller::service_status)) + .route("/publish", post(Controller::publish)) + .route("/suscribe", post(Controller::suscribe)) } async fn status() -> (StatusCode, Json) { @@ -38,7 +40,7 @@ impl Controller { (StatusCode::ACCEPTED, Json(result)) } - async fn service_publish(headers: HeaderMap, Json(dto): Json) -> impl IntoResponse { + async fn publish(headers: HeaderMap, Json(dto): Json) -> impl IntoResponse { let o_service = BuilderDBService::make(dto); if let Err(error) = o_service { return Err(error.into_response()); @@ -46,27 +48,34 @@ impl Controller { let service = o_service.unwrap(); - let token = Controller::make_token(headers, service.clone()); - if token.is_err() { - return Err(token.unwrap_err().into_response()); + let r_token = Controller::make_token(headers, service.clone()); + if r_token.is_err() { + return Err(r_token.unwrap_err().into_response()); } + let token = r_token.unwrap(); + let db_service = Configuration::push_service(service.clone()); if let Err(error) = db_service { let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); return Err(exception.into_response()); } - let response = Response::builder() - .header("db-token", token.unwrap()) + let mut builder = Response::builder(); + if token.is_some() { + let cookie = format!("{}={}; Path=/; Secure; HttpOnly; SameSite=Lax", WebConfiguration::COOKIE_NAME, token.unwrap()); + builder = builder.header("Set-Cookie", cookie); + } + + let response = builder .status(StatusCode::ACCEPTED) - .body(Body::from(service.name())) + .body(Body::empty()) .unwrap(); Ok(response) } - async fn service_suscribe(headers: HeaderMap, Json(dto): Json) -> impl IntoResponse { + async fn suscribe(headers: HeaderMap, Json(dto): Json) -> impl IntoResponse { let o_service = Configuration::find_service(dto.name); if o_service.is_none() { let exception = ApiException::new(StatusCode::NOT_FOUND.as_u16(), String::from("Service not found.")); @@ -80,15 +89,22 @@ impl Controller { return Err(exception.into_response()); } - let token = Controller::make_token(headers, service.clone()); - if token.is_err() { - return Err(token.unwrap_err().into_response()); + let r_token = Controller::make_token(headers, service.clone()); + if r_token.is_err() { + return Err(r_token.unwrap_err().into_response()); } - let response = Response::builder() - .header("db-token", token.unwrap()) + let token = r_token.unwrap(); + + let mut builder = Response::builder(); + if token.is_some() { + let cookie = format!("{}={}; Path=/; Secure; HttpOnly; SameSite=Lax", WebConfiguration::COOKIE_NAME, token.unwrap()); + builder = builder.header("Set-Cookie", cookie); + } + + let response = builder .status(StatusCode::ACCEPTED) - .body(Body::from(service.name())) + .body(Body::empty()) .unwrap(); Ok(response) @@ -115,23 +131,43 @@ impl Controller { Ok((StatusCode::ACCEPTED, String::from("Service up."))) } - fn make_token(headers: HeaderMap, service: DBService) -> Result { - match headers.get("db-token") { + async fn service_remove(Path(service): Path) -> Result { + let db_service = Configuration::find_service(service); + if db_service.is_none() { + return Err(Controller::not_found()); + } + + Configuration::remove_service(db_service.unwrap()); + + Ok(StatusCode::ACCEPTED) + } + + fn make_token(headers: HeaderMap, service: DBService) -> Result, ApiException> { + match headers.get(WebConfiguration::COOKIE_NAME) { Some(header) => { - let result = ServicesJWT::update(header.to_str().unwrap().to_owned(), service.clone()); + let token = header.to_str().unwrap().to_owned(); + if !service.is_protected() { + return Ok(Some(token)); + } + + let result = ServicesJWT::update(token, service.clone()); if result.is_err() { return Err(result.unwrap_err()); } - Ok(result.unwrap()) + Ok(Some(result.unwrap())) }, None => { + if !service.is_protected() { + return Ok(None); + } + let result = ServicesJWT::sign(service.clone()); if result.is_err() { return Err(result.unwrap_err()); } - Ok(result.unwrap()) + Ok(Some(result.unwrap())) }, } } diff --git a/src/infrastructure/dto/db_service/dto_db_service.rs b/src/infrastructure/dto/db_service/dto_db_service.rs index af57dc5..e1819a3 100644 --- a/src/infrastructure/dto/db_service/dto_db_service.rs +++ b/src/infrastructure/dto/db_service/dto_db_service.rs @@ -5,7 +5,8 @@ use super::dto_db_connection_data::DTOConnectionData; #[derive(Clone, Deserialize)] pub struct DTODBService { pub name: String, - pub password: String, pub owner: String, + pub protected: bool, + pub password: String, pub connection_data: DTOConnectionData } \ No newline at end of file diff --git a/src/infrastructure/handler.rs b/src/infrastructure/handler.rs new file mode 100644 index 0000000..bf46c1f --- /dev/null +++ b/src/infrastructure/handler.rs @@ -0,0 +1,40 @@ +use axum::{extract::{Path, Request}, http::{HeaderMap, StatusCode}, middleware::Next, response::{IntoResponse, Response}}; +use rust_db_manager_core::commons::configuration::configuration::Configuration; + +use crate::commons::{configuration::web_configuration::WebConfiguration, exception::api_exception::ApiException}; + +use super::services_jwt::ServicesJWT; + +pub(crate) async fn autentication_handler(headers: HeaderMap, Path(service): Path, request: Request, next: Next) -> Result { + let o_service = Configuration::find_service(service); + if o_service.is_none() { + let exception = ApiException::new(StatusCode::NOT_FOUND.as_u16(), String::from("Service not found.")); + return Err(exception.into_response()); + } + + let service = o_service.unwrap(); + if service.is_protected() { + let o_token = headers.get(String::from(WebConfiguration::COOKIE_NAME)); + if o_token.is_none() { + let error = ApiException::new(StatusCode::UNAUTHORIZED.as_u16(), String::from("Token not found")); + return Err(error.into_response()); + } + + let token = o_token.unwrap().to_str().unwrap().to_owned(); + + let result = ServicesJWT::verify(token.clone()); + if result.is_err() { + return Err(result.unwrap_err().into_response()); + } + + let services = result.unwrap(); + + let authorized = services.iter().find(|s| s.is_same(service.clone())); + if authorized.is_none() { + let error = ApiException::new(StatusCode::UNAUTHORIZED.as_u16(), String::from("Token not found")); + return Err(error.into_response()); + } + } + + return Ok(next.run(request).await); +} \ No newline at end of file diff --git a/src/infrastructure/services_jwt.rs b/src/infrastructure/services_jwt.rs index f9f2612..97ac6e5 100644 --- a/src/infrastructure/services_jwt.rs +++ b/src/infrastructure/services_jwt.rs @@ -60,10 +60,12 @@ impl ServicesJWT { ServicesJWT::sign_services(services) } - pub fn verify(token: String) -> Result<(), ApiException> { - let salt = ServicesJWT::find_services(token.clone())?.iter() + pub fn verify(token: String) -> Result, ApiException> { + let services = ServicesJWT::find_services(token.clone())?; + let salt = services.iter() .map(|s| s.salt()) - .collect::>().join("#"); + .collect::>() + .join("#"); let key: Result, hmac::digest::InvalidLength> = Hmac::new_from_slice(salt.as_bytes()); if key.is_err() { @@ -77,7 +79,7 @@ impl ServicesJWT { return Err(exception); } - Ok(()) + Ok(services) } fn find_services(token: String) -> Result, ApiException> { diff --git a/src/lib.rs b/src/lib.rs index 7707a03..ce6a579 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,6 +28,7 @@ pub mod infrastructure { } pub mod controller; pub mod db_assets; + pub mod handler; pub mod pagination; pub mod services_jwt; pub mod utils; From ca87cc823ae6a7d84bcae3258ef201d9f08e6320 Mon Sep 17 00:00:00 2001 From: Rafael Ruiz Date: Thu, 2 May 2024 17:25:26 +0200 Subject: [PATCH 04/22] [FEATURE] Cookie managment updated: - Minor code optimizations. --- Cargo.lock | 64 ++++++++++- Cargo.toml | 1 + .../configuration/web_configuration.rs | 2 +- src/domain/builder_db_connection_data.rs | 2 +- src/domain/cookie/builder_cookie.rs | 70 ++++++++++++ src/domain/cookie/builder_jar.rs | 73 +++++++++++++ src/domain/cookie/cookie.rs | 66 ++++++++++++ src/domain/cookie/jar.rs | 26 +++++ src/domain/cookie/same_site.rs | 29 +++++ src/domain/utils.rs | 15 --- src/infrastructure/controller.rs | 101 +++++++++++++----- src/infrastructure/db_assets.rs | 2 +- .../dto/db_service/dto_db_service_lite.rs | 4 +- .../db_service/dto_db_service_web_category.rs | 2 +- src/infrastructure/handler.rs | 19 ++-- src/infrastructure/services_jwt.rs | 31 ++++-- src/infrastructure/utils.rs | 56 +++++++++- src/lib.rs | 7 ++ src/main.rs | 4 +- 19 files changed, 505 insertions(+), 69 deletions(-) create mode 100644 src/domain/cookie/builder_cookie.rs create mode 100644 src/domain/cookie/builder_jar.rs create mode 100644 src/domain/cookie/cookie.rs create mode 100644 src/domain/cookie/jar.rs create mode 100644 src/domain/cookie/same_site.rs delete mode 100644 src/domain/utils.rs diff --git a/Cargo.lock b/Cargo.lock index 6372db5..bb2eef7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -30,6 +30,15 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "android-tzdata" version = "0.1.1" @@ -1178,6 +1187,35 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "regex" +version = "1.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" + [[package]] name = "resolv-conf" version = "0.7.0" @@ -1215,6 +1253,7 @@ dependencies = [ "hmac", "jwt", "lazy_static", + "regex", "rust_db_manager_core", "serde", "serde_json", @@ -1227,7 +1266,7 @@ dependencies = [ [[package]] name = "rust_db_manager_core" version = "0.1.1" -source = "git+https://github.com/Rafael24595/rust-db-manager.git?branch=dev#c3332de183f5fc9eb20d1f1c102de4a8c9a185c1" +source = "git+https://github.com/Rafael24595/rust-db-manager.git?branch=dev#45053f5f44d616b1c25e9a9a6f0face43fd7a4d9" dependencies = [ "argon2", "async-trait", @@ -1237,6 +1276,7 @@ dependencies = [ "lazy_static", "mongodb", "serde_json", + "strum", "tokio", "uuid", ] @@ -1553,6 +1593,28 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strum" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a3417fc93d76740d974a01654a09777cb500428cc874ca9f45edfe0c4d4cd18" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.58", +] + [[package]] name = "subtle" version = "2.5.0" diff --git a/Cargo.toml b/Cargo.toml index 3ce50e3..43ad76a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,4 +20,5 @@ base64-compat = "1.0.0" hmac = "0.12.1" sha2 = "0.10.8" jwt = "0.16.0" +regex = "1.10.4" rust_db_manager_core = { git = "https://github.com/Rafael24595/rust-db-manager.git", branch = "dev" } \ No newline at end of file diff --git a/src/commons/configuration/web_configuration.rs b/src/commons/configuration/web_configuration.rs index 5aa4513..a0c0f57 100644 --- a/src/commons/configuration/web_configuration.rs +++ b/src/commons/configuration/web_configuration.rs @@ -18,7 +18,7 @@ pub struct WebConfiguration { impl WebConfiguration { - pub const COOKIE_NAME: &'static str = "db-token"; + pub const COOKIE_NAME: &'static str = "DB_TOKEN"; pub fn initialize() -> WebConfiguration { let _ = Configuration::initialize(); diff --git a/src/domain/builder_db_connection_data.rs b/src/domain/builder_db_connection_data.rs index 350d14b..ef88232 100644 --- a/src/domain/builder_db_connection_data.rs +++ b/src/domain/builder_db_connection_data.rs @@ -8,7 +8,7 @@ pub struct BuilderConnectionData { impl BuilderConnectionData { pub fn make(dto: DTOConnectionData) -> Result { - let category = EDBRepository::from_string(dto.category.clone()); + let category = EDBRepository::from_string(&dto.category); if let None = category { let message = format!("Data base type '{}' not supported.", dto.category); return Err(ApiException::new(404, message)); diff --git a/src/domain/cookie/builder_cookie.rs b/src/domain/cookie/builder_cookie.rs new file mode 100644 index 0000000..65c40a1 --- /dev/null +++ b/src/domain/cookie/builder_cookie.rs @@ -0,0 +1,70 @@ +use crate::commons::exception::api_exception::ApiException; + +use super::{cookie::Cookie, same_site::SameSite}; + +pub(crate) struct BuilderCookie { +} + +impl BuilderCookie { + + pub(crate) fn make(cookie_string: &str) -> Result { + let mut parts: Vec<&str> = cookie_string.split(';').collect(); + + let code_value = parts.remove(0).trim() + .split('=') + .map(|f| String::from(f.trim())) + .collect::>(); + if code_value.len() != 2 { + let message = String::from("Invalid cookie format"); + return Err(ApiException::new(301, message)); + } + + let code = code_value.get(0).cloned().unwrap(); + let value = code_value.get(1).cloned().unwrap(); + + let mut cookie = Cookie::new(code, value); + + for part in parts { + let key_value: Vec = part.trim() + .split('=') + .map(|f| String::from(f.trim())) + .collect::>(); + + let key = &key_value[0]; + let value = key_value.get(1).cloned(); + + match key.to_lowercase().as_str() { + "secure" => cookie.secure = Some(true), + "httponly" => cookie.http_only = Some(true), + "expires" => cookie.expiration = Some(value.unwrap_or_default()), + "domain" => cookie.domain = Some(value.unwrap_or_default()), + "path" => cookie.path = Some(value.unwrap_or_default()), + "max-age" => cookie.max_age = { + let string_max_age = value.unwrap_or_default(); + let max_age: Result = string_max_age.parse(); + if max_age.is_err() { + let exception = ApiException::new(301, max_age.unwrap_err().to_string()); + return Err(exception); + } + Some(max_age.unwrap()) + }, + "samesite" => cookie.same_site = { + let string_samesite= value.unwrap_or_default(); + let samesite = SameSite::from_string(&string_samesite.clone()); + if samesite.is_none() { + let message = String::from(format!("Unknown Same Site value: '{}'", string_samesite)); + return Err(ApiException::new(301, message)); + } + Some(samesite.unwrap()) + }, + _ => { + let message = String::from(format!("Unknown field code: '{}'", key)); + return Err(ApiException::new(301, message)); + } + } + } + + Ok(cookie) + } + +} \ No newline at end of file diff --git a/src/domain/cookie/builder_jar.rs b/src/domain/cookie/builder_jar.rs new file mode 100644 index 0000000..dd73690 --- /dev/null +++ b/src/domain/cookie/builder_jar.rs @@ -0,0 +1,73 @@ +use regex::Regex; + +use crate::commons::exception::api_exception::ApiException; + +use super::{cookie::Cookie, jar::Jar}; + +pub(crate) struct BuilderJar { + last: usize, + buffer: Vec, + jar_string: String, + jar: Jar +} + +impl BuilderJar { + + pub(crate) fn make(jar_string: String) -> Result { + let mut instance = Self { + last: 0, + buffer: Vec::new(), + jar_string: jar_string, + jar: Jar::new() + }; + instance._make() + } + + fn _make(&mut self) -> Result { + let pattern = Regex::new(r";\s?[\w|.|-]+=").unwrap(); + + for capture in pattern.captures_iter(&self.jar_string.clone()) { + if let Some(index) = capture.get(0).map(|m| m.start()) { + self.manage_cookie(index)?; + self.last = index + 1; + } + } + + self.manage_cookie(self.jar_string.len())?; + + if !self.buffer.is_empty() { + self.flush_buffer()?; + } + + Ok(self.jar.clone()) + } + + fn manage_cookie(&mut self, index: usize) -> Result<(), ApiException> { + let jar_string = self.jar_string.clone(); + let fragment = jar_string[self.last..index].trim(); + println!("{}", self.buffer.join("; ")); + if self.last > 0 && Self::is_cookie_definition(fragment) { + self.flush_buffer()?; + } + + self.buffer.push(fragment.to_string()); + + Ok(()) + } + + fn flush_buffer(&mut self) -> Result<(), ApiException> { + self.jar.cookies.push(Cookie::from_string(&self.buffer.join("; "))?); + self.buffer.clear(); + Ok(()) + } + + fn is_cookie_definition(fragment: &str) -> bool { + let f = fragment.to_lowercase(); + + !f.starts_with("domain") && !f.starts_with("path") + && !f.starts_with("expires") && !f.starts_with("max-age") + && !f.starts_with("samesite") && !f.starts_with("secure") + && !f.starts_with("httponly") + } + +} \ No newline at end of file diff --git a/src/domain/cookie/cookie.rs b/src/domain/cookie/cookie.rs new file mode 100644 index 0000000..c4579c5 --- /dev/null +++ b/src/domain/cookie/cookie.rs @@ -0,0 +1,66 @@ +use crate::commons::exception::api_exception::ApiException; + +use super::{builder_cookie::BuilderCookie, same_site::SameSite}; + +#[derive(Debug, Clone)] +pub struct Cookie { + pub code: String, + pub value: String, + pub domain: Option, + pub path: Option, + pub expiration: Option, + pub max_age: Option, + pub secure: Option, + pub http_only: Option, + pub same_site: Option, +} + +impl Cookie { + + pub fn new(code: String, value: String) -> Self { + Cookie { + code: code, value: value, domain: None, + path: None, expiration: None, max_age: None, + secure: None, http_only: None, same_site: None + } + } + + pub fn to_string(&self) -> String { + let mut cookie_string = format!("{}", self.value); + + if let Some(domain) = &self.domain { + cookie_string.push_str(&format!("; Domain={}", domain)); + } + + if let Some(path) = &self.path { + cookie_string.push_str(&format!("; Path={}", path)); + } + + if let Some(expiration) = &self.expiration { + cookie_string.push_str(&format!("; Expires={}", expiration)); + } + + if let Some(max_age) = self.max_age { + cookie_string.push_str(&format!("; Max-Age={}", max_age)); + } + + if let Some(true) = self.secure { + cookie_string.push_str("; Secure"); + } + + if let Some(true) = self.http_only { + cookie_string.push_str("; HttpOnly"); + } + + if let Some(same_site) = &self.same_site { + cookie_string.push_str(&format!("; SameSite={}", same_site)); + } + + cookie_string + } + + pub fn from_string(cookie_string: &str) -> Result { + BuilderCookie::make(cookie_string) + } + +} \ No newline at end of file diff --git a/src/domain/cookie/jar.rs b/src/domain/cookie/jar.rs new file mode 100644 index 0000000..802d1fc --- /dev/null +++ b/src/domain/cookie/jar.rs @@ -0,0 +1,26 @@ +use crate::commons::exception::api_exception::ApiException; + +use super::{builder_jar::BuilderJar, cookie::Cookie}; + +#[derive(Debug, Clone)] +pub struct Jar { + pub(crate) cookies: Vec +} + +impl Jar { + + pub fn new() -> Self { + Jar { + cookies: Vec::new() + } + } + + pub fn from_string(jar_string: &str) -> Result { + BuilderJar::make(jar_string.to_string()) + } + + pub fn find(&self, code: &str) -> Option { + self.cookies.iter().find(|c| c.code == code).cloned() + } + +} \ No newline at end of file diff --git a/src/domain/cookie/same_site.rs b/src/domain/cookie/same_site.rs new file mode 100644 index 0000000..7553f03 --- /dev/null +++ b/src/domain/cookie/same_site.rs @@ -0,0 +1,29 @@ +use std::fmt::{Display, Formatter, Result}; + +#[derive(Debug, Clone)] +pub enum SameSite { + Strict, + Lax, + None +} + +impl Display for SameSite { + + fn fmt(&self, f: &mut Formatter) -> Result { + write!(f, "{:?}", self) + } + +} + +impl SameSite { + + pub fn from_string(category: &str) -> Option { + match category.to_lowercase().as_str() { + "strict" => Some(SameSite::Strict), + "lax" => Some(SameSite::Lax), + "none" => Some(SameSite::None), + _ => None, + } + } + +} \ No newline at end of file diff --git a/src/domain/utils.rs b/src/domain/utils.rs deleted file mode 100644 index 1fde8de..0000000 --- a/src/domain/utils.rs +++ /dev/null @@ -1,15 +0,0 @@ -use rust_db_manager_core::infrastructure::db_service::DBService; - -use crate::infrastructure::dto::db_service::dto_db_service::DTODBService; - -pub trait DTOBuilder { - fn from_dto(dto: K) -> T; -} - -impl DTOBuilder for DBService { - - fn from_dto(dto: DTODBService) -> DBService { - - } - -} \ No newline at end of file diff --git a/src/infrastructure/controller.rs b/src/infrastructure/controller.rs index fcf5372..b0173c7 100644 --- a/src/infrastructure/controller.rs +++ b/src/infrastructure/controller.rs @@ -1,10 +1,10 @@ -use axum::{body::Body, extract::{Path, Query}, http::{HeaderMap, Response, StatusCode}, middleware, response::IntoResponse, routing::{delete, get, post}, Json, Router}; +use axum::{body::Body, extract::{Path, Query}, http::{header::SET_COOKIE, HeaderMap, Response, StatusCode}, middleware, response::IntoResponse, routing::{delete, get, post}, Json, Router}; use rust_db_manager_core::{commons::configuration::configuration::Configuration, infrastructure::{db_service::DBService, repository::e_db_repository::EDBRepository}}; use crate::{commons::{configuration::web_configuration::WebConfiguration, exception::api_exception::ApiException}, domain::builder_db_service::BuilderDBService}; -use super::{db_assets::WebEDBRepository, dto::{db_service::{dto_db_service::DTODBService, dto_db_service_lite::DTODBServiceLite, dto_db_service_suscribe::DTODBServiceSuscribe, dto_db_service_web_category::DTODBServiceWebCategory}, dto_server_status::DTOServerStatus, pagination::{dto_paginated_collection::DTOPaginatedCollection, dto_query_pagination::DTOQueryPagination}}, handler, pagination::Pagination, services_jwt::ServicesJWT}; +use super::{db_assets::WebEDBRepository, dto::{db_service::{dto_db_service::DTODBService, dto_db_service_lite::DTODBServiceLite, dto_db_service_suscribe::DTODBServiceSuscribe, dto_db_service_web_category::DTODBServiceWebCategory}, dto_server_status::DTOServerStatus, pagination::{dto_paginated_collection::DTOPaginatedCollection, dto_query_pagination::DTOQueryPagination}}, handler, pagination::Pagination, services_jwt::ServicesJWT, utils::find_token}; pub struct Controller{ } @@ -46,16 +46,16 @@ impl Controller { return Err(error.into_response()); } - let service = o_service.unwrap(); + let service = &o_service.unwrap(); - let r_token = Controller::make_token(headers, service.clone()); + let r_token = Controller::make_token(headers, service); if r_token.is_err() { return Err(r_token.unwrap_err().into_response()); } let token = r_token.unwrap(); - let db_service = Configuration::push_service(service.clone()); + let db_service = Configuration::push_service(service); if let Err(error) = db_service { let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); return Err(exception.into_response()); @@ -63,12 +63,12 @@ impl Controller { let mut builder = Response::builder(); if token.is_some() { - let cookie = format!("{}={}; Path=/; Secure; HttpOnly; SameSite=Lax", WebConfiguration::COOKIE_NAME, token.unwrap()); - builder = builder.header("Set-Cookie", cookie); + let cookie = format!("{}={}; Path=/;", WebConfiguration::COOKIE_NAME, token.unwrap()); + builder = builder.header(SET_COOKIE, cookie); } let response = builder - .status(StatusCode::ACCEPTED) + .status(StatusCode::OK) .body(Body::empty()) .unwrap(); @@ -82,14 +82,14 @@ impl Controller { return Err(exception.into_response()); } - let service = o_service.unwrap(); + let service = &o_service.unwrap(); if service.is_authorized(dto.password).is_err() { let exception = ApiException::new(StatusCode::UNAUTHORIZED.as_u16(), String::from("Authentication error.")); return Err(exception.into_response()); } - let r_token = Controller::make_token(headers, service.clone()); + let r_token = Controller::make_token(headers, service); if r_token.is_err() { return Err(r_token.unwrap_err().into_response()); } @@ -98,12 +98,12 @@ impl Controller { let mut builder = Response::builder(); if token.is_some() { - let cookie = format!("{}={}; Path=/; Secure; HttpOnly; SameSite=Lax", WebConfiguration::COOKIE_NAME, token.unwrap()); - builder = builder.header("Set-Cookie", cookie); + let cookie = format!("{}={}; Path=/; HttpOnly", WebConfiguration::COOKIE_NAME, token.unwrap()); + builder = builder.header(SET_COOKIE, cookie); } let response = builder - .status(StatusCode::ACCEPTED) + .status(StatusCode::OK) .body(Body::empty()) .unwrap(); @@ -131,26 +131,51 @@ impl Controller { Ok((StatusCode::ACCEPTED, String::from("Service up."))) } - async fn service_remove(Path(service): Path) -> Result { - let db_service = Configuration::find_service(service); - if db_service.is_none() { + async fn service_remove(headers: HeaderMap, Path(service): Path) -> impl IntoResponse { + let o_db_service = Configuration::find_service(service); + if o_db_service.is_none() { return Err(Controller::not_found()); } - Configuration::remove_service(db_service.unwrap()); - - Ok(StatusCode::ACCEPTED) + let db_service = o_db_service.unwrap(); + + let r_token = Controller::remove_token(headers, &db_service); + if r_token.is_err() { + return Err(r_token.unwrap_err().into_response()); + } + + let token = r_token.unwrap(); + + Configuration::remove_service(db_service); + + let mut builder = Response::builder(); + if token.is_some() { + let cookie = format!("{}={}; Path=/; HttpOnly", WebConfiguration::COOKIE_NAME, token.unwrap()); + builder = builder.header(SET_COOKIE, cookie); + } + + let response = builder + .status(StatusCode::OK) + .body(Body::empty()) + .unwrap(); + + Ok(response) } - fn make_token(headers: HeaderMap, service: DBService) -> Result, ApiException> { - match headers.get(WebConfiguration::COOKIE_NAME) { - Some(header) => { - let token = header.to_str().unwrap().to_owned(); + fn make_token(headers: HeaderMap, service: &DBService) -> Result, ApiException> { + let o_token = find_token(headers); + if o_token.is_err() { + return Err(o_token.unwrap_err()); + } + + match o_token.unwrap() { + Some(token) => { if !service.is_protected() { - return Ok(Some(token)); + let result = Some(token); + return Ok(result); } - let result = ServicesJWT::update(token, service.clone()); + let result = ServicesJWT::update(&token, service); if result.is_err() { return Err(result.unwrap_err()); } @@ -162,7 +187,7 @@ impl Controller { return Ok(None); } - let result = ServicesJWT::sign(service.clone()); + let result = ServicesJWT::sign(service); if result.is_err() { return Err(result.unwrap_err()); } @@ -171,6 +196,30 @@ impl Controller { }, } } + + fn remove_token(headers: HeaderMap, service: &DBService) -> Result, ApiException> { + let o_token = find_token(headers); + if o_token.is_err() { + return Err(o_token.unwrap_err()); + } + + match o_token.unwrap() { + Some(token) => { + if !service.is_protected() { + let result = Some(token); + return Ok(result); + } + + let result = ServicesJWT::remove(&token, service); + if result.is_err() { + return Err(result.unwrap_err()); + } + + Ok(Some(result.unwrap())) + }, + None => return Ok(None), + } + } fn not_found() -> Response { let error = ApiException::new( diff --git a/src/infrastructure/db_assets.rs b/src/infrastructure/db_assets.rs index e6303bf..5afbc0c 100644 --- a/src/infrastructure/db_assets.rs +++ b/src/infrastructure/db_assets.rs @@ -13,7 +13,7 @@ impl WebEDBRepository for EDBRepository { fn supported() -> Vec { EDBRepository::items().iter() - .map(|e| DTODBServiceWebCategory::from(e.clone())) + .map(|e| DTODBServiceWebCategory::from(e)) .collect() } diff --git a/src/infrastructure/dto/db_service/dto_db_service_lite.rs b/src/infrastructure/dto/db_service/dto_db_service_lite.rs index d637c92..81d7e5f 100644 --- a/src/infrastructure/dto/db_service/dto_db_service_lite.rs +++ b/src/infrastructure/dto/db_service/dto_db_service_lite.rs @@ -12,7 +12,9 @@ pub struct DTODBServiceLite { impl DTODBServiceLite { pub fn from_vec(collection: Vec) -> Vec { - collection.iter().map(|s| DTODBServiceLite{name: s.name(), category: DTODBServiceWebCategory::from(s.category())}).collect() + collection.iter() + .map(|s| DTODBServiceLite{name: s.name(), category: DTODBServiceWebCategory::from(&s.category())}) + .collect() } } \ No newline at end of file diff --git a/src/infrastructure/dto/db_service/dto_db_service_web_category.rs b/src/infrastructure/dto/db_service/dto_db_service_web_category.rs index dc5fd54..e5b7bd2 100644 --- a/src/infrastructure/dto/db_service/dto_db_service_web_category.rs +++ b/src/infrastructure/dto/db_service/dto_db_service_web_category.rs @@ -12,7 +12,7 @@ pub struct DTODBServiceWebCategory { impl DTODBServiceWebCategory { - pub fn from(category: EDBRepository) -> DTODBServiceWebCategory { + pub fn from(category: &EDBRepository) -> DTODBServiceWebCategory { DTODBServiceWebCategory { category: category.to_string(), resources: category.resources() diff --git a/src/infrastructure/handler.rs b/src/infrastructure/handler.rs index bf46c1f..889c65e 100644 --- a/src/infrastructure/handler.rs +++ b/src/infrastructure/handler.rs @@ -1,9 +1,9 @@ use axum::{extract::{Path, Request}, http::{HeaderMap, StatusCode}, middleware::Next, response::{IntoResponse, Response}}; use rust_db_manager_core::commons::configuration::configuration::Configuration; -use crate::commons::{configuration::web_configuration::WebConfiguration, exception::api_exception::ApiException}; +use crate::commons::exception::api_exception::ApiException; -use super::services_jwt::ServicesJWT; +use super::{services_jwt::ServicesJWT, utils::find_token}; pub(crate) async fn autentication_handler(headers: HeaderMap, Path(service): Path, request: Request, next: Next) -> Result { let o_service = Configuration::find_service(service); @@ -14,15 +14,18 @@ pub(crate) async fn autentication_handler(headers: HeaderMap, Path(service): Pat let service = o_service.unwrap(); if service.is_protected() { - let o_token = headers.get(String::from(WebConfiguration::COOKIE_NAME)); - if o_token.is_none() { - let error = ApiException::new(StatusCode::UNAUTHORIZED.as_u16(), String::from("Token not found")); - return Err(error.into_response()); + let o_token = find_token(headers); + if o_token.is_err() { + return Err(o_token.unwrap_err().into_response()); } - let token = o_token.unwrap().to_str().unwrap().to_owned(); + let token = o_token.unwrap(); + if token.is_none() { + let exception = ApiException::new(StatusCode::UNAUTHORIZED.as_u16(), String::from("Token not found")); + return Err(exception.into_response()); + } - let result = ServicesJWT::verify(token.clone()); + let result = ServicesJWT::verify(&token.unwrap()); if result.is_err() { return Err(result.unwrap_err().into_response()); } diff --git a/src/infrastructure/services_jwt.rs b/src/infrastructure/services_jwt.rs index 97ac6e5..2727891 100644 --- a/src/infrastructure/services_jwt.rs +++ b/src/infrastructure/services_jwt.rs @@ -13,8 +13,8 @@ pub struct ServicesJWT { impl ServicesJWT { - pub fn sign(service: DBService) -> Result { - ServicesJWT::sign_services(Vec::from(vec![service])) + pub fn sign(service: &DBService) -> Result { + ServicesJWT::sign_services(Vec::from(vec![service.clone()])) } pub fn sign_services(services: Vec) -> Result { @@ -46,22 +46,33 @@ impl ServicesJWT { Ok(token_str.unwrap()) } - pub fn update(token: String, service: DBService) -> Result { - let _ = ServicesJWT::verify(token.clone())?; + pub fn update(token: &str, service: &DBService) -> Result { + let _ = ServicesJWT::verify(token)?; - let mut services = ServicesJWT::find_services(token.clone())?; + let mut services = ServicesJWT::find_services(token)?; if services.iter().find(|s| s.name() == service.name()).is_some() { let exception = ApiException::new(500, String::from("This token is already subscribed to the service.")); return Err(exception); } - services.push(service); + services.push(service.clone()); ServicesJWT::sign_services(services) } - pub fn verify(token: String) -> Result, ApiException> { - let services = ServicesJWT::find_services(token.clone())?; + pub fn remove(token: &str, service: &DBService) -> Result { + let _ = ServicesJWT::verify(token)?; + + let mut services = ServicesJWT::find_services(token)?; + if let Some(position) = services.iter().position(|s| s.name() == service.name()) { + services.remove(position); + } + + ServicesJWT::sign_services(services) + } + + pub fn verify(token: &str) -> Result, ApiException> { + let services = ServicesJWT::find_services(token)?; let salt = services.iter() .map(|s| s.salt()) .collect::>() @@ -82,7 +93,7 @@ impl ServicesJWT { Ok(services) } - fn find_services(token: String) -> Result, ApiException> { + fn find_services(token: &str) -> Result, ApiException> { let fragments = token.split(".").collect::>(); if fragments.len() != 3 { let exception = ApiException::new(401, String::from("Invalid token.")); @@ -118,7 +129,7 @@ impl ServicesJWT { let mut collection = Vec::new(); - for s_service in v_services.unwrap().split("-").collect::>() { + for s_service in v_services.unwrap().split("-").filter(|s| !s.is_empty()).collect::>() { let service = Configuration::find_service(String::from(s_service)); if service.is_none() { let exception = ApiException::new(500, String::from("Unknown service.")); diff --git a/src/infrastructure/utils.rs b/src/infrastructure/utils.rs index 087845a..f7717b8 100644 --- a/src/infrastructure/utils.rs +++ b/src/infrastructure/utils.rs @@ -1,6 +1,6 @@ -use axum::{body::Body, http::Response, response::IntoResponse}; +use axum::{body::Body, http::{header::COOKIE, HeaderMap, Response, StatusCode}, response::IntoResponse}; -use crate::commons::exception::api_exception::ApiException; +use crate::{commons::{configuration::web_configuration::WebConfiguration, exception::api_exception::ApiException}, domain::cookie::jar::Jar}; impl IntoResponse for ApiException { @@ -11,4 +11,56 @@ impl IntoResponse for ApiException { .unwrap() } +} + +pub(crate) fn find_token(headers: HeaderMap) -> Result, ApiException> { + let o_cookies = headers.get(COOKIE); + if o_cookies.is_none() { + return Ok(None); + } + + let cookies = o_cookies.unwrap().to_str(); + if cookies.is_err() { + let exception = ApiException::new(StatusCode::UNAUTHORIZED.as_u16(), String::from("Token has non valid format")); + return Err(exception); + } + + let jar = Jar::from_string(cookies.unwrap()); + if jar.is_err() { + return Err(jar.unwrap_err()); + } + + let o_cookie = jar.unwrap().find(WebConfiguration::COOKIE_NAME); + if o_cookie.is_none() { + return Ok(None); + } + + Ok(Some(o_cookie.unwrap().value)) +} + +pub(crate) fn find_token_strict(headers: HeaderMap) -> Result { + let o_cookies = headers.get(COOKIE); + if o_cookies.is_none() { + let error = ApiException::new(StatusCode::UNAUTHORIZED.as_u16(), String::from("Cookies not found")); + return Err(error); + } + + let cookies = o_cookies.unwrap().to_str(); + if cookies.is_err() { + let exception = ApiException::new(StatusCode::UNAUTHORIZED.as_u16(), String::from("Token has non valid format")); + return Err(exception); + } + + let jar = Jar::from_string(cookies.unwrap()); + if jar.is_err() { + return Err(jar.unwrap_err()); + } + + let o_cookie = jar.unwrap().find(WebConfiguration::COOKIE_NAME); + if o_cookie.is_none() { + let exception = ApiException::new(StatusCode::UNAUTHORIZED.as_u16(), String::from("Token not found")); + return Err(exception); + } + + Ok(o_cookie.unwrap().value) } \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index ce6a579..1395862 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,13 @@ pub mod commons { pub mod domain { pub mod builder_db_connection_data; pub mod builder_db_service; + pub mod cookie { + pub mod builder_cookie; + pub mod builder_jar; + pub mod cookie; + pub mod jar; + pub mod same_site; + } } pub mod infrastructure { pub mod dto { diff --git a/src/main.rs b/src/main.rs index c7cc543..f7790a2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,10 +8,10 @@ use rust_db_manager_api::{commons::configuration::web_configuration::WebConfigur #[tokio::main] async fn main() { let _ = WebConfiguration::initialize(); - + let router = Router::new(); let app = Controller::route(router) - .layer(CorsLayer::permissive()) + .layer(CorsLayer::very_permissive()) .into_make_service_with_connect_info::(); let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); From 99c54aa907813cca167f91d946b96cae9f975b6f Mon Sep 17 00:00:00 2001 From: Rafael Ruiz Date: Thu, 2 May 2024 17:37:22 +0200 Subject: [PATCH 05/22] [FEATURE] AuthException struct created. --- src/commons/exception/api_exception.rs | 2 - src/commons/exception/auth_exception.rs | 45 ++++++++++++++++++++++ src/infrastructure/services_jwt.rs | 4 ++ src/infrastructure/utils.rs | 50 +++++++++++-------------- src/lib.rs | 1 + 5 files changed, 71 insertions(+), 31 deletions(-) create mode 100644 src/commons/exception/auth_exception.rs diff --git a/src/commons/exception/api_exception.rs b/src/commons/exception/api_exception.rs index 64dbf18..00df042 100644 --- a/src/commons/exception/api_exception.rs +++ b/src/commons/exception/api_exception.rs @@ -3,8 +3,6 @@ use std::error::Error; use rust_db_manager_core::commons::exception::connect_exception::ConnectException; -pub(crate) const EXCEPTION_HEADER: &str = "Error-Code"; - #[derive(Debug, Clone)] pub struct ApiException { status: u16, diff --git a/src/commons/exception/auth_exception.rs b/src/commons/exception/auth_exception.rs new file mode 100644 index 0000000..d4e5a46 --- /dev/null +++ b/src/commons/exception/auth_exception.rs @@ -0,0 +1,45 @@ +use std::fmt; +use std::error::Error; + +use super::api_exception::ApiException; + +#[derive(Debug, Clone)] +pub struct AuthException { + status: u16, + message: String, +} + +impl fmt::Display for AuthException { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "AuthException: {}", self.message) + } +} + +impl Error for AuthException {} + + +impl AuthException { + + pub fn from(status: u16, exception: ApiException) -> AuthException { + AuthException { + status: status, + message: exception.message() + } + } + + pub fn new(status: u16, message: String) -> AuthException { + AuthException { + status, + message + } + } + + pub fn status(&self) -> u16 { + return self.status; + } + + pub fn message(&self) -> String { + return self.message.clone(); + } + +} \ No newline at end of file diff --git a/src/infrastructure/services_jwt.rs b/src/infrastructure/services_jwt.rs index 2727891..229c0fa 100644 --- a/src/infrastructure/services_jwt.rs +++ b/src/infrastructure/services_jwt.rs @@ -17,6 +17,10 @@ impl ServicesJWT { ServicesJWT::sign_services(Vec::from(vec![service.clone()])) } + pub fn sign_empty() -> Result { + ServicesJWT::sign_services(Vec::new()) + } + pub fn sign_services(services: Vec) -> Result { let s_key = services.iter() .map(|s| s.salt()) diff --git a/src/infrastructure/utils.rs b/src/infrastructure/utils.rs index f7717b8..f3dc4f2 100644 --- a/src/infrastructure/utils.rs +++ b/src/infrastructure/utils.rs @@ -1,11 +1,19 @@ -use axum::{body::Body, http::{header::COOKIE, HeaderMap, Response, StatusCode}, response::IntoResponse}; +use axum::{body::Body, http::{header::{COOKIE, SET_COOKIE}, HeaderMap, Response, StatusCode}, response::IntoResponse}; -use crate::{commons::{configuration::web_configuration::WebConfiguration, exception::api_exception::ApiException}, domain::cookie::jar::Jar}; +use crate::{commons::{configuration::web_configuration::WebConfiguration, exception::{api_exception::ApiException, auth_exception::AuthException}}, domain::cookie::jar::Jar}; + +use super::services_jwt::ServicesJWT; impl IntoResponse for ApiException { fn into_response(self) -> Response { - Response::builder() + let mut builder = Response::builder(); + if let Ok(token) = ServicesJWT::sign_empty() { + let cookie = format!("{}={}; Path=/; HttpOnly", WebConfiguration::COOKIE_NAME, token); + builder = builder.header(SET_COOKIE, cookie); + } + + builder .status(self.status()) .body(Body::from(self.message())) .unwrap() @@ -13,36 +21,21 @@ impl IntoResponse for ApiException { } -pub(crate) fn find_token(headers: HeaderMap) -> Result, ApiException> { - let o_cookies = headers.get(COOKIE); - if o_cookies.is_none() { - return Ok(None); - } - - let cookies = o_cookies.unwrap().to_str(); - if cookies.is_err() { - let exception = ApiException::new(StatusCode::UNAUTHORIZED.as_u16(), String::from("Token has non valid format")); - return Err(exception); - } - - let jar = Jar::from_string(cookies.unwrap()); - if jar.is_err() { - return Err(jar.unwrap_err()); - } +impl IntoResponse for AuthException { - let o_cookie = jar.unwrap().find(WebConfiguration::COOKIE_NAME); - if o_cookie.is_none() { - return Ok(None); + fn into_response(self) -> Response { + Response::builder() + .status(self.status()) + .body(Body::from(self.message())) + .unwrap() } - Ok(Some(o_cookie.unwrap().value)) } -pub(crate) fn find_token_strict(headers: HeaderMap) -> Result { +pub(crate) fn find_token(headers: HeaderMap) -> Result, ApiException> { let o_cookies = headers.get(COOKIE); if o_cookies.is_none() { - let error = ApiException::new(StatusCode::UNAUTHORIZED.as_u16(), String::from("Cookies not found")); - return Err(error); + return Ok(None); } let cookies = o_cookies.unwrap().to_str(); @@ -58,9 +51,8 @@ pub(crate) fn find_token_strict(headers: HeaderMap) -> Result Date: Thu, 2 May 2024 19:43:31 +0200 Subject: [PATCH 06/22] [FEATURE] Reset cookie control implemented. --- src/commons/exception/auth_exception.rs | 21 ++++++++++++-- src/domain/cookie/builder_cookie.rs | 12 ++++---- src/domain/cookie/builder_jar.rs | 10 +++---- src/domain/cookie/cookie.rs | 4 +-- src/domain/cookie/jar.rs | 4 +-- src/infrastructure/controller.rs | 6 ++-- src/infrastructure/services_jwt.rs | 38 ++++++++++++------------- src/infrastructure/utils.rs | 22 +++++++------- 8 files changed, 67 insertions(+), 50 deletions(-) diff --git a/src/commons/exception/auth_exception.rs b/src/commons/exception/auth_exception.rs index d4e5a46..c2db963 100644 --- a/src/commons/exception/auth_exception.rs +++ b/src/commons/exception/auth_exception.rs @@ -7,6 +7,7 @@ use super::api_exception::ApiException; pub struct AuthException { status: u16, message: String, + reset: bool, } impl fmt::Display for AuthException { @@ -20,17 +21,27 @@ impl Error for AuthException {} impl AuthException { - pub fn from(status: u16, exception: ApiException) -> AuthException { + pub fn from(status: u16, exception: ApiException, reset: bool) -> AuthException { AuthException { status: status, - message: exception.message() + message: exception.message(), + reset: reset } } pub fn new(status: u16, message: String) -> AuthException { AuthException { status, - message + message, + reset: false + } + } + + pub fn new_reset(status: u16, message: String) -> AuthException { + AuthException { + status, + message, + reset: true } } @@ -42,4 +53,8 @@ impl AuthException { return self.message.clone(); } + pub fn reset(&self) -> bool { + return self.reset; + } + } \ No newline at end of file diff --git a/src/domain/cookie/builder_cookie.rs b/src/domain/cookie/builder_cookie.rs index 65c40a1..d24d7a2 100644 --- a/src/domain/cookie/builder_cookie.rs +++ b/src/domain/cookie/builder_cookie.rs @@ -1,4 +1,4 @@ -use crate::commons::exception::api_exception::ApiException; +use crate::commons::exception::auth_exception::AuthException; use super::{cookie::Cookie, same_site::SameSite}; @@ -7,7 +7,7 @@ pub(crate) struct BuilderCookie { impl BuilderCookie { - pub(crate) fn make(cookie_string: &str) -> Result { + pub(crate) fn make(cookie_string: &str) -> Result { let mut parts: Vec<&str> = cookie_string.split(';').collect(); let code_value = parts.remove(0).trim() @@ -16,7 +16,7 @@ impl BuilderCookie { .collect::>(); if code_value.len() != 2 { let message = String::from("Invalid cookie format"); - return Err(ApiException::new(301, message)); + return Err(AuthException::new_reset(301, message)); } let code = code_value.get(0).cloned().unwrap(); @@ -43,7 +43,7 @@ impl BuilderCookie { let string_max_age = value.unwrap_or_default(); let max_age: Result = string_max_age.parse(); if max_age.is_err() { - let exception = ApiException::new(301, max_age.unwrap_err().to_string()); + let exception = AuthException::new_reset(301, max_age.unwrap_err().to_string()); return Err(exception); } Some(max_age.unwrap()) @@ -53,13 +53,13 @@ impl BuilderCookie { let samesite = SameSite::from_string(&string_samesite.clone()); if samesite.is_none() { let message = String::from(format!("Unknown Same Site value: '{}'", string_samesite)); - return Err(ApiException::new(301, message)); + return Err(AuthException::new_reset(301, message)); } Some(samesite.unwrap()) }, _ => { let message = String::from(format!("Unknown field code: '{}'", key)); - return Err(ApiException::new(301, message)); + return Err(AuthException::new_reset(301, message)); } } } diff --git a/src/domain/cookie/builder_jar.rs b/src/domain/cookie/builder_jar.rs index dd73690..c2294b1 100644 --- a/src/domain/cookie/builder_jar.rs +++ b/src/domain/cookie/builder_jar.rs @@ -1,6 +1,6 @@ use regex::Regex; -use crate::commons::exception::api_exception::ApiException; +use crate::commons::exception::auth_exception::AuthException; use super::{cookie::Cookie, jar::Jar}; @@ -13,7 +13,7 @@ pub(crate) struct BuilderJar { impl BuilderJar { - pub(crate) fn make(jar_string: String) -> Result { + pub(crate) fn make(jar_string: String) -> Result { let mut instance = Self { last: 0, buffer: Vec::new(), @@ -23,7 +23,7 @@ impl BuilderJar { instance._make() } - fn _make(&mut self) -> Result { + fn _make(&mut self) -> Result { let pattern = Regex::new(r";\s?[\w|.|-]+=").unwrap(); for capture in pattern.captures_iter(&self.jar_string.clone()) { @@ -42,7 +42,7 @@ impl BuilderJar { Ok(self.jar.clone()) } - fn manage_cookie(&mut self, index: usize) -> Result<(), ApiException> { + fn manage_cookie(&mut self, index: usize) -> Result<(), AuthException> { let jar_string = self.jar_string.clone(); let fragment = jar_string[self.last..index].trim(); println!("{}", self.buffer.join("; ")); @@ -55,7 +55,7 @@ impl BuilderJar { Ok(()) } - fn flush_buffer(&mut self) -> Result<(), ApiException> { + fn flush_buffer(&mut self) -> Result<(), AuthException> { self.jar.cookies.push(Cookie::from_string(&self.buffer.join("; "))?); self.buffer.clear(); Ok(()) diff --git a/src/domain/cookie/cookie.rs b/src/domain/cookie/cookie.rs index c4579c5..543a505 100644 --- a/src/domain/cookie/cookie.rs +++ b/src/domain/cookie/cookie.rs @@ -1,4 +1,4 @@ -use crate::commons::exception::api_exception::ApiException; +use crate::commons::exception::auth_exception::AuthException; use super::{builder_cookie::BuilderCookie, same_site::SameSite}; @@ -59,7 +59,7 @@ impl Cookie { cookie_string } - pub fn from_string(cookie_string: &str) -> Result { + pub fn from_string(cookie_string: &str) -> Result { BuilderCookie::make(cookie_string) } diff --git a/src/domain/cookie/jar.rs b/src/domain/cookie/jar.rs index 802d1fc..5b4c1cd 100644 --- a/src/domain/cookie/jar.rs +++ b/src/domain/cookie/jar.rs @@ -1,4 +1,4 @@ -use crate::commons::exception::api_exception::ApiException; +use crate::commons::exception::auth_exception::AuthException; use super::{builder_jar::BuilderJar, cookie::Cookie}; @@ -15,7 +15,7 @@ impl Jar { } } - pub fn from_string(jar_string: &str) -> Result { + pub fn from_string(jar_string: &str) -> Result { BuilderJar::make(jar_string.to_string()) } diff --git a/src/infrastructure/controller.rs b/src/infrastructure/controller.rs index b0173c7..db8efad 100644 --- a/src/infrastructure/controller.rs +++ b/src/infrastructure/controller.rs @@ -2,7 +2,7 @@ use axum::{body::Body, extract::{Path, Query}, http::{header::SET_COOKIE, Header use rust_db_manager_core::{commons::configuration::configuration::Configuration, infrastructure::{db_service::DBService, repository::e_db_repository::EDBRepository}}; -use crate::{commons::{configuration::web_configuration::WebConfiguration, exception::api_exception::ApiException}, domain::builder_db_service::BuilderDBService}; +use crate::{commons::{configuration::web_configuration::WebConfiguration, exception::{api_exception::ApiException, auth_exception::AuthException}}, domain::builder_db_service::BuilderDBService}; use super::{db_assets::WebEDBRepository, dto::{db_service::{dto_db_service::DTODBService, dto_db_service_lite::DTODBServiceLite, dto_db_service_suscribe::DTODBServiceSuscribe, dto_db_service_web_category::DTODBServiceWebCategory}, dto_server_status::DTOServerStatus, pagination::{dto_paginated_collection::DTOPaginatedCollection, dto_query_pagination::DTOQueryPagination}}, handler, pagination::Pagination, services_jwt::ServicesJWT, utils::find_token}; @@ -162,7 +162,7 @@ impl Controller { Ok(response) } - fn make_token(headers: HeaderMap, service: &DBService) -> Result, ApiException> { + fn make_token(headers: HeaderMap, service: &DBService) -> Result, AuthException> { let o_token = find_token(headers); if o_token.is_err() { return Err(o_token.unwrap_err()); @@ -197,7 +197,7 @@ impl Controller { } } - fn remove_token(headers: HeaderMap, service: &DBService) -> Result, ApiException> { + fn remove_token(headers: HeaderMap, service: &DBService) -> Result, AuthException> { let o_token = find_token(headers); if o_token.is_err() { return Err(o_token.unwrap_err()); diff --git a/src/infrastructure/services_jwt.rs b/src/infrastructure/services_jwt.rs index 229c0fa..e210d20 100644 --- a/src/infrastructure/services_jwt.rs +++ b/src/infrastructure/services_jwt.rs @@ -5,7 +5,7 @@ use std::collections::BTreeMap; use rust_db_manager_core::{commons::configuration::configuration::Configuration, infrastructure::db_service::DBService}; -use crate::commons::exception::api_exception::ApiException; +use crate::commons::exception::auth_exception::AuthException; pub struct ServicesJWT { @@ -13,15 +13,15 @@ pub struct ServicesJWT { impl ServicesJWT { - pub fn sign(service: &DBService) -> Result { + pub fn sign(service: &DBService) -> Result { ServicesJWT::sign_services(Vec::from(vec![service.clone()])) } - pub fn sign_empty() -> Result { + pub fn sign_empty() -> Result { ServicesJWT::sign_services(Vec::new()) } - pub fn sign_services(services: Vec) -> Result { + pub fn sign_services(services: Vec) -> Result { let s_key = services.iter() .map(|s| s.salt()) .collect::>() @@ -29,7 +29,7 @@ impl ServicesJWT { let key: Result, hmac::digest::InvalidLength> = Hmac::new_from_slice(s_key.as_bytes()); if key.is_err() { - let exception = ApiException::new(500, key.unwrap_err().to_string()); + let exception = AuthException::new_reset(500, key.unwrap_err().to_string()); return Err(exception); } @@ -43,19 +43,19 @@ impl ServicesJWT { let token_str = claims.sign_with_key(&key.unwrap()); if token_str.is_err() { - let exception = ApiException::new(500, token_str.unwrap_err().to_string()); + let exception = AuthException::new_reset(500, token_str.unwrap_err().to_string()); return Err(exception); } Ok(token_str.unwrap()) } - pub fn update(token: &str, service: &DBService) -> Result { + pub fn update(token: &str, service: &DBService) -> Result { let _ = ServicesJWT::verify(token)?; let mut services = ServicesJWT::find_services(token)?; if services.iter().find(|s| s.name() == service.name()).is_some() { - let exception = ApiException::new(500, String::from("This token is already subscribed to the service.")); + let exception = AuthException::new(500, String::from("This token is already subscribed to the service.")); return Err(exception); } @@ -64,7 +64,7 @@ impl ServicesJWT { ServicesJWT::sign_services(services) } - pub fn remove(token: &str, service: &DBService) -> Result { + pub fn remove(token: &str, service: &DBService) -> Result { let _ = ServicesJWT::verify(token)?; let mut services = ServicesJWT::find_services(token)?; @@ -75,7 +75,7 @@ impl ServicesJWT { ServicesJWT::sign_services(services) } - pub fn verify(token: &str) -> Result, ApiException> { + pub fn verify(token: &str) -> Result, AuthException> { let services = ServicesJWT::find_services(token)?; let salt = services.iter() .map(|s| s.salt()) @@ -84,42 +84,42 @@ impl ServicesJWT { let key: Result, hmac::digest::InvalidLength> = Hmac::new_from_slice(salt.as_bytes()); if key.is_err() { - let exception = ApiException::new(500, key.unwrap_err().to_string()); + let exception = AuthException::new_reset(500, key.unwrap_err().to_string()); return Err(exception); } let result: Result, jwt::Error> = token.verify_with_key(&key.unwrap()); if result.is_err() { - let exception = ApiException::new(500, result.unwrap_err().to_string()); + let exception = AuthException::new_reset(500, result.unwrap_err().to_string()); return Err(exception); } Ok(services) } - fn find_services(token: &str) -> Result, ApiException> { + fn find_services(token: &str) -> Result, AuthException> { let fragments = token.split(".").collect::>(); if fragments.len() != 3 { - let exception = ApiException::new(401, String::from("Invalid token.")); + let exception = AuthException::new_reset(401, String::from("Invalid token.")); return Err(exception); } let claims = fragments.get(1).unwrap().trim(); let b_services = base64::decode_config(claims, base64::URL_SAFE_NO_PAD); if b_services.is_err() { - let exception = ApiException::new(500, b_services.unwrap_err().to_string()); + let exception = AuthException::new_reset(500, b_services.unwrap_err().to_string()); return Err(exception); } let s_services = String::from_utf8(b_services.unwrap()); if s_services.is_err() { - let exception = ApiException::new(500, s_services.unwrap_err().to_string()); + let exception = AuthException::new_reset(500, s_services.unwrap_err().to_string()); return Err(exception); } let m_services: Result, serde_json::Error> = serde_json::from_str(&s_services.unwrap()); if m_services.is_err() { - let exception = ApiException::new(500, m_services.unwrap_err().to_string()); + let exception = AuthException::new_reset(500, m_services.unwrap_err().to_string()); return Err(exception); } @@ -127,7 +127,7 @@ impl ServicesJWT { let v_services = b_m_services.get("sub"); if v_services.is_none() { - let exception = ApiException::new(500, String::from("No services found.")); + let exception = AuthException::new_reset(500, String::from("No services found.")); return Err(exception); } @@ -136,7 +136,7 @@ impl ServicesJWT { for s_service in v_services.unwrap().split("-").filter(|s| !s.is_empty()).collect::>() { let service = Configuration::find_service(String::from(s_service)); if service.is_none() { - let exception = ApiException::new(500, String::from("Unknown service.")); + let exception = AuthException::new_reset(500, String::from("Unknown service.")); return Err(exception); } collection.push(service.unwrap()); diff --git a/src/infrastructure/utils.rs b/src/infrastructure/utils.rs index f3dc4f2..d1228ab 100644 --- a/src/infrastructure/utils.rs +++ b/src/infrastructure/utils.rs @@ -7,13 +7,7 @@ use super::services_jwt::ServicesJWT; impl IntoResponse for ApiException { fn into_response(self) -> Response { - let mut builder = Response::builder(); - if let Ok(token) = ServicesJWT::sign_empty() { - let cookie = format!("{}={}; Path=/; HttpOnly", WebConfiguration::COOKIE_NAME, token); - builder = builder.header(SET_COOKIE, cookie); - } - - builder + Response::builder() .status(self.status()) .body(Body::from(self.message())) .unwrap() @@ -24,7 +18,15 @@ impl IntoResponse for ApiException { impl IntoResponse for AuthException { fn into_response(self) -> Response { - Response::builder() + let mut builder = Response::builder(); + if let Ok(token) = ServicesJWT::sign_empty() { + if self.reset() { + let cookie = format!("{}={}; Path=/; HttpOnly", WebConfiguration::COOKIE_NAME, token); + builder = builder.header(SET_COOKIE, cookie); + } + } + + builder .status(self.status()) .body(Body::from(self.message())) .unwrap() @@ -32,7 +34,7 @@ impl IntoResponse for AuthException { } -pub(crate) fn find_token(headers: HeaderMap) -> Result, ApiException> { +pub(crate) fn find_token(headers: HeaderMap) -> Result, AuthException> { let o_cookies = headers.get(COOKIE); if o_cookies.is_none() { return Ok(None); @@ -40,7 +42,7 @@ pub(crate) fn find_token(headers: HeaderMap) -> Result, ApiExcept let cookies = o_cookies.unwrap().to_str(); if cookies.is_err() { - let exception = ApiException::new(StatusCode::UNAUTHORIZED.as_u16(), String::from("Token has non valid format")); + let exception = AuthException::new_reset(StatusCode::UNAUTHORIZED.as_u16(), String::from("Token has non valid format")); return Err(exception); } From 4df9fa75141071237a76e2578799e2610b4c5141 Mon Sep 17 00:00:00 2001 From: Rafael Ruiz Date: Fri, 3 May 2024 02:50:41 +0200 Subject: [PATCH 07/22] [FEATURE] General refactorization. --- src/domain/cookie/cookie.rs | 2 +- src/infrastructure/controller.rs | 147 ++++++++++------------------- src/infrastructure/handler.rs | 10 +- src/infrastructure/services_jwt.rs | 40 ++++---- src/infrastructure/utils.rs | 16 +--- 5 files changed, 85 insertions(+), 130 deletions(-) diff --git a/src/domain/cookie/cookie.rs b/src/domain/cookie/cookie.rs index 543a505..83c2d8e 100644 --- a/src/domain/cookie/cookie.rs +++ b/src/domain/cookie/cookie.rs @@ -26,7 +26,7 @@ impl Cookie { } pub fn to_string(&self) -> String { - let mut cookie_string = format!("{}", self.value); + let mut cookie_string = format!("{}={}", self.code, self.value); if let Some(domain) = &self.domain { cookie_string.push_str(&format!("; Domain={}", domain)); diff --git a/src/infrastructure/controller.rs b/src/infrastructure/controller.rs index db8efad..604ff2c 100644 --- a/src/infrastructure/controller.rs +++ b/src/infrastructure/controller.rs @@ -2,7 +2,7 @@ use axum::{body::Body, extract::{Path, Query}, http::{header::SET_COOKIE, Header use rust_db_manager_core::{commons::configuration::configuration::Configuration, infrastructure::{db_service::DBService, repository::e_db_repository::EDBRepository}}; -use crate::{commons::{configuration::web_configuration::WebConfiguration, exception::{api_exception::ApiException, auth_exception::AuthException}}, domain::builder_db_service::BuilderDBService}; +use crate::{commons::{configuration::web_configuration::WebConfiguration, exception::{api_exception::ApiException, auth_exception::AuthException}}, domain::{builder_db_service::BuilderDBService, cookie::cookie::Cookie}}; use super::{db_assets::WebEDBRepository, dto::{db_service::{dto_db_service::DTODBService, dto_db_service_lite::DTODBServiceLite, dto_db_service_suscribe::DTODBServiceSuscribe, dto_db_service_web_category::DTODBServiceWebCategory}, dto_server_status::DTOServerStatus, pagination::{dto_paginated_collection::DTOPaginatedCollection, dto_query_pagination::DTOQueryPagination}}, handler, pagination::Pagination, services_jwt::ServicesJWT, utils::find_token}; @@ -48,75 +48,49 @@ impl Controller { let service = &o_service.unwrap(); - let r_token = Controller::make_token(headers, service); - if r_token.is_err() { - return Err(r_token.unwrap_err().into_response()); + let r_cookie = Controller::make_token(headers, service); + if let Err(exception) = r_cookie { + return Err(exception.into_response()); } - let token = r_token.unwrap(); - let db_service = Configuration::push_service(service); - if let Err(error) = db_service { - let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + if let Err(exception) = db_service { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), exception); return Err(exception.into_response()); } - let mut builder = Response::builder(); - if token.is_some() { - let cookie = format!("{}={}; Path=/;", WebConfiguration::COOKIE_NAME, token.unwrap()); - builder = builder.header(SET_COOKIE, cookie); - } - - let response = builder - .status(StatusCode::OK) - .body(Body::empty()) - .unwrap(); - - Ok(response) + Ok(Self::build_token_response(r_cookie.unwrap(), Body::empty())) } async fn suscribe(headers: HeaderMap, Json(dto): Json) -> impl IntoResponse { - let o_service = Configuration::find_service(dto.name); - if o_service.is_none() { + let o_db_service = Configuration::find_service(dto.name); + if o_db_service.is_none() { let exception = ApiException::new(StatusCode::NOT_FOUND.as_u16(), String::from("Service not found.")); return Err(exception.into_response()); } - let service = &o_service.unwrap(); + let db_service = &o_db_service.unwrap(); - if service.is_authorized(dto.password).is_err() { + if db_service.is_authorized(dto.password).is_err() { let exception = ApiException::new(StatusCode::UNAUTHORIZED.as_u16(), String::from("Authentication error.")); return Err(exception.into_response()); } - let r_token = Controller::make_token(headers, service); - if r_token.is_err() { - return Err(r_token.unwrap_err().into_response()); + let r_cookie = Controller::make_token(headers, db_service); + if let Err(exception) = r_cookie { + return Err(exception.into_response()); } - let token = r_token.unwrap(); - - let mut builder = Response::builder(); - if token.is_some() { - let cookie = format!("{}={}; Path=/; HttpOnly", WebConfiguration::COOKIE_NAME, token.unwrap()); - builder = builder.header(SET_COOKIE, cookie); - } - - let response = builder - .status(StatusCode::OK) - .body(Body::empty()) - .unwrap(); - - Ok(response) + Ok(Self::build_token_response(r_cookie.unwrap(), Body::empty())) } async fn service_status(Path(service): Path) -> Result<(StatusCode, String), impl IntoResponse> { - let db_service = Configuration::find_service(service); - if db_service.is_none() { + let o_db_service = Configuration::find_service(service); + if o_db_service.is_none() { return Err(Controller::not_found()); } - let result = db_service.unwrap().instance().await; + let result = o_db_service.unwrap().instance().await; if let Err(error) = result { let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); return Err(exception.into_response()); @@ -139,88 +113,67 @@ impl Controller { let db_service = o_db_service.unwrap(); - let r_token = Controller::remove_token(headers, &db_service); - if r_token.is_err() { - return Err(r_token.unwrap_err().into_response()); + let r_cookie = Controller::remove_token(headers, &db_service); + if let Err(exception) = r_cookie { + return Err(exception.into_response()); } - let token = r_token.unwrap(); - Configuration::remove_service(db_service); - let mut builder = Response::builder(); - if token.is_some() { - let cookie = format!("{}={}; Path=/; HttpOnly", WebConfiguration::COOKIE_NAME, token.unwrap()); - builder = builder.header(SET_COOKIE, cookie); - } - - let response = builder - .status(StatusCode::OK) - .body(Body::empty()) - .unwrap(); - - Ok(response) + Ok(Self::build_token_response(r_cookie.unwrap(), Body::empty())) } - fn make_token(headers: HeaderMap, service: &DBService) -> Result, AuthException> { - let o_token = find_token(headers); - if o_token.is_err() { - return Err(o_token.unwrap_err()); + fn make_token(headers: HeaderMap, service: &DBService) -> Result, AuthException> { + let o_cookie = find_token(headers); + if o_cookie.is_err() { + return Err(o_cookie.unwrap_err()); } - match o_token.unwrap() { - Some(token) => { + match o_cookie.unwrap() { + Some(cookie) => { if !service.is_protected() { - let result = Some(token); - return Ok(result); - } - - let result = ServicesJWT::update(&token, service); - if result.is_err() { - return Err(result.unwrap_err()); + return Ok(Some(cookie)); } - Ok(Some(result.unwrap())) + Ok(Some(ServicesJWT::update(&cookie.value, service)?)) }, None => { if !service.is_protected() { return Ok(None); } - - let result = ServicesJWT::sign(service); - if result.is_err() { - return Err(result.unwrap_err()); - } - - Ok(Some(result.unwrap())) + Ok(Some(ServicesJWT::sign(service)?)) }, } } - fn remove_token(headers: HeaderMap, service: &DBService) -> Result, AuthException> { - let o_token = find_token(headers); - if o_token.is_err() { - return Err(o_token.unwrap_err()); + fn remove_token(headers: HeaderMap, service: &DBService) -> Result, AuthException> { + let o_cookie = find_token(headers); + if o_cookie.is_err() { + return Err(o_cookie.unwrap_err()); } - match o_token.unwrap() { - Some(token) => { + match o_cookie.unwrap() { + Some(cookie) => { if !service.is_protected() { - let result = Some(token); - return Ok(result); + return Ok(Some(cookie)); } - - let result = ServicesJWT::remove(&token, service); - if result.is_err() { - return Err(result.unwrap_err()); - } - - Ok(Some(result.unwrap())) + Ok(Some(ServicesJWT::remove(&cookie.value, service)?)) }, None => return Ok(None), } } + fn build_token_response(cookie: Option, body: Body) -> impl IntoResponse { + let mut builder = Response::builder(); + if cookie.is_some() { + builder = builder.header(SET_COOKIE, cookie.unwrap().to_string()); + } + + builder.status(StatusCode::OK) + .body(body) + .unwrap() + } + fn not_found() -> Response { let error = ApiException::new( StatusCode::NOT_FOUND.as_u16(), diff --git a/src/infrastructure/handler.rs b/src/infrastructure/handler.rs index 889c65e..74260a3 100644 --- a/src/infrastructure/handler.rs +++ b/src/infrastructure/handler.rs @@ -15,8 +15,8 @@ pub(crate) async fn autentication_handler(headers: HeaderMap, Path(service): Pat let service = o_service.unwrap(); if service.is_protected() { let o_token = find_token(headers); - if o_token.is_err() { - return Err(o_token.unwrap_err().into_response()); + if let Err(exception) = o_token { + return Err(exception.into_response()); } let token = o_token.unwrap(); @@ -25,9 +25,9 @@ pub(crate) async fn autentication_handler(headers: HeaderMap, Path(service): Pat return Err(exception.into_response()); } - let result = ServicesJWT::verify(&token.unwrap()); - if result.is_err() { - return Err(result.unwrap_err().into_response()); + let result = ServicesJWT::verify(&token.unwrap().value); + if let Err(exception) = result { + return Err(exception.into_response()); } let services = result.unwrap(); diff --git a/src/infrastructure/services_jwt.rs b/src/infrastructure/services_jwt.rs index e210d20..b4a3fcd 100644 --- a/src/infrastructure/services_jwt.rs +++ b/src/infrastructure/services_jwt.rs @@ -5,7 +5,7 @@ use std::collections::BTreeMap; use rust_db_manager_core::{commons::configuration::configuration::Configuration, infrastructure::db_service::DBService}; -use crate::commons::exception::auth_exception::AuthException; +use crate::{commons::{configuration::web_configuration::WebConfiguration, exception::auth_exception::AuthException}, domain::cookie::cookie::Cookie}; pub struct ServicesJWT { @@ -13,15 +13,15 @@ pub struct ServicesJWT { impl ServicesJWT { - pub fn sign(service: &DBService) -> Result { - ServicesJWT::sign_services(Vec::from(vec![service.clone()])) + pub fn sign(service: &DBService) -> Result { + Self::sign_services(Vec::from(vec![service.clone()])) } - pub fn sign_empty() -> Result { - ServicesJWT::sign_services(Vec::new()) + pub fn sign_empty() -> Result { + Self::sign_services(Vec::new()) } - pub fn sign_services(services: Vec) -> Result { + pub fn sign_services(services: Vec) -> Result { let s_key = services.iter() .map(|s| s.salt()) .collect::>() @@ -47,13 +47,13 @@ impl ServicesJWT { return Err(exception); } - Ok(token_str.unwrap()) + Ok(Self::default_cookie(token_str.unwrap())) } - pub fn update(token: &str, service: &DBService) -> Result { - let _ = ServicesJWT::verify(token)?; + pub fn update(token: &str, service: &DBService) -> Result { + let _ = Self::verify(token)?; - let mut services = ServicesJWT::find_services(token)?; + let mut services = Self::find_services(token)?; if services.iter().find(|s| s.name() == service.name()).is_some() { let exception = AuthException::new(500, String::from("This token is already subscribed to the service.")); return Err(exception); @@ -61,22 +61,22 @@ impl ServicesJWT { services.push(service.clone()); - ServicesJWT::sign_services(services) + Ok(Self::sign_services(services)?) } - pub fn remove(token: &str, service: &DBService) -> Result { - let _ = ServicesJWT::verify(token)?; + pub fn remove(token: &str, service: &DBService) -> Result { + let _ = Self::verify(token)?; - let mut services = ServicesJWT::find_services(token)?; + let mut services = Self::find_services(token)?; if let Some(position) = services.iter().position(|s| s.name() == service.name()) { services.remove(position); } - ServicesJWT::sign_services(services) + Ok(Self::sign_services(services)?) } pub fn verify(token: &str) -> Result, AuthException> { - let services = ServicesJWT::find_services(token)?; + let services = Self::find_services(token)?; let salt = services.iter() .map(|s| s.salt()) .collect::>() @@ -145,4 +145,12 @@ impl ServicesJWT { Ok(collection) } + fn default_cookie(token: String) -> Cookie { + let mut cookie = Cookie::new(String::from(WebConfiguration::COOKIE_NAME), token); + cookie.path = Some(String::from("/")); + cookie.http_only = Some(true); + + cookie + } + } \ No newline at end of file diff --git a/src/infrastructure/utils.rs b/src/infrastructure/utils.rs index d1228ab..14bc61f 100644 --- a/src/infrastructure/utils.rs +++ b/src/infrastructure/utils.rs @@ -1,6 +1,6 @@ use axum::{body::Body, http::{header::{COOKIE, SET_COOKIE}, HeaderMap, Response, StatusCode}, response::IntoResponse}; -use crate::{commons::{configuration::web_configuration::WebConfiguration, exception::{api_exception::ApiException, auth_exception::AuthException}}, domain::cookie::jar::Jar}; +use crate::{commons::{configuration::web_configuration::WebConfiguration, exception::{api_exception::ApiException, auth_exception::AuthException}}, domain::cookie::{cookie::Cookie, jar::Jar}}; use super::services_jwt::ServicesJWT; @@ -19,10 +19,9 @@ impl IntoResponse for AuthException { fn into_response(self) -> Response { let mut builder = Response::builder(); - if let Ok(token) = ServicesJWT::sign_empty() { + if let Ok(cookie) = ServicesJWT::sign_empty() { if self.reset() { - let cookie = format!("{}={}; Path=/; HttpOnly", WebConfiguration::COOKIE_NAME, token); - builder = builder.header(SET_COOKIE, cookie); + builder = builder.header(SET_COOKIE, cookie.to_string()); } } @@ -34,7 +33,7 @@ impl IntoResponse for AuthException { } -pub(crate) fn find_token(headers: HeaderMap) -> Result, AuthException> { +pub(crate) fn find_token(headers: HeaderMap) -> Result, AuthException> { let o_cookies = headers.get(COOKIE); if o_cookies.is_none() { return Ok(None); @@ -51,10 +50,5 @@ pub(crate) fn find_token(headers: HeaderMap) -> Result, AuthExcep return Err(jar.unwrap_err()); } - let o_cookie = jar.unwrap().find(WebConfiguration::COOKIE_NAME); - if o_cookie.is_none() { - return Ok(None); - } - - Ok(Some(o_cookie.unwrap().value)) + Ok(jar.unwrap().find(WebConfiguration::COOKIE_NAME)) } \ No newline at end of file From 79a62c8687124058563165d15b763dee8fc77f49 Mon Sep 17 00:00:00 2001 From: Rafael Ruiz Date: Fri, 3 May 2024 20:25:31 +0200 Subject: [PATCH 08/22] [FIX] Status code fixed in BuilderCookie results. - Controller refactorization. --- src/domain/cookie/builder_cookie.rs | 8 ++++---- src/infrastructure/controller.rs | 24 ++++++++++++------------ 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/domain/cookie/builder_cookie.rs b/src/domain/cookie/builder_cookie.rs index d24d7a2..be55a07 100644 --- a/src/domain/cookie/builder_cookie.rs +++ b/src/domain/cookie/builder_cookie.rs @@ -16,7 +16,7 @@ impl BuilderCookie { .collect::>(); if code_value.len() != 2 { let message = String::from("Invalid cookie format"); - return Err(AuthException::new_reset(301, message)); + return Err(AuthException::new_reset(422, message)); } let code = code_value.get(0).cloned().unwrap(); @@ -43,7 +43,7 @@ impl BuilderCookie { let string_max_age = value.unwrap_or_default(); let max_age: Result = string_max_age.parse(); if max_age.is_err() { - let exception = AuthException::new_reset(301, max_age.unwrap_err().to_string()); + let exception = AuthException::new_reset(422, max_age.unwrap_err().to_string()); return Err(exception); } Some(max_age.unwrap()) @@ -53,13 +53,13 @@ impl BuilderCookie { let samesite = SameSite::from_string(&string_samesite.clone()); if samesite.is_none() { let message = String::from(format!("Unknown Same Site value: '{}'", string_samesite)); - return Err(AuthException::new_reset(301, message)); + return Err(AuthException::new_reset(422, message)); } Some(samesite.unwrap()) }, _ => { let message = String::from(format!("Unknown field code: '{}'", key)); - return Err(AuthException::new_reset(301, message)); + return Err(AuthException::new_reset(422, message)); } } } diff --git a/src/infrastructure/controller.rs b/src/infrastructure/controller.rs index 604ff2c..eaa116e 100644 --- a/src/infrastructure/controller.rs +++ b/src/infrastructure/controller.rs @@ -13,14 +13,14 @@ impl Controller { pub fn route(router: Router) -> Router { router - .route("/:service/status", get(Controller::service_status)) - .route("/:service", delete(Controller::service_remove)) + .route("/:service/status", get(Self::service_status)) + .route("/:service", delete(Self::service_remove)) .route_layer(middleware::from_fn(handler::autentication_handler)) - .route("/status", get(Controller::status)) - .route("/support", get(Controller::support)) - .route("/services", get(Controller::services)) - .route("/publish", post(Controller::publish)) - .route("/suscribe", post(Controller::suscribe)) + .route("/status", get(Self::status)) + .route("/support", get(Self::support)) + .route("/services", get(Self::services)) + .route("/publish", post(Self::publish)) + .route("/suscribe", post(Self::suscribe)) } async fn status() -> (StatusCode, Json) { @@ -48,7 +48,7 @@ impl Controller { let service = &o_service.unwrap(); - let r_cookie = Controller::make_token(headers, service); + let r_cookie = Self::make_token(headers, service); if let Err(exception) = r_cookie { return Err(exception.into_response()); } @@ -76,7 +76,7 @@ impl Controller { return Err(exception.into_response()); } - let r_cookie = Controller::make_token(headers, db_service); + let r_cookie = Self::make_token(headers, db_service); if let Err(exception) = r_cookie { return Err(exception.into_response()); } @@ -87,7 +87,7 @@ impl Controller { async fn service_status(Path(service): Path) -> Result<(StatusCode, String), impl IntoResponse> { let o_db_service = Configuration::find_service(service); if o_db_service.is_none() { - return Err(Controller::not_found()); + return Err(Self::not_found()); } let result = o_db_service.unwrap().instance().await; @@ -108,12 +108,12 @@ impl Controller { async fn service_remove(headers: HeaderMap, Path(service): Path) -> impl IntoResponse { let o_db_service = Configuration::find_service(service); if o_db_service.is_none() { - return Err(Controller::not_found()); + return Err(Self::not_found()); } let db_service = o_db_service.unwrap(); - let r_cookie = Controller::remove_token(headers, &db_service); + let r_cookie = Self::remove_token(headers, &db_service); if let Err(exception) = r_cookie { return Err(exception.into_response()); } From 0e342bdf69028d9e548518fea4ea0b93facb0355 Mon Sep 17 00:00:00 2001 From: Rafael Ruiz Date: Sat, 4 May 2024 23:59:41 +0200 Subject: [PATCH 09/22] [FEATURE] Database controller created. - Updated Mongo image resource. --- .gitignore | 3 +- src/infrastructure/controller_database.rs | 62 +++++++++++++++++++ .../{controller.rs => controller_server.rs} | 39 ++---------- src/infrastructure/db_assets.rs | 2 +- src/infrastructure/utils.rs | 7 +++ src/lib.rs | 3 +- src/main.rs | 9 +-- 7 files changed, 84 insertions(+), 41 deletions(-) create mode 100644 src/infrastructure/controller_database.rs rename src/infrastructure/{controller.rs => controller_server.rs} (82%) diff --git a/.gitignore b/.gitignore index ad592ec..963ab03 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /.vscode /target -/docker \ No newline at end of file +/docker +/assets \ No newline at end of file diff --git a/src/infrastructure/controller_database.rs b/src/infrastructure/controller_database.rs new file mode 100644 index 0000000..1670c41 --- /dev/null +++ b/src/infrastructure/controller_database.rs @@ -0,0 +1,62 @@ +use axum::{extract::Path, http::StatusCode, middleware, response::IntoResponse, routing::get, Json, Router}; +use rust_db_manager_core::commons::configuration::configuration::Configuration; + +use crate::commons::exception::api_exception::ApiException; + +use super::{handler, utils}; + +pub struct ControllerDataBase { +} + +impl ControllerDataBase { + + pub fn route(router: Router) -> Router { + router + .route("/:service/status", get(Self::status)) + .route("/:service/data-base", get(Self::data_bases)) + .route_layer(middleware::from_fn(handler::autentication_handler)) + } + + async fn status(Path(service): Path) -> Result<(StatusCode, String), impl IntoResponse> { + let o_db_service = Configuration::find_service(service); + if o_db_service.is_none() { + return Err(utils::not_found()); + } + + let result = o_db_service.unwrap().instance().await; + if let Err(error) = result { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + let status = result.unwrap().status().await; + if let Err(error) = status { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + Ok((StatusCode::ACCEPTED, String::from("Service up."))) + } + + async fn data_bases(Path(service): Path) -> Result>, impl IntoResponse> { + let o_db_service = Configuration::find_service(service); + if o_db_service.is_none() { + return Err(utils::not_found()); + } + + let result = o_db_service.unwrap().instance().await; + if let Err(error) = result { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + let collection = result.unwrap().list_data_bases().await; + if let Err(error) = collection { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + Ok(Json(collection.unwrap())) + } + +} \ No newline at end of file diff --git a/src/infrastructure/controller.rs b/src/infrastructure/controller_server.rs similarity index 82% rename from src/infrastructure/controller.rs rename to src/infrastructure/controller_server.rs index eaa116e..56622b9 100644 --- a/src/infrastructure/controller.rs +++ b/src/infrastructure/controller_server.rs @@ -4,16 +4,15 @@ use rust_db_manager_core::{commons::configuration::configuration::Configuration, use crate::{commons::{configuration::web_configuration::WebConfiguration, exception::{api_exception::ApiException, auth_exception::AuthException}}, domain::{builder_db_service::BuilderDBService, cookie::cookie::Cookie}}; -use super::{db_assets::WebEDBRepository, dto::{db_service::{dto_db_service::DTODBService, dto_db_service_lite::DTODBServiceLite, dto_db_service_suscribe::DTODBServiceSuscribe, dto_db_service_web_category::DTODBServiceWebCategory}, dto_server_status::DTOServerStatus, pagination::{dto_paginated_collection::DTOPaginatedCollection, dto_query_pagination::DTOQueryPagination}}, handler, pagination::Pagination, services_jwt::ServicesJWT, utils::find_token}; +use super::{db_assets::WebEDBRepository, dto::{db_service::{dto_db_service::DTODBService, dto_db_service_lite::DTODBServiceLite, dto_db_service_suscribe::DTODBServiceSuscribe, dto_db_service_web_category::DTODBServiceWebCategory}, dto_server_status::DTOServerStatus, pagination::{dto_paginated_collection::DTOPaginatedCollection, dto_query_pagination::DTOQueryPagination}}, handler, pagination::Pagination, services_jwt::ServicesJWT, utils::{self, find_token}}; -pub struct Controller{ +pub struct ControllerServer { } -impl Controller { +impl ControllerServer { pub fn route(router: Router) -> Router { router - .route("/:service/status", get(Self::service_status)) .route("/:service", delete(Self::service_remove)) .route_layer(middleware::from_fn(handler::autentication_handler)) .route("/status", get(Self::status)) @@ -82,33 +81,12 @@ impl Controller { } Ok(Self::build_token_response(r_cookie.unwrap(), Body::empty())) - } - - async fn service_status(Path(service): Path) -> Result<(StatusCode, String), impl IntoResponse> { - let o_db_service = Configuration::find_service(service); - if o_db_service.is_none() { - return Err(Self::not_found()); - } - - let result = o_db_service.unwrap().instance().await; - if let Err(error) = result { - let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); - return Err(exception.into_response()); - } - - let status = result.unwrap().status().await; - if let Err(error) = status { - let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); - return Err(exception.into_response()); - } - - Ok((StatusCode::ACCEPTED, String::from("Service up."))) - } + } async fn service_remove(headers: HeaderMap, Path(service): Path) -> impl IntoResponse { let o_db_service = Configuration::find_service(service); if o_db_service.is_none() { - return Err(Self::not_found()); + return Err(utils::not_found()); } let db_service = o_db_service.unwrap(); @@ -174,11 +152,4 @@ impl Controller { .unwrap() } - fn not_found() -> Response { - let error = ApiException::new( - StatusCode::NOT_FOUND.as_u16(), - String::from("Not found")); - return error.into_response(); - } - } \ No newline at end of file diff --git a/src/infrastructure/db_assets.rs b/src/infrastructure/db_assets.rs index 5afbc0c..044bdda 100644 --- a/src/infrastructure/db_assets.rs +++ b/src/infrastructure/db_assets.rs @@ -22,7 +22,7 @@ impl WebEDBRepository for EDBRepository { EDBRepository::MongoDB => DTODBResources::new( String::from("https://www.mongodb.com/"), String::from("#00ED64"), - String::from("https://thumbs.bfldr.com/at/hj345wvxsvpbc82vchqcj9qh?expiry=1714820885&fit=bounds&height=162&sig=NTU3MjQ1YzFjYzljYzFhY2UxNjI0ZDA4ZjhjNTc4ZDI2YzViNmMxOQ%3D%3D&width=262") + String::from("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHgAAAEBCAYAAACzPmFvAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAA3eSURBVHgB7Z1bchvHFYZPD6AqvZmsyKrKU6AVRFpBaG3ASjYQxiZdfpOcBURMFhDbby5biuQNRPYGYmUFUlYg+ClVlmzIb67iYNp9ZgDiNpfume6ePqf7q5JIAsML8M+59pkegJhYnH0KP6p/ESEgFt6cn4KQT8rPpfwj3Hj8DURAHAIvPp5Bcfmdermz1SNvIZvegeMv5sCcDGJA5k+2xEWOqsf4w1/gxdlDkHBy8Dg+9uPZA2AObxe9OL8NhXzRekwB78G7j54DU/hacBl3i2edx2Uq8VqcHgFT+Aq8vLzYi7sNqGOKKdt4zFNgjLtC/NngO+5xjcf8YnDpmvNX0IeluAM3v3oJjOBlwVf1bk8mKmYzi8e8BC4uP9WLu00IdvGYj8AYd0Hcg+Gwisc8YrBOvWsKk/qYvgXr1rumlPWx+tnEoS/wMn82LO42oX4mg341bYHLehdugyuqfjXp9WO6MXh7fdc1WXYKx19+DQShKXBV76qkSviqWcmuH9Nz0ZvFe58NiaPydxJsgtATeHAzoy/qd8qp/WzdMbQEttfM6AcmXeXfQAc6MdhnUtUFoaSLhsD+k6ouyCRd4bvocZKqLsgkXeEL7KxTNRRBIukKW2C8EsFlp2ooBDpd4QqM2WoBFJbtHoS8vBhmkvXmo3sgClo1Z6DLi+EJfHiZCRWCzKzDc9E0xUXwcpjgZrrCEhiTKhAzoIpUCWExDarTFY7Ai/P7RJKqLoJKusKIweF1qoYSTDwOw4LD61QNJZhO1/gCU4+7jYhZCPF4XBcd0gqRK0beLmI8genWu6aMGo/Hc9GH2ypwZdTtIsYRuGlbBa6MuF2Efxc95PJO2oziqv1b8JDLO2lTtTI941fgamhuBrGCrczXZxfgEX8uOl7XfIjHnQT8WXC8rvmQqb/a34/AsbvmfTy6avcuOrnmJrxk1e4teITMkQheGiBuBcZeswx4KnJsPDRA3LnoeHrNQ1GuOr8Fx0/fggPcWbD2VoLRc+RyWdGNBf9wfhsmlne94Y6jsVs3FjwhNtMcAhNwYsX2BcbECsQMEmY4SrjsuuiUWA3FesJl2YLz+0ncQRxBPrVqxfYsOHWs7JFNb9nqcNmz4LIsSljBYofLjgWnssg+lsomOxY8lVHdLs4Llsqm4QJX/eYTSNgF39PXZycwkOECi4LUvlGksGDFwwROTQ23WLDiYQIn63XPQCvuL3CyXj8MtOL+Aifr9ccAK+4ncLJevwyw4n4CZ9LktnEJG/S0YnOB8UxKda9/elqxucBCnkJiHHpYsVkvOq0YjY/hSpOZBacVo/HJL43Wi/UtuLJeXDFie7dsIhhNfehb8HJ5AkncEDCa+tAXODU2wkHA+7qH6glcpudiBokwEOXViSc6h+oJnEqj8Mjgvs5h3UlWKo1CRSvZ6rbgKrlKhMcRXE5Puw7ScNFSyxUkRmDanWy1u+jknsOnY/qy3YINuyaJUThpe7Jd4Ey/3kqMBGbTr89anm4i1b5UwO7iSdOTzQKn2pcQsvGWuy0Cwx8gQYMse7/JTdcLvDi/ndwzJeQMGtx0vcC5HO8u24m+nNQ9WC+wSNkzOSYqpC5ODx4+FBibGyHf0nUIEr5V/7zs8uqdahDyYL3+UGDOvWchFurknQNXltcOQmuNiy5OgCuF/F6d6T8DV2oqn0znoAQVVHL8w/nOI7sCx1AeSelkT8hAwBi8kz/tCrxkvjOsEHP1H2eBFfJk+6tdgYVM5RF1JuL321/uxWDJ24InUmXRxRxYo+Lw/z+++mojMNa/IGbAmZxxBr0B4/Bs/cVG4OVl2pmdC9c2vYyNwLFcEiqzOXBHFlfGuhF4LzizJJ/OIQa2tNy24Dhc9JR7mQSVlqtEqxK4TLAiuLDs+i9K3Ev+Am8lWpXAeT6DGHB0Z5MguZ6XbroSOK57G8Uh8iXcwg+VwFkMAmObEuKx4lWitRb4d5Dghay6kvG46O1VJCHnwB0hjnCEZ10mxbA1QzwJVkk5aXmUVWvACZb8cl0JnMv4NlYpImh2IKpUytTy2QxiQKyyaCSLxF0vxXGmmu8ziA0ZicBSzlSSFYmLLorNWjDnycodpIrBmE5HQSRxd5sseyeLssnBe7JyQ+WiI2E7yYrHmo+yVUGc4IgKv/FYME5UrokliwasCGUkSdb2RGVWRCKwLC04bRHMl4hc9A7xlEzxCLw9USmTwLyJYbJyRTwClxOVa6KYrCyJR+CYJiq3iDTJiqkOjmE+aV/QeKz5bSQWHOFKUomIReBaohA9uxoI54ysCUMigiVD9bpjtuAoyKCA7yFGYpisVGvgWRTTDaImDMUwWVkUP2fxZpgxgFk0+22FYHei8uqxCEJT5aIj2JQkVi9VZtHTfA4Jnkh00cdP58CduhksyXjf6DU3v3pZ1cHc+9FZhC56tbP96gJw8RJig/9kZfn6KoFxJ3TO1LUquU9WSvk//LC24DkkeLHSdBWDmd6JpBXmcVlsx+Bpzlvguj0quU9WrjStBMYJhzgmO+IAM+jV1MpmubAQ/wWu7ExUruDd4LlKmrcEZhyHY5uoLOD5+tONwFEmWkzZ0nIjMN9Eq956Obdo3330fP3pRuAy0dqYNh8ia1PKXQ33boxVdT8igp/4clfDvaE78Q1wQ7aUfywnK3c13BW4isNphIcyW/EX2RW4isPxZNPcJivlYQ51OBedw7fACdGykMJtsrI41O5Q4Gs5vzgcCzXaHQqM9SGnvnTdROXVc5wmK5VmNbV9/aUrS05uOpI6uKjXrOHaJIblEnvqNasXuEq1eZz5bbNXbCYrlXveK4/WNF9dWMivgQNxTFQ+b3qi5fLRCNw0l8nKZfZ501PNAqPJc8im21qVLCYr5SsccG96tv0C8KXg4aY5k8Hf259uY5p/BqzhEJ+XraNW7QJzWCNuu+s39clKqWrfjsGF7j06lu0uIDEiE3jSdUi3wNRr4rqJyjWkJytVcnX8qLPjqLfLTgGfA1W4TlRmep5VT+Aq2UqDAEGx1Jpj1xMYrUBKigsQ7Scl2clK+VT3b9ffCE2Kp0AOpm3KbKmd+OoLXHa2OI7VUgs9+taLmG1lSK1kkhqtVmqTlYVZd9FMYL5WTAMcqmtYFmzCfDNSbo0PSpOV0vy9NxeYkhULja0pqExW9rBepN92wql96R/Z7z3vJzAVK26bqLw6hsJkpcqce1gv0n9DcBJWzKQONqh7D74V+lJZ8XeQcIxZ3bvPsC39Rf4BhIzOzFXok5UDrLf8dhgCnlmFDHfqg/pEZaHC4MB++TCBkWl5hi2AKsFOVqr1XgsjU8MFLtdbi39AiOi0KkOdrMxK6x38tw0XGPnNvz5LLUyLSHgBx4+tTLTaERgh2/wIME5P8j+BJewJjGVTaAmXztRkaJOVFhKrbewJjJQJl0oOQmE5obbW+0oZygVYxK7A5WgP/BUoEdJkZSGs9xXsCozceIwXrT2DEPjtF3MgQ/9+cxv2BS5/atnholsbe0e55oEdqybcCFxNYY7dxtSLvyFMVqJrdvR3uBEYQVc9alZNpE2J75ED17zGncBIaFl1MyOdDJg1P/4EHOJWYHTVDjJDLaTBxetjTVZmy7vgGLcCI2UDJI34HGC5odGEe4ERLN7TcMAWZUl0AR7wIzBSDgd4jcfd81hX+LwxmLuSqA5/ApfDAR7jsRAh1uGyjLseSzN/AiOhxmNfk5VL1cb1XHf7FRipYo/7VmZoN9zEevfmI+99Af8Cl7/VezweF1zAd1zvNjGOwFgfVzWguzhpMmvldLJSncgWF/BNGUdgpEy6AF+4BBcEMVEpF76Tqn3GExgpe7AFrfVjEyR8MPZixrgCIziw5yKzNmpVFvrH6oKvqVobH5XxBUYws5aqu8MFFNdTp6qLMARGbjz+i2pnvgDqSH9tSB3CERgR+V1r5ZPJtKTM5mADLIfwRA2IsAS+Kp8siOx7ohLFneTOl/9MCUtgBLNOWyLrMniyclXrBrhtYngCIzZE9jZRWa4O3R27HGoiTIGRMSzZmLDFRcIVGPElci+BwhcXCVtgpJfIrhfwaYiLhC8w0kfkxZn2oUbHEhIXoSEwYiSynKn/LmDxUfehlbgXoAOWQoTERQRQY3E6g2LyH/Wn3+o8Nsuw6fAUjr9s+FkrcQt4CF2s61xiO8jTExhBkeX03+pNv9N5bJPIRuKq9mNgHSpd6Ljobcp7HJddo+7Rn6LAO5Oc7sRZfXHlalWIpLgITQve5vXZhTpNu62wuonFBVSv+aGWuDgkN8IclU3oC4xUIv8Nul5PJfI7StwHrcfhJAYu1gewnjsUHgIjbz68p17NP7WSr1ZolUFd8BEYMcmw68BkarL8hNO9lngJvOanD1+p9eCZybeUG27fePQeMINmFt1Jr1alwbVMdOApcJ/9J8O8lmkwXAU2t8bQLnWxBFeB52BKsLvODiO56DXU95ZugKfAfbYINhmUJwTXLDrdCncFT4EnPeac8+kcGMLUgi/NLZjUvpb68BTYvI/M1qUzteCSQC4AHxe+ApvtXpcsmB5G/WiWfWiEr8AmWyMxrYERvgJLExfNt27m7KL1RRM+tzL0C+Mky2DfjeSiKWJyhX9y0fSYLF9qH3vtcg5MSY0OhNGQ3T58BdYWjW+ChXC2YOxmzTuPYZxgIbwF1rNOtl0shLfAOt2sZMGE0elmyRSD6aIj3jVgvTE5b4F1hu/yFIPpMpl0NzuYzmKtYZ5Fa8xmMZ3FSkTCr3nPZT1IStkiAAAAAElFTkSuQmCC") ), } } diff --git a/src/infrastructure/utils.rs b/src/infrastructure/utils.rs index 14bc61f..e87db73 100644 --- a/src/infrastructure/utils.rs +++ b/src/infrastructure/utils.rs @@ -51,4 +51,11 @@ pub(crate) fn find_token(headers: HeaderMap) -> Result, AuthExcep } Ok(jar.unwrap().find(WebConfiguration::COOKIE_NAME)) +} + +pub(crate) fn not_found() -> Response { + let error = ApiException::new( + StatusCode::NOT_FOUND.as_u16(), + String::from("Not found")); + return error.into_response(); } \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 89690ba..0551ed5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,7 +34,8 @@ pub mod infrastructure { } pub mod dto_server_status; } - pub mod controller; + pub mod controller_database; + pub mod controller_server; pub mod db_assets; pub mod handler; pub mod pagination; diff --git a/src/main.rs b/src/main.rs index f7790a2..24c58d1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,17 +3,18 @@ use std::net::SocketAddr; use tower_http::cors::CorsLayer; use axum::Router; -use rust_db_manager_api::{commons::configuration::web_configuration::WebConfiguration, infrastructure::controller::Controller}; +use rust_db_manager_api::{commons::configuration::web_configuration::WebConfiguration, infrastructure::{controller_database::ControllerDataBase, controller_server::ControllerServer}}; #[tokio::main] async fn main() { let _ = WebConfiguration::initialize(); - let router = Router::new(); - let app = Controller::route(router) + let app = Router::new() + .merge(ControllerServer::route(Router::new())) + .merge(ControllerDataBase::route(Router::new())) .layer(CorsLayer::very_permissive()) .into_make_service_with_connect_info::(); let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); axum::serve(listener, app).await.unwrap(); -} +} \ No newline at end of file From 0c6dafca798f0b99d24687ff4463176c136d86cc Mon Sep 17 00:00:00 2001 From: Rafael Ruiz Date: Sun, 5 May 2024 05:09:49 +0200 Subject: [PATCH 10/22] [FEATURE] Service status response updated. --- src/infrastructure/controller_database.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/infrastructure/controller_database.rs b/src/infrastructure/controller_database.rs index 1670c41..7be6be2 100644 --- a/src/infrastructure/controller_database.rs +++ b/src/infrastructure/controller_database.rs @@ -35,7 +35,7 @@ impl ControllerDataBase { return Err(exception.into_response()); } - Ok((StatusCode::ACCEPTED, String::from("Service up."))) + Ok((StatusCode::ACCEPTED, String::from("listening"))) } async fn data_bases(Path(service): Path) -> Result>, impl IntoResponse> { From e26ce19c2cd68f0fda4cd94c98f8da052abdf9db Mon Sep 17 00:00:00 2001 From: Rafael Ruiz Date: Sun, 5 May 2024 15:23:23 +0200 Subject: [PATCH 11/22] [FEATURE] Service metadata end-point created. --- src/infrastructure/controller_database.rs | 28 ++++++++++++++++++- src/infrastructure/controller_server.rs | 4 +-- src/infrastructure/dto/dto_data_base_field.rs | 23 +++++++++++++++ src/infrastructure/dto/dto_data_base_group.rs | 25 +++++++++++++++++ src/lib.rs | 2 ++ 5 files changed, 79 insertions(+), 3 deletions(-) create mode 100644 src/infrastructure/dto/dto_data_base_field.rs create mode 100644 src/infrastructure/dto/dto_data_base_group.rs diff --git a/src/infrastructure/controller_database.rs b/src/infrastructure/controller_database.rs index 7be6be2..05a8462 100644 --- a/src/infrastructure/controller_database.rs +++ b/src/infrastructure/controller_database.rs @@ -3,7 +3,7 @@ use rust_db_manager_core::commons::configuration::configuration::Configuration; use crate::commons::exception::api_exception::ApiException; -use super::{handler, utils}; +use super::{dto::dto_data_base_group::DTODataBaseGroup, handler, utils}; pub struct ControllerDataBase { } @@ -13,6 +13,7 @@ impl ControllerDataBase { pub fn route(router: Router) -> Router { router .route("/:service/status", get(Self::status)) + .route("/:service/metadata", get(Self::metadata)) .route("/:service/data-base", get(Self::data_bases)) .route_layer(middleware::from_fn(handler::autentication_handler)) } @@ -38,6 +39,31 @@ impl ControllerDataBase { Ok((StatusCode::ACCEPTED, String::from("listening"))) } + async fn metadata(Path(service): Path) -> Result>, impl IntoResponse> { + let o_db_service = Configuration::find_service(service); + if o_db_service.is_none() { + return Err(utils::not_found()); + } + + let result = o_db_service.unwrap().instance().await; + if let Err(error) = result { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + let metadata = result.unwrap().metadata().await; + if let Err(error) = metadata { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + let dto = metadata.unwrap().iter() + .map(|g| DTODataBaseGroup::from(g)) + .collect::>(); + + Ok(Json(dto)) + } + async fn data_bases(Path(service): Path) -> Result>, impl IntoResponse> { let o_db_service = Configuration::find_service(service); if o_db_service.is_none() { diff --git a/src/infrastructure/controller_server.rs b/src/infrastructure/controller_server.rs index 56622b9..9e2b35f 100644 --- a/src/infrastructure/controller_server.rs +++ b/src/infrastructure/controller_server.rs @@ -15,14 +15,14 @@ impl ControllerServer { router .route("/:service", delete(Self::service_remove)) .route_layer(middleware::from_fn(handler::autentication_handler)) - .route("/status", get(Self::status)) + .route("/metadata", get(Self::metadata)) .route("/support", get(Self::support)) .route("/services", get(Self::services)) .route("/publish", post(Self::publish)) .route("/suscribe", post(Self::suscribe)) } - async fn status() -> (StatusCode, Json) { + async fn metadata() -> (StatusCode, Json) { let result = WebConfiguration::as_dto(); (StatusCode::ACCEPTED, Json(result)) } diff --git a/src/infrastructure/dto/dto_data_base_field.rs b/src/infrastructure/dto/dto_data_base_field.rs new file mode 100644 index 0000000..02aac6f --- /dev/null +++ b/src/infrastructure/dto/dto_data_base_field.rs @@ -0,0 +1,23 @@ +use rust_db_manager_core::domain::data_base_field::DataBaseField; +use serde::Serialize; + +#[derive(Clone, Serialize)] +pub struct DTODataBaseField { + order: usize, + name: String, + value: String, + json_type: String, +} + +impl DTODataBaseField { + + pub fn from(data: &DataBaseField) -> DTODataBaseField { + Self { + order: data.order(), + name: data.name(), + value: data.value(), + json_type: data.json_type(), + } + } + +} \ No newline at end of file diff --git a/src/infrastructure/dto/dto_data_base_group.rs b/src/infrastructure/dto/dto_data_base_group.rs new file mode 100644 index 0000000..b1986c2 --- /dev/null +++ b/src/infrastructure/dto/dto_data_base_group.rs @@ -0,0 +1,25 @@ +use rust_db_manager_core::domain::data_base_group_data::DataBaseDataGroup; +use serde::Serialize; + +use super::dto_data_base_field::DTODataBaseField; + +#[derive(Clone, Serialize)] +pub struct DTODataBaseGroup { + order: usize, + name: String, + fields: Vec, +} + +impl DTODataBaseGroup { + + pub fn from(data: &DataBaseDataGroup) -> DTODataBaseGroup { + Self { + order: data.order(), + name: data.name(), + fields: data.fields().iter() + .map(|f| DTODataBaseField::from(f)) + .collect() + } + } + +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 0551ed5..aedb0be 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,6 +32,8 @@ pub mod infrastructure { pub mod dto_paginated_collection; pub mod dto_query_pagination; } + pub mod dto_data_base_field; + pub mod dto_data_base_group; pub mod dto_server_status; } pub mod controller_database; From 24511a634b5098132ecfe02fd327bf946d333f5d Mon Sep 17 00:00:00 2001 From: Rafael Ruiz Date: Mon, 6 May 2024 15:11:35 +0200 Subject: [PATCH 12/22] [FEATURE] - Find service end-point implemented. - Insert and delete data base end points implemented. - Autentication handler updated. --- src/infrastructure/controller_database.rs | 60 +++++++++++++++++-- src/infrastructure/controller_server.rs | 16 ++++- .../dto/db_service/db/dto_db_create.rs | 6 ++ .../dto/db_service/dto_db_service_response.rs | 27 +++++++++ .../dto_db_service_response_connection.rs | 24 ++++++++ src/infrastructure/handler.rs | 12 +++- src/infrastructure/services_jwt.rs | 2 +- src/lib.rs | 5 ++ 8 files changed, 140 insertions(+), 12 deletions(-) create mode 100644 src/infrastructure/dto/db_service/db/dto_db_create.rs create mode 100644 src/infrastructure/dto/db_service/dto_db_service_response.rs create mode 100644 src/infrastructure/dto/db_service/dto_db_service_response_connection.rs diff --git a/src/infrastructure/controller_database.rs b/src/infrastructure/controller_database.rs index 05a8462..9fb8bfc 100644 --- a/src/infrastructure/controller_database.rs +++ b/src/infrastructure/controller_database.rs @@ -1,9 +1,9 @@ -use axum::{extract::Path, http::StatusCode, middleware, response::IntoResponse, routing::get, Json, Router}; -use rust_db_manager_core::commons::configuration::configuration::Configuration; +use axum::{extract::Path, http::StatusCode, middleware, response::IntoResponse, routing::{delete, get, post}, Json, Router}; +use rust_db_manager_core::{commons::configuration::configuration::Configuration, domain::generate::generate_database_query::GenerateDatabaseQuery}; use crate::commons::exception::api_exception::ApiException; -use super::{dto::dto_data_base_group::DTODataBaseGroup, handler, utils}; +use super::{dto::{db_service::db::dto_db_create::DTODBCreate, dto_data_base_group::DTODataBaseGroup}, handler, utils}; pub struct ControllerDataBase { } @@ -15,11 +15,13 @@ impl ControllerDataBase { .route("/:service/status", get(Self::status)) .route("/:service/metadata", get(Self::metadata)) .route("/:service/data-base", get(Self::data_bases)) + .route("/:service/data-base", post(Self::insert_base)) + .route("/:service/data-base/:data_base", delete(Self::delete_base)) .route_layer(middleware::from_fn(handler::autentication_handler)) } async fn status(Path(service): Path) -> Result<(StatusCode, String), impl IntoResponse> { - let o_db_service = Configuration::find_service(service); + let o_db_service = Configuration::find_service(&service); if o_db_service.is_none() { return Err(utils::not_found()); } @@ -40,7 +42,7 @@ impl ControllerDataBase { } async fn metadata(Path(service): Path) -> Result>, impl IntoResponse> { - let o_db_service = Configuration::find_service(service); + let o_db_service = Configuration::find_service(&service); if o_db_service.is_none() { return Err(utils::not_found()); } @@ -65,7 +67,7 @@ impl ControllerDataBase { } async fn data_bases(Path(service): Path) -> Result>, impl IntoResponse> { - let o_db_service = Configuration::find_service(service); + let o_db_service = Configuration::find_service(&service); if o_db_service.is_none() { return Err(utils::not_found()); } @@ -85,4 +87,50 @@ impl ControllerDataBase { Ok(Json(collection.unwrap())) } + async fn insert_base(Path(service): Path, Json(dto): Json) -> Result { + let o_db_service = Configuration::find_service(&service); + if o_db_service.is_none() { + return Err(utils::not_found()); + } + + let result = o_db_service.unwrap().instance().await; + if let Err(error) = result { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + let query = GenerateDatabaseQuery::new(dto.data_base); + + let collection = result.unwrap().create_data_base(query).await; + if let Err(error) = collection { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + Ok(StatusCode::ACCEPTED) + } + + async fn delete_base(Path((service, data_base)): Path<(String, String)>) -> Result { + let o_db_service = Configuration::find_service(&service); + if o_db_service.is_none() { + return Err(utils::not_found()); + } + + let result = o_db_service.unwrap().instance().await; + if let Err(error) = result { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + let query = GenerateDatabaseQuery::new(data_base); + + let collection = result.unwrap().drop_data_base(query).await; + if let Err(error) = collection { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + Ok(StatusCode::ACCEPTED) + } + } \ No newline at end of file diff --git a/src/infrastructure/controller_server.rs b/src/infrastructure/controller_server.rs index 9e2b35f..4e3dfc8 100644 --- a/src/infrastructure/controller_server.rs +++ b/src/infrastructure/controller_server.rs @@ -4,7 +4,7 @@ use rust_db_manager_core::{commons::configuration::configuration::Configuration, use crate::{commons::{configuration::web_configuration::WebConfiguration, exception::{api_exception::ApiException, auth_exception::AuthException}}, domain::{builder_db_service::BuilderDBService, cookie::cookie::Cookie}}; -use super::{db_assets::WebEDBRepository, dto::{db_service::{dto_db_service::DTODBService, dto_db_service_lite::DTODBServiceLite, dto_db_service_suscribe::DTODBServiceSuscribe, dto_db_service_web_category::DTODBServiceWebCategory}, dto_server_status::DTOServerStatus, pagination::{dto_paginated_collection::DTOPaginatedCollection, dto_query_pagination::DTOQueryPagination}}, handler, pagination::Pagination, services_jwt::ServicesJWT, utils::{self, find_token}}; +use super::{db_assets::WebEDBRepository, dto::{db_service::{dto_db_service::DTODBService, dto_db_service_lite::DTODBServiceLite, dto_db_service_response::DTODBServiceResponse, dto_db_service_suscribe::DTODBServiceSuscribe, dto_db_service_web_category::DTODBServiceWebCategory}, dto_server_status::DTOServerStatus, pagination::{dto_paginated_collection::DTOPaginatedCollection, dto_query_pagination::DTOQueryPagination}}, handler, pagination::Pagination, services_jwt::ServicesJWT, utils::{self, find_token}}; pub struct ControllerServer { } @@ -13,6 +13,7 @@ impl ControllerServer { pub fn route(router: Router) -> Router { router + .route("/:service", get(Self::service_find)) .route("/:service", delete(Self::service_remove)) .route_layer(middleware::from_fn(handler::autentication_handler)) .route("/metadata", get(Self::metadata)) @@ -62,7 +63,7 @@ impl ControllerServer { } async fn suscribe(headers: HeaderMap, Json(dto): Json) -> impl IntoResponse { - let o_db_service = Configuration::find_service(dto.name); + let o_db_service = Configuration::find_service(&dto.name); if o_db_service.is_none() { let exception = ApiException::new(StatusCode::NOT_FOUND.as_u16(), String::from("Service not found.")); return Err(exception.into_response()); @@ -81,10 +82,19 @@ impl ControllerServer { } Ok(Self::build_token_response(r_cookie.unwrap(), Body::empty())) + } + + async fn service_find(Path(service): Path) -> Result,impl IntoResponse> { + let o_db_service = Configuration::find_service(&service); + if o_db_service.is_none() { + return Err(utils::not_found()); + } + + Ok(Json(DTODBServiceResponse::from(o_db_service.unwrap()))) } async fn service_remove(headers: HeaderMap, Path(service): Path) -> impl IntoResponse { - let o_db_service = Configuration::find_service(service); + let o_db_service = Configuration::find_service(&service); if o_db_service.is_none() { return Err(utils::not_found()); } diff --git a/src/infrastructure/dto/db_service/db/dto_db_create.rs b/src/infrastructure/dto/db_service/db/dto_db_create.rs new file mode 100644 index 0000000..ed43828 --- /dev/null +++ b/src/infrastructure/dto/db_service/db/dto_db_create.rs @@ -0,0 +1,6 @@ +use serde::Deserialize; + +#[derive(Clone, Deserialize)] +pub struct DTODBCreate { + pub data_base: String +} \ No newline at end of file diff --git a/src/infrastructure/dto/db_service/dto_db_service_response.rs b/src/infrastructure/dto/db_service/dto_db_service_response.rs new file mode 100644 index 0000000..a0dba4e --- /dev/null +++ b/src/infrastructure/dto/db_service/dto_db_service_response.rs @@ -0,0 +1,27 @@ +use rust_db_manager_core::infrastructure::db_service::DBService; +use serde::{Deserialize, Serialize}; + +use super::dto_db_service_response_connection::DTODBServiceResponseConnection; + +#[derive(Clone, Serialize, Deserialize)] +pub struct DTODBServiceResponse { + pub name: String, + pub owner: String, + pub protected: bool, + pub timestamp: u128, + pub connection_data: DTODBServiceResponseConnection, +} + +impl DTODBServiceResponse { + + pub fn from(service: DBService) -> DTODBServiceResponse { + DTODBServiceResponse { + name: service.name(), + owner: service.owner(), + protected: service.is_protected(), + timestamp: service.timestamp(), + connection_data: DTODBServiceResponseConnection::from(service.connection_data()) + } + } + +} \ No newline at end of file diff --git a/src/infrastructure/dto/db_service/dto_db_service_response_connection.rs b/src/infrastructure/dto/db_service/dto_db_service_response_connection.rs new file mode 100644 index 0000000..047e751 --- /dev/null +++ b/src/infrastructure/dto/db_service/dto_db_service_response_connection.rs @@ -0,0 +1,24 @@ +use rust_db_manager_core::domain::connection_data::ConnectionData; +use serde::{Deserialize, Serialize}; + +use super::dto_db_resources::DTODBResources; +use crate::infrastructure::db_assets::WebEDBRepository; + +#[derive(Clone, Serialize, Deserialize)] +pub struct DTODBServiceResponseConnection { + pub category: String, + pub connection: String, + pub resources: DTODBResources +} + +impl DTODBServiceResponseConnection { + + pub fn from(connection: ConnectionData) -> DTODBServiceResponseConnection { + DTODBServiceResponseConnection { + category: connection.category().to_string(), + connection: connection.connection(), + resources: connection.category().resources() + } + } + +} \ No newline at end of file diff --git a/src/infrastructure/handler.rs b/src/infrastructure/handler.rs index 74260a3..31511bf 100644 --- a/src/infrastructure/handler.rs +++ b/src/infrastructure/handler.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use axum::{extract::{Path, Request}, http::{HeaderMap, StatusCode}, middleware::Next, response::{IntoResponse, Response}}; use rust_db_manager_core::commons::configuration::configuration::Configuration; @@ -5,8 +7,14 @@ use crate::commons::exception::api_exception::ApiException; use super::{services_jwt::ServicesJWT, utils::find_token}; -pub(crate) async fn autentication_handler(headers: HeaderMap, Path(service): Path, request: Request, next: Next) -> Result { - let o_service = Configuration::find_service(service); +pub(crate) async fn autentication_handler(headers: HeaderMap, Path(params): Path>, request: Request, next: Next) -> Result { + let service = params.get("service"); + if service.is_none() { + let exception = ApiException::new(StatusCode::NOT_FOUND.as_u16(), String::from("Service not defined.")); + return Err(exception.into_response()); + } + + let o_service = Configuration::find_service(&service.unwrap()); if o_service.is_none() { let exception = ApiException::new(StatusCode::NOT_FOUND.as_u16(), String::from("Service not found.")); return Err(exception.into_response()); diff --git a/src/infrastructure/services_jwt.rs b/src/infrastructure/services_jwt.rs index b4a3fcd..fb855c0 100644 --- a/src/infrastructure/services_jwt.rs +++ b/src/infrastructure/services_jwt.rs @@ -134,7 +134,7 @@ impl ServicesJWT { let mut collection = Vec::new(); for s_service in v_services.unwrap().split("-").filter(|s| !s.is_empty()).collect::>() { - let service = Configuration::find_service(String::from(s_service)); + let service = Configuration::find_service(s_service); if service.is_none() { let exception = AuthException::new_reset(500, String::from("Unknown service.")); return Err(exception); diff --git a/src/lib.rs b/src/lib.rs index aedb0be..6ba1935 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,10 +21,15 @@ pub mod domain { pub mod infrastructure { pub mod dto { pub mod db_service { + pub mod db { + pub mod dto_db_create; + } pub mod dto_db_connection_data; pub mod dto_db_resources; pub mod dto_db_service_web_category; pub mod dto_db_service_lite; + pub mod dto_db_service_response_connection; + pub mod dto_db_service_response; pub mod dto_db_service_suscribe; pub mod dto_db_service; } From eac7ca4a3ab0b76e6bfba1f8cdec9a006d5c2419 Mon Sep 17 00:00:00 2001 From: Rafael Ruiz Date: Tue, 7 May 2024 00:58:19 +0200 Subject: [PATCH 13/22] [FEATURE] Collection and document controllers created. --- src/infrastructure/controller_collection.rs | 94 +++++++++++++++++++++ src/infrastructure/controller_database.rs | 18 ++-- src/infrastructure/controller_document.rs | 42 +++++++++ src/lib.rs | 2 + src/main.rs | 4 +- 5 files changed, 150 insertions(+), 10 deletions(-) create mode 100644 src/infrastructure/controller_collection.rs create mode 100644 src/infrastructure/controller_document.rs diff --git a/src/infrastructure/controller_collection.rs b/src/infrastructure/controller_collection.rs new file mode 100644 index 0000000..bc0fc4f --- /dev/null +++ b/src/infrastructure/controller_collection.rs @@ -0,0 +1,94 @@ +use axum::{extract::Path, http::StatusCode, middleware, response::IntoResponse, routing::{delete, get}, Json, Router}; +use rust_db_manager_core::{commons::configuration::configuration::Configuration, domain::{filter::data_base_query::DataBaseQuery, generate::generate_collection_query::GenerateCollectionQuery}}; + +use crate::commons::exception::api_exception::ApiException; + +use super::{dto::dto_data_base_group::DTODataBaseGroup, handler, utils}; + +pub struct ControllerCollection { +} + +impl ControllerCollection { + + pub fn route(router: Router) -> Router { + router + .route("/:service/data-base/:data_base/metadata", get(Self::metadata)) + .route("/:service/data-base/:data_base/collection", get(Self::find_all)) + .route("/:service/data-base/:data_base/collection/:collection", delete(Self::delete)) + .route_layer(middleware::from_fn(handler::autentication_handler)) + } + + async fn metadata(Path((service, data_base)): Path<(String, String)>) -> Result>, impl IntoResponse> { + let o_db_service = Configuration::find_service(&service); + if o_db_service.is_none() { + return Err(utils::not_found()); + } + + let result = o_db_service.unwrap().instance().await; + if let Err(error) = result { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + let query = DataBaseQuery::from_data_base(data_base); + + let metadata = result.unwrap().data_base_collections_metadata(&query).await; + if let Err(error) = metadata { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + let dto = metadata.unwrap().iter() + .map(|g| DTODataBaseGroup::from(g)) + .collect::>(); + + Ok(Json(dto)) + } + + async fn find_all(Path((service, data_base)): Path<(String, String)>) -> Result>, impl IntoResponse> { + let o_db_service = Configuration::find_service(&service); + if o_db_service.is_none() { + return Err(utils::not_found()); + } + + let result = o_db_service.unwrap().instance().await; + if let Err(error) = result { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + let query = DataBaseQuery::from_data_base(data_base); + + let collections = result.unwrap().list_collections(&query).await; + if let Err(error) = collections { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + Ok(Json(collections.unwrap())) + } + + async fn delete(Path((service, data_base, collection)): Path<(String, String, String)>) -> Result { + let o_db_service = Configuration::find_service(&service); + if o_db_service.is_none() { + return Err(utils::not_found()); + } + + let result = o_db_service.unwrap().instance().await; + if let Err(error) = result { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + let query = GenerateCollectionQuery::new(data_base, collection); + + let collection = result.unwrap().drop_collection(&query).await; + if let Err(error) = collection { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + Ok(StatusCode::ACCEPTED) + } + +} \ No newline at end of file diff --git a/src/infrastructure/controller_database.rs b/src/infrastructure/controller_database.rs index 9fb8bfc..24ce9dc 100644 --- a/src/infrastructure/controller_database.rs +++ b/src/infrastructure/controller_database.rs @@ -14,9 +14,9 @@ impl ControllerDataBase { router .route("/:service/status", get(Self::status)) .route("/:service/metadata", get(Self::metadata)) - .route("/:service/data-base", get(Self::data_bases)) - .route("/:service/data-base", post(Self::insert_base)) - .route("/:service/data-base/:data_base", delete(Self::delete_base)) + .route("/:service/data-base", get(Self::find_all)) + .route("/:service/data-base", post(Self::insert)) + .route("/:service/data-base/:data_base", delete(Self::delete)) .route_layer(middleware::from_fn(handler::autentication_handler)) } @@ -53,7 +53,7 @@ impl ControllerDataBase { return Err(exception.into_response()); } - let metadata = result.unwrap().metadata().await; + let metadata = result.unwrap().data_base_metadata().await; if let Err(error) = metadata { let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); return Err(exception.into_response()); @@ -66,7 +66,7 @@ impl ControllerDataBase { Ok(Json(dto)) } - async fn data_bases(Path(service): Path) -> Result>, impl IntoResponse> { + async fn find_all(Path(service): Path) -> Result>, impl IntoResponse> { let o_db_service = Configuration::find_service(&service); if o_db_service.is_none() { return Err(utils::not_found()); @@ -87,7 +87,7 @@ impl ControllerDataBase { Ok(Json(collection.unwrap())) } - async fn insert_base(Path(service): Path, Json(dto): Json) -> Result { + async fn insert(Path(service): Path, Json(dto): Json) -> Result { let o_db_service = Configuration::find_service(&service); if o_db_service.is_none() { return Err(utils::not_found()); @@ -101,7 +101,7 @@ impl ControllerDataBase { let query = GenerateDatabaseQuery::new(dto.data_base); - let collection = result.unwrap().create_data_base(query).await; + let collection = result.unwrap().create_data_base(&query).await; if let Err(error) = collection { let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); return Err(exception.into_response()); @@ -110,7 +110,7 @@ impl ControllerDataBase { Ok(StatusCode::ACCEPTED) } - async fn delete_base(Path((service, data_base)): Path<(String, String)>) -> Result { + async fn delete(Path((service, data_base)): Path<(String, String)>) -> Result { let o_db_service = Configuration::find_service(&service); if o_db_service.is_none() { return Err(utils::not_found()); @@ -124,7 +124,7 @@ impl ControllerDataBase { let query = GenerateDatabaseQuery::new(data_base); - let collection = result.unwrap().drop_data_base(query).await; + let collection = result.unwrap().drop_data_base(&query).await; if let Err(error) = collection { let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); return Err(exception.into_response()); diff --git a/src/infrastructure/controller_document.rs b/src/infrastructure/controller_document.rs new file mode 100644 index 0000000..04676b9 --- /dev/null +++ b/src/infrastructure/controller_document.rs @@ -0,0 +1,42 @@ +use axum::{extract::Path, http::StatusCode, middleware, response::IntoResponse, routing::get, Json, Router}; +use rust_db_manager_core::{commons::configuration::configuration::Configuration, domain::filter::data_base_query::DataBaseQuery}; + +use crate::commons::exception::api_exception::ApiException; + +use super::{handler, utils}; + +pub struct ControllerDocument { +} + +impl ControllerDocument { + + pub fn route(router: Router) -> Router { + router + .route("/:service/data-base/:data_base/collection/:collection", get(Self::find_all)) + .route_layer(middleware::from_fn(handler::autentication_handler)) + } + + async fn find_all(Path((service, data_base, collection)): Path<(String, String, String)>) -> Result>, impl IntoResponse> { + let o_db_service = Configuration::find_service(&service); + if o_db_service.is_none() { + return Err(utils::not_found()); + } + + let result = o_db_service.unwrap().instance().await; + if let Err(error) = result { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + let query = DataBaseQuery::from(data_base, collection); + + let documents = result.unwrap().find_all(&query).await; + if let Err(error) = documents { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + Ok(Json(documents.unwrap())) + } + +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 6ba1935..51948e6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -41,7 +41,9 @@ pub mod infrastructure { pub mod dto_data_base_group; pub mod dto_server_status; } + pub mod controller_collection; pub mod controller_database; + pub mod controller_document; pub mod controller_server; pub mod db_assets; pub mod handler; diff --git a/src/main.rs b/src/main.rs index 24c58d1..70fd520 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,7 @@ use std::net::SocketAddr; use tower_http::cors::CorsLayer; use axum::Router; -use rust_db_manager_api::{commons::configuration::web_configuration::WebConfiguration, infrastructure::{controller_database::ControllerDataBase, controller_server::ControllerServer}}; +use rust_db_manager_api::{commons::configuration::web_configuration::WebConfiguration, infrastructure::{controller_collection::ControllerCollection, controller_database::ControllerDataBase, controller_document::ControllerDocument, controller_server::ControllerServer}}; #[tokio::main] async fn main() { @@ -12,6 +12,8 @@ async fn main() { let app = Router::new() .merge(ControllerServer::route(Router::new())) .merge(ControllerDataBase::route(Router::new())) + .merge(ControllerCollection::route(Router::new())) + .merge(ControllerDocument::route(Router::new())) .layer(CorsLayer::very_permissive()) .into_make_service_with_connect_info::(); From 00afb87fe280bdd22c297f7ac3eb5d85ef86813c Mon Sep 17 00:00:00 2001 From: Rafael Ruiz Date: Wed, 8 May 2024 01:52:50 +0200 Subject: [PATCH 14/22] [FEATURE] Collection definition implemented. --- src/infrastructure/controller_database.rs | 28 +++++++++++++++- .../dto_field_attribute_default_definition.rs | 19 +++++++++++ .../dto_field_attribute_definition.rs | 25 ++++++++++++++ .../dto/definition/dto_field_definition.rs | 33 +++++++++++++++++++ src/lib.rs | 5 +++ 5 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 src/infrastructure/dto/definition/dto_field_attribute_default_definition.rs create mode 100644 src/infrastructure/dto/definition/dto_field_attribute_definition.rs create mode 100644 src/infrastructure/dto/definition/dto_field_definition.rs diff --git a/src/infrastructure/controller_database.rs b/src/infrastructure/controller_database.rs index 24ce9dc..6b9c155 100644 --- a/src/infrastructure/controller_database.rs +++ b/src/infrastructure/controller_database.rs @@ -3,7 +3,7 @@ use rust_db_manager_core::{commons::configuration::configuration::Configuration, use crate::commons::exception::api_exception::ApiException; -use super::{dto::{db_service::db::dto_db_create::DTODBCreate, dto_data_base_group::DTODataBaseGroup}, handler, utils}; +use super::{dto::{db_service::db::dto_db_create::DTODBCreate, definition::dto_field_definition::DTOFieldDefinition, dto_data_base_group::DTODataBaseGroup}, handler, utils}; pub struct ControllerDataBase { } @@ -14,6 +14,7 @@ impl ControllerDataBase { router .route("/:service/status", get(Self::status)) .route("/:service/metadata", get(Self::metadata)) + .route("/:service/definition", get(Self::definition)) .route("/:service/data-base", get(Self::find_all)) .route("/:service/data-base", post(Self::insert)) .route("/:service/data-base/:data_base", delete(Self::delete)) @@ -66,6 +67,31 @@ impl ControllerDataBase { Ok(Json(dto)) } + async fn definition(Path(service): Path) -> Result>, impl IntoResponse> { + let o_db_service = Configuration::find_service(&service); + if o_db_service.is_none() { + return Err(utils::not_found()); + } + + let result = o_db_service.unwrap().instance().await; + if let Err(error) = result { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + let definition = result.unwrap().collection_accept_definition().await; + if let Err(error) = definition { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + let dto = definition.unwrap().iter() + .map(|d| DTOFieldDefinition::from(d)) + .collect::>(); + + Ok(Json(dto)) + } + async fn find_all(Path(service): Path) -> Result>, impl IntoResponse> { let o_db_service = Configuration::find_service(&service); if o_db_service.is_none() { diff --git a/src/infrastructure/dto/definition/dto_field_attribute_default_definition.rs b/src/infrastructure/dto/definition/dto_field_attribute_default_definition.rs new file mode 100644 index 0000000..8c62c6b --- /dev/null +++ b/src/infrastructure/dto/definition/dto_field_attribute_default_definition.rs @@ -0,0 +1,19 @@ +use rust_db_manager_core::domain::definition::field::field_attribute_default_definition::FieldAttributeDefaultDefinition; +use serde::Serialize; + +#[derive(Clone, Serialize)] +pub struct DTOFieldAttributeDefaultDefinition { + key: String, + value: String +} + +impl DTOFieldAttributeDefaultDefinition { + + pub fn from(default: &FieldAttributeDefaultDefinition) -> Self { + Self { + key: default.key(), + value: default.value() + } + } + +} \ No newline at end of file diff --git a/src/infrastructure/dto/definition/dto_field_attribute_definition.rs b/src/infrastructure/dto/definition/dto_field_attribute_definition.rs new file mode 100644 index 0000000..727205d --- /dev/null +++ b/src/infrastructure/dto/definition/dto_field_attribute_definition.rs @@ -0,0 +1,25 @@ +use rust_db_manager_core::domain::definition::field::field_attribute_definition::FieldAttributeDefinition; +use serde::Serialize; + +use super::dto_field_attribute_default_definition::DTOFieldAttributeDefaultDefinition; + +#[derive(Clone, Serialize)] +pub struct DTOFieldAttributeDefinition { + name: String, + code: String, + values: Vec, +} + +impl DTOFieldAttributeDefinition { + + pub fn from(attribute: &FieldAttributeDefinition) -> Self { + Self { + name: attribute.name(), + code: attribute.code(), + values: attribute.values().iter() + .map(|a| DTOFieldAttributeDefaultDefinition::from(a)) + .collect() + } + } + +} \ No newline at end of file diff --git a/src/infrastructure/dto/definition/dto_field_definition.rs b/src/infrastructure/dto/definition/dto_field_definition.rs new file mode 100644 index 0000000..7b9c7cf --- /dev/null +++ b/src/infrastructure/dto/definition/dto_field_definition.rs @@ -0,0 +1,33 @@ +use rust_db_manager_core::domain::definition::field::field_definition::FieldDefinition; +use serde::Serialize; + +use super::dto_field_attribute_definition::DTOFieldAttributeDefinition; + +#[derive(Clone, Serialize)] +pub struct DTOFieldDefinition { + order: usize, + name: String, + code: String, + category: String, + size: bool, + multiple: bool, + attributes: Vec +} + +impl DTOFieldDefinition { + + pub fn from(definition: &FieldDefinition) -> Self { + Self { + order: definition.order(), + name: definition.name(), + code: definition.code().to_string(), + category: definition.category().to_string(), + size: definition.size(), + multiple: definition.multiple(), + attributes: definition.attributes().iter() + .map(|a| DTOFieldAttributeDefinition::from(a)) + .collect() + } + } + +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 51948e6..e2d687b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,6 +33,11 @@ pub mod infrastructure { pub mod dto_db_service_suscribe; pub mod dto_db_service; } + pub mod definition { + pub mod dto_field_attribute_default_definition; + pub mod dto_field_attribute_definition; + pub mod dto_field_definition; + } pub mod pagination { pub mod dto_paginated_collection; pub mod dto_query_pagination; From 648af3c0dd434168c38f84f97b77f15231e87510 Mon Sep 17 00:00:00 2001 From: Rafael Ruiz Date: Thu, 9 May 2024 20:54:40 +0200 Subject: [PATCH 15/22] [FEATURE] Collection updates: - Collection definition end-point response struct updated. - Collection insert end-point implemented. --- src/infrastructure/controller_collection.rs | 33 +++++++++- src/infrastructure/controller_database.rs | 12 ++-- .../definition/dto_collection_definition.rs | 27 ++++++++ .../dto/definition/dto_field_atribute.rs | 23 +++++++ .../dto/definition/dto_field_data.rs | 61 +++++++++++++++++++ .../dto/definition/dto_field_definition.rs | 6 +- .../dto/definition/dto_field_reference.rs | 23 +++++++ .../request/dto_generate_collection_query.rs | 25 ++++++++ src/lib.rs | 7 +++ 9 files changed, 202 insertions(+), 15 deletions(-) create mode 100644 src/infrastructure/dto/definition/dto_collection_definition.rs create mode 100644 src/infrastructure/dto/definition/dto_field_atribute.rs create mode 100644 src/infrastructure/dto/definition/dto_field_data.rs create mode 100644 src/infrastructure/dto/definition/dto_field_reference.rs create mode 100644 src/infrastructure/dto/request/dto_generate_collection_query.rs diff --git a/src/infrastructure/controller_collection.rs b/src/infrastructure/controller_collection.rs index bc0fc4f..929ce5d 100644 --- a/src/infrastructure/controller_collection.rs +++ b/src/infrastructure/controller_collection.rs @@ -1,9 +1,9 @@ -use axum::{extract::Path, http::StatusCode, middleware, response::IntoResponse, routing::{delete, get}, Json, Router}; +use axum::{extract::Path, http::StatusCode, middleware, response::IntoResponse, routing::{delete, get, post}, Json, Router}; use rust_db_manager_core::{commons::configuration::configuration::Configuration, domain::{filter::data_base_query::DataBaseQuery, generate::generate_collection_query::GenerateCollectionQuery}}; use crate::commons::exception::api_exception::ApiException; -use super::{dto::dto_data_base_group::DTODataBaseGroup, handler, utils}; +use super::{dto::{dto_data_base_group::DTODataBaseGroup, request::dto_generate_collection_query::DTOGenerateCollectionQuery}, handler, utils}; pub struct ControllerCollection { } @@ -14,6 +14,7 @@ impl ControllerCollection { router .route("/:service/data-base/:data_base/metadata", get(Self::metadata)) .route("/:service/data-base/:data_base/collection", get(Self::find_all)) + .route("/:service/data-base/:data_base/collection", post(Self::insert)) .route("/:service/data-base/:data_base/collection/:collection", delete(Self::delete)) .route_layer(middleware::from_fn(handler::autentication_handler)) } @@ -68,6 +69,32 @@ impl ControllerCollection { Ok(Json(collections.unwrap())) } + async fn insert(Path((service, _)): Path<(String, String)>, Json(dto): Json) -> Result { + let o_db_service = Configuration::find_service(&service); + if o_db_service.is_none() { + return Err(utils::not_found()); + } + + let result = o_db_service.unwrap().instance().await; + if let Err(error) = result { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + let query = dto.from_dto(); + if let Err(exception) = query { + return Err(exception.into_response()); + } + + let collection = result.unwrap().create_collection(&query.unwrap()).await; + if let Err(error) = collection { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + Ok(StatusCode::ACCEPTED) + } + async fn delete(Path((service, data_base, collection)): Path<(String, String, String)>) -> Result { let o_db_service = Configuration::find_service(&service); if o_db_service.is_none() { @@ -80,7 +107,7 @@ impl ControllerCollection { return Err(exception.into_response()); } - let query = GenerateCollectionQuery::new(data_base, collection); + let query = GenerateCollectionQuery::from_collection(data_base, collection); let collection = result.unwrap().drop_collection(&query).await; if let Err(error) = collection { diff --git a/src/infrastructure/controller_database.rs b/src/infrastructure/controller_database.rs index 6b9c155..28c7809 100644 --- a/src/infrastructure/controller_database.rs +++ b/src/infrastructure/controller_database.rs @@ -3,7 +3,7 @@ use rust_db_manager_core::{commons::configuration::configuration::Configuration, use crate::commons::exception::api_exception::ApiException; -use super::{dto::{db_service::db::dto_db_create::DTODBCreate, definition::dto_field_definition::DTOFieldDefinition, dto_data_base_group::DTODataBaseGroup}, handler, utils}; +use super::{dto::{db_service::db::dto_db_create::DTODBCreate, definition::dto_collection_definition::DTOCollectionDefinition, dto_data_base_group::DTODataBaseGroup}, handler, utils}; pub struct ControllerDataBase { } @@ -67,7 +67,7 @@ impl ControllerDataBase { Ok(Json(dto)) } - async fn definition(Path(service): Path) -> Result>, impl IntoResponse> { + async fn definition(Path(service): Path) -> Result, impl IntoResponse> { let o_db_service = Configuration::find_service(&service); if o_db_service.is_none() { return Err(utils::not_found()); @@ -84,12 +84,8 @@ impl ControllerDataBase { let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); return Err(exception.into_response()); } - - let dto = definition.unwrap().iter() - .map(|d| DTOFieldDefinition::from(d)) - .collect::>(); - - Ok(Json(dto)) + + Ok(Json(DTOCollectionDefinition::from(definition.unwrap()))) } async fn find_all(Path(service): Path) -> Result>, impl IntoResponse> { diff --git a/src/infrastructure/dto/definition/dto_collection_definition.rs b/src/infrastructure/dto/definition/dto_collection_definition.rs new file mode 100644 index 0000000..98b8754 --- /dev/null +++ b/src/infrastructure/dto/definition/dto_collection_definition.rs @@ -0,0 +1,27 @@ +use rust_db_manager_core::domain::definition::collection_definition::CollectionDefinition; +use serde::Serialize; + +use super::{dto_field_data::DTOFieldData, dto_field_definition::DTOFieldDefinition}; + +#[derive(Clone, Serialize)] +pub struct DTOCollectionDefinition { + swrelational: bool, + definition: Vec, + defaults: Vec +} + +impl DTOCollectionDefinition { + + pub fn from(definition: CollectionDefinition) -> Self { + Self { + swrelational: definition.is_relational(), + definition: definition.definition().iter() + .map(|f| DTOFieldDefinition::from(f)) + .collect(), + defaults: definition.defaults().iter() + .map(|d| DTOFieldData::from(d)) + .collect() + } + } + +} \ No newline at end of file diff --git a/src/infrastructure/dto/definition/dto_field_atribute.rs b/src/infrastructure/dto/definition/dto_field_atribute.rs new file mode 100644 index 0000000..7dab8ad --- /dev/null +++ b/src/infrastructure/dto/definition/dto_field_atribute.rs @@ -0,0 +1,23 @@ +use rust_db_manager_core::domain::generate::field::field_attribute::FieldAttribute; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Serialize, Deserialize)] +pub struct DTOFieldAttribute { + key: String, + value: String, +} + +impl DTOFieldAttribute { + + pub fn from(attribute: &FieldAttribute) -> Self { + Self { + key: attribute.key(), + value: attribute.value() + } + } + + pub fn from_dto(&self) -> FieldAttribute { + FieldAttribute::new(self.key.clone(), self.value.clone()) + } + +} \ No newline at end of file diff --git a/src/infrastructure/dto/definition/dto_field_data.rs b/src/infrastructure/dto/definition/dto_field_data.rs new file mode 100644 index 0000000..6ff7b5b --- /dev/null +++ b/src/infrastructure/dto/definition/dto_field_data.rs @@ -0,0 +1,61 @@ +use rust_db_manager_core::domain::{definition::field::e_field_code::EFieldCode, generate::field::field_data::FieldData}; +use serde::{Deserialize, Serialize}; + +use crate::commons::exception::api_exception::ApiException; + +use super::{dto_field_atribute::DTOFieldAttribute, dto_field_reference::DTOFieldReference}; + +#[derive(Clone, Serialize, Deserialize)] +pub struct DTOFieldData { + order: i32, + code: String, + value: String, + swsize: bool, + size: i32, + mutable: bool, + attributes: Vec, + reference: Vec +} + +impl DTOFieldData { + + pub fn from(field: &FieldData) -> Self { + Self { + order: field.order(), + code: field.code().to_string(), + value: field.value(), + swsize: field.is_resize(), + size: field.size(), + mutable: field.is_mutable(), + attributes: field.attributes().iter() + .map(|a| DTOFieldAttribute::from(a)) + .collect(), + reference: field.reference().iter() + .map(|r| DTOFieldReference::from(r)) + .collect() + } + } + + pub fn from_dto(&self) -> Result { + let code = EFieldCode::from_string(&self.code); + if let None = code { + let exception = ApiException::new(422, String::from("Field code not recognized.")); + return Err(exception); + } + + let attributes = self.attributes.iter() + .map(|a| a.from_dto()) + .collect(); + + let reference = self.reference.iter() + .map(|a| a.from_dto()) + .collect(); + + Ok(FieldData::new( + self.order, code.unwrap(), self.value.clone(), + self.swsize, self.size, self.mutable, + attributes, reference + )) + } + +} \ No newline at end of file diff --git a/src/infrastructure/dto/definition/dto_field_definition.rs b/src/infrastructure/dto/definition/dto_field_definition.rs index 7b9c7cf..9842388 100644 --- a/src/infrastructure/dto/definition/dto_field_definition.rs +++ b/src/infrastructure/dto/definition/dto_field_definition.rs @@ -8,8 +8,7 @@ pub struct DTOFieldDefinition { order: usize, name: String, code: String, - category: String, - size: bool, + swsize: bool, multiple: bool, attributes: Vec } @@ -21,8 +20,7 @@ impl DTOFieldDefinition { order: definition.order(), name: definition.name(), code: definition.code().to_string(), - category: definition.category().to_string(), - size: definition.size(), + swsize: definition.swsize(), multiple: definition.multiple(), attributes: definition.attributes().iter() .map(|a| DTOFieldAttributeDefinition::from(a)) diff --git a/src/infrastructure/dto/definition/dto_field_reference.rs b/src/infrastructure/dto/definition/dto_field_reference.rs new file mode 100644 index 0000000..2015512 --- /dev/null +++ b/src/infrastructure/dto/definition/dto_field_reference.rs @@ -0,0 +1,23 @@ +use rust_db_manager_core::domain::generate::field::field_reference::FieldReference; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Serialize, Deserialize)] +pub struct DTOFieldReference { + collection: String, + field: String +} + +impl DTOFieldReference { + + pub fn from(reference: &FieldReference) -> Self { + Self { + collection: reference.collection(), + field: reference.field() + } + } + + pub fn from_dto(&self) -> FieldReference { + FieldReference::new(self.collection.clone(), self.field.clone()) + } + +} \ No newline at end of file diff --git a/src/infrastructure/dto/request/dto_generate_collection_query.rs b/src/infrastructure/dto/request/dto_generate_collection_query.rs new file mode 100644 index 0000000..b1f4860 --- /dev/null +++ b/src/infrastructure/dto/request/dto_generate_collection_query.rs @@ -0,0 +1,25 @@ +use rust_db_manager_core::domain::generate::generate_collection_query::GenerateCollectionQuery; +use serde::Deserialize; + +use crate::{commons::exception::api_exception::ApiException, infrastructure::dto::definition::dto_field_data::DTOFieldData}; + +#[derive(Clone, Deserialize)] +pub struct DTOGenerateCollectionQuery { + data_base: String, + collection: String, + fields: Option> +} + +impl DTOGenerateCollectionQuery { + + pub fn from_dto(&self) -> Result { + let mut fields = Vec::new(); + if let Some(dtos) = self.fields.clone() { + for dto in dtos { + fields.push(dto.from_dto()?); + } + } + Ok(GenerateCollectionQuery::new(self.data_base.clone(), self.collection.clone(), fields)) + } + +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index e2d687b..b19a245 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,14 +34,21 @@ pub mod infrastructure { pub mod dto_db_service; } pub mod definition { + pub mod dto_collection_definition; + pub mod dto_field_atribute; pub mod dto_field_attribute_default_definition; pub mod dto_field_attribute_definition; pub mod dto_field_definition; + pub mod dto_field_reference; + pub mod dto_field_data; } pub mod pagination { pub mod dto_paginated_collection; pub mod dto_query_pagination; } + pub mod request { + pub mod dto_generate_collection_query; + } pub mod dto_data_base_field; pub mod dto_data_base_group; pub mod dto_server_status; From 38353fe0ac3e62086d5dbdaa2140e7e2ed0fd9db Mon Sep 17 00:00:00 2001 From: Rafael Ruiz Date: Fri, 10 May 2024 13:36:55 +0200 Subject: [PATCH 16/22] [FEATURE] Project reorganization and refactoring. --- src/domain/builder_db_connection_data.rs | 4 +- src/domain/builder_db_service.rs | 4 +- src/infrastructure/controller_collection.rs | 10 +-- src/infrastructure/controller_database.rs | 12 ++-- src/infrastructure/controller_document.rs | 38 ++++++++++- src/infrastructure/controller_server.rs | 66 +++++++++---------- src/infrastructure/db_assets.rs | 16 ++--- .../dto_collection_definition.rs | 4 +- .../dto_generate_collection_query.rs | 4 +- .../dto_generate_data_base_query.rs} | 2 +- .../dto/db_service/dto_db_service_lite.rs | 20 ------ .../dto/db_service/dto_db_service_response.rs | 27 -------- .../db_service/dto_db_service_web_category.rs | 22 ------- .../dto/document/dto_document_data.rs | 33 ++++++++++ .../dto/document/dto_document_key.rs | 26 ++++++++ .../document/dto_document_key_attribute.rs | 20 ++++++ src/infrastructure/dto/dto_data_base_group.rs | 25 ------- .../dto_field_attribute_default_definition.rs | 2 +- .../dto_field_attribute_definition.rs | 2 +- .../definition/dto_field_definition.rs | 2 +- .../generate}/dto_field_atribute.rs | 2 +- .../generate}/dto_field_data.rs | 2 +- .../generate}/dto_field_reference.rs | 2 +- .../dto/service/definition/dto_service.rs | 27 ++++++++ .../definition/dto_service_category.rs} | 17 ++--- .../definition/dto_service_category_lite.rs | 23 +++++++ .../service/definition/dto_service_lite.rs | 21 ++++++ .../definition/dto_service_resources.rs} | 8 +-- .../generate}/dto_db_connection_data.rs | 2 +- .../generate/dto_service_create_request.rs} | 6 +- .../generate/dto_service_suscribe_request.rs} | 2 +- .../dto_table_data_field.rs} | 8 +-- .../dto/table/dto_table_data_group.rs | 25 +++++++ src/lib.rs | 63 +++++++++++------- 34 files changed, 337 insertions(+), 210 deletions(-) rename src/infrastructure/dto/{definition => collection}/dto_collection_definition.rs (76%) rename src/infrastructure/dto/{request => collection}/dto_generate_collection_query.rs (80%) rename src/infrastructure/dto/{db_service/db/dto_db_create.rs => data_base/dto_generate_data_base_query.rs} (68%) delete mode 100644 src/infrastructure/dto/db_service/dto_db_service_lite.rs delete mode 100644 src/infrastructure/dto/db_service/dto_db_service_response.rs delete mode 100644 src/infrastructure/dto/db_service/dto_db_service_web_category.rs create mode 100644 src/infrastructure/dto/document/dto_document_data.rs create mode 100644 src/infrastructure/dto/document/dto_document_key.rs create mode 100644 src/infrastructure/dto/document/dto_document_key_attribute.rs delete mode 100644 src/infrastructure/dto/dto_data_base_group.rs rename src/infrastructure/dto/{ => field}/definition/dto_field_attribute_default_definition.rs (86%) rename src/infrastructure/dto/{ => field}/definition/dto_field_attribute_definition.rs (91%) rename src/infrastructure/dto/{ => field}/definition/dto_field_definition.rs (92%) rename src/infrastructure/dto/{definition => field/generate}/dto_field_atribute.rs (88%) rename src/infrastructure/dto/{definition => field/generate}/dto_field_data.rs (93%) rename src/infrastructure/dto/{definition => field/generate}/dto_field_reference.rs (88%) create mode 100644 src/infrastructure/dto/service/definition/dto_service.rs rename src/infrastructure/dto/{db_service/dto_db_service_response_connection.rs => service/definition/dto_service_category.rs} (50%) create mode 100644 src/infrastructure/dto/service/definition/dto_service_category_lite.rs create mode 100644 src/infrastructure/dto/service/definition/dto_service_lite.rs rename src/infrastructure/dto/{db_service/dto_db_resources.rs => service/definition/dto_service_resources.rs} (74%) rename src/infrastructure/dto/{db_service => service/generate}/dto_db_connection_data.rs (76%) rename src/infrastructure/dto/{db_service/dto_db_service.rs => service/generate/dto_service_create_request.rs} (53%) rename src/infrastructure/dto/{db_service/dto_db_service_suscribe.rs => service/generate/dto_service_suscribe_request.rs} (76%) rename src/infrastructure/dto/{dto_data_base_field.rs => table/dto_table_data_field.rs} (63%) create mode 100644 src/infrastructure/dto/table/dto_table_data_group.rs diff --git a/src/domain/builder_db_connection_data.rs b/src/domain/builder_db_connection_data.rs index ef88232..b71958f 100644 --- a/src/domain/builder_db_connection_data.rs +++ b/src/domain/builder_db_connection_data.rs @@ -1,13 +1,13 @@ use rust_db_manager_core::{domain::connection_data::ConnectionData, infrastructure::repository::e_db_repository::EDBRepository}; -use crate::{commons::exception::api_exception::ApiException, infrastructure::dto::db_service::dto_db_connection_data::DTOConnectionData}; +use crate::{commons::exception::api_exception::ApiException, infrastructure::dto::service::generate::dto_db_connection_data::DTODBConnectionData}; pub struct BuilderConnectionData { } impl BuilderConnectionData { - pub fn make(dto: DTOConnectionData) -> Result { + pub fn make(dto: DTODBConnectionData) -> Result { let category = EDBRepository::from_string(&dto.category); if let None = category { let message = format!("Data base type '{}' not supported.", dto.category); diff --git a/src/domain/builder_db_service.rs b/src/domain/builder_db_service.rs index 150c609..8c23879 100644 --- a/src/domain/builder_db_service.rs +++ b/src/domain/builder_db_service.rs @@ -1,6 +1,6 @@ use rust_db_manager_core::infrastructure::db_service::DBService; -use crate::{commons::exception::api_exception::ApiException, infrastructure::dto::db_service::dto_db_service::DTODBService}; +use crate::{commons::exception::api_exception::ApiException, infrastructure::dto::service::generate::dto_service_create_request::DTOServiceRequest}; use super::builder_db_connection_data::BuilderConnectionData; @@ -9,7 +9,7 @@ pub struct BuilderDBService { impl BuilderDBService { - pub fn make(dto: DTODBService) -> Result { + pub fn make(dto: DTOServiceRequest) -> Result { let connection_data = BuilderConnectionData::make(dto.connection_data)?; let service = DBService::new(dto.name, dto.owner, dto.protected, dto.password, connection_data); diff --git a/src/infrastructure/controller_collection.rs b/src/infrastructure/controller_collection.rs index 929ce5d..48a07ab 100644 --- a/src/infrastructure/controller_collection.rs +++ b/src/infrastructure/controller_collection.rs @@ -1,9 +1,9 @@ use axum::{extract::Path, http::StatusCode, middleware, response::IntoResponse, routing::{delete, get, post}, Json, Router}; -use rust_db_manager_core::{commons::configuration::configuration::Configuration, domain::{filter::data_base_query::DataBaseQuery, generate::generate_collection_query::GenerateCollectionQuery}}; +use rust_db_manager_core::{commons::configuration::configuration::Configuration, domain::{collection::generate_collection_query::GenerateCollectionQuery, filter::data_base_query::DataBaseQuery}}; use crate::commons::exception::api_exception::ApiException; -use super::{dto::{dto_data_base_group::DTODataBaseGroup, request::dto_generate_collection_query::DTOGenerateCollectionQuery}, handler, utils}; +use super::{dto::{collection::dto_generate_collection_query::DTOGenerateCollectionQuery, table::dto_table_data_group::DTOTableDataGroup}, handler, utils}; pub struct ControllerCollection { } @@ -19,7 +19,7 @@ impl ControllerCollection { .route_layer(middleware::from_fn(handler::autentication_handler)) } - async fn metadata(Path((service, data_base)): Path<(String, String)>) -> Result>, impl IntoResponse> { + async fn metadata(Path((service, data_base)): Path<(String, String)>) -> Result>, impl IntoResponse> { let o_db_service = Configuration::find_service(&service); if o_db_service.is_none() { return Err(utils::not_found()); @@ -40,8 +40,8 @@ impl ControllerCollection { } let dto = metadata.unwrap().iter() - .map(|g| DTODataBaseGroup::from(g)) - .collect::>(); + .map(|g| DTOTableDataGroup::from(g)) + .collect(); Ok(Json(dto)) } diff --git a/src/infrastructure/controller_database.rs b/src/infrastructure/controller_database.rs index 28c7809..033ee20 100644 --- a/src/infrastructure/controller_database.rs +++ b/src/infrastructure/controller_database.rs @@ -1,9 +1,9 @@ use axum::{extract::Path, http::StatusCode, middleware, response::IntoResponse, routing::{delete, get, post}, Json, Router}; -use rust_db_manager_core::{commons::configuration::configuration::Configuration, domain::generate::generate_database_query::GenerateDatabaseQuery}; +use rust_db_manager_core::{commons::configuration::configuration::Configuration, domain::data_base::generate_database_query::GenerateDatabaseQuery}; use crate::commons::exception::api_exception::ApiException; -use super::{dto::{db_service::db::dto_db_create::DTODBCreate, definition::dto_collection_definition::DTOCollectionDefinition, dto_data_base_group::DTODataBaseGroup}, handler, utils}; +use super::{dto::{collection::dto_collection_definition::DTOCollectionDefinition, data_base::dto_generate_data_base_query::DTOGenerateDatabaseQuery, table::dto_table_data_group::DTOTableDataGroup}, handler, utils}; pub struct ControllerDataBase { } @@ -42,7 +42,7 @@ impl ControllerDataBase { Ok((StatusCode::ACCEPTED, String::from("listening"))) } - async fn metadata(Path(service): Path) -> Result>, impl IntoResponse> { + async fn metadata(Path(service): Path) -> Result>, impl IntoResponse> { let o_db_service = Configuration::find_service(&service); if o_db_service.is_none() { return Err(utils::not_found()); @@ -61,8 +61,8 @@ impl ControllerDataBase { } let dto = metadata.unwrap().iter() - .map(|g| DTODataBaseGroup::from(g)) - .collect::>(); + .map(|g| DTOTableDataGroup::from(g)) + .collect(); Ok(Json(dto)) } @@ -109,7 +109,7 @@ impl ControllerDataBase { Ok(Json(collection.unwrap())) } - async fn insert(Path(service): Path, Json(dto): Json) -> Result { + async fn insert(Path(service): Path, Json(dto): Json) -> Result { let o_db_service = Configuration::find_service(&service); if o_db_service.is_none() { return Err(utils::not_found()); diff --git a/src/infrastructure/controller_document.rs b/src/infrastructure/controller_document.rs index 04676b9..3394785 100644 --- a/src/infrastructure/controller_document.rs +++ b/src/infrastructure/controller_document.rs @@ -3,7 +3,7 @@ use rust_db_manager_core::{commons::configuration::configuration::Configuration, use crate::commons::exception::api_exception::ApiException; -use super::{handler, utils}; +use super::{dto::{document::dto_document_data::DTODocumentData, table::dto_table_data_group::DTOTableDataGroup}, handler, utils}; pub struct ControllerDocument { } @@ -12,11 +12,40 @@ impl ControllerDocument { pub fn route(router: Router) -> Router { router + .route("/:service/data-base/:data_base/collection/:collection/metadata", get(Self::metadata)) .route("/:service/data-base/:data_base/collection/:collection", get(Self::find_all)) .route_layer(middleware::from_fn(handler::autentication_handler)) } - async fn find_all(Path((service, data_base, collection)): Path<(String, String, String)>) -> Result>, impl IntoResponse> { + async fn metadata(Path((service, data_base, collection)): Path<(String, String, String)>) -> Result>, impl IntoResponse> { + let o_db_service = Configuration::find_service(&service); + if o_db_service.is_none() { + return Err(utils::not_found()); + } + + let result = o_db_service.unwrap().instance().await; + if let Err(error) = result { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + let query = DataBaseQuery::from(data_base, collection); + + let metadata = result.unwrap().collection_metadata(&query).await; + if let Err(error) = metadata { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + let dto = metadata.unwrap().iter() + .map(|g| DTOTableDataGroup::from(g)) + .collect(); + + Ok(Json(dto)) + } + + + async fn find_all(Path((service, data_base, collection)): Path<(String, String, String)>) -> Result>, impl IntoResponse> { let o_db_service = Configuration::find_service(&service); if o_db_service.is_none() { return Err(utils::not_found()); @@ -36,7 +65,10 @@ impl ControllerDocument { return Err(exception.into_response()); } - Ok(Json(documents.unwrap())) + Ok(Json(documents.unwrap().iter() + .map(|d| DTODocumentData::from(d)) + .collect()) + ) } } \ No newline at end of file diff --git a/src/infrastructure/controller_server.rs b/src/infrastructure/controller_server.rs index 4e3dfc8..f083c91 100644 --- a/src/infrastructure/controller_server.rs +++ b/src/infrastructure/controller_server.rs @@ -4,7 +4,7 @@ use rust_db_manager_core::{commons::configuration::configuration::Configuration, use crate::{commons::{configuration::web_configuration::WebConfiguration, exception::{api_exception::ApiException, auth_exception::AuthException}}, domain::{builder_db_service::BuilderDBService, cookie::cookie::Cookie}}; -use super::{db_assets::WebEDBRepository, dto::{db_service::{dto_db_service::DTODBService, dto_db_service_lite::DTODBServiceLite, dto_db_service_response::DTODBServiceResponse, dto_db_service_suscribe::DTODBServiceSuscribe, dto_db_service_web_category::DTODBServiceWebCategory}, dto_server_status::DTOServerStatus, pagination::{dto_paginated_collection::DTOPaginatedCollection, dto_query_pagination::DTOQueryPagination}}, handler, pagination::Pagination, services_jwt::ServicesJWT, utils::{self, find_token}}; +use super::{db_assets::WebEDBRepository, dto::{dto_server_status::DTOServerStatus, pagination::{dto_paginated_collection::DTOPaginatedCollection, dto_query_pagination::DTOQueryPagination}, service::{definition::{dto_service::DTOService, dto_service_category_lite::DTOServiceCategoryLite, dto_service_lite::DTOServiceLite}, generate::{dto_service_create_request::DTOServiceRequest, dto_service_suscribe_request::DTOServiceSuscribeRequest}}}, handler, pagination::Pagination, services_jwt::ServicesJWT, utils::{self, find_token}}; pub struct ControllerServer { } @@ -23,24 +23,51 @@ impl ControllerServer { .route("/suscribe", post(Self::suscribe)) } + async fn service_find(Path(service): Path) -> Result,impl IntoResponse> { + let o_db_service = Configuration::find_service(&service); + if o_db_service.is_none() { + return Err(utils::not_found()); + } + + Ok(Json(DTOService::from(o_db_service.unwrap()))) + } + + async fn service_remove(headers: HeaderMap, Path(service): Path) -> impl IntoResponse { + let o_db_service = Configuration::find_service(&service); + if o_db_service.is_none() { + return Err(utils::not_found()); + } + + let db_service = o_db_service.unwrap(); + + let r_cookie = Self::remove_token(headers, &db_service); + if let Err(exception) = r_cookie { + return Err(exception.into_response()); + } + + Configuration::remove_service(db_service); + + Ok(Self::build_token_response(r_cookie.unwrap(), Body::empty())) + } + async fn metadata() -> (StatusCode, Json) { let result = WebConfiguration::as_dto(); (StatusCode::ACCEPTED, Json(result)) } - async fn support() -> (StatusCode, Json>) { + async fn support() -> (StatusCode, Json>) { let dto = EDBRepository::supported(); (StatusCode::ACCEPTED, Json(dto)) } - async fn services(Query(params): Query) -> (StatusCode, Json>) { + async fn services(Query(params): Query) -> (StatusCode, Json>) { let services = Configuration::find_services(); - let dto = DTODBServiceLite::from_vec(services); + let dto = services.iter().map(|s| DTOServiceLite::from(s)).collect(); let result = Pagination::paginate(params, dto); (StatusCode::ACCEPTED, Json(result)) } - async fn publish(headers: HeaderMap, Json(dto): Json) -> impl IntoResponse { + async fn publish(headers: HeaderMap, Json(dto): Json) -> impl IntoResponse { let o_service = BuilderDBService::make(dto); if let Err(error) = o_service { return Err(error.into_response()); @@ -62,7 +89,7 @@ impl ControllerServer { Ok(Self::build_token_response(r_cookie.unwrap(), Body::empty())) } - async fn suscribe(headers: HeaderMap, Json(dto): Json) -> impl IntoResponse { + async fn suscribe(headers: HeaderMap, Json(dto): Json) -> impl IntoResponse { let o_db_service = Configuration::find_service(&dto.name); if o_db_service.is_none() { let exception = ApiException::new(StatusCode::NOT_FOUND.as_u16(), String::from("Service not found.")); @@ -84,33 +111,6 @@ impl ControllerServer { Ok(Self::build_token_response(r_cookie.unwrap(), Body::empty())) } - async fn service_find(Path(service): Path) -> Result,impl IntoResponse> { - let o_db_service = Configuration::find_service(&service); - if o_db_service.is_none() { - return Err(utils::not_found()); - } - - Ok(Json(DTODBServiceResponse::from(o_db_service.unwrap()))) - } - - async fn service_remove(headers: HeaderMap, Path(service): Path) -> impl IntoResponse { - let o_db_service = Configuration::find_service(&service); - if o_db_service.is_none() { - return Err(utils::not_found()); - } - - let db_service = o_db_service.unwrap(); - - let r_cookie = Self::remove_token(headers, &db_service); - if let Err(exception) = r_cookie { - return Err(exception.into_response()); - } - - Configuration::remove_service(db_service); - - Ok(Self::build_token_response(r_cookie.unwrap(), Body::empty())) - } - fn make_token(headers: HeaderMap, service: &DBService) -> Result, AuthException> { let o_cookie = find_token(headers); if o_cookie.is_err() { diff --git a/src/infrastructure/db_assets.rs b/src/infrastructure/db_assets.rs index 044bdda..02ed47b 100644 --- a/src/infrastructure/db_assets.rs +++ b/src/infrastructure/db_assets.rs @@ -1,25 +1,23 @@ use rust_db_manager_core::infrastructure::repository::e_db_repository::EDBRepository; -use super::dto::db_service::{dto_db_resources::DTODBResources, dto_db_service_web_category::DTODBServiceWebCategory}; +use super::dto::service::definition::{dto_service_category_lite::DTOServiceCategoryLite, dto_service_resources::DTOServiceResources}; pub trait WebEDBRepository { - - fn supported() -> Vec; - fn resources(&self) -> DTODBResources; - + fn supported() -> Vec; + fn resources(&self) -> DTOServiceResources; } impl WebEDBRepository for EDBRepository { - fn supported() -> Vec { + fn supported() -> Vec { EDBRepository::items().iter() - .map(|e| DTODBServiceWebCategory::from(e)) + .map(|e| DTOServiceCategoryLite::from(e)) .collect() } - fn resources(&self) -> DTODBResources { + fn resources(&self) -> DTOServiceResources { match self { - EDBRepository::MongoDB => DTODBResources::new( + EDBRepository::MongoDB => DTOServiceResources::new( String::from("https://www.mongodb.com/"), String::from("#00ED64"), String::from("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHgAAAEBCAYAAACzPmFvAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAA3eSURBVHgB7Z1bchvHFYZPD6AqvZmsyKrKU6AVRFpBaG3ASjYQxiZdfpOcBURMFhDbby5biuQNRPYGYmUFUlYg+ClVlmzIb67iYNp9ZgDiNpfume6ePqf7q5JIAsML8M+59pkegJhYnH0KP6p/ESEgFt6cn4KQT8rPpfwj3Hj8DURAHAIvPp5Bcfmdermz1SNvIZvegeMv5sCcDGJA5k+2xEWOqsf4w1/gxdlDkHBy8Dg+9uPZA2AObxe9OL8NhXzRekwB78G7j54DU/hacBl3i2edx2Uq8VqcHgFT+Aq8vLzYi7sNqGOKKdt4zFNgjLtC/NngO+5xjcf8YnDpmvNX0IeluAM3v3oJjOBlwVf1bk8mKmYzi8e8BC4uP9WLu00IdvGYj8AYd0Hcg+Gwisc8YrBOvWsKk/qYvgXr1rumlPWx+tnEoS/wMn82LO42oX4mg341bYHLehdugyuqfjXp9WO6MXh7fdc1WXYKx19+DQShKXBV76qkSviqWcmuH9Nz0ZvFe58NiaPydxJsgtATeHAzoy/qd8qp/WzdMbQEttfM6AcmXeXfQAc6MdhnUtUFoaSLhsD+k6ouyCRd4bvocZKqLsgkXeEL7KxTNRRBIukKW2C8EsFlp2ooBDpd4QqM2WoBFJbtHoS8vBhmkvXmo3sgClo1Z6DLi+EJfHiZCRWCzKzDc9E0xUXwcpjgZrrCEhiTKhAzoIpUCWExDarTFY7Ai/P7RJKqLoJKusKIweF1qoYSTDwOw4LD61QNJZhO1/gCU4+7jYhZCPF4XBcd0gqRK0beLmI8genWu6aMGo/Hc9GH2ypwZdTtIsYRuGlbBa6MuF2Efxc95PJO2oziqv1b8JDLO2lTtTI941fgamhuBrGCrczXZxfgEX8uOl7XfIjHnQT8WXC8rvmQqb/a34/AsbvmfTy6avcuOrnmJrxk1e4teITMkQheGiBuBcZeswx4KnJsPDRA3LnoeHrNQ1GuOr8Fx0/fggPcWbD2VoLRc+RyWdGNBf9wfhsmlne94Y6jsVs3FjwhNtMcAhNwYsX2BcbECsQMEmY4SrjsuuiUWA3FesJl2YLz+0ncQRxBPrVqxfYsOHWs7JFNb9nqcNmz4LIsSljBYofLjgWnssg+lsomOxY8lVHdLs4Llsqm4QJX/eYTSNgF39PXZycwkOECi4LUvlGksGDFwwROTQ23WLDiYQIn63XPQCvuL3CyXj8MtOL+Aifr9ccAK+4ncLJevwyw4n4CZ9LktnEJG/S0YnOB8UxKda9/elqxucBCnkJiHHpYsVkvOq0YjY/hSpOZBacVo/HJL43Wi/UtuLJeXDFie7dsIhhNfehb8HJ5AkncEDCa+tAXODU2wkHA+7qH6glcpudiBokwEOXViSc6h+oJnEqj8Mjgvs5h3UlWKo1CRSvZ6rbgKrlKhMcRXE5Puw7ScNFSyxUkRmDanWy1u+jknsOnY/qy3YINuyaJUThpe7Jd4Ey/3kqMBGbTr89anm4i1b5UwO7iSdOTzQKn2pcQsvGWuy0Cwx8gQYMse7/JTdcLvDi/ndwzJeQMGtx0vcC5HO8u24m+nNQ9WC+wSNkzOSYqpC5ODx4+FBibGyHf0nUIEr5V/7zs8uqdahDyYL3+UGDOvWchFurknQNXltcOQmuNiy5OgCuF/F6d6T8DV2oqn0znoAQVVHL8w/nOI7sCx1AeSelkT8hAwBi8kz/tCrxkvjOsEHP1H2eBFfJk+6tdgYVM5RF1JuL321/uxWDJ24InUmXRxRxYo+Lw/z+++mojMNa/IGbAmZxxBr0B4/Bs/cVG4OVl2pmdC9c2vYyNwLFcEiqzOXBHFlfGuhF4LzizJJ/OIQa2tNy24Dhc9JR7mQSVlqtEqxK4TLAiuLDs+i9K3Ev+Am8lWpXAeT6DGHB0Z5MguZ6XbroSOK57G8Uh8iXcwg+VwFkMAmObEuKx4lWitRb4d5Dghay6kvG46O1VJCHnwB0hjnCEZ10mxbA1QzwJVkk5aXmUVWvACZb8cl0JnMv4NlYpImh2IKpUytTy2QxiQKyyaCSLxF0vxXGmmu8ziA0ZicBSzlSSFYmLLorNWjDnycodpIrBmE5HQSRxd5sseyeLssnBe7JyQ+WiI2E7yYrHmo+yVUGc4IgKv/FYME5UrokliwasCGUkSdb2RGVWRCKwLC04bRHMl4hc9A7xlEzxCLw9USmTwLyJYbJyRTwClxOVa6KYrCyJR+CYJiq3iDTJiqkOjmE+aV/QeKz5bSQWHOFKUomIReBaohA9uxoI54ysCUMigiVD9bpjtuAoyKCA7yFGYpisVGvgWRTTDaImDMUwWVkUP2fxZpgxgFk0+22FYHei8uqxCEJT5aIj2JQkVi9VZtHTfA4Jnkh00cdP58CduhksyXjf6DU3v3pZ1cHc+9FZhC56tbP96gJw8RJig/9kZfn6KoFxJ3TO1LUquU9WSvk//LC24DkkeLHSdBWDmd6JpBXmcVlsx+Bpzlvguj0quU9WrjStBMYJhzgmO+IAM+jV1MpmubAQ/wWu7ExUruDd4LlKmrcEZhyHY5uoLOD5+tONwFEmWkzZ0nIjMN9Eq956Obdo3330fP3pRuAy0dqYNh8ia1PKXQ33boxVdT8igp/4clfDvaE78Q1wQ7aUfywnK3c13BW4isNphIcyW/EX2RW4isPxZNPcJivlYQ51OBedw7fACdGykMJtsrI41O5Q4Gs5vzgcCzXaHQqM9SGnvnTdROXVc5wmK5VmNbV9/aUrS05uOpI6uKjXrOHaJIblEnvqNasXuEq1eZz5bbNXbCYrlXveK4/WNF9dWMivgQNxTFQ+b3qi5fLRCNw0l8nKZfZ501PNAqPJc8im21qVLCYr5SsccG96tv0C8KXg4aY5k8Hf259uY5p/BqzhEJ+XraNW7QJzWCNuu+s39clKqWrfjsGF7j06lu0uIDEiE3jSdUi3wNRr4rqJyjWkJytVcnX8qLPjqLfLTgGfA1W4TlRmep5VT+Aq2UqDAEGx1Jpj1xMYrUBKigsQ7Scl2clK+VT3b9ffCE2Kp0AOpm3KbKmd+OoLXHa2OI7VUgs9+taLmG1lSK1kkhqtVmqTlYVZd9FMYL5WTAMcqmtYFmzCfDNSbo0PSpOV0vy9NxeYkhULja0pqExW9rBepN92wql96R/Z7z3vJzAVK26bqLw6hsJkpcqce1gv0n9DcBJWzKQONqh7D74V+lJZ8XeQcIxZ3bvPsC39Rf4BhIzOzFXok5UDrLf8dhgCnlmFDHfqg/pEZaHC4MB++TCBkWl5hi2AKsFOVqr1XgsjU8MFLtdbi39AiOi0KkOdrMxK6x38tw0XGPnNvz5LLUyLSHgBx4+tTLTaERgh2/wIME5P8j+BJewJjGVTaAmXztRkaJOVFhKrbewJjJQJl0oOQmE5obbW+0oZygVYxK7A5WgP/BUoEdJkZSGs9xXsCozceIwXrT2DEPjtF3MgQ/9+cxv2BS5/atnholsbe0e55oEdqybcCFxNYY7dxtSLvyFMVqJrdvR3uBEYQVc9alZNpE2J75ED17zGncBIaFl1MyOdDJg1P/4EHOJWYHTVDjJDLaTBxetjTVZmy7vgGLcCI2UDJI34HGC5odGEe4ERLN7TcMAWZUl0AR7wIzBSDgd4jcfd81hX+LwxmLuSqA5/ApfDAR7jsRAh1uGyjLseSzN/AiOhxmNfk5VL1cb1XHf7FRipYo/7VmZoN9zEevfmI+99Af8Cl7/VezweF1zAd1zvNjGOwFgfVzWguzhpMmvldLJSncgWF/BNGUdgpEy6AF+4BBcEMVEpF76Tqn3GExgpe7AFrfVjEyR8MPZixrgCIziw5yKzNmpVFvrH6oKvqVobH5XxBUYws5aqu8MFFNdTp6qLMARGbjz+i2pnvgDqSH9tSB3CERgR+V1r5ZPJtKTM5mADLIfwRA2IsAS+Kp8siOx7ohLFneTOl/9MCUtgBLNOWyLrMniyclXrBrhtYngCIzZE9jZRWa4O3R27HGoiTIGRMSzZmLDFRcIVGPElci+BwhcXCVtgpJfIrhfwaYiLhC8w0kfkxZn2oUbHEhIXoSEwYiSynKn/LmDxUfehlbgXoAOWQoTERQRQY3E6g2LyH/Wn3+o8Nsuw6fAUjr9s+FkrcQt4CF2s61xiO8jTExhBkeX03+pNv9N5bJPIRuKq9mNgHSpd6Ljobcp7HJddo+7Rn6LAO5Oc7sRZfXHlalWIpLgITQve5vXZhTpNu62wuonFBVSv+aGWuDgkN8IclU3oC4xUIv8Nul5PJfI7StwHrcfhJAYu1gewnjsUHgIjbz68p17NP7WSr1ZolUFd8BEYMcmw68BkarL8hNO9lngJvOanD1+p9eCZybeUG27fePQeMINmFt1Jr1alwbVMdOApcJ/9J8O8lmkwXAU2t8bQLnWxBFeB52BKsLvODiO56DXU95ZugKfAfbYINhmUJwTXLDrdCncFT4EnPeac8+kcGMLUgi/NLZjUvpb68BTYvI/M1qUzteCSQC4AHxe+ApvtXpcsmB5G/WiWfWiEr8AmWyMxrYERvgJLExfNt27m7KL1RRM+tzL0C+Mky2DfjeSiKWJyhX9y0fSYLF9qH3vtcg5MSY0OhNGQ3T58BdYWjW+ChXC2YOxmzTuPYZxgIbwF1rNOtl0shLfAOt2sZMGE0elmyRSD6aIj3jVgvTE5b4F1hu/yFIPpMpl0NzuYzmKtYZ5Fa8xmMZ3FSkTCr3nPZT1IStkiAAAAAElFTkSuQmCC") diff --git a/src/infrastructure/dto/definition/dto_collection_definition.rs b/src/infrastructure/dto/collection/dto_collection_definition.rs similarity index 76% rename from src/infrastructure/dto/definition/dto_collection_definition.rs rename to src/infrastructure/dto/collection/dto_collection_definition.rs index 98b8754..75ef7ee 100644 --- a/src/infrastructure/dto/definition/dto_collection_definition.rs +++ b/src/infrastructure/dto/collection/dto_collection_definition.rs @@ -1,7 +1,7 @@ -use rust_db_manager_core::domain::definition::collection_definition::CollectionDefinition; +use rust_db_manager_core::domain::collection::collection_definition::CollectionDefinition; use serde::Serialize; -use super::{dto_field_data::DTOFieldData, dto_field_definition::DTOFieldDefinition}; +use crate::infrastructure::dto::field::{definition::dto_field_definition::DTOFieldDefinition, generate::dto_field_data::DTOFieldData}; #[derive(Clone, Serialize)] pub struct DTOCollectionDefinition { diff --git a/src/infrastructure/dto/request/dto_generate_collection_query.rs b/src/infrastructure/dto/collection/dto_generate_collection_query.rs similarity index 80% rename from src/infrastructure/dto/request/dto_generate_collection_query.rs rename to src/infrastructure/dto/collection/dto_generate_collection_query.rs index b1f4860..8e3d129 100644 --- a/src/infrastructure/dto/request/dto_generate_collection_query.rs +++ b/src/infrastructure/dto/collection/dto_generate_collection_query.rs @@ -1,7 +1,7 @@ -use rust_db_manager_core::domain::generate::generate_collection_query::GenerateCollectionQuery; +use rust_db_manager_core::domain::collection::generate_collection_query::GenerateCollectionQuery; use serde::Deserialize; -use crate::{commons::exception::api_exception::ApiException, infrastructure::dto::definition::dto_field_data::DTOFieldData}; +use crate::{commons::exception::api_exception::ApiException, infrastructure::dto::field::generate::dto_field_data::DTOFieldData}; #[derive(Clone, Deserialize)] pub struct DTOGenerateCollectionQuery { diff --git a/src/infrastructure/dto/db_service/db/dto_db_create.rs b/src/infrastructure/dto/data_base/dto_generate_data_base_query.rs similarity index 68% rename from src/infrastructure/dto/db_service/db/dto_db_create.rs rename to src/infrastructure/dto/data_base/dto_generate_data_base_query.rs index ed43828..b401090 100644 --- a/src/infrastructure/dto/db_service/db/dto_db_create.rs +++ b/src/infrastructure/dto/data_base/dto_generate_data_base_query.rs @@ -1,6 +1,6 @@ use serde::Deserialize; #[derive(Clone, Deserialize)] -pub struct DTODBCreate { +pub struct DTOGenerateDatabaseQuery { pub data_base: String } \ No newline at end of file diff --git a/src/infrastructure/dto/db_service/dto_db_service_lite.rs b/src/infrastructure/dto/db_service/dto_db_service_lite.rs deleted file mode 100644 index 81d7e5f..0000000 --- a/src/infrastructure/dto/db_service/dto_db_service_lite.rs +++ /dev/null @@ -1,20 +0,0 @@ -use rust_db_manager_core::infrastructure::db_service_lite::DBServiceLite; -use serde::{Deserialize, Serialize}; - -use super::dto_db_service_web_category::DTODBServiceWebCategory; - -#[derive(Clone, Serialize, Deserialize)] -pub struct DTODBServiceLite { - pub name: String, - pub category: DTODBServiceWebCategory -} - -impl DTODBServiceLite { - - pub fn from_vec(collection: Vec) -> Vec { - collection.iter() - .map(|s| DTODBServiceLite{name: s.name(), category: DTODBServiceWebCategory::from(&s.category())}) - .collect() - } - -} \ No newline at end of file diff --git a/src/infrastructure/dto/db_service/dto_db_service_response.rs b/src/infrastructure/dto/db_service/dto_db_service_response.rs deleted file mode 100644 index a0dba4e..0000000 --- a/src/infrastructure/dto/db_service/dto_db_service_response.rs +++ /dev/null @@ -1,27 +0,0 @@ -use rust_db_manager_core::infrastructure::db_service::DBService; -use serde::{Deserialize, Serialize}; - -use super::dto_db_service_response_connection::DTODBServiceResponseConnection; - -#[derive(Clone, Serialize, Deserialize)] -pub struct DTODBServiceResponse { - pub name: String, - pub owner: String, - pub protected: bool, - pub timestamp: u128, - pub connection_data: DTODBServiceResponseConnection, -} - -impl DTODBServiceResponse { - - pub fn from(service: DBService) -> DTODBServiceResponse { - DTODBServiceResponse { - name: service.name(), - owner: service.owner(), - protected: service.is_protected(), - timestamp: service.timestamp(), - connection_data: DTODBServiceResponseConnection::from(service.connection_data()) - } - } - -} \ No newline at end of file diff --git a/src/infrastructure/dto/db_service/dto_db_service_web_category.rs b/src/infrastructure/dto/db_service/dto_db_service_web_category.rs deleted file mode 100644 index e5b7bd2..0000000 --- a/src/infrastructure/dto/db_service/dto_db_service_web_category.rs +++ /dev/null @@ -1,22 +0,0 @@ -use rust_db_manager_core::infrastructure::repository::e_db_repository::EDBRepository; -use serde::{Deserialize, Serialize}; - -use super::dto_db_resources::DTODBResources; -use crate::infrastructure::db_assets::WebEDBRepository; - -#[derive(Clone, Serialize, Deserialize)] -pub struct DTODBServiceWebCategory { - pub category: String, - pub resources: DTODBResources -} - -impl DTODBServiceWebCategory { - - pub fn from(category: &EDBRepository) -> DTODBServiceWebCategory { - DTODBServiceWebCategory { - category: category.to_string(), - resources: category.resources() - } - } - -} \ No newline at end of file diff --git a/src/infrastructure/dto/document/dto_document_data.rs b/src/infrastructure/dto/document/dto_document_data.rs new file mode 100644 index 0000000..607af68 --- /dev/null +++ b/src/infrastructure/dto/document/dto_document_data.rs @@ -0,0 +1,33 @@ +use rust_db_manager_core::domain::document::document_data::DocumentData; +use serde::Serialize; + +use super::dto_document_key::DTODocumentKey; + + +#[derive(Clone, Serialize)] +pub struct DTODocumentData { + data_base: String, + collection: String, + base_key: Option, + keys: Vec, + document: String +} + +impl DTODocumentData { + + pub fn from(document: &DocumentData) -> Self { + Self { + data_base: document.data_base(), + collection: document.collection(), + base_key: match document.base_key() { + Some(key) => Some(DTODocumentKey::from(&key)), + None => None, + }, + keys: document.keys().iter() + .map(|k| DTODocumentKey::from(k)) + .collect(), + document: document.document(), + } + } + +} \ No newline at end of file diff --git a/src/infrastructure/dto/document/dto_document_key.rs b/src/infrastructure/dto/document/dto_document_key.rs new file mode 100644 index 0000000..bee438d --- /dev/null +++ b/src/infrastructure/dto/document/dto_document_key.rs @@ -0,0 +1,26 @@ +use rust_db_manager_core::domain::document::document_key::DocumentKey; +use serde::Serialize; + +use super::dto_document_key_attribute::DTODocumentKeyAttribute; + + +#[derive(Clone, Serialize)] +pub struct DTODocumentKey { + name: String, + value: String, + attributes: Vec +} + +impl DTODocumentKey { + + pub fn from(key: &DocumentKey) -> Self { + Self { + name: key.name(), + value: key.value(), + attributes: key.attributes().iter() + .map(|a| DTODocumentKeyAttribute::from(a)) + .collect() + } + } + +} \ No newline at end of file diff --git a/src/infrastructure/dto/document/dto_document_key_attribute.rs b/src/infrastructure/dto/document/dto_document_key_attribute.rs new file mode 100644 index 0000000..6973fd4 --- /dev/null +++ b/src/infrastructure/dto/document/dto_document_key_attribute.rs @@ -0,0 +1,20 @@ +use rust_db_manager_core::domain::document::document_key_attribute::DocumentKeyAttribute; +use serde::Serialize; + + +#[derive(Clone, Serialize)] +pub struct DTODocumentKeyAttribute { + key: String, + value: String +} + +impl DTODocumentKeyAttribute { + + pub fn from(attribute: &DocumentKeyAttribute) -> Self { + Self { + key: attribute.key(), + value: attribute.value() + } + } + +} \ No newline at end of file diff --git a/src/infrastructure/dto/dto_data_base_group.rs b/src/infrastructure/dto/dto_data_base_group.rs deleted file mode 100644 index b1986c2..0000000 --- a/src/infrastructure/dto/dto_data_base_group.rs +++ /dev/null @@ -1,25 +0,0 @@ -use rust_db_manager_core::domain::data_base_group_data::DataBaseDataGroup; -use serde::Serialize; - -use super::dto_data_base_field::DTODataBaseField; - -#[derive(Clone, Serialize)] -pub struct DTODataBaseGroup { - order: usize, - name: String, - fields: Vec, -} - -impl DTODataBaseGroup { - - pub fn from(data: &DataBaseDataGroup) -> DTODataBaseGroup { - Self { - order: data.order(), - name: data.name(), - fields: data.fields().iter() - .map(|f| DTODataBaseField::from(f)) - .collect() - } - } - -} \ No newline at end of file diff --git a/src/infrastructure/dto/definition/dto_field_attribute_default_definition.rs b/src/infrastructure/dto/field/definition/dto_field_attribute_default_definition.rs similarity index 86% rename from src/infrastructure/dto/definition/dto_field_attribute_default_definition.rs rename to src/infrastructure/dto/field/definition/dto_field_attribute_default_definition.rs index 8c62c6b..2de520b 100644 --- a/src/infrastructure/dto/definition/dto_field_attribute_default_definition.rs +++ b/src/infrastructure/dto/field/definition/dto_field_attribute_default_definition.rs @@ -1,4 +1,4 @@ -use rust_db_manager_core::domain::definition::field::field_attribute_default_definition::FieldAttributeDefaultDefinition; +use rust_db_manager_core::domain::field::definition::field_attribute_default_definition::FieldAttributeDefaultDefinition; use serde::Serialize; #[derive(Clone, Serialize)] diff --git a/src/infrastructure/dto/definition/dto_field_attribute_definition.rs b/src/infrastructure/dto/field/definition/dto_field_attribute_definition.rs similarity index 91% rename from src/infrastructure/dto/definition/dto_field_attribute_definition.rs rename to src/infrastructure/dto/field/definition/dto_field_attribute_definition.rs index 727205d..a3acea2 100644 --- a/src/infrastructure/dto/definition/dto_field_attribute_definition.rs +++ b/src/infrastructure/dto/field/definition/dto_field_attribute_definition.rs @@ -1,4 +1,4 @@ -use rust_db_manager_core::domain::definition::field::field_attribute_definition::FieldAttributeDefinition; +use rust_db_manager_core::domain::field::definition::field_attribute_definition::FieldAttributeDefinition; use serde::Serialize; use super::dto_field_attribute_default_definition::DTOFieldAttributeDefaultDefinition; diff --git a/src/infrastructure/dto/definition/dto_field_definition.rs b/src/infrastructure/dto/field/definition/dto_field_definition.rs similarity index 92% rename from src/infrastructure/dto/definition/dto_field_definition.rs rename to src/infrastructure/dto/field/definition/dto_field_definition.rs index 9842388..9216d6d 100644 --- a/src/infrastructure/dto/definition/dto_field_definition.rs +++ b/src/infrastructure/dto/field/definition/dto_field_definition.rs @@ -1,4 +1,4 @@ -use rust_db_manager_core::domain::definition::field::field_definition::FieldDefinition; +use rust_db_manager_core::domain::field::definition::field_definition::FieldDefinition; use serde::Serialize; use super::dto_field_attribute_definition::DTOFieldAttributeDefinition; diff --git a/src/infrastructure/dto/definition/dto_field_atribute.rs b/src/infrastructure/dto/field/generate/dto_field_atribute.rs similarity index 88% rename from src/infrastructure/dto/definition/dto_field_atribute.rs rename to src/infrastructure/dto/field/generate/dto_field_atribute.rs index 7dab8ad..95fd9fd 100644 --- a/src/infrastructure/dto/definition/dto_field_atribute.rs +++ b/src/infrastructure/dto/field/generate/dto_field_atribute.rs @@ -1,4 +1,4 @@ -use rust_db_manager_core::domain::generate::field::field_attribute::FieldAttribute; +use rust_db_manager_core::domain::field::generate::field_attribute::FieldAttribute; use serde::{Deserialize, Serialize}; #[derive(Clone, Serialize, Deserialize)] diff --git a/src/infrastructure/dto/definition/dto_field_data.rs b/src/infrastructure/dto/field/generate/dto_field_data.rs similarity index 93% rename from src/infrastructure/dto/definition/dto_field_data.rs rename to src/infrastructure/dto/field/generate/dto_field_data.rs index 6ff7b5b..f614dc6 100644 --- a/src/infrastructure/dto/definition/dto_field_data.rs +++ b/src/infrastructure/dto/field/generate/dto_field_data.rs @@ -1,4 +1,4 @@ -use rust_db_manager_core::domain::{definition::field::e_field_code::EFieldCode, generate::field::field_data::FieldData}; +use rust_db_manager_core::domain::field::{e_field_code::EFieldCode, generate::field_data::FieldData}; use serde::{Deserialize, Serialize}; use crate::commons::exception::api_exception::ApiException; diff --git a/src/infrastructure/dto/definition/dto_field_reference.rs b/src/infrastructure/dto/field/generate/dto_field_reference.rs similarity index 88% rename from src/infrastructure/dto/definition/dto_field_reference.rs rename to src/infrastructure/dto/field/generate/dto_field_reference.rs index 2015512..425d12e 100644 --- a/src/infrastructure/dto/definition/dto_field_reference.rs +++ b/src/infrastructure/dto/field/generate/dto_field_reference.rs @@ -1,4 +1,4 @@ -use rust_db_manager_core::domain::generate::field::field_reference::FieldReference; +use rust_db_manager_core::domain::field::generate::field_reference::FieldReference; use serde::{Deserialize, Serialize}; #[derive(Clone, Serialize, Deserialize)] diff --git a/src/infrastructure/dto/service/definition/dto_service.rs b/src/infrastructure/dto/service/definition/dto_service.rs new file mode 100644 index 0000000..8d2440f --- /dev/null +++ b/src/infrastructure/dto/service/definition/dto_service.rs @@ -0,0 +1,27 @@ +use rust_db_manager_core::infrastructure::db_service::DBService; +use serde::Serialize; + +use super::dto_service_category::DTOServiceCategory; + +#[derive(Clone, Serialize)] +pub struct DTOService { + pub name: String, + pub owner: String, + pub protected: bool, + pub timestamp: u128, + pub connection_data: DTOServiceCategory, +} + +impl DTOService { + + pub fn from(service: DBService) -> Self { + Self { + name: service.name(), + owner: service.owner(), + protected: service.is_protected(), + timestamp: service.timestamp(), + connection_data: DTOServiceCategory::from(service.connection_data()) + } + } + +} \ No newline at end of file diff --git a/src/infrastructure/dto/db_service/dto_db_service_response_connection.rs b/src/infrastructure/dto/service/definition/dto_service_category.rs similarity index 50% rename from src/infrastructure/dto/db_service/dto_db_service_response_connection.rs rename to src/infrastructure/dto/service/definition/dto_service_category.rs index 047e751..b3028c0 100644 --- a/src/infrastructure/dto/db_service/dto_db_service_response_connection.rs +++ b/src/infrastructure/dto/service/definition/dto_service_category.rs @@ -1,20 +1,21 @@ use rust_db_manager_core::domain::connection_data::ConnectionData; -use serde::{Deserialize, Serialize}; +use serde::Serialize; -use super::dto_db_resources::DTODBResources; use crate::infrastructure::db_assets::WebEDBRepository; -#[derive(Clone, Serialize, Deserialize)] -pub struct DTODBServiceResponseConnection { +use super::dto_service_resources::DTOServiceResources; + +#[derive(Clone, Serialize)] +pub struct DTOServiceCategory { pub category: String, pub connection: String, - pub resources: DTODBResources + pub resources: DTOServiceResources } -impl DTODBServiceResponseConnection { +impl DTOServiceCategory { - pub fn from(connection: ConnectionData) -> DTODBServiceResponseConnection { - DTODBServiceResponseConnection { + pub fn from(connection: ConnectionData) -> Self { + Self { category: connection.category().to_string(), connection: connection.connection(), resources: connection.category().resources() diff --git a/src/infrastructure/dto/service/definition/dto_service_category_lite.rs b/src/infrastructure/dto/service/definition/dto_service_category_lite.rs new file mode 100644 index 0000000..16313d1 --- /dev/null +++ b/src/infrastructure/dto/service/definition/dto_service_category_lite.rs @@ -0,0 +1,23 @@ +use rust_db_manager_core::infrastructure::repository::e_db_repository::EDBRepository; +use serde::Serialize; + +use crate::infrastructure::db_assets::WebEDBRepository; + +use super::dto_service_resources::DTOServiceResources; + +#[derive(Clone, Serialize)] +pub struct DTOServiceCategoryLite { + pub category: String, + pub resources: DTOServiceResources +} + +impl DTOServiceCategoryLite { + + pub fn from(category: &EDBRepository) -> Self { + Self { + category: category.to_string(), + resources: category.resources() + } + } + +} \ No newline at end of file diff --git a/src/infrastructure/dto/service/definition/dto_service_lite.rs b/src/infrastructure/dto/service/definition/dto_service_lite.rs new file mode 100644 index 0000000..5e61d16 --- /dev/null +++ b/src/infrastructure/dto/service/definition/dto_service_lite.rs @@ -0,0 +1,21 @@ +use rust_db_manager_core::infrastructure::db_service_lite::DBServiceLite; +use serde::Serialize; + +use super::dto_service_category_lite::DTOServiceCategoryLite; + +#[derive(Clone, Serialize)] +pub struct DTOServiceLite { + pub name: String, + pub category: DTOServiceCategoryLite +} + +impl DTOServiceLite { + + pub fn from(service: &DBServiceLite) -> Self { + Self { + name: service.name(), + category: DTOServiceCategoryLite::from(&service.category()) + } + } + +} \ No newline at end of file diff --git a/src/infrastructure/dto/db_service/dto_db_resources.rs b/src/infrastructure/dto/service/definition/dto_service_resources.rs similarity index 74% rename from src/infrastructure/dto/db_service/dto_db_resources.rs rename to src/infrastructure/dto/service/definition/dto_service_resources.rs index 234c824..3919e9f 100644 --- a/src/infrastructure/dto/db_service/dto_db_resources.rs +++ b/src/infrastructure/dto/service/definition/dto_service_resources.rs @@ -1,16 +1,16 @@ use serde::{Deserialize, Serialize}; #[derive(Clone, Serialize, Deserialize)] -pub struct DTODBResources { +pub struct DTOServiceResources { pub web_site: String, pub color: String, pub image: String } -impl DTODBResources { +impl DTOServiceResources { - pub fn new(web_site: String, color: String, image: String) -> DTODBResources { - DTODBResources { + pub fn new(web_site: String, color: String, image: String) -> Self { + Self { web_site, color, image } } diff --git a/src/infrastructure/dto/db_service/dto_db_connection_data.rs b/src/infrastructure/dto/service/generate/dto_db_connection_data.rs similarity index 76% rename from src/infrastructure/dto/db_service/dto_db_connection_data.rs rename to src/infrastructure/dto/service/generate/dto_db_connection_data.rs index 928f281..5969d4e 100644 --- a/src/infrastructure/dto/db_service/dto_db_connection_data.rs +++ b/src/infrastructure/dto/service/generate/dto_db_connection_data.rs @@ -1,7 +1,7 @@ use serde::Deserialize; #[derive(Clone, Deserialize)] -pub struct DTOConnectionData { +pub struct DTODBConnectionData { pub category: String, pub connection: String } \ No newline at end of file diff --git a/src/infrastructure/dto/db_service/dto_db_service.rs b/src/infrastructure/dto/service/generate/dto_service_create_request.rs similarity index 53% rename from src/infrastructure/dto/db_service/dto_db_service.rs rename to src/infrastructure/dto/service/generate/dto_service_create_request.rs index e1819a3..e20b8f4 100644 --- a/src/infrastructure/dto/db_service/dto_db_service.rs +++ b/src/infrastructure/dto/service/generate/dto_service_create_request.rs @@ -1,12 +1,12 @@ use serde::Deserialize; -use super::dto_db_connection_data::DTOConnectionData; +use super::dto_db_connection_data::DTODBConnectionData; #[derive(Clone, Deserialize)] -pub struct DTODBService { +pub struct DTOServiceRequest { pub name: String, pub owner: String, pub protected: bool, pub password: String, - pub connection_data: DTOConnectionData + pub connection_data: DTODBConnectionData } \ No newline at end of file diff --git a/src/infrastructure/dto/db_service/dto_db_service_suscribe.rs b/src/infrastructure/dto/service/generate/dto_service_suscribe_request.rs similarity index 76% rename from src/infrastructure/dto/db_service/dto_db_service_suscribe.rs rename to src/infrastructure/dto/service/generate/dto_service_suscribe_request.rs index df6f1e2..600c660 100644 --- a/src/infrastructure/dto/db_service/dto_db_service_suscribe.rs +++ b/src/infrastructure/dto/service/generate/dto_service_suscribe_request.rs @@ -1,7 +1,7 @@ use serde::Deserialize; #[derive(Clone, Deserialize)] -pub struct DTODBServiceSuscribe { +pub struct DTOServiceSuscribeRequest { pub name: String, pub password: String, pub owner: String, diff --git a/src/infrastructure/dto/dto_data_base_field.rs b/src/infrastructure/dto/table/dto_table_data_field.rs similarity index 63% rename from src/infrastructure/dto/dto_data_base_field.rs rename to src/infrastructure/dto/table/dto_table_data_field.rs index 02aac6f..7122f7d 100644 --- a/src/infrastructure/dto/dto_data_base_field.rs +++ b/src/infrastructure/dto/table/dto_table_data_field.rs @@ -1,17 +1,17 @@ -use rust_db_manager_core::domain::data_base_field::DataBaseField; +use rust_db_manager_core::domain::table::table_data_field::TableDataField; use serde::Serialize; #[derive(Clone, Serialize)] -pub struct DTODataBaseField { +pub struct DTOTableDataField { order: usize, name: String, value: String, json_type: String, } -impl DTODataBaseField { +impl DTOTableDataField { - pub fn from(data: &DataBaseField) -> DTODataBaseField { + pub fn from(data: &TableDataField) -> Self { Self { order: data.order(), name: data.name(), diff --git a/src/infrastructure/dto/table/dto_table_data_group.rs b/src/infrastructure/dto/table/dto_table_data_group.rs new file mode 100644 index 0000000..6050ff3 --- /dev/null +++ b/src/infrastructure/dto/table/dto_table_data_group.rs @@ -0,0 +1,25 @@ +use rust_db_manager_core::domain::table::table_data_group::TableDataGroup; +use serde::Serialize; + +use super::dto_table_data_field::DTOTableDataField; + +#[derive(Clone, Serialize)] +pub struct DTOTableDataGroup { + order: usize, + name: String, + fields: Vec, +} + +impl DTOTableDataGroup { + + pub fn from(data: &TableDataGroup) -> Self { + Self { + order: data.order(), + name: data.name(), + fields: data.fields().iter() + .map(|f| DTOTableDataField::from(f)) + .collect() + } + } + +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index b19a245..7f1c591 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,37 +20,52 @@ pub mod domain { } pub mod infrastructure { pub mod dto { - pub mod db_service { - pub mod db { - pub mod dto_db_create; - } - pub mod dto_db_connection_data; - pub mod dto_db_resources; - pub mod dto_db_service_web_category; - pub mod dto_db_service_lite; - pub mod dto_db_service_response_connection; - pub mod dto_db_service_response; - pub mod dto_db_service_suscribe; - pub mod dto_db_service; - } - pub mod definition { + pub mod collection { + pub mod dto_generate_collection_query; pub mod dto_collection_definition; - pub mod dto_field_atribute; - pub mod dto_field_attribute_default_definition; - pub mod dto_field_attribute_definition; - pub mod dto_field_definition; - pub mod dto_field_reference; - pub mod dto_field_data; + } + pub mod data_base { + pub mod dto_generate_data_base_query; + } + pub mod document { + pub mod dto_document_data; + pub mod dto_document_key_attribute; + pub mod dto_document_key; + } + pub mod field { + pub mod definition { + pub mod dto_field_attribute_default_definition; + pub mod dto_field_attribute_definition; + pub mod dto_field_definition; + } + pub mod generate { + pub mod dto_field_atribute; + pub mod dto_field_data; + pub mod dto_field_reference; + } } pub mod pagination { pub mod dto_paginated_collection; pub mod dto_query_pagination; } - pub mod request { - pub mod dto_generate_collection_query; + pub mod service { + pub mod definition { + pub mod dto_service_category_lite; + pub mod dto_service_category; + pub mod dto_service_lite; + pub mod dto_service_resources; + pub mod dto_service; + } + pub mod generate { + pub mod dto_db_connection_data; + pub mod dto_service_create_request; + pub mod dto_service_suscribe_request; + } + } + pub mod table { + pub mod dto_table_data_field; + pub mod dto_table_data_group; } - pub mod dto_data_base_field; - pub mod dto_data_base_group; pub mod dto_server_status; } pub mod controller_collection; From 036ba8a3d22bd4db02c330eb15f48637b8bb997d Mon Sep 17 00:00:00 2001 From: Rafael Ruiz Date: Sun, 12 May 2024 21:16:36 +0200 Subject: [PATCH 17/22] [FEATURE] Find document end-point implemented. --- src/infrastructure/controller_document.rs | 48 +++++++++++++++++-- .../dto/document/dto_document_key.rs | 27 +++++++++-- .../document/dto_document_key_attribute.rs | 8 ++-- 3 files changed, 72 insertions(+), 11 deletions(-) diff --git a/src/infrastructure/controller_document.rs b/src/infrastructure/controller_document.rs index 3394785..f9dfb14 100644 --- a/src/infrastructure/controller_document.rs +++ b/src/infrastructure/controller_document.rs @@ -1,9 +1,9 @@ -use axum::{extract::Path, http::StatusCode, middleware, response::IntoResponse, routing::get, Json, Router}; -use rust_db_manager_core::{commons::configuration::configuration::Configuration, domain::filter::data_base_query::DataBaseQuery}; +use axum::{extract::Path, http::StatusCode, middleware, response::IntoResponse, routing::{get, post}, Json, Router}; +use rust_db_manager_core::{commons::{configuration::configuration::Configuration, utils::document_keys_to_filter_element}, domain::filter::data_base_query::DataBaseQuery}; use crate::commons::exception::api_exception::ApiException; -use super::{dto::{document::dto_document_data::DTODocumentData, table::dto_table_data_group::DTOTableDataGroup}, handler, utils}; +use super::{dto::{document::{dto_document_data::DTODocumentData, dto_document_key::DTODocumentKey}, table::dto_table_data_group::DTOTableDataGroup}, handler, utils}; pub struct ControllerDocument { } @@ -14,6 +14,7 @@ impl ControllerDocument { router .route("/:service/data-base/:data_base/collection/:collection/metadata", get(Self::metadata)) .route("/:service/data-base/:data_base/collection/:collection", get(Self::find_all)) + .route("/:service/data-base/:data_base/collection/:collection/document", post(Self::find)) .route_layer(middleware::from_fn(handler::autentication_handler)) } @@ -44,7 +45,6 @@ impl ControllerDocument { Ok(Json(dto)) } - async fn find_all(Path((service, data_base, collection)): Path<(String, String, String)>) -> Result>, impl IntoResponse> { let o_db_service = Configuration::find_service(&service); if o_db_service.is_none() { @@ -71,4 +71,44 @@ impl ControllerDocument { ) } + async fn find(Path((service, data_base, collection)): Path<(String, String, String)>, Json(dto): Json>) -> Result, impl IntoResponse> { + let o_db_service = Configuration::find_service(&service); + if o_db_service.is_none() { + return Err(utils::not_found()); + } + + let result = o_db_service.unwrap().instance().await; + if let Err(error) = result { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + let mut keys = Vec::new(); + for dto_key in dto { + let key = dto_key.from_dto(); + if let Err(exception) = key { + return Err(exception.into_response()); + } + keys.push(key.unwrap()); + } + + let filter = document_keys_to_filter_element(keys); + let query = DataBaseQuery::from_filter(data_base, collection, filter); + + let r_document = result.unwrap().find(&query).await; + if let Err(error) = r_document { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + let document = r_document.unwrap(); + + if let None = document { + let exception = ApiException::new(StatusCode::NOT_FOUND.as_u16(), String::from("Document not found.")); + return Err(exception.into_response()); + } + + Ok(Json(DTODocumentData::from(&document.unwrap()))) + } + } \ No newline at end of file diff --git a/src/infrastructure/dto/document/dto_document_key.rs b/src/infrastructure/dto/document/dto_document_key.rs index bee438d..41b91ea 100644 --- a/src/infrastructure/dto/document/dto_document_key.rs +++ b/src/infrastructure/dto/document/dto_document_key.rs @@ -1,13 +1,16 @@ -use rust_db_manager_core::domain::document::document_key::DocumentKey; -use serde::Serialize; +use rust_db_manager_core::domain::{document::{document_key::DocumentKey, document_key_attribute::DocumentKeyAttribute}, e_json_type::EJSONType}; +use serde::{Deserialize, Serialize}; + +use crate::commons::exception::api_exception::ApiException; use super::dto_document_key_attribute::DTODocumentKeyAttribute; -#[derive(Clone, Serialize)] +#[derive(Clone, Serialize, Deserialize)] pub struct DTODocumentKey { name: String, value: String, + jtype: String, attributes: Vec } @@ -17,10 +20,28 @@ impl DTODocumentKey { Self { name: key.name(), value: key.value(), + jtype: key.jtype().to_string(), attributes: key.attributes().iter() .map(|a| DTODocumentKeyAttribute::from(a)) .collect() } } + pub fn from_dto(&self) -> Result { + let jstype = EJSONType::from_string(&self.jtype); + if let None = jstype { + let exception = ApiException::new(422, String::from("Field type not recognized.")); + return Err(exception); + } + + Ok(DocumentKey::new( + self.name.clone(), + self.value.clone(), + jstype.unwrap(), + self.attributes.iter() + .map(|a| DocumentKeyAttribute::new(a.key.clone(), a.value.clone())) + .collect() + )) + } + } \ No newline at end of file diff --git a/src/infrastructure/dto/document/dto_document_key_attribute.rs b/src/infrastructure/dto/document/dto_document_key_attribute.rs index 6973fd4..29cb116 100644 --- a/src/infrastructure/dto/document/dto_document_key_attribute.rs +++ b/src/infrastructure/dto/document/dto_document_key_attribute.rs @@ -1,11 +1,11 @@ use rust_db_manager_core::domain::document::document_key_attribute::DocumentKeyAttribute; -use serde::Serialize; +use serde::{Deserialize, Serialize}; -#[derive(Clone, Serialize)] +#[derive(Clone, Serialize, Deserialize)] pub struct DTODocumentKeyAttribute { - key: String, - value: String + pub key: String, + pub value: String } impl DTODocumentKeyAttribute { From c4a5a8fb3895562f8b7abda147340b1d886f37a5 Mon Sep 17 00:00:00 2001 From: Rafael Ruiz Date: Tue, 14 May 2024 02:55:53 +0200 Subject: [PATCH 18/22] [FEATURE] Major improvements: - Document schema end-point implemented. - Update end-point implemented. - Insert end-point sign updated. - General refactorization. --- src/infrastructure/controller_collection.rs | 33 +++- src/infrastructure/controller_database.rs | 37 ++-- src/infrastructure/controller_document.rs | 169 ++++++++++++++++-- .../dto/document/dto_document_schema.rs | 23 +++ src/infrastructure/dto/dto_create_document.rs | 8 + src/infrastructure/dto/dto_update_document.rs | 9 + src/lib.rs | 3 + 7 files changed, 253 insertions(+), 29 deletions(-) create mode 100644 src/infrastructure/dto/document/dto_document_schema.rs create mode 100644 src/infrastructure/dto/dto_create_document.rs create mode 100644 src/infrastructure/dto/dto_update_document.rs diff --git a/src/infrastructure/controller_collection.rs b/src/infrastructure/controller_collection.rs index 48a07ab..73ff384 100644 --- a/src/infrastructure/controller_collection.rs +++ b/src/infrastructure/controller_collection.rs @@ -1,9 +1,28 @@ -use axum::{extract::Path, http::StatusCode, middleware, response::IntoResponse, routing::{delete, get, post}, Json, Router}; -use rust_db_manager_core::{commons::configuration::configuration::Configuration, domain::{collection::generate_collection_query::GenerateCollectionQuery, filter::data_base_query::DataBaseQuery}}; +use axum::{ + extract::Path, + http::StatusCode, + middleware, + response::IntoResponse, + routing::{delete, get, post}, + Json, Router, +}; +use rust_db_manager_core::{ + commons::configuration::configuration::Configuration, + domain::{ + collection::generate_collection_query::GenerateCollectionQuery, + filter::data_base_query::DataBaseQuery, + }, +}; use crate::commons::exception::api_exception::ApiException; -use super::{dto::{collection::dto_generate_collection_query::DTOGenerateCollectionQuery, table::dto_table_data_group::DTOTableDataGroup}, handler, utils}; +use super::{ + dto::{ + collection::dto_generate_collection_query::DTOGenerateCollectionQuery, + table::dto_table_data_group::DTOTableDataGroup, + }, + handler, utils, +}; pub struct ControllerCollection { } @@ -33,7 +52,7 @@ impl ControllerCollection { let query = DataBaseQuery::from_data_base(data_base); - let metadata = result.unwrap().data_base_collections_metadata(&query).await; + let metadata = result.unwrap().data_base_metadata(&query).await; if let Err(error) = metadata { let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); return Err(exception.into_response()); @@ -60,7 +79,7 @@ impl ControllerCollection { let query = DataBaseQuery::from_data_base(data_base); - let collections = result.unwrap().list_collections(&query).await; + let collections = result.unwrap().collection_find_all(&query).await; if let Err(error) = collections { let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); return Err(exception.into_response()); @@ -86,7 +105,7 @@ impl ControllerCollection { return Err(exception.into_response()); } - let collection = result.unwrap().create_collection(&query.unwrap()).await; + let collection = result.unwrap().collection_create(&query.unwrap()).await; if let Err(error) = collection { let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); return Err(exception.into_response()); @@ -109,7 +128,7 @@ impl ControllerCollection { let query = GenerateCollectionQuery::from_collection(data_base, collection); - let collection = result.unwrap().drop_collection(&query).await; + let collection = result.unwrap().collection_drop(&query).await; if let Err(error) = collection { let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); return Err(exception.into_response()); diff --git a/src/infrastructure/controller_database.rs b/src/infrastructure/controller_database.rs index 033ee20..ee776f7 100644 --- a/src/infrastructure/controller_database.rs +++ b/src/infrastructure/controller_database.rs @@ -1,9 +1,26 @@ -use axum::{extract::Path, http::StatusCode, middleware, response::IntoResponse, routing::{delete, get, post}, Json, Router}; -use rust_db_manager_core::{commons::configuration::configuration::Configuration, domain::data_base::generate_database_query::GenerateDatabaseQuery}; +use axum::{ + extract::Path, + http::StatusCode, + middleware, + response::IntoResponse, + routing::{delete, get, post}, + Json, Router, +}; +use rust_db_manager_core::{ + commons::configuration::configuration::Configuration, + domain::data_base::generate_database_query::GenerateDatabaseQuery, +}; use crate::commons::exception::api_exception::ApiException; -use super::{dto::{collection::dto_collection_definition::DTOCollectionDefinition, data_base::dto_generate_data_base_query::DTOGenerateDatabaseQuery, table::dto_table_data_group::DTOTableDataGroup}, handler, utils}; +use super::{ + dto::{ + collection::dto_collection_definition::DTOCollectionDefinition, + data_base::dto_generate_data_base_query::DTOGenerateDatabaseQuery, + table::dto_table_data_group::DTOTableDataGroup, + }, + handler, utils, +}; pub struct ControllerDataBase { } @@ -14,7 +31,7 @@ impl ControllerDataBase { router .route("/:service/status", get(Self::status)) .route("/:service/metadata", get(Self::metadata)) - .route("/:service/definition", get(Self::definition)) + .route("/:service/schema", get(Self::schema)) .route("/:service/data-base", get(Self::find_all)) .route("/:service/data-base", post(Self::insert)) .route("/:service/data-base/:data_base", delete(Self::delete)) @@ -54,7 +71,7 @@ impl ControllerDataBase { return Err(exception.into_response()); } - let metadata = result.unwrap().data_base_metadata().await; + let metadata = result.unwrap().metadata().await; if let Err(error) = metadata { let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); return Err(exception.into_response()); @@ -67,7 +84,7 @@ impl ControllerDataBase { Ok(Json(dto)) } - async fn definition(Path(service): Path) -> Result, impl IntoResponse> { + async fn schema(Path(service): Path) -> Result, impl IntoResponse> { let o_db_service = Configuration::find_service(&service); if o_db_service.is_none() { return Err(utils::not_found()); @@ -79,7 +96,7 @@ impl ControllerDataBase { return Err(exception.into_response()); } - let definition = result.unwrap().collection_accept_definition().await; + let definition = result.unwrap().collection_accept_schema().await; if let Err(error) = definition { let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); return Err(exception.into_response()); @@ -100,7 +117,7 @@ impl ControllerDataBase { return Err(exception.into_response()); } - let collection = result.unwrap().list_data_bases().await; + let collection = result.unwrap().data_base_find_all().await; if let Err(error) = collection { let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); return Err(exception.into_response()); @@ -123,7 +140,7 @@ impl ControllerDataBase { let query = GenerateDatabaseQuery::new(dto.data_base); - let collection = result.unwrap().create_data_base(&query).await; + let collection = result.unwrap().data_base_create(&query).await; if let Err(error) = collection { let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); return Err(exception.into_response()); @@ -146,7 +163,7 @@ impl ControllerDataBase { let query = GenerateDatabaseQuery::new(data_base); - let collection = result.unwrap().drop_data_base(&query).await; + let collection = result.unwrap().data_base_drop(&query).await; if let Err(error) = collection { let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); return Err(exception.into_response()); diff --git a/src/infrastructure/controller_document.rs b/src/infrastructure/controller_document.rs index f9dfb14..774239d 100644 --- a/src/infrastructure/controller_document.rs +++ b/src/infrastructure/controller_document.rs @@ -1,9 +1,32 @@ -use axum::{extract::Path, http::StatusCode, middleware, response::IntoResponse, routing::{get, post}, Json, Router}; -use rust_db_manager_core::{commons::{configuration::configuration::Configuration, utils::document_keys_to_filter_element}, domain::filter::data_base_query::DataBaseQuery}; +use axum::{ + extract::Path, + http::StatusCode, + middleware, + response::IntoResponse, + routing::{delete, get, post, put}, + Json, Router, +}; +use rust_db_manager_core::{ + commons::{ + configuration::configuration::Configuration, utils::document_keys_to_filter_element, + }, + domain::filter::data_base_query::DataBaseQuery, +}; use crate::commons::exception::api_exception::ApiException; -use super::{dto::{document::{dto_document_data::DTODocumentData, dto_document_key::DTODocumentKey}, table::dto_table_data_group::DTOTableDataGroup}, handler, utils}; +use super::{ + dto::{ + document::{ + dto_document_data::DTODocumentData, dto_document_key::DTODocumentKey, + dto_document_schema::DTODocumentSchema, + }, + dto_create_document::DTOCreateDocument, + dto_update_document::DTOUpdateDocument, + table::dto_table_data_group::DTOTableDataGroup, + }, + handler, utils, +}; pub struct ControllerDocument { } @@ -13,8 +36,12 @@ impl ControllerDocument { pub fn route(router: Router) -> Router { router .route("/:service/data-base/:data_base/collection/:collection/metadata", get(Self::metadata)) - .route("/:service/data-base/:data_base/collection/:collection", get(Self::find_all)) - .route("/:service/data-base/:data_base/collection/:collection/document", post(Self::find)) + .route("/:service/data-base/:data_base/collection/:collection/schema", get(Self::schema)) + .route("/:service/data-base/:data_base/collection/:collection/document/find", get(Self::find_all)) + .route("/:service/data-base/:data_base/collection/:collection/document/find", post(Self::find)) + .route("/:service/data-base/:data_base/collection/:collection/document/query", post(Self::insert)) + .route("/:service/data-base/:data_base/collection/:collection/document/query", put(Self::update)) + .route("/:service/data-base/:data_base/collection/:collection/document/query", delete(Self::delete)) .route_layer(middleware::from_fn(handler::autentication_handler)) } @@ -45,7 +72,7 @@ impl ControllerDocument { Ok(Json(dto)) } - async fn find_all(Path((service, data_base, collection)): Path<(String, String, String)>) -> Result>, impl IntoResponse> { + async fn schema(Path((service, data_base, collection)): Path<(String, String, String)>) -> Result, impl IntoResponse> { let o_db_service = Configuration::find_service(&service); if o_db_service.is_none() { return Err(utils::not_found()); @@ -59,16 +86,13 @@ impl ControllerDocument { let query = DataBaseQuery::from(data_base, collection); - let documents = result.unwrap().find_all(&query).await; - if let Err(error) = documents { + let schema = result.unwrap().schema(&query).await; + if let Err(error) = schema { let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); return Err(exception.into_response()); } - Ok(Json(documents.unwrap().iter() - .map(|d| DTODocumentData::from(d)) - .collect()) - ) + Ok(Json(DTODocumentSchema::from(&schema.unwrap()))) } async fn find(Path((service, data_base, collection)): Path<(String, String, String)>, Json(dto): Json>) -> Result, impl IntoResponse> { @@ -111,4 +135,125 @@ impl ControllerDocument { Ok(Json(DTODocumentData::from(&document.unwrap()))) } + async fn find_all(Path((service, data_base, collection)): Path<(String, String, String)>) -> Result>, impl IntoResponse> { + let o_db_service = Configuration::find_service(&service); + if o_db_service.is_none() { + return Err(utils::not_found()); + } + + let result = o_db_service.unwrap().instance().await; + if let Err(error) = result { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + let query = DataBaseQuery::from(data_base, collection); + + let documents = result.unwrap().find_all(&query).await; + if let Err(error) = documents { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + Ok(Json(documents.unwrap().iter() + .map(|d| DTODocumentData::from(d)) + .collect()) + ) + } + + async fn insert(Path((service, data_base, collection)): Path<(String, String, String)>, Json(dto): Json) -> Result, impl IntoResponse> { + let o_db_service = Configuration::find_service(&service); + if o_db_service.is_none() { + return Err(utils::not_found()); + } + + let result = o_db_service.unwrap().instance().await; + if let Err(error) = result { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + let query = DataBaseQuery::from(data_base, collection); + + let document = result.unwrap().insert(&query, &dto.document).await; + if let Err(error) = document { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + Ok(Json(DTODocumentData::from(&document.unwrap()))) + } + + async fn update(Path((service, data_base, collection)): Path<(String, String, String)>, Json(dto): Json) -> Result>, impl IntoResponse> { + let o_db_service = Configuration::find_service(&service); + if o_db_service.is_none() { + return Err(utils::not_found()); + } + + let result = o_db_service.unwrap().instance().await; + if let Err(error) = result { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + let mut keys = Vec::new(); + for dto_key in dto.keys { + let key = dto_key.from_dto(); + if let Err(exception) = key { + return Err(exception.into_response()); + } + keys.push(key.unwrap()); + } + + let filter = document_keys_to_filter_element(keys); + let query = DataBaseQuery::from_filter(data_base, collection, filter); + + let documents = result.unwrap().update(&query, &dto.document).await; + if let Err(error) = documents { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + Ok(Json(documents.unwrap().iter() + .map(|d| DTODocumentData::from(d)) + .collect()) + ) + } + + async fn delete(Path((service, data_base, collection)): Path<(String, String, String)>, Json(dto): Json>) -> Result>, impl IntoResponse> { + let o_db_service = Configuration::find_service(&service); + if o_db_service.is_none() { + return Err(utils::not_found()); + } + + let result = o_db_service.unwrap().instance().await; + if let Err(error) = result { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + let mut keys = Vec::new(); + for dto_key in dto { + let key = dto_key.from_dto(); + if let Err(exception) = key { + return Err(exception.into_response()); + } + keys.push(key.unwrap()); + } + + let filter = document_keys_to_filter_element(keys); + let query = DataBaseQuery::from_filter(data_base, collection, filter); + + let documents = result.unwrap().delete(&query).await; + if let Err(error) = documents { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + Ok(Json(documents.unwrap().iter() + .map(|d| DTODocumentData::from(d)) + .collect()) + ) + } + } \ No newline at end of file diff --git a/src/infrastructure/dto/document/dto_document_schema.rs b/src/infrastructure/dto/document/dto_document_schema.rs new file mode 100644 index 0000000..2badd62 --- /dev/null +++ b/src/infrastructure/dto/document/dto_document_schema.rs @@ -0,0 +1,23 @@ +use rust_db_manager_core::domain::document::document_schema::DocumentSchema; +use serde::Serialize; + +use crate::infrastructure::dto::field::generate::dto_field_data::DTOFieldData; + +#[derive(Clone, Serialize)] +pub struct DTODocumentSchema { + comments: Vec, + fields: Vec +} + +impl DTODocumentSchema { + + pub fn from(schema: &DocumentSchema) -> Self { + Self { + comments: schema.comments(), + fields: schema.fields().iter() + .map(|f| DTOFieldData::from(f)) + .collect() + } + } + +} \ No newline at end of file diff --git a/src/infrastructure/dto/dto_create_document.rs b/src/infrastructure/dto/dto_create_document.rs new file mode 100644 index 0000000..3dd0cf4 --- /dev/null +++ b/src/infrastructure/dto/dto_create_document.rs @@ -0,0 +1,8 @@ +use serde::Deserialize; + +use super::document::dto_document_key::DTODocumentKey; + +#[derive(Clone, Deserialize)] +pub struct DTOCreateDocument { + pub document: String +} \ No newline at end of file diff --git a/src/infrastructure/dto/dto_update_document.rs b/src/infrastructure/dto/dto_update_document.rs new file mode 100644 index 0000000..ccc4e96 --- /dev/null +++ b/src/infrastructure/dto/dto_update_document.rs @@ -0,0 +1,9 @@ +use serde::Deserialize; + +use super::document::dto_document_key::DTODocumentKey; + +#[derive(Clone, Deserialize)] +pub struct DTOUpdateDocument { + pub document: String, + pub keys: Vec, +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 7f1c591..f1b0f30 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,6 +31,7 @@ pub mod infrastructure { pub mod dto_document_data; pub mod dto_document_key_attribute; pub mod dto_document_key; + pub mod dto_document_schema; } pub mod field { pub mod definition { @@ -66,7 +67,9 @@ pub mod infrastructure { pub mod dto_table_data_field; pub mod dto_table_data_group; } + pub mod dto_create_document; pub mod dto_server_status; + pub mod dto_update_document; } pub mod controller_collection; pub mod controller_database; From 9cf6336fe4fb7b189910ed404bf8ed47cca51614 Mon Sep 17 00:00:00 2001 From: Rafael Ruiz Date: Thu, 16 May 2024 00:01:04 +0200 Subject: [PATCH 19/22] [FEATURE] Rename, export and import API methods implemented for collection: - API definition updated. - Service controller created. --- src/infrastructure/controller_collection.rs | 148 ++++++++-- src/infrastructure/controller_database.rs | 100 ++----- src/infrastructure/controller_document.rs | 68 +---- src/infrastructure/controller_server.rs | 162 ++--------- src/infrastructure/controller_service.rs | 257 ++++++++++++++++++ src/infrastructure/db_assets.rs | 4 +- .../collection/dto_rename_collection_query.rs | 6 + src/lib.rs | 2 + src/main.rs | 3 +- 9 files changed, 442 insertions(+), 308 deletions(-) create mode 100644 src/infrastructure/controller_service.rs create mode 100644 src/infrastructure/dto/collection/dto_rename_collection_query.rs diff --git a/src/infrastructure/controller_collection.rs b/src/infrastructure/controller_collection.rs index 73ff384..89e62a1 100644 --- a/src/infrastructure/controller_collection.rs +++ b/src/infrastructure/controller_collection.rs @@ -18,8 +18,7 @@ use crate::commons::exception::api_exception::ApiException; use super::{ dto::{ - collection::dto_generate_collection_query::DTOGenerateCollectionQuery, - table::dto_table_data_group::DTOTableDataGroup, + collection::{dto_generate_collection_query::DTOGenerateCollectionQuery, dto_rename_collection_query::DTORenameCollectionQuery}, document::{dto_document_data::DTODocumentData, dto_document_schema::DTODocumentSchema}, table::dto_table_data_group::DTOTableDataGroup }, handler, utils, }; @@ -31,19 +30,23 @@ impl ControllerCollection { pub fn route(router: Router) -> Router { router - .route("/:service/data-base/:data_base/metadata", get(Self::metadata)) - .route("/:service/data-base/:data_base/collection", get(Self::find_all)) - .route("/:service/data-base/:data_base/collection", post(Self::insert)) - .route("/:service/data-base/:data_base/collection/:collection", delete(Self::delete)) + .route("/api/v1/service/:service/data-base/:data_base/collection", get(Self::find_all)) + .route("/api/v1/service/:service/data-base/:data_base/collection", post(Self::insert)) + .route("/api/v1/service/:service/data-base/:data_base/collection/:collection", delete(Self::delete)) + .route("/api/v1/service/:service/data-base/:data_base/collection/:collection/metadata", get(Self::metadata)) + .route("/api/v1/service/:service/data-base/:data_base/collection/:collection/schema", get(Self::schema)) + .route("/api/v1/service/:service/data-base/:data_base/collection/:collection/rename", post(Self::rename)) + .route("/api/v1/service/:service/data-base/:data_base/collection/:collection/export", get(Self::export)) + .route("/api/v1/service/:service/data-base/:data_base/collection/:collection/import", post(Self::import)) .route_layer(middleware::from_fn(handler::autentication_handler)) } - async fn metadata(Path((service, data_base)): Path<(String, String)>) -> Result>, impl IntoResponse> { + async fn find_all(Path((service, data_base)): Path<(String, String)>) -> Result>, impl IntoResponse> { let o_db_service = Configuration::find_service(&service); if o_db_service.is_none() { return Err(utils::not_found()); } - + let result = o_db_service.unwrap().instance().await; if let Err(error) = result { let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); @@ -52,7 +55,79 @@ impl ControllerCollection { let query = DataBaseQuery::from_data_base(data_base); - let metadata = result.unwrap().data_base_metadata(&query).await; + let collections = result.unwrap().collection_find_all(&query).await; + if let Err(error) = collections { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + Ok(Json(collections.unwrap())) + } + + async fn insert(Path((service, _)): Path<(String, String)>, Json(dto): Json) -> Result { + let o_db_service = Configuration::find_service(&service); + if o_db_service.is_none() { + return Err(utils::not_found()); + } + + let result = o_db_service.unwrap().instance().await; + if let Err(error) = result { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + let query = dto.from_dto(); + if let Err(exception) = query { + return Err(exception.into_response()); + } + + let collection = result.unwrap().collection_create(&query.unwrap()).await; + if let Err(error) = collection { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + Ok(StatusCode::ACCEPTED) + } + + async fn delete(Path((service, data_base, collection)): Path<(String, String, String)>) -> Result { + let o_db_service = Configuration::find_service(&service); + if o_db_service.is_none() { + return Err(utils::not_found()); + } + + let result = o_db_service.unwrap().instance().await; + if let Err(error) = result { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + let query = GenerateCollectionQuery::from_collection(data_base, collection); + + let collection = result.unwrap().collection_drop(&query).await; + if let Err(error) = collection { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + Ok(StatusCode::ACCEPTED) + } + + async fn metadata(Path((service, data_base, collection)): Path<(String, String, String)>) -> Result>, impl IntoResponse> { + let o_db_service = Configuration::find_service(&service); + if o_db_service.is_none() { + return Err(utils::not_found()); + } + + let result = o_db_service.unwrap().instance().await; + if let Err(error) = result { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + let query = DataBaseQuery::from(data_base, collection); + + let metadata = result.unwrap().collection_metadata(&query).await; if let Err(error) = metadata { let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); return Err(exception.into_response()); @@ -65,7 +140,7 @@ impl ControllerCollection { Ok(Json(dto)) } - async fn find_all(Path((service, data_base)): Path<(String, String)>) -> Result>, impl IntoResponse> { + async fn schema(Path((service, data_base, collection)): Path<(String, String, String)>) -> Result, impl IntoResponse> { let o_db_service = Configuration::find_service(&service); if o_db_service.is_none() { return Err(utils::not_found()); @@ -77,18 +152,18 @@ impl ControllerCollection { return Err(exception.into_response()); } - let query = DataBaseQuery::from_data_base(data_base); + let query = DataBaseQuery::from(data_base, collection); - let collections = result.unwrap().collection_find_all(&query).await; - if let Err(error) = collections { + let schema = result.unwrap().schema(&query).await; + if let Err(error) = schema { let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); return Err(exception.into_response()); } - Ok(Json(collections.unwrap())) + Ok(Json(DTODocumentSchema::from(&schema.unwrap()))) } - async fn insert(Path((service, _)): Path<(String, String)>, Json(dto): Json) -> Result { + async fn rename(Path((service, data_base, collection)): Path<(String, String, String)>, Json(dto): Json) -> Result { let o_db_service = Configuration::find_service(&service); if o_db_service.is_none() { return Err(utils::not_found()); @@ -100,21 +175,44 @@ impl ControllerCollection { return Err(exception.into_response()); } - let query = dto.from_dto(); - if let Err(exception) = query { + let query = DataBaseQuery::from(data_base, collection); + + let documents = result.unwrap().collection_rename(&query, &dto.collection).await; + if let Err(error) = documents { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); return Err(exception.into_response()); } + + Ok(StatusCode::OK) + } - let collection = result.unwrap().collection_create(&query.unwrap()).await; - if let Err(error) = collection { + async fn export(Path((service, data_base, collection)): Path<(String, String, String)>) -> Result>, impl IntoResponse> { + let o_db_service = Configuration::find_service(&service); + if o_db_service.is_none() { + return Err(utils::not_found()); + } + + let result = o_db_service.unwrap().instance().await; + if let Err(error) = result { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + let query = DataBaseQuery::from(data_base, collection); + + let documents = result.unwrap().collection_export(&query).await; + if let Err(error) = documents { let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); return Err(exception.into_response()); } - Ok(StatusCode::ACCEPTED) + Ok(Json(documents.unwrap().iter() + .map(|d| DTODocumentData::from(d)) + .collect()) + ) } - async fn delete(Path((service, data_base, collection)): Path<(String, String, String)>) -> Result { + async fn import(Path((service, data_base, collection)): Path<(String, String, String)>, documents: Json>) -> Result { let o_db_service = Configuration::find_service(&service); if o_db_service.is_none() { return Err(utils::not_found()); @@ -126,15 +224,15 @@ impl ControllerCollection { return Err(exception.into_response()); } - let query = GenerateCollectionQuery::from_collection(data_base, collection); + let query = DataBaseQuery::from(data_base, collection); - let collection = result.unwrap().collection_drop(&query).await; - if let Err(error) = collection { + let result = result.unwrap().collection_import(&query, documents.to_vec()).await; + if let Err(error) = result { let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); return Err(exception.into_response()); } - Ok(StatusCode::ACCEPTED) + Ok(StatusCode::OK) } } \ No newline at end of file diff --git a/src/infrastructure/controller_database.rs b/src/infrastructure/controller_database.rs index ee776f7..2505f5a 100644 --- a/src/infrastructure/controller_database.rs +++ b/src/infrastructure/controller_database.rs @@ -8,17 +8,13 @@ use axum::{ }; use rust_db_manager_core::{ commons::configuration::configuration::Configuration, - domain::data_base::generate_database_query::GenerateDatabaseQuery, + domain::{data_base::generate_database_query::GenerateDatabaseQuery, filter::data_base_query::DataBaseQuery}, }; use crate::commons::exception::api_exception::ApiException; use super::{ - dto::{ - collection::dto_collection_definition::DTOCollectionDefinition, - data_base::dto_generate_data_base_query::DTOGenerateDatabaseQuery, - table::dto_table_data_group::DTOTableDataGroup, - }, + dto::{data_base::dto_generate_data_base_query::DTOGenerateDatabaseQuery, table::dto_table_data_group::DTOTableDataGroup}, handler, utils, }; @@ -29,37 +25,14 @@ impl ControllerDataBase { pub fn route(router: Router) -> Router { router - .route("/:service/status", get(Self::status)) - .route("/:service/metadata", get(Self::metadata)) - .route("/:service/schema", get(Self::schema)) - .route("/:service/data-base", get(Self::find_all)) - .route("/:service/data-base", post(Self::insert)) - .route("/:service/data-base/:data_base", delete(Self::delete)) + .route("/api/v1/service/:service/data-base", get(Self::find_all)) + .route("/api/v1/service/:service/data-base", post(Self::insert)) + .route("/api/v1/service/:service/data-base/:data_base", delete(Self::delete)) + .route("/api/v1/service/:service/data-base/:data_base/metadata", get(Self::metadata)) .route_layer(middleware::from_fn(handler::autentication_handler)) } - async fn status(Path(service): Path) -> Result<(StatusCode, String), impl IntoResponse> { - let o_db_service = Configuration::find_service(&service); - if o_db_service.is_none() { - return Err(utils::not_found()); - } - - let result = o_db_service.unwrap().instance().await; - if let Err(error) = result { - let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); - return Err(exception.into_response()); - } - - let status = result.unwrap().status().await; - if let Err(error) = status { - let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); - return Err(exception.into_response()); - } - - Ok((StatusCode::ACCEPTED, String::from("listening"))) - } - - async fn metadata(Path(service): Path) -> Result>, impl IntoResponse> { + async fn find_all(Path(service): Path) -> Result>, impl IntoResponse> { let o_db_service = Configuration::find_service(&service); if o_db_service.is_none() { return Err(utils::not_found()); @@ -71,20 +44,16 @@ impl ControllerDataBase { return Err(exception.into_response()); } - let metadata = result.unwrap().metadata().await; - if let Err(error) = metadata { + let collection = result.unwrap().data_base_find_all().await; + if let Err(error) = collection { let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); return Err(exception.into_response()); } - let dto = metadata.unwrap().iter() - .map(|g| DTOTableDataGroup::from(g)) - .collect(); - - Ok(Json(dto)) + Ok(Json(collection.unwrap())) } - async fn schema(Path(service): Path) -> Result, impl IntoResponse> { + async fn insert(Path(service): Path, Json(dto): Json) -> Result { let o_db_service = Configuration::find_service(&service); if o_db_service.is_none() { return Err(utils::not_found()); @@ -96,37 +65,18 @@ impl ControllerDataBase { return Err(exception.into_response()); } - let definition = result.unwrap().collection_accept_schema().await; - if let Err(error) = definition { - let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); - return Err(exception.into_response()); - } - - Ok(Json(DTOCollectionDefinition::from(definition.unwrap()))) - } - - async fn find_all(Path(service): Path) -> Result>, impl IntoResponse> { - let o_db_service = Configuration::find_service(&service); - if o_db_service.is_none() { - return Err(utils::not_found()); - } - - let result = o_db_service.unwrap().instance().await; - if let Err(error) = result { - let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); - return Err(exception.into_response()); - } + let query = GenerateDatabaseQuery::new(dto.data_base); - let collection = result.unwrap().data_base_find_all().await; + let collection = result.unwrap().data_base_create(&query).await; if let Err(error) = collection { let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); return Err(exception.into_response()); } - Ok(Json(collection.unwrap())) + Ok(StatusCode::ACCEPTED) } - async fn insert(Path(service): Path, Json(dto): Json) -> Result { + async fn delete(Path((service, data_base)): Path<(String, String)>) -> Result { let o_db_service = Configuration::find_service(&service); if o_db_service.is_none() { return Err(utils::not_found()); @@ -138,9 +88,9 @@ impl ControllerDataBase { return Err(exception.into_response()); } - let query = GenerateDatabaseQuery::new(dto.data_base); + let query = GenerateDatabaseQuery::new(data_base); - let collection = result.unwrap().data_base_create(&query).await; + let collection = result.unwrap().data_base_drop(&query).await; if let Err(error) = collection { let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); return Err(exception.into_response()); @@ -149,27 +99,31 @@ impl ControllerDataBase { Ok(StatusCode::ACCEPTED) } - async fn delete(Path((service, data_base)): Path<(String, String)>) -> Result { + async fn metadata(Path((service, data_base)): Path<(String, String)>) -> Result>, impl IntoResponse> { let o_db_service = Configuration::find_service(&service); if o_db_service.is_none() { return Err(utils::not_found()); } - + let result = o_db_service.unwrap().instance().await; if let Err(error) = result { let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); return Err(exception.into_response()); } - let query = GenerateDatabaseQuery::new(data_base); + let query = DataBaseQuery::from_data_base(data_base); - let collection = result.unwrap().data_base_drop(&query).await; - if let Err(error) = collection { + let metadata = result.unwrap().data_base_metadata(&query).await; + if let Err(error) = metadata { let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); return Err(exception.into_response()); } - Ok(StatusCode::ACCEPTED) + let dto = metadata.unwrap().iter() + .map(|g| DTOTableDataGroup::from(g)) + .collect(); + + Ok(Json(dto)) } } \ No newline at end of file diff --git a/src/infrastructure/controller_document.rs b/src/infrastructure/controller_document.rs index 774239d..383f841 100644 --- a/src/infrastructure/controller_document.rs +++ b/src/infrastructure/controller_document.rs @@ -17,13 +17,9 @@ use crate::commons::exception::api_exception::ApiException; use super::{ dto::{ - document::{ - dto_document_data::DTODocumentData, dto_document_key::DTODocumentKey, - dto_document_schema::DTODocumentSchema, - }, + document::{dto_document_data::DTODocumentData, dto_document_key::DTODocumentKey}, dto_create_document::DTOCreateDocument, dto_update_document::DTOUpdateDocument, - table::dto_table_data_group::DTOTableDataGroup, }, handler, utils, }; @@ -35,66 +31,14 @@ impl ControllerDocument { pub fn route(router: Router) -> Router { router - .route("/:service/data-base/:data_base/collection/:collection/metadata", get(Self::metadata)) - .route("/:service/data-base/:data_base/collection/:collection/schema", get(Self::schema)) - .route("/:service/data-base/:data_base/collection/:collection/document/find", get(Self::find_all)) - .route("/:service/data-base/:data_base/collection/:collection/document/find", post(Self::find)) - .route("/:service/data-base/:data_base/collection/:collection/document/query", post(Self::insert)) - .route("/:service/data-base/:data_base/collection/:collection/document/query", put(Self::update)) - .route("/:service/data-base/:data_base/collection/:collection/document/query", delete(Self::delete)) + .route("/api/v1/service/:service/data-base/:data_base/collection/:collection/document/find", get(Self::find_all)) + .route("/api/v1/service/:service/data-base/:data_base/collection/:collection/document/find", post(Self::find)) + .route("/api/v1/service/:service/data-base/:data_base/collection/:collection/document/action", post(Self::insert)) + .route("/api/v1/service/:service/data-base/:data_base/collection/:collection/document/action", put(Self::update)) + .route("/api/v1/service/:service/data-base/:data_base/collection/:collection/document/action", delete(Self::delete)) .route_layer(middleware::from_fn(handler::autentication_handler)) } - async fn metadata(Path((service, data_base, collection)): Path<(String, String, String)>) -> Result>, impl IntoResponse> { - let o_db_service = Configuration::find_service(&service); - if o_db_service.is_none() { - return Err(utils::not_found()); - } - - let result = o_db_service.unwrap().instance().await; - if let Err(error) = result { - let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); - return Err(exception.into_response()); - } - - let query = DataBaseQuery::from(data_base, collection); - - let metadata = result.unwrap().collection_metadata(&query).await; - if let Err(error) = metadata { - let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); - return Err(exception.into_response()); - } - - let dto = metadata.unwrap().iter() - .map(|g| DTOTableDataGroup::from(g)) - .collect(); - - Ok(Json(dto)) - } - - async fn schema(Path((service, data_base, collection)): Path<(String, String, String)>) -> Result, impl IntoResponse> { - let o_db_service = Configuration::find_service(&service); - if o_db_service.is_none() { - return Err(utils::not_found()); - } - - let result = o_db_service.unwrap().instance().await; - if let Err(error) = result { - let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); - return Err(exception.into_response()); - } - - let query = DataBaseQuery::from(data_base, collection); - - let schema = result.unwrap().schema(&query).await; - if let Err(error) = schema { - let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); - return Err(exception.into_response()); - } - - Ok(Json(DTODocumentSchema::from(&schema.unwrap()))) - } - async fn find(Path((service, data_base, collection)): Path<(String, String, String)>, Json(dto): Json>) -> Result, impl IntoResponse> { let o_db_service = Configuration::find_service(&service); if o_db_service.is_none() { diff --git a/src/infrastructure/controller_server.rs b/src/infrastructure/controller_server.rs index f083c91..6757ffd 100644 --- a/src/infrastructure/controller_server.rs +++ b/src/infrastructure/controller_server.rs @@ -1,10 +1,17 @@ -use axum::{body::Body, extract::{Path, Query}, http::{header::SET_COOKIE, HeaderMap, Response, StatusCode}, middleware, response::IntoResponse, routing::{delete, get, post}, Json, Router}; +use axum::{ + http::StatusCode, + routing::get, + Json, Router, +}; -use rust_db_manager_core::{commons::configuration::configuration::Configuration, infrastructure::{db_service::DBService, repository::e_db_repository::EDBRepository}}; +use rust_db_manager_core::infrastructure::repository::e_db_repository::EDBRepository; -use crate::{commons::{configuration::web_configuration::WebConfiguration, exception::{api_exception::ApiException, auth_exception::AuthException}}, domain::{builder_db_service::BuilderDBService, cookie::cookie::Cookie}}; +use crate::commons::configuration::web_configuration::WebConfiguration; -use super::{db_assets::WebEDBRepository, dto::{dto_server_status::DTOServerStatus, pagination::{dto_paginated_collection::DTOPaginatedCollection, dto_query_pagination::DTOQueryPagination}, service::{definition::{dto_service::DTOService, dto_service_category_lite::DTOServiceCategoryLite, dto_service_lite::DTOServiceLite}, generate::{dto_service_create_request::DTOServiceRequest, dto_service_suscribe_request::DTOServiceSuscribeRequest}}}, handler, pagination::Pagination, services_jwt::ServicesJWT, utils::{self, find_token}}; +use super::{ + db_assets::WebEDBRepository, + dto::{dto_server_status::DTOServerStatus, service::definition::dto_service_category_lite::DTOServiceCategoryLite} +}; pub struct ControllerServer { } @@ -13,153 +20,18 @@ impl ControllerServer { pub fn route(router: Router) -> Router { router - .route("/:service", get(Self::service_find)) - .route("/:service", delete(Self::service_remove)) - .route_layer(middleware::from_fn(handler::autentication_handler)) - .route("/metadata", get(Self::metadata)) - .route("/support", get(Self::support)) - .route("/services", get(Self::services)) - .route("/publish", post(Self::publish)) - .route("/suscribe", post(Self::suscribe)) - } - - async fn service_find(Path(service): Path) -> Result,impl IntoResponse> { - let o_db_service = Configuration::find_service(&service); - if o_db_service.is_none() { - return Err(utils::not_found()); - } - - Ok(Json(DTOService::from(o_db_service.unwrap()))) - } - - async fn service_remove(headers: HeaderMap, Path(service): Path) -> impl IntoResponse { - let o_db_service = Configuration::find_service(&service); - if o_db_service.is_none() { - return Err(utils::not_found()); - } - - let db_service = o_db_service.unwrap(); - - let r_cookie = Self::remove_token(headers, &db_service); - if let Err(exception) = r_cookie { - return Err(exception.into_response()); - } - - Configuration::remove_service(db_service); - - Ok(Self::build_token_response(r_cookie.unwrap(), Body::empty())) + .route("/api/v1/metadata", get(Self::metadata)) + .route("/api/v1/available", get(Self::available)) } async fn metadata() -> (StatusCode, Json) { let result = WebConfiguration::as_dto(); - (StatusCode::ACCEPTED, Json(result)) - } - - async fn support() -> (StatusCode, Json>) { - let dto = EDBRepository::supported(); - (StatusCode::ACCEPTED, Json(dto)) - } - - async fn services(Query(params): Query) -> (StatusCode, Json>) { - let services = Configuration::find_services(); - let dto = services.iter().map(|s| DTOServiceLite::from(s)).collect(); - let result = Pagination::paginate(params, dto); - (StatusCode::ACCEPTED, Json(result)) - } - - async fn publish(headers: HeaderMap, Json(dto): Json) -> impl IntoResponse { - let o_service = BuilderDBService::make(dto); - if let Err(error) = o_service { - return Err(error.into_response()); - } - - let service = &o_service.unwrap(); - - let r_cookie = Self::make_token(headers, service); - if let Err(exception) = r_cookie { - return Err(exception.into_response()); - } - - let db_service = Configuration::push_service(service); - if let Err(exception) = db_service { - let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), exception); - return Err(exception.into_response()); - } - - Ok(Self::build_token_response(r_cookie.unwrap(), Body::empty())) - } - - async fn suscribe(headers: HeaderMap, Json(dto): Json) -> impl IntoResponse { - let o_db_service = Configuration::find_service(&dto.name); - if o_db_service.is_none() { - let exception = ApiException::new(StatusCode::NOT_FOUND.as_u16(), String::from("Service not found.")); - return Err(exception.into_response()); - } - - let db_service = &o_db_service.unwrap(); - - if db_service.is_authorized(dto.password).is_err() { - let exception = ApiException::new(StatusCode::UNAUTHORIZED.as_u16(), String::from("Authentication error.")); - return Err(exception.into_response()); - } - - let r_cookie = Self::make_token(headers, db_service); - if let Err(exception) = r_cookie { - return Err(exception.into_response()); - } - - Ok(Self::build_token_response(r_cookie.unwrap(), Body::empty())) + (StatusCode::OK, Json(result)) } - fn make_token(headers: HeaderMap, service: &DBService) -> Result, AuthException> { - let o_cookie = find_token(headers); - if o_cookie.is_err() { - return Err(o_cookie.unwrap_err()); - } - - match o_cookie.unwrap() { - Some(cookie) => { - if !service.is_protected() { - return Ok(Some(cookie)); - } - - Ok(Some(ServicesJWT::update(&cookie.value, service)?)) - }, - None => { - if !service.is_protected() { - return Ok(None); - } - Ok(Some(ServicesJWT::sign(service)?)) - }, - } - } - - fn remove_token(headers: HeaderMap, service: &DBService) -> Result, AuthException> { - let o_cookie = find_token(headers); - if o_cookie.is_err() { - return Err(o_cookie.unwrap_err()); - } - - match o_cookie.unwrap() { - Some(cookie) => { - if !service.is_protected() { - return Ok(Some(cookie)); - } - Ok(Some(ServicesJWT::remove(&cookie.value, service)?)) - }, - None => return Ok(None), - } - } - - fn build_token_response(cookie: Option, body: Body) -> impl IntoResponse { - let mut builder = Response::builder(); - if cookie.is_some() { - builder = builder.header(SET_COOKIE, cookie.unwrap().to_string()); - } - - builder.status(StatusCode::OK) - .body(body) - .unwrap() + async fn available() -> (StatusCode, Json>) { + let dto = EDBRepository::availables(); + (StatusCode::OK, Json(dto)) } } \ No newline at end of file diff --git a/src/infrastructure/controller_service.rs b/src/infrastructure/controller_service.rs new file mode 100644 index 0000000..0de66d2 --- /dev/null +++ b/src/infrastructure/controller_service.rs @@ -0,0 +1,257 @@ +use axum::{ + body::Body, + extract::{Path, Query}, + http::{header::SET_COOKIE, HeaderMap, Response, StatusCode}, + middleware, + response::IntoResponse, + routing::{delete, get, patch, post}, + Json, Router, +}; + +use rust_db_manager_core::{ + commons::configuration::configuration::Configuration, infrastructure::db_service::DBService, +}; + +use crate::{ + commons::exception::{api_exception::ApiException, auth_exception::AuthException}, + domain::{builder_db_service::BuilderDBService, cookie::cookie::Cookie}, +}; + +use super::{ + dto::{ + collection::dto_collection_definition::DTOCollectionDefinition, pagination::{ + dto_paginated_collection::DTOPaginatedCollection, + dto_query_pagination::DTOQueryPagination, + }, service::{ + definition::{ + dto_service::DTOService, + dto_service_lite::DTOServiceLite, + }, + generate::{ + dto_service_create_request::DTOServiceRequest, + dto_service_suscribe_request::DTOServiceSuscribeRequest, + }, + }, table::dto_table_data_group::DTOTableDataGroup + }, + handler, + pagination::Pagination, + services_jwt::ServicesJWT, + utils::{self, find_token}, +}; + +pub struct ControllerService { +} + +impl ControllerService { + + pub fn route(router: Router) -> Router { + router + .route("/api/v1/service/:service", get(Self::find)) + .route("/api/v1/service/:service", delete(Self::delete)) + .route("/api/v1/service/:service/status", get(Self::status)) + .route("/api/v1/service/:service/metadata", get(Self::metadata)) + .route("/api/v1/service/:service/schema", get(Self::schema)) + .route_layer(middleware::from_fn(handler::autentication_handler)) + + .route("/api/v1/service", get(Self::find_all)) + .route("/api/v1/service", post(Self::insert)) + .route("/api/v1/service", patch(Self::suscribe)) + } + + async fn find(Path(service): Path) -> Result,impl IntoResponse> { + let o_db_service = Configuration::find_service(&service); + if o_db_service.is_none() { + return Err(utils::not_found()); + } + + Ok(Json(DTOService::from(o_db_service.unwrap()))) + } + + async fn delete(headers: HeaderMap, Path(service): Path) -> impl IntoResponse { + let o_db_service = Configuration::find_service(&service); + if o_db_service.is_none() { + return Err(utils::not_found()); + } + + let db_service = o_db_service.unwrap(); + + let r_cookie = Self::remove_token(headers, &db_service); + if let Err(exception) = r_cookie { + return Err(exception.into_response()); + } + + Configuration::remove_service(db_service); + + Ok(Self::build_token_response(r_cookie.unwrap(), Body::empty())) + } + + async fn status(Path(service): Path) -> Result<(StatusCode, String), impl IntoResponse> { + let o_db_service = Configuration::find_service(&service); + if o_db_service.is_none() { + return Err(utils::not_found()); + } + + let result = o_db_service.unwrap().instance().await; + if let Err(error) = result { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + let status = result.unwrap().status().await; + if let Err(error) = status { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + Ok((StatusCode::ACCEPTED, String::from("listening"))) + } + + async fn metadata(Path(service): Path) -> Result>, impl IntoResponse> { + let o_db_service = Configuration::find_service(&service); + if o_db_service.is_none() { + return Err(utils::not_found()); + } + + let result = o_db_service.unwrap().instance().await; + if let Err(error) = result { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + let metadata = result.unwrap().metadata().await; + if let Err(error) = metadata { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + let dto = metadata.unwrap().iter() + .map(|g| DTOTableDataGroup::from(g)) + .collect(); + + Ok(Json(dto)) + } + + async fn schema(Path(service): Path) -> Result, impl IntoResponse> { + let o_db_service = Configuration::find_service(&service); + if o_db_service.is_none() { + return Err(utils::not_found()); + } + + let result = o_db_service.unwrap().instance().await; + if let Err(error) = result { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + let definition = result.unwrap().collection_accept_schema().await; + if let Err(error) = definition { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); + return Err(exception.into_response()); + } + + Ok(Json(DTOCollectionDefinition::from(definition.unwrap()))) + } + + async fn find_all(Query(params): Query) -> (StatusCode, Json>) { + let services = Configuration::find_services(); + let dto = services.iter().map(|s| DTOServiceLite::from(s)).collect(); + let result = Pagination::paginate(params, dto); + (StatusCode::ACCEPTED, Json(result)) + } + + async fn insert(headers: HeaderMap, Json(dto): Json) -> impl IntoResponse { + let o_service = BuilderDBService::make(dto); + if let Err(error) = o_service { + return Err(error.into_response()); + } + + let service = &o_service.unwrap(); + + let r_cookie = Self::make_token(headers, service); + if let Err(exception) = r_cookie { + return Err(exception.into_response()); + } + + let db_service = Configuration::push_service(service); + if let Err(exception) = db_service { + let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), exception); + return Err(exception.into_response()); + } + + Ok(Self::build_token_response(r_cookie.unwrap(), Body::empty())) + } + + async fn suscribe(headers: HeaderMap, Json(dto): Json) -> impl IntoResponse { + let o_db_service = Configuration::find_service(&dto.name); + if o_db_service.is_none() { + let exception = ApiException::new(StatusCode::NOT_FOUND.as_u16(), String::from("Service not found.")); + return Err(exception.into_response()); + } + + let db_service = &o_db_service.unwrap(); + + if db_service.is_authorized(dto.password).is_err() { + let exception = ApiException::new(StatusCode::UNAUTHORIZED.as_u16(), String::from("Authentication error.")); + return Err(exception.into_response()); + } + + let r_cookie = Self::make_token(headers, db_service); + if let Err(exception) = r_cookie { + return Err(exception.into_response()); + } + + Ok(Self::build_token_response(r_cookie.unwrap(), Body::empty())) + } + + fn make_token(headers: HeaderMap, service: &DBService) -> Result, AuthException> { + let o_cookie = find_token(headers); + if o_cookie.is_err() { + return Err(o_cookie.unwrap_err()); + } + + match o_cookie.unwrap() { + Some(cookie) => { + if !service.is_protected() { + return Ok(Some(cookie)); + } + + Ok(Some(ServicesJWT::update(&cookie.value, service)?)) + }, + None => { + if !service.is_protected() { + return Ok(None); + } + Ok(Some(ServicesJWT::sign(service)?)) + }, + } + } + + fn remove_token(headers: HeaderMap, service: &DBService) -> Result, AuthException> { + let o_cookie = find_token(headers); + if o_cookie.is_err() { + return Err(o_cookie.unwrap_err()); + } + + match o_cookie.unwrap() { + Some(cookie) => { + if !service.is_protected() { + return Ok(Some(cookie)); + } + Ok(Some(ServicesJWT::remove(&cookie.value, service)?)) + }, + None => return Ok(None), + } + } + + fn build_token_response(cookie: Option, body: Body) -> impl IntoResponse { + let mut builder = Response::builder(); + if cookie.is_some() { + builder = builder.header(SET_COOKIE, cookie.unwrap().to_string()); + } + + builder.status(StatusCode::OK) + .body(body) + .unwrap() + } + +} \ No newline at end of file diff --git a/src/infrastructure/db_assets.rs b/src/infrastructure/db_assets.rs index 02ed47b..943c2aa 100644 --- a/src/infrastructure/db_assets.rs +++ b/src/infrastructure/db_assets.rs @@ -3,13 +3,13 @@ use rust_db_manager_core::infrastructure::repository::e_db_repository::EDBReposi use super::dto::service::definition::{dto_service_category_lite::DTOServiceCategoryLite, dto_service_resources::DTOServiceResources}; pub trait WebEDBRepository { - fn supported() -> Vec; + fn availables() -> Vec; fn resources(&self) -> DTOServiceResources; } impl WebEDBRepository for EDBRepository { - fn supported() -> Vec { + fn availables() -> Vec { EDBRepository::items().iter() .map(|e| DTOServiceCategoryLite::from(e)) .collect() diff --git a/src/infrastructure/dto/collection/dto_rename_collection_query.rs b/src/infrastructure/dto/collection/dto_rename_collection_query.rs new file mode 100644 index 0000000..8227019 --- /dev/null +++ b/src/infrastructure/dto/collection/dto_rename_collection_query.rs @@ -0,0 +1,6 @@ +use serde::Deserialize; + +#[derive(Clone, Deserialize)] +pub struct DTORenameCollectionQuery { + pub collection: String +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index f1b0f30..b8a6c9a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,6 +23,7 @@ pub mod infrastructure { pub mod collection { pub mod dto_generate_collection_query; pub mod dto_collection_definition; + pub mod dto_rename_collection_query; } pub mod data_base { pub mod dto_generate_data_base_query; @@ -75,6 +76,7 @@ pub mod infrastructure { pub mod controller_database; pub mod controller_document; pub mod controller_server; + pub mod controller_service; pub mod db_assets; pub mod handler; pub mod pagination; diff --git a/src/main.rs b/src/main.rs index 70fd520..4b36d76 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,7 @@ use std::net::SocketAddr; use tower_http::cors::CorsLayer; use axum::Router; -use rust_db_manager_api::{commons::configuration::web_configuration::WebConfiguration, infrastructure::{controller_collection::ControllerCollection, controller_database::ControllerDataBase, controller_document::ControllerDocument, controller_server::ControllerServer}}; +use rust_db_manager_api::{commons::configuration::web_configuration::WebConfiguration, infrastructure::{controller_collection::ControllerCollection, controller_database::ControllerDataBase, controller_document::ControllerDocument, controller_server::ControllerServer, controller_service::ControllerService}}; #[tokio::main] async fn main() { @@ -11,6 +11,7 @@ async fn main() { let app = Router::new() .merge(ControllerServer::route(Router::new())) + .merge(ControllerService::route(Router::new())) .merge(ControllerDataBase::route(Router::new())) .merge(ControllerCollection::route(Router::new())) .merge(ControllerDocument::route(Router::new())) From 4bc28663d88b872566f544bfbf3e052478fb1f41 Mon Sep 17 00:00:00 2001 From: Rafael Ruiz Date: Thu, 16 May 2024 01:43:54 +0200 Subject: [PATCH 20/22] [FEATURE] DocumentSchema struct updated. --- src/infrastructure/dto/document/dto_document_schema.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/infrastructure/dto/document/dto_document_schema.rs b/src/infrastructure/dto/document/dto_document_schema.rs index 2badd62..613fe2b 100644 --- a/src/infrastructure/dto/document/dto_document_schema.rs +++ b/src/infrastructure/dto/document/dto_document_schema.rs @@ -6,6 +6,7 @@ use crate::infrastructure::dto::field::generate::dto_field_data::DTOFieldData; #[derive(Clone, Serialize)] pub struct DTODocumentSchema { comments: Vec, + sw_strict: bool, fields: Vec } @@ -14,6 +15,7 @@ impl DTODocumentSchema { pub fn from(schema: &DocumentSchema) -> Self { Self { comments: schema.comments(), + sw_strict: schema.is_strict(), fields: schema.fields().iter() .map(|f| DTOFieldData::from(f)) .collect() From 02f11d71457c1e7711c2d721ea3f2def6ab70e69 Mon Sep 17 00:00:00 2001 From: Rafael Ruiz Date: Thu, 16 May 2024 16:40:34 +0200 Subject: [PATCH 21/22] [FEATURE] Pagination logic added. --- src/infrastructure/controller_collection.rs | 14 ++++----- src/infrastructure/controller_database.rs | 2 +- src/infrastructure/controller_document.rs | 29 ++++++++----------- .../dto/collection/dto_collection_data.rs | 27 +++++++++++++++++ .../dto/pagination/dto_query_pagination.rs | 8 ++--- src/lib.rs | 1 + 6 files changed, 52 insertions(+), 29 deletions(-) create mode 100644 src/infrastructure/dto/collection/dto_collection_data.rs diff --git a/src/infrastructure/controller_collection.rs b/src/infrastructure/controller_collection.rs index 89e62a1..2d1502b 100644 --- a/src/infrastructure/controller_collection.rs +++ b/src/infrastructure/controller_collection.rs @@ -10,7 +10,7 @@ use rust_db_manager_core::{ commons::configuration::configuration::Configuration, domain::{ collection::generate_collection_query::GenerateCollectionQuery, - filter::data_base_query::DataBaseQuery, + filter::{collection_query::CollectionQuery, data_base_query::DataBaseQuery, document_query::DocumentQuery}, }, }; @@ -53,7 +53,7 @@ impl ControllerCollection { return Err(exception.into_response()); } - let query = DataBaseQuery::from_data_base(data_base); + let query = DataBaseQuery::from(data_base); let collections = result.unwrap().collection_find_all(&query).await; if let Err(error) = collections { @@ -125,7 +125,7 @@ impl ControllerCollection { return Err(exception.into_response()); } - let query = DataBaseQuery::from(data_base, collection); + let query = CollectionQuery::from(data_base, collection); let metadata = result.unwrap().collection_metadata(&query).await; if let Err(error) = metadata { @@ -152,7 +152,7 @@ impl ControllerCollection { return Err(exception.into_response()); } - let query = DataBaseQuery::from(data_base, collection); + let query = CollectionQuery::from(data_base, collection); let schema = result.unwrap().schema(&query).await; if let Err(error) = schema { @@ -175,7 +175,7 @@ impl ControllerCollection { return Err(exception.into_response()); } - let query = DataBaseQuery::from(data_base, collection); + let query = CollectionQuery::from(data_base, collection); let documents = result.unwrap().collection_rename(&query, &dto.collection).await; if let Err(error) = documents { @@ -198,7 +198,7 @@ impl ControllerCollection { return Err(exception.into_response()); } - let query = DataBaseQuery::from(data_base, collection); + let query = CollectionQuery::from(data_base, collection); let documents = result.unwrap().collection_export(&query).await; if let Err(error) = documents { @@ -224,7 +224,7 @@ impl ControllerCollection { return Err(exception.into_response()); } - let query = DataBaseQuery::from(data_base, collection); + let query = CollectionQuery::from(data_base, collection); let result = result.unwrap().collection_import(&query, documents.to_vec()).await; if let Err(error) = result { diff --git a/src/infrastructure/controller_database.rs b/src/infrastructure/controller_database.rs index 2505f5a..fdf92f3 100644 --- a/src/infrastructure/controller_database.rs +++ b/src/infrastructure/controller_database.rs @@ -111,7 +111,7 @@ impl ControllerDataBase { return Err(exception.into_response()); } - let query = DataBaseQuery::from_data_base(data_base); + let query = DataBaseQuery::from(data_base); let metadata = result.unwrap().data_base_metadata(&query).await; if let Err(error) = metadata { diff --git a/src/infrastructure/controller_document.rs b/src/infrastructure/controller_document.rs index 383f841..f3c6394 100644 --- a/src/infrastructure/controller_document.rs +++ b/src/infrastructure/controller_document.rs @@ -1,5 +1,5 @@ use axum::{ - extract::Path, + extract::{Path, Query}, http::StatusCode, middleware, response::IntoResponse, @@ -10,16 +10,14 @@ use rust_db_manager_core::{ commons::{ configuration::configuration::Configuration, utils::document_keys_to_filter_element, }, - domain::filter::data_base_query::DataBaseQuery, + domain::filter::{collection_query::CollectionQuery, document_query::DocumentQuery}, }; use crate::commons::exception::api_exception::ApiException; use super::{ dto::{ - document::{dto_document_data::DTODocumentData, dto_document_key::DTODocumentKey}, - dto_create_document::DTOCreateDocument, - dto_update_document::DTOUpdateDocument, + collection::dto_collection_data::DTOCollectionData, document::{dto_document_data::DTODocumentData, dto_document_key::DTODocumentKey}, dto_create_document::DTOCreateDocument, dto_update_document::DTOUpdateDocument, pagination::dto_query_pagination::DTOQueryPagination }, handler, utils, }; @@ -61,7 +59,7 @@ impl ControllerDocument { } let filter = document_keys_to_filter_element(keys); - let query = DataBaseQuery::from_filter(data_base, collection, filter); + let query = DocumentQuery::from_filter(data_base, collection, filter); let r_document = result.unwrap().find(&query).await; if let Err(error) = r_document { @@ -79,7 +77,7 @@ impl ControllerDocument { Ok(Json(DTODocumentData::from(&document.unwrap()))) } - async fn find_all(Path((service, data_base, collection)): Path<(String, String, String)>) -> Result>, impl IntoResponse> { + async fn find_all(Path((service, data_base, collection)): Path<(String, String, String)>, Query(params): Query) -> Result, impl IntoResponse> { let o_db_service = Configuration::find_service(&service); if o_db_service.is_none() { return Err(utils::not_found()); @@ -91,18 +89,15 @@ impl ControllerDocument { return Err(exception.into_response()); } - let query = DataBaseQuery::from(data_base, collection); + let query = DocumentQuery::from(data_base, collection, Some(params.limit), Some(params.offset), None); - let documents = result.unwrap().find_all(&query).await; - if let Err(error) = documents { + let data = result.unwrap().find_all(&query).await; + if let Err(error) = data { let exception = ApiException::from(StatusCode::INTERNAL_SERVER_ERROR.as_u16(), error); return Err(exception.into_response()); } - Ok(Json(documents.unwrap().iter() - .map(|d| DTODocumentData::from(d)) - .collect()) - ) + Ok(Json(DTOCollectionData::from(&data.unwrap()))) } async fn insert(Path((service, data_base, collection)): Path<(String, String, String)>, Json(dto): Json) -> Result, impl IntoResponse> { @@ -117,7 +112,7 @@ impl ControllerDocument { return Err(exception.into_response()); } - let query = DataBaseQuery::from(data_base, collection); + let query = CollectionQuery::from(data_base, collection); let document = result.unwrap().insert(&query, &dto.document).await; if let Err(error) = document { @@ -150,7 +145,7 @@ impl ControllerDocument { } let filter = document_keys_to_filter_element(keys); - let query = DataBaseQuery::from_filter(data_base, collection, filter); + let query = DocumentQuery::from_filter(data_base, collection, filter); let documents = result.unwrap().update(&query, &dto.document).await; if let Err(error) = documents { @@ -186,7 +181,7 @@ impl ControllerDocument { } let filter = document_keys_to_filter_element(keys); - let query = DataBaseQuery::from_filter(data_base, collection, filter); + let query = DocumentQuery::from_filter(data_base, collection, filter); let documents = result.unwrap().delete(&query).await; if let Err(error) = documents { diff --git a/src/infrastructure/dto/collection/dto_collection_data.rs b/src/infrastructure/dto/collection/dto_collection_data.rs new file mode 100644 index 0000000..ce4c309 --- /dev/null +++ b/src/infrastructure/dto/collection/dto_collection_data.rs @@ -0,0 +1,27 @@ +use rust_db_manager_core::domain::collection::collection_data::CollectionData; +use serde::Serialize; + +use crate::infrastructure::dto::document::dto_document_data::DTODocumentData; + +#[derive(Clone, Serialize)] +pub struct DTOCollectionData { + total: usize, + limit: Option, + offset: Option, + documents: Vec +} + +impl DTOCollectionData { + + pub fn from(data: &CollectionData) -> Self { + Self { + total: data.total(), + limit: data.limit(), + offset: data.offset(), + documents: data.documents().iter() + .map(|d| DTODocumentData::from(d)) + .collect() + } + } + +} \ No newline at end of file diff --git a/src/infrastructure/dto/pagination/dto_query_pagination.rs b/src/infrastructure/dto/pagination/dto_query_pagination.rs index cf07616..9d71465 100644 --- a/src/infrastructure/dto/pagination/dto_query_pagination.rs +++ b/src/infrastructure/dto/pagination/dto_query_pagination.rs @@ -2,17 +2,17 @@ use serde::Deserialize; #[derive(Debug, Deserialize)] pub struct DTOQueryPagination { - #[serde(default = "default_offset")] - pub offset: usize, #[serde(default = "default_limit")] pub limit: usize, + #[serde(default = "default_offset")] + pub offset: usize, } -fn default_offset() -> usize { +fn default_limit() -> usize { 0 } -fn default_limit() -> usize { +fn default_offset() -> usize { 10 } diff --git a/src/lib.rs b/src/lib.rs index b8a6c9a..8bb2381 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,6 +21,7 @@ pub mod domain { pub mod infrastructure { pub mod dto { pub mod collection { + pub mod dto_collection_data; pub mod dto_generate_collection_query; pub mod dto_collection_definition; pub mod dto_rename_collection_query; From 9b0dd03048269b7ba8e40c6a0a7100b8f9b3562b Mon Sep 17 00:00:00 2001 From: Rafael Ruiz Date: Fri, 17 May 2024 15:36:34 +0200 Subject: [PATCH 22/22] [FEATURE] Import max size fixed. --- Cargo.toml | 6 ++++ src/infrastructure/controller_collection.rs | 7 ++-- src/infrastructure/pagination.rs | 36 ++++++++++----------- 3 files changed, 28 insertions(+), 21 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 43ad76a..ad1b64c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,12 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[profile.dev] +opt-level = 3 + +[profile.release] +opt-level = 3 + [dependencies] tokio = { version = "1", features = ["full"] } lazy_static = "1.4.0" diff --git a/src/infrastructure/controller_collection.rs b/src/infrastructure/controller_collection.rs index 2d1502b..e040911 100644 --- a/src/infrastructure/controller_collection.rs +++ b/src/infrastructure/controller_collection.rs @@ -1,5 +1,5 @@ use axum::{ - extract::Path, + extract::{DefaultBodyLimit, Path}, http::StatusCode, middleware, response::IntoResponse, @@ -10,7 +10,7 @@ use rust_db_manager_core::{ commons::configuration::configuration::Configuration, domain::{ collection::generate_collection_query::GenerateCollectionQuery, - filter::{collection_query::CollectionQuery, data_base_query::DataBaseQuery, document_query::DocumentQuery}, + filter::{collection_query::CollectionQuery, data_base_query::DataBaseQuery}, }, }; @@ -30,6 +30,8 @@ impl ControllerCollection { pub fn route(router: Router) -> Router { router + .route("/api/v1/service/:service/data-base/:data_base/collection/:collection/import", post(Self::import)) + .layer(DefaultBodyLimit::max(52428800 )) .route("/api/v1/service/:service/data-base/:data_base/collection", get(Self::find_all)) .route("/api/v1/service/:service/data-base/:data_base/collection", post(Self::insert)) .route("/api/v1/service/:service/data-base/:data_base/collection/:collection", delete(Self::delete)) @@ -37,7 +39,6 @@ impl ControllerCollection { .route("/api/v1/service/:service/data-base/:data_base/collection/:collection/schema", get(Self::schema)) .route("/api/v1/service/:service/data-base/:data_base/collection/:collection/rename", post(Self::rename)) .route("/api/v1/service/:service/data-base/:data_base/collection/:collection/export", get(Self::export)) - .route("/api/v1/service/:service/data-base/:data_base/collection/:collection/import", post(Self::import)) .route_layer(middleware::from_fn(handler::autentication_handler)) } diff --git a/src/infrastructure/pagination.rs b/src/infrastructure/pagination.rs index 3aefc1c..f2e0283 100644 --- a/src/infrastructure/pagination.rs +++ b/src/infrastructure/pagination.rs @@ -6,48 +6,48 @@ impl Pagination { pub fn paginate(params: DTOQueryPagination, collection: Vec) -> DTOPaginatedCollection { let size = collection.len(); - let offset = params.offset; let limit = params.limit; + let offset = params.offset; - if offset >= size { - let previous = Pagination::calculate_previous(size, offset, limit); + if limit >= size { + let previous = Pagination::calculate_previous(size, limit, offset); return DTOPaginatedCollection::new(size, previous, size, Vec::new()); } - if size == 0 || limit == 0 { - return DTOPaginatedCollection::new(size, offset, offset, Vec::new()); + if size == 0 || offset == 0 { + return DTOPaginatedCollection::new(size, limit, limit, Vec::new()); } - let mut limit_fixed = limit; - if offset + limit >= size { - limit_fixed = size - offset; + let mut offset_fixed = offset; + if limit + offset >= size { + offset_fixed = size - limit; } - let cursor = offset + limit_fixed; + let cursor = limit + offset_fixed; - let next = Pagination::calculate_next(size, cursor, limit); - let previous = Pagination::calculate_previous(size, cursor, limit); + let next = Pagination::calculate_next(size, cursor, offset); + let previous = Pagination::calculate_previous(size, cursor, offset); - let slice = collection[offset..=cursor-1].to_vec(); + let slice = collection[limit..=cursor-1].to_vec(); return DTOPaginatedCollection::new(size, previous, next, slice); } - fn calculate_next(size: usize, cursor: usize, limit: usize) -> usize { - let next = cursor + limit; + fn calculate_next(size: usize, cursor: usize, offset: usize) -> usize { + let next = cursor + offset; if next >= size { return size; } return next; } - fn calculate_previous(size: usize, cursor: usize, limit: usize) -> usize { - if cursor.checked_sub(limit).is_none() { + fn calculate_previous(size: usize, cursor: usize, offset: usize) -> usize { + if cursor.checked_sub(offset).is_none() { return 0; } - let previous = cursor - limit; + let previous = cursor - offset; if previous > size { - return Pagination::calculate_previous(size, size, limit); + return Pagination::calculate_previous(size, size, offset); } return previous; }