Skip to content

Commit

Permalink
Merge branch 'main' into 18-update-model-endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
sabotack authored Nov 28, 2023
2 parents f8579d2 + d29b0c0 commit 3dc208a
Show file tree
Hide file tree
Showing 25 changed files with 2,761 additions and 2,691 deletions.
41 changes: 35 additions & 6 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,48 @@ on:

env:
CARGO_TERM_COLOR: always
TEST_DATABASE_URL: "sqlite::memory:"

jobs:
test:
# Label of the container job
container-job:
# Containers must run in Linux based operating systems
runs-on: ubuntu-latest
# Docker Hub image that `container-job` executes in
container: rust

# Service containers to run with `container-job`
services:
# Label used to access the service container
postgres:
# Docker Hub image
image: postgres
ports:
- 5432:5432
# Provide the password and db name for postgres
env:
POSTGRES_PASSWORD: 1234
POSTGRES_DB: ecdar_api
# Set health checks to wait until postgres has started
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v3
- name: Check out repository code
uses: actions/checkout@v4
with:
submodules: true
- name: Install Protoc
submodules: 'true'

- name: Install dependencies
uses: arduino/setup-protoc@v2

- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true

- name: Run tests
run: cargo test --verbose
env:
TEST_DATABASE_URL: "postgresql://postgres:1234@postgres:5432/ecdar_api"
run: cargo test -- --test-threads=1
21 changes: 21 additions & 0 deletions src/api/context_collection.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use crate::api::hashing_context::HashingContextTrait;
use crate::api::server::server::ecdar_backend_server::EcdarBackend;
use crate::database::access_context::AccessContextTrait;
use crate::database::in_use_context::InUseContextTrait;
use crate::database::model_context::ModelContextTrait;
use crate::database::query_context::QueryContextTrait;
use crate::database::session_context::SessionContextTrait;
use crate::database::user_context::UserContextTrait;
use std::sync::Arc;

#[derive(Clone)]
pub struct ContextCollection {
pub(crate) access_context: Arc<dyn AccessContextTrait>,
pub(crate) in_use_context: Arc<dyn InUseContextTrait>,
pub(crate) model_context: Arc<dyn ModelContextTrait>,
pub(crate) query_context: Arc<dyn QueryContextTrait>,
pub(crate) session_context: Arc<dyn SessionContextTrait>,
pub(crate) user_context: Arc<dyn UserContextTrait>,
pub(crate) reveaal_context: Arc<dyn EcdarBackend>,
pub(crate) hashing_context: Arc<dyn HashingContextTrait>,
}
122 changes: 62 additions & 60 deletions src/api/ecdar_api.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
use bcrypt::hash;
use chrono::Local;
use crate::api::context_collection::ContextCollection;

Check warning on line 1 in src/api/ecdar_api.rs

View workflow job for this annotation

GitHub Actions / cargo fmt

Diff in /home/runner/work/Ecdar-API/Ecdar-API/src/api/ecdar_api.rs
use regex::Regex;
use sea_orm::SqlErr;
use serde_json;
use std::sync::Arc;
use tonic::{Code, Request, Response, Status};

use crate::api::auth::{RequestExt, Token, TokenType};
use crate::database::{
access_context::AccessContextTrait, in_use_context::InUseContextTrait,

Check warning on line 9 in src/api/ecdar_api.rs

View workflow job for this annotation

GitHub Actions / Clippy lint and check

unused imports: `in_use_context::InUseContextTrait`, `model_context::ModelContextTrait`, `query_context::QueryContextTrait`

warning: unused imports: `in_use_context::InUseContextTrait`, `model_context::ModelContextTrait`, `query_context::QueryContextTrait` --> src/api/ecdar_api.rs:9:41 | 9 | access_context::AccessContextTrait, in_use_context::InUseContextTrait, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 10 | model_context::ModelContextTrait, query_context::QueryContextTrait, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(unused_imports)]` on by default

Check warning on line 9 in src/api/ecdar_api.rs

View workflow job for this annotation

GitHub Actions / Clippy lint and check

unused imports: `in_use_context::InUseContextTrait`, `model_context::ModelContextTrait`, `query_context::QueryContextTrait`

warning: unused imports: `in_use_context::InUseContextTrait`, `model_context::ModelContextTrait`, `query_context::QueryContextTrait` --> src/api/ecdar_api.rs:9:41 | 9 | access_context::AccessContextTrait, in_use_context::InUseContextTrait, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 10 | model_context::ModelContextTrait, query_context::QueryContextTrait, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(unused_imports)]` on by default

