Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: TES struct #36

Closed
wants to merge 94 commits into from
Closed
Changes from 1 commit
Commits
Show all changes
94 commits
Select commit Hold shift + click to select a range
7cf9dfd
Initial commit
aaravm Jun 7, 2024
0087881
added tes-autogenerated code for all the models
aaravm Jun 7, 2024
4560fc4
corrected workflow
aaravm Jun 7, 2024
4210498
added function create_task from autogenerated, trying some stuff for …
aaravm Jun 7, 2024
61fcb7a
made service.rs class
aaravm Jun 9, 2024
780e7ff
changed return type of service class, added TES class, using service …
aaravm Jun 11, 2024
47e3ca5
removed return type temporarily fortes class
aaravm Jun 11, 2024
df64e6d
added tests for service class
aaravm Jun 12, 2024
b0ea439
added unit test for tes class
aaravm Jun 15, 2024
b54b56f
changed a lot of names
aaravm Jun 16, 2024
4e5c8dc
merged configuration and service class
aaravm Jun 16, 2024
ce11ae1
refactoring
pavelnikonorov Jun 16, 2024
395b00a
tes.rs code moved to mod.rs to avoid import as ::tes::tes
pavelnikonorov Jun 17, 2024
b1c12fe
corrected tests in service.rs
aaravm Jun 18, 2024
432ca96
added service-info(not complete) and made build.sh
aaravm Jun 18, 2024
57406a1
Update Readme.md
aaravm Jun 18, 2024
ab08a44
Update Readme.md
aaravm Jun 18, 2024
02685c0
added serviceinfo class
aaravm Jun 20, 2024
cbba4ae
checking whether correct from servicinfo
aaravm Jun 20, 2024
be2e486
Merge remote-tracking branch 'origin/dev' into dev
aaravm Jun 20, 2024
e9ccf43
checking tests
aaravm Jun 20, 2024
d4faee2
added service-info unit test using funnel
aaravm Jun 22, 2024
f1bd0c5
added check function in TES
aaravm Jun 22, 2024
c9cdbfc
openapi models auto-generation: build script update, rm generated mod…
pavelnikonorov Jun 23, 2024
27bab17
refactoring, fixes, tests improvement
pavelnikonorov Jun 24, 2024
d656d3b
CI/CD workflow update
pavelnikonorov Jun 24, 2024
afbe68c
CI/CD workflow fix
pavelnikonorov Jun 24, 2024
da80b57
CI/CD workflow fix
pavelnikonorov Jun 24, 2024
a655a90
CI/CD workflow fix
pavelnikonorov Jun 24, 2024
6514f6e
CI/CD workflow fix
pavelnikonorov Jun 24, 2024
e0bdcf5
commenting out non-finished Task struct that fails CI/CD
pavelnikonorov Jun 24, 2024
a1cd3bf
applying code formating using
pavelnikonorov Jun 24, 2024
97595e0
rustfmt.toml: ignore code formatting checks of the autogenerated models
pavelnikonorov Jun 24, 2024
a6b63ac
changed return type of create to Task
aaravm Jun 26, 2024
431a831
added status to Task
aaravm Jun 26, 2024
e447b5a
corrected CI/CD
aaravm Jun 26, 2024
bcce6ec
trying out tes_status unit tests
aaravm Jun 26, 2024
f08c9dc
unit test working, with no params
aaravm Jun 27, 2024
d8ef075
completed the unit test of tes_status
aaravm Jun 27, 2024
49a702d
CI/CD framework
aaravm Jun 27, 2024
c110379
CI/CD
aaravm Jun 27, 2024
4d88c44
added cancel function
aaravm Jun 28, 2024
dda27a7
added cancel fn in Task
aaravm Jun 28, 2024
df1fc62
adding cancel unit test
aaravm Jun 28, 2024
a25c499
changed transport struct for optional params and data
aaravm Jun 28, 2024
3c92b70
changed the status fn to Task struct
aaravm Jun 28, 2024
bc07b65
moved cancel to Task, added get in TES
aaravm Jun 28, 2024
0f7decc
added list tasks fn
aaravm Jun 29, 2024
19ece96
correcting get list() fn
aaravm Jun 29, 2024
4e415a5
cargo fmt: code formatting
pavelnikonorov Jul 1, 2024
ac64bb3
tests simplifaction though TES and Task objects reuse
pavelnikonorov Jul 1, 2024
1487961
corrected the list fn
aaravm Jul 1, 2024
976846b
merged changes
aaravm Jul 1, 2024
c96b636
Sorcery-ai suggestion
aaravm Jul 1, 2024
d613722
Sorcery-ai suggestion
aaravm Jul 1, 2024
4f482d3
Apply suggestions from code review
aaravm Jul 1, 2024
ebc9bec
sorcery-ai suggestions
aaravm Jul 1, 2024
e56a623
sorcery-ai suggestions
aaravm Jul 1, 2024
d37b000
sorcery-ai suggestions
aaravm Jul 1, 2024
7725298
added some Pavel's suggestions
aaravm Jul 4, 2024
bad81eb
readding accidently deleted workflows folder
aaravm Jul 4, 2024
c95ac80
Update Readme.md
aaravm Jul 4, 2024
198e0f8
changed Cargo.toml
aaravm Jul 4, 2024
206c6c4
changed the sample file, changed author name
aaravm Jul 4, 2024
2f2b6a8
changed versions of ci.yml
aaravm Jul 5, 2024
4a41548
added parameters in a much easier manner
aaravm Jul 7, 2024
2d48bdd
changing git ignore file
aaravm Jul 7, 2024
a5b484e
changed local and github workflows
aaravm Jul 8, 2024
598646f
correct github workflows
aaravm Jul 8, 2024
1369f7e
correct github workflows
aaravm Jul 8, 2024
8ec57cc
using cargo nextest for tests
aaravm Jul 8, 2024
1913d3d
correcting ci/cd
aaravm Jul 8, 2024
ee0948c
correcting ci/cd
aaravm Jul 8, 2024
44a1d64
removing commented out code
aaravm Jul 8, 2024
5a13d19
moved ListTaskParam to model.rs
aaravm Jul 8, 2024
0e29df7
added unit coverage
aaravm Jul 8, 2024
e02346c
trying to remove installing rust in ci/cd
aaravm Jul 8, 2024
dff1a21
trying to remove installing rust in ci/cd
aaravm Jul 8, 2024
bedf68f
trying to remove installing rust in ci/cd
aaravm Jul 8, 2024
4ee3302
trying to remove installing rust in ci/cd
aaravm Jul 8, 2024
b8ea658
trying to remove installing rust in ci/cd
aaravm Jul 8, 2024
2d24c43
trying to remove installing rust in ci/cd
aaravm Jul 8, 2024
f6ef231
trying to remove installing rust in ci/cd
aaravm Jul 8, 2024
5d9c4bf
remove comments
aaravm Jul 8, 2024
0d8d25c
changed openapi generator to Bash Launcher
aaravm Jul 9, 2024
7fa6bd6
remove nodejs
aaravm Jul 9, 2024
00b6367
changed gitignore
aaravm Jul 9, 2024
6566d7d
sorcery suggestions
aaravm Jul 11, 2024
9e9a4a7
sorcery-ai suggestions
aaravm Jul 11, 2024
81e26e6
sorcery-ai suggestions
aaravm Jul 11, 2024
816e47c
sorcery ai suggestion
aaravm Jul 11, 2024
a9ee309
sorcery-ai suggestion
aaravm Jul 11, 2024
a9b3c0b
separate cargo.toml
aaravm Jul 15, 2024
8ddb90b
initial commit
aaravm Aug 12, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
applying code formating using
pavelnikonorov committed Jun 24, 2024
commit a1cd3bf901bd683dbd2c862fd9bc1cccf7ef3f27
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -109,4 +109,4 @@ jobs:
- name: Format
run: |
. $HOME/.cargo/env
cargo fmt -- --check
cargo fmt
11 changes: 6 additions & 5 deletions lib/src/configuration.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@


