From 4bb7a4cbaa91d347fd665661d1963e41b8eee4c4 Mon Sep 17 00:00:00 2001 From: williamwoldum Date: Mon, 20 Nov 2023 15:17:54 +0100 Subject: [PATCH] Applying DI to api layer --- Cargo.toml | 1 + src/api/ecdar_api.rs | 49 ++++++++++++------------ src/api/mod.rs | 1 + src/api/reveaal_context.rs | 67 +++++++++++++++++++++++++++++++++ src/api/server.rs | 4 +- src/database/entity_context.rs | 1 + src/database/session_context.rs | 1 + src/database/user_context.rs | 1 + src/main.rs | 4 ++ src/tests/api/ecdar_api.rs | 8 +++- src/tests/api/helpers.rs | 11 +++++- 11 files changed, 120 insertions(+), 28 deletions(-) create mode 100644 src/api/reveaal_context.rs diff --git a/Cargo.toml b/Cargo.toml index 5fcf8d3..e8c6383 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ serde = "1.0.189" chrono = "0.4.31" uuid = { version = "1.5.0", features = ["v4"] } regex = "1.10.2" +mockall = "0.11.4" [build-dependencies] tonic-build = "0.10.2" diff --git a/src/api/ecdar_api.rs b/src/api/ecdar_api.rs index 4921cf7..b05dcd7 100644 --- a/src/api/ecdar_api.rs +++ b/src/api/ecdar_api.rs @@ -1,17 +1,16 @@ use std::env; +use std::fmt::{Debug, Formatter}; use std::sync::Arc; use crate::api::server::server::get_auth_token_request::user_credentials; use crate::entities::session::Model; use chrono::Local; +use mockall::automock; use regex::Regex; use sea_orm::SqlErr; use tonic::{Code, Request, Response, Status}; -use crate::api::server::server::{ - ecdar_api_auth_server::EcdarApiAuth, ecdar_api_server::EcdarApi, - ecdar_backend_client::EcdarBackendClient, -}; +use crate::api::server::server::{ecdar_api_auth_server::EcdarApiAuth, ecdar_api_server::EcdarApi}; use crate::database::access_context::AccessContextTrait; use crate::database::in_use_context::InUseContextTrait; use crate::database::model_context::ModelContextTrait; @@ -29,9 +28,9 @@ use super::{ }, }; -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct ConcreteEcdarApi { - reveaal_address: String, + reveaal_context: Arc, model_context: Arc, user_context: Arc, access_context: Arc, @@ -40,6 +39,19 @@ pub struct ConcreteEcdarApi { in_use_context: Arc, } +impl Debug for ConcreteEcdarApi { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("") + .field(&self.model_context) + .field(&self.user_context) + .field(&self.access_context) + .field(&self.query_context) + .field(&self.session_context) + .field(&self.in_use_context) + .finish() + } +} + /// Updates or creates a session in the database for a given user. /// /// @@ -125,10 +137,10 @@ impl ConcreteEcdarApi { query_context: Arc, session_context: Arc, in_use_context: Arc, + reveaal_context: Arc, ) -> Self { ConcreteEcdarApi { - reveaal_address: env::var("REVEAAL_ADDRESS") - .expect("Expected REVEAAL_ADDRESS to be set."), + reveaal_context, model_context, user_context, access_context, @@ -370,45 +382,34 @@ impl EcdarApiAuth for ConcreteEcdarApi { /// Implementation of the EcdarBackend trait, which is used to ensure backwards compatability with the Reveaal engine. #[tonic::async_trait] +#[automock] impl EcdarBackend for ConcreteEcdarApi { async fn get_user_token( &self, _request: Request<()>, ) -> Result, Status> { - let mut client = EcdarBackendClient::connect(self.reveaal_address.clone()) - .await - .unwrap(); - client.get_user_token(_request).await + self.reveaal_context.get_user_token(_request).await } async fn send_query( &self, request: Request, ) -> Result, Status> { - let mut client = EcdarBackendClient::connect(self.reveaal_address.clone()) - .await - .unwrap(); - client.send_query(request).await + self.reveaal_context.send_query(request).await } async fn start_simulation( &self, request: Request, ) -> Result, Status> { - let mut client = EcdarBackendClient::connect(self.reveaal_address.clone()) - .await - .unwrap(); - client.start_simulation(request).await + self.reveaal_context.start_simulation(request).await } async fn take_simulation_step( &self, request: Request, ) -> Result, Status> { - let mut client = EcdarBackendClient::connect(self.reveaal_address.clone()) - .await - .unwrap(); - client.take_simulation_step(request).await + self.reveaal_context.take_simulation_step(request).await } } diff --git a/src/api/mod.rs b/src/api/mod.rs index 62bad22..eae1c43 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -1,3 +1,4 @@ pub mod auth; pub mod ecdar_api; +pub mod reveaal_context; pub mod server; diff --git a/src/api/reveaal_context.rs b/src/api/reveaal_context.rs new file mode 100644 index 0000000..f0c391b --- /dev/null +++ b/src/api/reveaal_context.rs @@ -0,0 +1,67 @@ +use crate::api::server::server::ecdar_backend_client::EcdarBackendClient; +use crate::api::server::server::ecdar_backend_server::EcdarBackend; +use crate::api::server::server::{ + QueryRequest, QueryResponse, SimulationStartRequest, SimulationStepRequest, + SimulationStepResponse, UserTokenResponse, +}; +use async_trait::async_trait; +use std::env; +use tonic::transport::Channel; +use tonic::{Request, Response, Status}; + +#[derive(Debug)] +pub struct ReveaalContext; + +impl ReveaalContext { + async fn get_connection() -> EcdarBackendClient { + let url = env::var("REVEAAL_ADDRESS").expect("Expected REVEAAL_ADDRESS to be set."); + EcdarBackendClient::connect(url).await.unwrap() + } +} + +#[async_trait] +impl EcdarBackend for ReveaalContext { + async fn get_user_token( + &self, + request: Request<()>, + ) -> Result, Status> { + Ok(ReveaalContext::get_connection() + .await + .get_user_token(request) + .await + .unwrap()) + } + + async fn send_query( + &self, + request: Request, + ) -> Result, Status> { + Ok(ReveaalContext::get_connection() + .await + .send_query(request) + .await + .unwrap()) + } + + async fn start_simulation( + &self, + request: Request, + ) -> Result, Status> { + Ok(ReveaalContext::get_connection() + .await + .start_simulation(request) + .await + .unwrap()) + } + + async fn take_simulation_step( + &self, + request: Request, + ) -> Result, Status> { + Ok(ReveaalContext::get_connection() + .await + .take_simulation_step(request) + .await + .unwrap()) + } +} diff --git a/src/api/server.rs b/src/api/server.rs index 2053060..c798d82 100644 --- a/src/api/server.rs +++ b/src/api/server.rs @@ -7,7 +7,7 @@ use crate::api::auth; use crate::api::ecdar_api::ConcreteEcdarApi; use crate::api::server::server::ecdar_api_auth_server::EcdarApiAuthServer; use crate::api::server::server::ecdar_api_server::EcdarApiServer; -use crate::api::server::server::ecdar_backend_server::EcdarBackendServer; +use crate::api::server::server::ecdar_backend_server::{EcdarBackend, EcdarBackendServer}; use crate::database::access_context::AccessContextTrait; use crate::database::in_use_context::InUseContextTrait; use crate::database::model_context::ModelContextTrait; @@ -20,6 +20,7 @@ pub mod server { } pub async fn start_grpc_server( + reveaal_context: Arc, model_context: Arc, user_context: Arc, access_context: Arc, @@ -42,6 +43,7 @@ pub async fn start_grpc_server( query_context, session_context, in_use_context, + reveaal_context, ) .await; diff --git a/src/database/entity_context.rs b/src/database/entity_context.rs index bdde912..fc95365 100644 --- a/src/database/entity_context.rs +++ b/src/database/entity_context.rs @@ -5,6 +5,7 @@ use std::fmt::Debug; use std::sync::Arc; #[async_trait] + pub trait EntityContextTrait: Send + Sync + Debug { fn new(db_context: Arc) -> Self where diff --git a/src/database/session_context.rs b/src/database/session_context.rs index 01dd9dc..01462fa 100644 --- a/src/database/session_context.rs +++ b/src/database/session_context.rs @@ -1,4 +1,5 @@ use chrono::{Local, Utc}; +use mockall::automock; use sea_orm::prelude::async_trait::async_trait; use sea_orm::ActiveValue::{Set, Unchanged}; use sea_orm::{ActiveModelTrait, ColumnTrait, DbErr, EntityTrait, QueryFilter}; diff --git a/src/database/user_context.rs b/src/database/user_context.rs index 2e281c7..1c63b9d 100644 --- a/src/database/user_context.rs +++ b/src/database/user_context.rs @@ -1,6 +1,7 @@ use crate::database::database_context::DatabaseContextTrait; use crate::database::entity_context::EntityContextTrait; use crate::entities::user; +use mockall::automock; use sea_orm::prelude::async_trait::async_trait; use sea_orm::ActiveValue::{Set, Unchanged}; use sea_orm::{ActiveModelTrait, ColumnTrait, DbErr, EntityTrait, QueryFilter}; diff --git a/src/main.rs b/src/main.rs index ef28c07..0fdc4c2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,8 @@ mod database; mod entities; mod tests; +use crate::api::reveaal_context::ReveaalContext; +use crate::api::server::server::ecdar_backend_server::EcdarBackend; use crate::database::access_context::AccessContext; use crate::database::database_context::{PostgresDatabaseContext, SQLiteDatabaseContext}; use crate::database::in_use_context::InUseContext; @@ -37,8 +39,10 @@ async fn main() -> Result<(), Box> { let query_context = Arc::new(QueryContext::new(db_context.clone())); let session_context = Arc::new(SessionContext::new(db_context.clone())); let in_use_context = Arc::new(InUseContext::new(db_context.clone())); + let reveaal_context = Arc::new(ReveaalContext); start_grpc_server( + reveaal_context, model_context, user_context, access_context, diff --git a/src/tests/api/ecdar_api.rs b/src/tests/api/ecdar_api.rs index 75166d5..937e6a1 100644 --- a/src/tests/api/ecdar_api.rs +++ b/src/tests/api/ecdar_api.rs @@ -1,6 +1,8 @@ #[cfg(test)] mod ecdar_api { - use crate::api::ecdar_api::handle_session; + use crate::api::ecdar_api::{ + handle_session, MockConcreteEcdarApi, MockConcreteEcdarApi_EcdarBackend, + }; use crate::api::server::server::ecdar_api_auth_server::EcdarApiAuth; use crate::api::server::server::get_auth_token_request::user_credentials; use crate::api::server::server::get_auth_token_request::UserCredentials; @@ -10,7 +12,9 @@ mod ecdar_api { entities::user::Model as User, }; use std::str::FromStr; + use std::sync::Arc; + use crate::api::server::server::ecdar_backend_server::EcdarBackend; use crate::tests::api::helpers::get_reset_concrete_ecdar_api; use tonic::{metadata, Request}; @@ -371,7 +375,7 @@ mod ecdar_api { #[tokio::test] async fn handle_session_update_non_existing_session_returns_err() { - let api = get_reset_concrete_ecdar_api().await; + let api = get_reset_concrete_ecdar_api(Arc::new(MockEcdarBackend)).await; let mut get_auth_token_request = Request::new(GetAuthTokenRequest { user_credentials: Some(UserCredentials { diff --git a/src/tests/api/helpers.rs b/src/tests/api/helpers.rs index f664f16..e3c47bc 100644 --- a/src/tests/api/helpers.rs +++ b/src/tests/api/helpers.rs @@ -1,6 +1,11 @@ #![cfg(test)] use crate::api::ecdar_api::ConcreteEcdarApi; +use crate::api::server::server::ecdar_backend_server::EcdarBackend; +use crate::api::server::server::{ + QueryRequest, QueryResponse, SimulationStartRequest, SimulationStepRequest, + SimulationStepResponse, UserTokenResponse, +}; use crate::database::access_context::AccessContext; use crate::database::database_context::{ DatabaseContextTrait, PostgresDatabaseContext, SQLiteDatabaseContext, @@ -15,8 +20,11 @@ use dotenv::dotenv; use sea_orm::{ConnectionTrait, Database, DbBackend}; use std::env; use std::sync::Arc; +use tonic::{Request, Response, Status}; -pub async fn get_reset_concrete_ecdar_api() -> ConcreteEcdarApi { +pub async fn get_reset_concrete_ecdar_api( + mock_ecdar_backend: Arc, +) -> ConcreteEcdarApi { dotenv().ok(); let url = env::var("TEST_DATABASE_URL").expect("TEST_DATABASE_URL must be set to run tests."); @@ -43,6 +51,7 @@ pub async fn get_reset_concrete_ecdar_api() -> ConcreteEcdarApi { query_context, session_context, in_use_context, + mock_ecdar_backend, ) .await }