Check warning on line 9 in src/api/ecdar_api.rs

View workflow job for this annotation

GitHub Actions / Clippy lint and check

unused import: `access_context::AccessContextTrait`

warning: unused import: `access_context::AccessContextTrait` --> src/api/ecdar_api.rs:9:5 | 9 | access_context::AccessContextTrait, in_use_context::InUseContextTrait, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Check warning on line 9 in src/api/ecdar_api.rs

View workflow job for this annotation

GitHub Actions / Clippy lint and check

unused import: `access_context::AccessContextTrait`

warning: unused import: `access_context::AccessContextTrait` --> src/api/ecdar_api.rs:9:5 | 9 | access_context::AccessContextTrait, in_use_context::InUseContextTrait, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
model_context::ModelContextTrait, query_context::QueryContextTrait,
session_context::SessionContextTrait, user_context::UserContextTrait,
};
use crate::entities::access::Model;

Check warning on line 13 in src/api/ecdar_api.rs

View workflow job for this annotation

GitHub Actions / Clippy lint and check

unused import: `crate::entities::access::Model`

warning: unused import: `crate::entities::access::Model` --> src/api/ecdar_api.rs:13:5 | 13 | use crate::entities::access::Model; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Check warning on line 13 in src/api/ecdar_api.rs

View workflow job for this annotation

GitHub Actions / Clippy lint and check

unused import: `crate::entities::access::Model`

warning: unused import: `crate::entities::access::Model` --> src/api/ecdar_api.rs:13:5 | 13 | use crate::entities::access::Model; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