#[derive(Debug, Clone)]
pub struct Configuration {
pub base_path: String,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Consider using more specific types for some Configuration fields

Some fields in the Configuration struct are using Option<String>. Consider using more specific types for some of these, like Option<Url> for base_path. This would provide better type safety and make the intended use of these fields clearer.

pub struct Configuration {
    /// The base path for API requests.
    pub base_path: Url,
    pub user_agent: Option<String>,
    pub client: reqwest::Client,
    pub basic_auth: Option<BasicAuth>,
    pub oauth_access_token: Option<String>,
    pub bearer_access_token: Option<String>,
}

@@ -19,9 +17,12 @@ pub struct ApiKey {
pub key: String,
}


impl Configuration {
pub fn new(base_path: String, user_agent: Option<String>, oauth_access_token: Option<String>) -> Self {
pub fn new(
base_path: String,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): Add validation for the base_path URL in the Configuration::new method

Adding validation for the base_path URL in the new method would prevent issues down the line. Consider checking if the URL is valid and returning a Result instead of assuming it's always correct.

    pub fn new(base_path: Url) -> Result<Self, url::ParseError> {
        if !base_path.has_host() {
            return Err(url::ParseError::EmptyHost);
        }
        Ok(Self { base_path })
    }

user_agent: Option<String>,
oauth_access_token: Option<String>,
) -> Self {
Configuration {
base_path,
user_agent,
@@ -49,4 +50,4 @@ impl Default for Configuration {
api_key: None,
}
}
}
}
4 changes: 2 additions & 2 deletions lib/src/lib.rs
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@ extern crate serde_derive;
#[cfg(test)]
mod test_utils;

pub mod configuration;
pub mod serviceinfo;
pub mod tes;
pub mod transport;
pub mod serviceinfo;
pub mod configuration;
29 changes: 12 additions & 17 deletions lib/src/serviceinfo/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
pub mod models;
use crate::transport::Transport;
use crate::configuration::Configuration;
use crate::transport::Transport;

#[derive(Clone)]
pub struct ServiceInfo {
@@ -19,31 +19,26 @@ impl ServiceInfo {
pub async fn get(&self) -> Result<models::Service, Box<dyn std::error::Error>> {
let response = self.transport.get("/service-info", None).await;
match response {
Ok(response_body) => {
match serde_json::from_str::<models::Service>(&response_body) {
Ok(tes_create_task_response) => Ok(tes_create_task_response),
Err(e) => {
log::error!("Failed to deserialize response: {}", e);
Err("Failed to deserialize response".into())
},
Ok(response_body) => match serde_json::from_str::<models::Service>(&response_body) {
Ok(tes_create_task_response) => Ok(tes_create_task_response),
Err(e) => {
log::error!("Failed to deserialize response: {}", e);
Err("Failed to deserialize response".into())
}
},
Err(e) => {
log::error!("Error: {}", e);
Err(e)
},
}
}
}

}



#[cfg(test)]
mod tests {
use crate::test_utils::{setup, ensure_funnel_running};
use crate::serviceinfo::ServiceInfo;
use crate::configuration::Configuration;
use crate::serviceinfo::ServiceInfo;
use crate::test_utils::{ensure_funnel_running, setup};
use tokio;

#[tokio::test]
@@ -62,7 +57,7 @@ mod tests {
// assert_eq!(result.unwrap().id, "test");
// assert_eq!(result.unwrap().name, "test");
}
#[tokio::test]
#[tokio::test]
async fn test_get_service_info_from_funnel() {
setup();
let mut config = Configuration::default();
@@ -74,10 +69,10 @@ mod tests {
match service_info.get().await {
Ok(service) => {
println!("Service Info: {:?}", service);
},
}
Err(e) => {
println!("Failed to get service info: {}", e);
},
}
}
}
}
69 changes: 39 additions & 30 deletions lib/src/tes/mod.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
pub mod models;
use crate::transport::Transport;
use crate::configuration::Configuration;
use crate::serviceinfo::models::Service;
use serde_json;
use serde_json::json;
use crate::serviceinfo::ServiceInfo;
use crate::configuration::Configuration;
use crate::tes::models::TesTask;
use crate::tes::models::TesCreateTaskResponse;
use crate::tes::models::TesState;
use crate::tes::models::TesTask;
use crate::transport::Transport;
use serde_json;
use serde_json::json;

// ***
// should TES.create return Task? which in turn can do status() and other existing-task-related stuff
@@ -59,57 +59,65 @@ impl TES {

fn check(&self) -> bool {
let resp = &self.service;
return resp.as_ref().unwrap().r#type.artifact == "tes"
return resp.as_ref().unwrap().r#type.artifact == "tes";
}

pub async fn create(&self, task: TesTask/*, params: models::TesTask*/) -> Result<TesCreateTaskResponse, Box<dyn std::error::Error>> {
pub async fn create(
&self,
task: TesTask, /*, params: models::TesTask*/
) -> Result<TesCreateTaskResponse, Box<dyn std::error::Error>> {
// First, check if the service is of TES class
if !self.check() {
// If check fails, log an error and return an Err immediately
log::error!("Service check failed");
return Err("Service check failed".into());
// If check fails, log an error and return an Err immediately
log::error!("Service check failed");
return Err("Service check failed".into());
}
// todo: version in url based on serviceinfo or user config
let response = self.transport.post("/ga4gh/tes/v1/tasks", json!(task)).await;
let response = self
.transport
.post("/ga4gh/tes/v1/tasks", json!(task))
.await;
match response {
Ok(response_body) => {
match serde_json::from_str::<TesCreateTaskResponse>(&response_body) {
Ok(tes_create_task_response) => Ok(tes_create_task_response),
Err(e) => {
log::error!("Failed to deserialize response: {}", e);
Err("Failed to deserialize response".into())
},
}
}
},
}
Err(e) => {
log::error!("Error: {}", e);
Err(e)
},
}
}
}

// pub async fn status(&self, task: &TesCreateTaskResponse) -> Result<TesState, Box<dyn std::error::Error>> {
pub async fn status(&self, task_id: &str, view: &str) -> Result<TesState, Box<dyn std::error::Error>> {
pub async fn status(
&self,
task_id: &str,
view: &str,
) -> Result<TesState, Box<dyn std::error::Error>> {
// ?? move to Task::status()
// todo: version in url based on serviceinfo or user config
let url = format!("/ga4gh/tes/v1/tasks/{}", task_id);
let params = [("view", view)];
let params_value = serde_json::json!(params);
let response = self.transport.get(&url, Some(params_value)).await;
match response {
Ok(response_body) => {
match serde_json::from_str::<TesState>(&response_body) {
Ok(tes_state) => Ok(tes_state),
Err(e) => {
log::error!("Failed to deserialize response: {}", e);
Err("Failed to deserialize response".into())
},
Ok(response_body) => match serde_json::from_str::<TesState>(&response_body) {
Ok(tes_state) => Ok(tes_state),
Err(e) => {
log::error!("Failed to deserialize response: {}", e);
Err("Failed to deserialize response".into())
}
},
Err(e) => {
log::error!("Error: {}", e);
Err(e)
},
}
}
}

@@ -118,10 +126,10 @@ impl TES {

#[cfg(test)]
mod tests {
use crate::test_utils::{setup, ensure_funnel_running, FUNNEL_PORT};
use crate::tes::TES;
use crate::configuration::Configuration;
use crate::tes::models::TesTask;
use crate::tes::TES;
use crate::test_utils::{ensure_funnel_running, setup, FUNNEL_PORT};
// use crate::tes::models::TesCreateTaskResponse;

async fn create_task() -> Result<String, Box<dyn std::error::Error>> {
@@ -130,10 +138,11 @@ mod tests {
let funnel_url = ensure_funnel_running().await;
config.set_base_path(&funnel_url);
let tes = TES::new(&config).await;

let task_json = std::fs::read_to_string("./lib/sample/grape.tes").expect("Unable to read file");

let task_json =
std::fs::read_to_string("./lib/sample/grape.tes").expect("Unable to read file");
let task: TesTask = serde_json::from_str(&task_json).expect("JSON was not well-formatted");

let task = tes?.create(task).await?;
Ok(task.id)
}
@@ -142,15 +151,15 @@ mod tests {
async fn test_task_create() {
setup();
ensure_funnel_running().await;

let task = create_task().await.expect("Failed to create task");
assert!(!task.is_empty(), "Task ID should not be empty"); // doube check if it's a correct assertion
}

// #[tokio::test]
// async fn test_task_status() {
// setup();

// let task = create_task().await.expect("Failed to create task");
// // Now use task to get the task status...
// // todo: assert_eq!(task.status().await, which status?);
4 changes: 2 additions & 2 deletions lib/src/test_utils.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use log::info;
use std::env;
use std::process::Command;
use std::str;
use log::info;
use std::sync::Once;

pub const FUNNEL_HOST: &str = "http://localhost";
@@ -32,4 +32,4 @@ pub async fn ensure_funnel_running() -> String {

let funnel_url = format!("{}:{}", FUNNEL_HOST, FUNNEL_PORT);
funnel_url
}
}
51 changes: 36 additions & 15 deletions lib/src/transport.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::configuration::Configuration;
use log::error;
use reqwest::Client;
use serde_json::Value;
use std::error::Error;
use crate::configuration::Configuration;
use log::error;

// note: could implement custom certs handling, such as in-TEE generated ephemerial certs
#[derive(Clone)]
@@ -29,13 +29,23 @@ impl Transport {
let full_url = format!("{}{}", self.config.base_path, endpoint);
let url = reqwest::Url::parse(&full_url);
if url.is_err() {
error!("Invalid endpoint (shouldn't contain base url): {}", endpoint);
return Err(Box::new(std::io::Error::new(std::io::ErrorKind::InvalidInput, "Invalid endpoint")));
error!(
"Invalid endpoint (shouldn't contain base url): {}",
endpoint
);
return Err(Box::new(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"Invalid endpoint",
)));
}

let resp = self.client
let resp = self
.client
.request(method, &full_url)
.header(reqwest::header::USER_AGENT, self.config.user_agent.clone().unwrap_or_default())
.header(
reqwest::header::USER_AGENT,
self.config.user_agent.clone().unwrap_or_default(),
)
.json(&data)
.query(&params)
.send()
@@ -47,33 +57,44 @@ impl Transport {
if status.is_success() {
Ok(content)
} else {
Err(Box::new(std::io::Error::new(std::io::ErrorKind::Other, content)))
Err(Box::new(std::io::Error::new(
std::io::ErrorKind::Other,
content,
)))
}
}

pub async fn get(&self, endpoint: &str, params: Option<Value>) -> Result<String, Box<dyn Error>> {
self.request(reqwest::Method::GET, endpoint, None, params).await
pub async fn get(
&self,
endpoint: &str,
params: Option<Value>,
) -> Result<String, Box<dyn Error>> {
self.request(reqwest::Method::GET, endpoint, None, params)
.await
}

pub async fn post(&self, endpoint: &str, data: Value) -> Result<String, Box<dyn Error>> {
self.request(reqwest::Method::POST, endpoint, Some(data), None).await
self.request(reqwest::Method::POST, endpoint, Some(data), None)
.await
}

pub async fn put(&self, endpoint: &str, data: Value) -> Result<String, Box<dyn Error>> {
self.request(reqwest::Method::PUT, endpoint, Some(data), None).await
self.request(reqwest::Method::PUT, endpoint, Some(data), None)
.await
}

pub async fn delete(&self, endpoint: &str) -> Result<String, Box<dyn Error>> {
self.request(reqwest::Method::DELETE, endpoint, None, None).await
self.request(reqwest::Method::DELETE, endpoint, None, None)
.await
}

// other HTTP methods can be added here
}

#[cfg(test)]
mod tests {
use crate::test_utils::setup;
use crate::configuration::Configuration;
use crate::test_utils::setup;
use crate::transport::Transport;
use mockito::mock;

@@ -86,7 +107,7 @@ mod tests {
let _m = mock("GET", "/test")
.with_status(200)
.with_header("content-type", "application/json")
.with_body(r#"{"message": "success"}"#)
.with_body(r#"{"message": "success"}"#)
.create();

let config = Configuration::new(base_url.clone(), None, None);
@@ -97,4 +118,4 @@ mod tests {
let body = response.unwrap();
assert_eq!(body, r#"{"message": "success"}"#);
}
}
}