Skip to content

Commit

Permalink
Merge pull request #70 from ECDAR-AAU-SW-P5/21-get-model
Browse files Browse the repository at this point in the history
21 get model
  • Loading branch information
AlexanderManich authored Nov 29, 2023
2 parents c61116c + 07be6b3 commit 0f812f3
Show file tree
Hide file tree
Showing 9 changed files with 1,411 additions and 868 deletions.
2 changes: 1 addition & 1 deletion Ecdar-ProtoBuf
165 changes: 136 additions & 29 deletions src/api/ecdar_api.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,3 @@
use crate::api::context_collection::ContextCollection;
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::{session_context::SessionContextTrait, user_context::UserContextTrait};

use super::server::server::{
ecdar_api_auth_server::EcdarApiAuth,
ecdar_api_server::EcdarApi,
Expand All @@ -16,11 +6,25 @@ use super::server::server::{
CreateAccessRequest, CreateModelRequest, CreateModelResponse, CreateQueryRequest,
CreateUserRequest, DeleteAccessRequest, DeleteModelRequest, DeleteQueryRequest,
GetAuthTokenRequest, GetAuthTokenResponse, GetModelRequest, GetModelResponse,
ListModelsInfoResponse, QueryRequest, QueryResponse, SimulationStartRequest,
SimulationStepRequest, SimulationStepResponse, UpdateAccessRequest, UpdateQueryRequest,
UpdateUserRequest, UserTokenResponse,
ListModelsInfoResponse, Query, QueryRequest, QueryResponse, SimulationStartRequest,
SimulationStepRequest, SimulationStepResponse, UpdateAccessRequest, UpdateModelRequest,
UpdateQueryRequest, UpdateUserRequest, UserTokenResponse,
};
use crate::api::context_collection::ContextCollection;
use crate::api::{
auth::{RequestExt, Token, TokenType},
server::server::Model,
};
use crate::database::{session_context::SessionContextTrait, user_context::UserContextTrait};
use crate::entities::{access, in_use, model, query, session, user};
use chrono::{Duration, Utc};
use regex::Regex;
use sea_orm::SqlErr;
use serde_json;
use std::sync::Arc;
use tonic::{Code, Request, Response, Status};

const IN_USE_DURATION_MINUTES: i64 = 10;

#[derive(Clone)]
pub struct ConcreteEcdarApi {
Expand Down Expand Up @@ -50,11 +54,8 @@ pub async fn handle_session(
updated_at: Default::default(),
user_id: uid.parse().unwrap(),
})
.await;
return match res {
Ok(_) => Ok(()),
Err(e) => Err(Status::new(Code::Internal, e.to_string())),
};
.await
.map_err(|err| Status::new(Code::Internal, err.to_string()))?;
} else {
let mut session = match session_context
.get_by_token(TokenType::RefreshToken, request.token_string().unwrap())
Expand All @@ -73,10 +74,10 @@ pub async fn handle_session(
session.access_token = access_token.clone();
session.refresh_token = refresh_token.clone();

match session_context.update(session).await {
Ok(_) => (),
Err(err) => return Err(Status::new(Code::Internal, err.to_string())),
};
session_context
.update(session)
.await
.map_err(|err| Status::new(Code::Internal, err.to_string()))?;
}
Ok(())
}
Expand All @@ -101,11 +102,114 @@ impl ConcreteEcdarApi {

#[tonic::async_trait]
impl EcdarApi for ConcreteEcdarApi {
/// Gets a Model and its queries from the database.
///
/// If the Model is not in use, it will now be in use by the requestees session,
/// given that they are an Editor.
async fn get_model(
&self,
_request: Request<GetModelRequest>,
request: Request<GetModelRequest>,
) -> Result<Response<GetModelResponse>, Status> {
todo!()
let message = request.get_ref().clone();

let model_id = message.id;

let uid = request
.uid()
.ok_or(Status::internal("Could not get uid from request metadata"))?;

let access = self
.contexts
.access_context
.get_access_by_uid_and_model_id(uid, model_id)
.await
.map_err(|err| Status::new(Code::Internal, err.to_string()))?
.ok_or_else(|| {
Status::new(Code::PermissionDenied, "User does not have access to model")
})?;

let model = self
.contexts
.model_context
.get_by_id(model_id)
.await
.map_err(|err| Status::new(Code::Internal, err.to_string()))?
.ok_or_else(|| Status::new(Code::Internal, "Model not found"))?;

let model = Model {
id: model.id,
name: model.name,
components_info: serde_json::from_value(model.components_info).unwrap(),
owner_id: model.owner_id,
};

let mut in_use_bool = true;
match self.contexts.in_use_context.get_by_id(model_id).await {
Ok(Some(in_use)) => {
// If model is not in use and user is an Editor, update the in use with the users session.
if in_use.latest_activity
<= (Utc::now().naive_utc() - Duration::minutes(IN_USE_DURATION_MINUTES))
{
in_use_bool = false;

if access.role == "Editor" {
let session = self
.contexts
.session_context
.get_by_token(TokenType::AccessToken, request.token_string().unwrap())
.await
.map_err(|err| Status::new(Code::Internal, err.to_string()))?
.ok_or_else(|| {
Status::new(
Code::Unauthenticated,
"No session found with given access token",
)
})?;

let in_use = in_use::Model {
model_id: in_use.model_id,
session_id: session.id,
latest_activity: Utc::now().naive_utc(),
};

self.contexts
.in_use_context
.update(in_use)
.await
.map_err(|err| Status::new(Code::Internal, err.to_string()))?;
}
}
}
Ok(None) => return Err(Status::new(Code::Internal, "No in use found for model")),
Err(err) => return Err(Status::new(Code::Internal, err.to_string())),
}

let queries = self
.contexts
.query_context
.get_all_by_model_id(model_id)
.await
.map_err(|err| Status::new(Code::Internal, err.to_string()))?;

let queries = queries
.into_iter()
.map(|query| Query {
id: query.id,
model_id: query.model_id,
query: query.string,
result: match query.result {
Some(result) => serde_json::from_value(result).unwrap(),
None => "".to_owned(),
},
outdated: query.outdated,
})
.collect::<Vec<Query>>();

Ok(Response::new(GetModelResponse {
model: Some(model),
queries,
in_use: in_use_bool,
}))
}

async fn create_model(
Expand Down Expand Up @@ -181,7 +285,10 @@ impl EcdarApi for ConcreteEcdarApi {
Ok(Response::new(CreateModelResponse { id: model.id }))
}

async fn update_model(&self, _request: Request<()>) -> Result<Response<()>, Status> {
async fn update_model(
&self,
_request: Request<UpdateModelRequest>,
) -> Result<Response<()>, Status> {
todo!()
}

Expand Down Expand Up @@ -690,14 +797,14 @@ mod query_logic_tests;
#[path = "../tests/api/access_logic.rs"]
mod access_logic_tests;

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

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

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

#[cfg(test)]
#[path = "../tests/api/session_logic.rs"]
mod session_logic_tests;
17 changes: 14 additions & 3 deletions src/database/query_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,27 @@ use crate::database::entity_context::EntityContextTrait;
use crate::entities::query;
use sea_orm::prelude::async_trait::async_trait;
use sea_orm::ActiveValue::{Set, Unchanged};
use sea_orm::{ActiveModelTrait, DbErr, EntityTrait, NotSet};
use sea_orm::{ActiveModelTrait, ColumnTrait, DbErr, EntityTrait, NotSet, QueryFilter};
use std::sync::Arc;

pub struct QueryContext {
db_context: Arc<dyn DatabaseContextTrait>,
}

pub trait QueryContextTrait: EntityContextTrait<query::Model> {}
#[async_trait]
pub trait QueryContextTrait: EntityContextTrait<query::Model> {
async fn get_all_by_model_id(&self, model_id: i32) -> Result<Vec<query::Model>, DbErr>;
}

impl QueryContextTrait for QueryContext {}
#[async_trait]
impl QueryContextTrait for QueryContext {
async fn get_all_by_model_id(&self, model_id: i32) -> Result<Vec<query::Model>, DbErr> {
query::Entity::find()
.filter(query::Column::ModelId.eq(model_id))
.all(&self.db_context.get_connection())
.await
}
}

impl QueryContext {
pub fn new(db_context: Arc<dyn DatabaseContextTrait>) -> QueryContext {
Expand Down
9 changes: 4 additions & 5 deletions src/database/session_context.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
use crate::api::auth::TokenType;
use crate::database::database_context::DatabaseContextTrait;
use crate::database::entity_context::EntityContextTrait;
use crate::entities::session;
use chrono::Local;
use sea_orm::prelude::async_trait::async_trait;
use sea_orm::ActiveValue::{Set, Unchanged};
use sea_orm::{ActiveModelTrait, ColumnTrait, DbErr, EntityTrait, NotSet, QueryFilter};
use std::sync::Arc;

use crate::api::auth::TokenType;
use crate::database::database_context::DatabaseContextTrait;
use crate::database::entity_context::EntityContextTrait;
use crate::entities::session;

pub struct SessionContext {
db_context: Arc<dyn DatabaseContextTrait>,
}
Expand Down
24 changes: 22 additions & 2 deletions src/tests/api/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,20 @@ mock! {
}
#[async_trait]
impl AccessContextTrait for AccessContext {
async fn get_access_by_uid_and_model_id(&self, uid: i32, model_id: i32) -> Result<Option<access::Model>, DbErr>;
async fn get_access_by_uid_and_model_id(
&self,
uid: i32,
model_id: i32,
) -> Result<Option<access::Model>, DbErr> {
access::Entity::find()
.filter(
Condition::all()
.add(access::Column::UserId.eq(uid))
.add(access::Column::ModelId.eq(model_id)),
)
.one(&self.db_context.get_connection())
.await
}
}
}

Expand Down Expand Up @@ -119,7 +132,14 @@ mock! {
async fn delete(&self, entity_id: i32) -> Result<query::Model, DbErr>;
}
#[async_trait]
impl QueryContextTrait for QueryContext {}
impl QueryContextTrait for QueryContext {
async fn get_all_by_model_id(&self, model_id: i32) -> Result<Vec<query::Model>, DbErr> {
query::Entity::find()
.filter(query::Column::ModelId.eq(model_id))
.all(&self.db_context.get_connection())
.await
}
}
}

mock! {
Expand Down
Loading

0 comments on commit 0f812f3

Please sign in to comment.