use super::server::server::UpdateModelRequest;
use super::server::server::{
ecdar_api_auth_server::EcdarApiAuth,
Expand All @@ -26,28 +23,20 @@ use super::server::server::{
SimulationStepRequest, SimulationStepResponse, UpdateAccessRequest, UpdateQueryRequest,

Check warning on line 23 in src/api/ecdar_api.rs

View workflow job for this annotation

GitHub Actions / cargo fmt

Diff in /home/runner/work/Ecdar-API/Ecdar-API/src/api/ecdar_api.rs
UpdateUserRequest, UserTokenResponse,
};
use crate::entities::{access, model, query, session, user};
use crate::entities::{access, query, session, user};

#[derive(Clone)]
pub struct ConcreteEcdarApi {
access_context: Arc<dyn AccessContextTrait>,
in_use_context: Arc<dyn InUseContextTrait>,
model_context: Arc<dyn ModelContextTrait>,
query_context: Arc<dyn QueryContextTrait>,
session_context: Arc<dyn SessionContextTrait>,
user_context: Arc<dyn UserContextTrait>,
reveaal_context: Arc<dyn EcdarBackend>,
contexts: ContextCollection,
}

const HASH_COST: u32 = 12;

/// Updates or creates a session in the database for a given user.
///
///
/// # Errors
/// This function will return an error if the database context returns an error
/// or if a session is not found when trying to update an existing one.
async fn handle_session(
pub async fn handle_session(
session_context: Arc<dyn SessionContextTrait>,
request: &Request<GetAuthTokenRequest>,
is_new_session: bool,
Expand All @@ -56,16 +45,19 @@ async fn handle_session(
uid: String,
) -> Result<(), Status> {
if is_new_session {
session_context
let res = session_context
.create(session::Model {
id: Default::default(),
access_token: access_token.clone(),
refresh_token: refresh_token.clone(),
updated_at: Local::now().naive_local(),
updated_at: Default::default(),
user_id: uid.parse().unwrap(),
})
.await
.unwrap();
.await;
return match res {
Ok(_) => Ok(()),
Err(e) => Err(Status::new(Code::Internal, e.to_string())),
};
} else {
let mut session = match session_context
.get_by_refresh_token(request.token_string().unwrap())
Expand Down Expand Up @@ -105,24 +97,8 @@ fn is_valid_username(username: &str) -> bool {
}

impl ConcreteEcdarApi {
pub fn new(
access_context: Arc<dyn AccessContextTrait>,
in_use_context: Arc<dyn InUseContextTrait>,
model_context: Arc<dyn ModelContextTrait>,
query_context: Arc<dyn QueryContextTrait>,
session_context: Arc<dyn SessionContextTrait>,
user_context: Arc<dyn UserContextTrait>,
reveaal_context: Arc<dyn EcdarBackend>,
) -> Self {
ConcreteEcdarApi {
access_context,
in_use_context,
model_context,
query_context,
session_context,
user_context,
reveaal_context,
}
pub fn new(contexts: ContextCollection) -> Self {
ConcreteEcdarApi { contexts }
}
}

Expand Down Expand Up @@ -258,7 +234,7 @@ impl EcdarApi for ConcreteEcdarApi {
user_id: access.user_id,
};

match self.access_context.create(access).await {
match self.contexts.access_context.create(access).await {
Ok(_) => Ok(Response::new(())),
Err(error) => Err(Status::new(Code::Internal, error.to_string())),
}
Expand All @@ -284,7 +260,7 @@ impl EcdarApi for ConcreteEcdarApi {
user_id: Default::default(),
};

match self.access_context.update(access).await {
match self.contexts.access_context.update(access).await {
Ok(_) => Ok(Response::new(())),
Err(error) => Err(Status::new(Code::Internal, error.to_string())),
}
Expand All @@ -298,7 +274,12 @@ impl EcdarApi for ConcreteEcdarApi {
&self,
request: Request<DeleteAccessRequest>,
) -> Result<Response<()>, Status> {
match self.access_context.delete(request.get_ref().id).await {
match self
.contexts
.access_context
.delete(request.get_ref().id)
.await
{
Ok(_) => Ok(Response::new(())),
Err(error) => match error {
sea_orm::DbErr::RecordNotFound(message) => {
Expand All @@ -325,6 +306,7 @@ impl EcdarApi for ConcreteEcdarApi {

// Get user from database
let user = self
.contexts
.user_context
.get_by_id(uid)
.await
Expand All @@ -333,7 +315,7 @@ impl EcdarApi for ConcreteEcdarApi {

// Record to be inserted in database
let new_user = user::Model {
id: Default::default(),
id: uid,
username: match message.clone().username {
Some(username) => {
if is_valid_username(username.as_str()) {
Expand All @@ -355,13 +337,13 @@ impl EcdarApi for ConcreteEcdarApi {
None => user.email,
},
password: match message.clone().password {
Some(password) => hash(password, HASH_COST).unwrap(),
Some(password) => self.contexts.hashing_context.hash_password(password),
None => user.password,
},
};

// Update user in database
match self.user_context.update(new_user).await {
match self.contexts.user_context.update(new_user).await {
Ok(_) => Ok(Response::new(())),
Err(error) => Err(Status::new(Code::Internal, error.to_string())),
}
Expand All @@ -377,7 +359,7 @@ impl EcdarApi for ConcreteEcdarApi {
.ok_or(Status::internal("Could not get uid from request metadata"))?;

// Delete user from database
match self.user_context.delete(uid).await {
match self.contexts.user_context.delete(uid).await {
Ok(_) => Ok(Response::new(())),
Err(error) => Err(Status::new(Code::Internal, error.to_string())),
}
Expand All @@ -399,7 +381,7 @@ impl EcdarApi for ConcreteEcdarApi {
model_id: query_request.model_id,
};

match self.query_context.create(query).await {
match self.contexts.query_context.create(query).await {
Ok(_) => Ok(Response::new(())),
Err(error) => Err(Status::new(Code::Internal, error.to_string())),
}
Expand All @@ -417,6 +399,7 @@ impl EcdarApi for ConcreteEcdarApi {
let message = request.get_ref().clone();

let old_query_res = self
.contexts
.query_context
.get_by_id(message.id)
.await
Expand All @@ -435,7 +418,7 @@ impl EcdarApi for ConcreteEcdarApi {
outdated: old_query.outdated,
};

match self.query_context.update(query).await {
match self.contexts.query_context.update(query).await {
Ok(_) => Ok(Response::new(())),
Err(error) => Err(Status::new(Code::Internal, error.to_string())),
}
Expand All @@ -448,7 +431,12 @@ impl EcdarApi for ConcreteEcdarApi {
&self,
request: Request<DeleteQueryRequest>,
) -> Result<Response<()>, Status> {
match self.query_context.delete(request.get_ref().id).await {
match self
.contexts
.query_context
.delete(request.get_ref().id)
.await
{
Ok(_) => Ok(Response::new(())),
Err(error) => match error {
sea_orm::DbErr::RecordNotFound(message) => {
Expand Down Expand Up @@ -505,8 +493,11 @@ impl EcdarApiAuth for ConcreteEcdarApi {
// Get user from credentials
if let Some(user_credentials) = message.user_credentials {
let input_password = user_credentials.password.clone();
user_from_db =
get_auth_find_user_helper(Arc::clone(&self.user_context), user_credentials).await?;
user_from_db = get_auth_find_user_helper(
Arc::clone(&self.contexts.user_context),
user_credentials,
)
.await?;

// Check if password in request matches users password
if input_password != user_from_db.password {
Expand Down Expand Up @@ -538,7 +529,7 @@ impl EcdarApiAuth for ConcreteEcdarApi {

// Update or create session in database
handle_session(
self.session_context.clone(),
self.contexts.session_context.clone(),
&request,
is_new_session,
access_token.clone(),
Expand Down Expand Up @@ -574,7 +565,7 @@ impl EcdarApiAuth for ConcreteEcdarApi {
email: message.clone().email,
};

match self.user_context.create(user).await {
match self.contexts.user_context.create(user).await {
Ok(_) => Ok(Response::new(())),
Err(e) => match e.sql_err() {
Some(SqlErr::UniqueConstraintViolation(e)) => {
Expand All @@ -598,38 +589,49 @@ impl EcdarBackend for ConcreteEcdarApi {
&self,
_request: Request<()>,
) -> Result<Response<UserTokenResponse>, Status> {
self.reveaal_context.get_user_token(_request).await
self.contexts.reveaal_context.get_user_token(_request).await
}

async fn send_query(
&self,
request: Request<QueryRequest>,
) -> Result<Response<QueryResponse>, Status> {
self.reveaal_context.send_query(request).await
self.contexts.reveaal_context.send_query(request).await
}

async fn start_simulation(
&self,
request: Request<SimulationStartRequest>,
) -> Result<Response<SimulationStepResponse>, Status> {
self.reveaal_context.start_simulation(request).await
self.contexts
.reveaal_context
.start_simulation(request)
.await
}

async fn take_simulation_step(
&self,
request: Request<SimulationStepRequest>,
) -> Result<Response<SimulationStepResponse>, Status> {
self.reveaal_context.take_simulation_step(request).await
self.contexts
.reveaal_context
.take_simulation_step(request)
.await
}
}

#[cfg(test)]
#[path = "../tests/api/query_logic.rs"]
mod query_logic_tests;

#[cfg(test)]
#[path = "../tests/api/access_logic.rs"]
mod access_logic;
mod access_logic_tests;

#[cfg(test)]
#[path = "../tests/api/ecdar_api.rs"]
mod tests;
#[path = "../tests/api/user_logic.rs"]
mod user_logic_tests;

#[cfg(test)]
#[path = "../tests/api/query_logic.rs"]
mod query_logic;
#[path = "../tests/api/session_logic.rs"]
mod session_logic_tests;
Loading

0 comments on commit 3dc208a

Please sign in to comment.