From 5d0466667043a9c08f230dd13206a6ee144bbfbc Mon Sep 17 00:00:00 2001 From: Manuel Holtgrewe Date: Wed, 20 Nov 2024 13:42:00 +0100 Subject: [PATCH] feat: proper OpenAPI-documented and versioned endpoints (#227) --- src/query/mod.rs | 30 ++++++------- src/server/run/hpo_genes.rs | 59 ++++++++++++------------- src/server/run/hpo_omims.rs | 67 ++++++++++++++--------------- src/server/run/hpo_sim/term_gene.rs | 23 +++++----- src/server/run/hpo_sim/term_term.rs | 47 ++++++++++---------- src/server/run/hpo_terms.rs | 55 ++++++++++++----------- src/server/run/mod.rs | 41 ++++++++++-------- 7 files changed, 159 insertions(+), 163 deletions(-) diff --git a/src/query/mod.rs b/src/query/mod.rs index c83e0d3..8bcfdf7 100644 --- a/src/query/mod.rs +++ b/src/query/mod.rs @@ -9,7 +9,7 @@ use clap::Parser; use hpo::{annotations::AnnotationId, term::HpoGroup, HpoTermId, Ontology}; use crate::algos::phenomizer; -use crate::query::query_result::TermDetails; +use crate::query::query_result::HpoSimTermGeneTermDetails; /// Command line arguments for `query` command. #[derive(Parser, Debug)] @@ -74,8 +74,7 @@ pub mod query_result { /// The performed query. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, utoipa::ToSchema)] - #[schema(title = "HpoSimTermGeneQuery")] - pub struct Query { + pub struct HpoSimTermGeneQuery { /// The query HPO terms. pub terms: Vec, /// The gene list to score. @@ -84,33 +83,30 @@ pub mod query_result { /// Result container data structure. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, utoipa::ToSchema)] - #[schema(title = "HpoSimTermGeneResult")] - pub struct Result { + pub struct HpoSimTermGeneResult { /// Version information. pub version: Version, /// The original query records. - pub query: Query, + pub query: HpoSimTermGeneQuery, /// The resulting records for the scored genes. - pub result: Vec, + pub result: Vec, } /// Store score for a record with information on individual terms. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, utoipa::ToSchema)] - #[schema(title = "HpoSimTermGeneResultEntry")] - pub struct ResultEntry { + pub struct HpoSimTermGeneResultEntry { /// The gene symbol. pub gene_symbol: String, /// The raw Phenomizer score. pub raw_score: f32, /// Details on individual terms. #[serde(default = "Option::default")] - pub terms: Option>, + pub terms: Option>, } /// Detailed term scores. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, utoipa::ToSchema)] - #[schema(title = "HpoSimTermGeneTermDetails")] - pub struct TermDetails { + pub struct HpoSimTermGeneTermDetails { /// The query HPO term. pub term_query: Option, /// The gene's HPO term. @@ -149,11 +145,11 @@ pub fn run_query( genes: &Vec<&hpo::annotations::Gene>, hpo: &Ontology, ncbi_to_hgnc: &HashMap, -) -> Result +) -> Result where S: std::hash::BuildHasher, { - let query = query_result::Query { + let query = query_result::HpoSimTermGeneQuery { terms: patient .iter() .map(|t| { @@ -166,7 +162,7 @@ where .collect(), genes: Vec::new(), }; - let mut result = query_result::Result { + let mut result = query_result::HpoSimTermGeneResult { version: crate::common::Version::new(&hpo.hpo_version()), query, result: Vec::new(), @@ -217,7 +213,7 @@ where None }; - TermDetails { + HpoSimTermGeneTermDetails { term_query, term_gene: HpoTerm { term_id: gene_term.id().to_string(), @@ -235,7 +231,7 @@ where hgnc_id: ncbi_to_hgnc.get(&ncbi_gene_id).cloned(), }); - result.result.push(query_result::ResultEntry { + result.result.push(query_result::HpoSimTermGeneResultEntry { gene_symbol: gene.name().to_string(), raw_score, terms: Some(terms), diff --git a/src/server/run/hpo_genes.rs b/src/server/run/hpo_genes.rs index a8bbdf5..df6f552 100644 --- a/src/server/run/hpo_genes.rs +++ b/src/server/run/hpo_genes.rs @@ -29,10 +29,9 @@ use super::{CustomError, Match, ResultHpoTerm}; /// /// - `match` -- how to match #[derive( - serde::Serialize, serde::Deserialize, utoipa::ToSchema, utoipa::IntoParams, Debug, Clone, + Debug, Clone, serde::Serialize, serde::Deserialize, utoipa::ToSchema, utoipa::IntoParams, )] -#[schema(title = "HpoGenesQuery")] -pub struct Query { +pub struct HpoGenesQuery { /// The gene ID to search for. pub gene_id: Option, /// The gene symbol to search for. @@ -60,19 +59,17 @@ fn _default_hpo_terms() -> bool { /// Result entry for `handle`. #[derive( - serde::Serialize, - serde::Deserialize, - utoipa::ToSchema, Debug, Clone, Eq, PartialEq, Ord, PartialOrd, + serde::Serialize, + serde::Deserialize, + utoipa::ToSchema, )] -#[serde_with::skip_serializing_none] -#[schema(title = "HpoGenesResultEntry")] -pub struct ResultEntry { +pub struct HpoGenesResultEntry { /// The gene's NCBI ID. pub gene_ncbi_id: u32, /// The gene's HGNC symbol. @@ -84,7 +81,7 @@ pub struct ResultEntry { pub hpo_terms: Option>, } -impl ResultEntry { +impl HpoGenesResultEntry { /// Create a `ResultEntry` from a `Gene` with an `Ontology`. pub fn from_gene_with_ontology( gene: &Gene, @@ -107,7 +104,7 @@ impl ResultEntry { } else { None }; - ResultEntry { + HpoGenesResultEntry { gene_ncbi_id: gene.id().as_u32(), gene_symbol: gene.name().to_string(), hgnc_id: ncbi_to_hgnc.get(&gene.id().as_u32()).cloned(), @@ -117,35 +114,39 @@ impl ResultEntry { } /// Container for the result. -#[derive(Debug, serde::Serialize, serde::Deserialize, utoipa::ToSchema)] -#[schema(title = "HpoGenesResult")] -pub struct Result { +#[derive(Debug, serde::Serialize, serde::Deserialize, utoipa::ToSchema, utoipa::ToResponse)] +pub struct HpoGenesResult { /// Version information. pub version: Version, /// The original query records. - pub query: Query, + pub query: HpoGenesQuery, /// The resulting records for the scored genes. - pub result: Vec, + pub result: Vec, } /// Query for genes in the HPO database. -#[allow(clippy::unused_async)] +/// +/// # Errors +/// +/// In the case that there is an error running the server. #[utoipa::path( - operation_id = "hpo_genes", - params(Query), + get, + operation_id = "hpoGenes", + params(HpoGenesQuery), responses( - (status = 200, description = "The query was successful.", body = Result), + (status = 200, description = "The query was successful.", body = HpoGenesResult), + (status = 500, description = "The server encountered an error.", body = CustomError) ) )] -#[get("/hpo/genes")] +#[get("/api/v1/hpo/genes")] async fn handle( data: Data>, _path: Path<()>, - query: web::Query, -) -> actix_web::Result, CustomError> { + query: web::Query, +) -> actix_web::Result, CustomError> { let ontology = &data.ontology; let match_ = query.match_.unwrap_or_default(); - let mut result: Vec = Vec::new(); + let mut result: Vec = Vec::new(); if match_ == Match::Exact { let gene = if let Some(gene_id) = &query.gene_id { @@ -163,7 +164,7 @@ async fn handle( None }; if let Some(gene) = gene { - result.push(ResultEntry::from_gene_with_ontology( + result.push(HpoGenesResultEntry::from_gene_with_ontology( gene, ontology, query.hpo_terms, @@ -182,7 +183,7 @@ async fn handle( Match::Exact => panic!("cannot happen here"), }; if is_match { - result.push(ResultEntry::from_gene_with_ontology( + result.push(HpoGenesResultEntry::from_gene_with_ontology( gene.expect("checked above"), ontology, query.hpo_terms, @@ -196,7 +197,7 @@ async fn handle( result.sort(); - let result = Result { + let result = HpoGenesResult { version: Version::new(&data.ontology.hpo_version()), query: query.into_inner(), result, @@ -234,7 +235,7 @@ pub(crate) mod test { pub async fn run_query( web_server_data: Arc, uri: &str, - ) -> Result { + ) -> Result { let app = actix_web::test::init_service( actix_web::App::new() .app_data(actix_web::web::Data::new(web_server_data)) @@ -242,7 +243,7 @@ pub(crate) mod test { ) .await; let req = actix_web::test::TestRequest::get().uri(uri).to_request(); - let resp: super::Result = actix_web::test::call_and_read_body_json(&app, req).await; + let resp: super::HpoGenesResult = actix_web::test::call_and_read_body_json(&app, req).await; Ok(resp) } diff --git a/src/server/run/hpo_omims.rs b/src/server/run/hpo_omims.rs index f2c35cf..87fe01f 100644 --- a/src/server/run/hpo_omims.rs +++ b/src/server/run/hpo_omims.rs @@ -30,17 +30,15 @@ use super::{CustomError, Match, ResultHpoTerm}; /// /// - `match` -- how to match #[derive( - serde::Serialize, serde::Deserialize, utoipa::ToSchema, utoipa::IntoParams, Debug, Clone, + Debug, Clone, serde::Serialize, serde::Deserialize, utoipa::ToSchema, utoipa::IntoParams, )] -#[schema(title = "HpoOmimsQuery")] -pub struct Query { +pub struct HpoOmimsQuery { /// The OMIM ID to search for. pub omim_id: Option, /// The disease name to search for. pub name: Option, /// The match mode, default is `Match::Exact`. - #[serde(alias = "match")] - pub match_: Option, + pub r#match: Option, /// Whether case is insentivie, default is `false`. pub ignore_case: Option, /// Maximal number of results to return. @@ -51,7 +49,7 @@ pub struct Query { pub hpo_terms: bool, } -impl Query { +impl HpoOmimsQuery { /// Strip "OMIM:" prefix from `omim_id`, if any. fn with_stripped_prefix(self) -> Self { Self { @@ -81,9 +79,8 @@ fn _default_hpo_terms() -> bool { } /// Result entry for `handle`. -#[derive(serde::Serialize, serde::Deserialize, utoipa::ToSchema, Debug, Clone)] -#[schema(title = "HpoOmimsResultEntry")] -pub struct ResultEntry { +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, utoipa::ToSchema)] +pub struct HpoOmimsResultEntry { /// The OMIM ID. pub omim_id: String, /// The OMIM disease name. @@ -93,21 +90,21 @@ pub struct ResultEntry { pub hpo_terms: Option>, } -impl PartialEq for ResultEntry { +impl PartialEq for HpoOmimsResultEntry { fn eq(&self, other: &Self) -> bool { (self.omim_id == other.omim_id) && (self.name == other.name) } } -impl Eq for ResultEntry {} +impl Eq for HpoOmimsResultEntry {} -impl PartialOrd for ResultEntry { +impl PartialOrd for HpoOmimsResultEntry { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } -impl Ord for ResultEntry { +impl Ord for HpoOmimsResultEntry { fn cmp(&self, other: &Self) -> std::cmp::Ordering { match self.omim_id.cmp(&other.omim_id) { core::cmp::Ordering::Equal => {} @@ -117,7 +114,7 @@ impl Ord for ResultEntry { } } -impl ResultEntry { +impl HpoOmimsResultEntry { /// Create a `ResultEntry` from an `OmimDisease`. pub fn from_omim_disease_with_ontology( omim_disease: &OmimDisease, @@ -142,7 +139,7 @@ impl ResultEntry { } else { None }; - ResultEntry { + HpoOmimsResultEntry { omim_id: omim_disease.id().to_string(), name: omim_disease.name().to_string(), hpo_terms, @@ -151,36 +148,36 @@ impl ResultEntry { } /// Container for the result. -#[derive(serde::Serialize, serde::Deserialize, utoipa::ToSchema, Debug)] -#[schema(title = "HpoOmimsResult")] -pub struct Result { +#[derive(Debug, serde::Serialize, serde::Deserialize, utoipa::ToSchema)] +pub struct HpoOmimsResult { /// Version information. pub version: Version, /// The original query records. - pub query: Query, + pub query: HpoOmimsQuery, /// The resulting records for the scored genes. - pub result: Vec, + pub result: Vec, } /// Query for OMIM diseases in the HPO database. -#[allow(clippy::unused_async)] #[allow(clippy::too_many_lines)] #[utoipa::path( - operation_id = "hpo_omims", - params(Query), + get, + operation_id = "hpoOmims", + params(HpoOmimsQuery), responses( - (status = 200, description = "The query was successful.", body = Result), + (status = 200, description = "The query was successful.", body = HpoOmimsResult), + (status = 500, description = "The server encountered an error.", body = CustomError) ) )] -#[get("/hpo/omims")] +#[get("/api/v1/hpo/omims")] async fn handle( data: Data>, _path: Path<()>, - query: web::Query, -) -> actix_web::Result, CustomError> { + query: web::Query, +) -> actix_web::Result, CustomError> { let ontology = &data.ontology; - let match_ = query.match_.unwrap_or_default(); - let mut result: Vec = Vec::new(); + let match_ = query.r#match.unwrap_or_default(); + let mut result: Vec = Vec::new(); // Strip "OMIM:" and "MIM:" prefix from `query.omim_id` if given. let query = query.into_inner().with_stripped_prefix(); @@ -216,7 +213,7 @@ async fn handle( None }; if let Some(omim_disease) = &omim_disease { - result.push(ResultEntry::from_omim_disease_with_ontology( + result.push(HpoOmimsResultEntry::from_omim_disease_with_ontology( omim_disease, ontology, query.hpo_terms, @@ -237,14 +234,14 @@ async fn handle( } else { omim_name.to_string() }; - let is_match = match query.match_.unwrap_or_default() { + let is_match = match query.r#match.unwrap_or_default() { Match::Prefix => omim_name.starts_with(&name), Match::Suffix => omim_name.ends_with(&name), Match::Contains => omim_name.contains(&name), Match::Exact => panic!("cannot happen here"), }; if is_match { - result.push(ResultEntry::from_omim_disease_with_ontology( + result.push(HpoOmimsResultEntry::from_omim_disease_with_ontology( omim_disease.as_ref().expect("checked above"), ontology, query.hpo_terms, @@ -257,7 +254,7 @@ async fn handle( result.sort(); - let result = Result { + let result = HpoOmimsResult { version: Version::new(&data.ontology.hpo_version()), query, result, @@ -277,7 +274,7 @@ mod test { pub async fn run_query( web_server_data: Arc, uri: &str, - ) -> Result { + ) -> Result { let app = actix_web::test::init_service( actix_web::App::new() .app_data(actix_web::web::Data::new(web_server_data)) @@ -285,7 +282,7 @@ mod test { ) .await; let req = actix_web::test::TestRequest::get().uri(uri).to_request(); - let resp: super::Result = actix_web::test::call_and_read_body_json(&app, req).await; + let resp: super::HpoOmimsResult = actix_web::test::call_and_read_body_json(&app, req).await; Ok(resp) } diff --git a/src/server/run/hpo_sim/term_gene.rs b/src/server/run/hpo_sim/term_gene.rs index ccdf78e..def9099 100644 --- a/src/server/run/hpo_sim/term_gene.rs +++ b/src/server/run/hpo_sim/term_gene.rs @@ -12,7 +12,7 @@ use hpo::{annotations::GeneId, term::HpoGroup, HpoTermId, Ontology}; use super::super::CustomError; use crate::{ - query::{self, query_result}, + query::{self, query_result::HpoSimTermGeneResult}, server::run::WebServerData, }; @@ -26,8 +26,7 @@ use crate::{ /// - `gene_symbols` -- set of symbols for genes to use as /// "database" #[derive(serde::Deserialize, Debug, Clone, utoipa::ToSchema, utoipa::IntoParams)] -#[schema(title = "HpoSimTermGeneQuery")] -pub struct Query { +pub struct HpoSimTermGeneQuery { /// Set of terms to use as query. #[serde(deserialize_with = "super::super::vec_str_deserialize")] pub terms: Vec, @@ -51,18 +50,20 @@ pub struct Query { /// list of genes. #[allow(clippy::unused_async)] #[utoipa::path( - operation_id = "hpo_sim_term_gene", - params(Query), + get, + operation_id = "hpoSimTermGene", + params(HpoSimTermGeneQuery), responses( - (status = 200, description = "The query was successful.", body = query_result::Result), + (status = 200, description = "The query was successful.", body = HpoSimTermGeneResult), + (status = 500, description = "The server encountered an error.", body = CustomError) ) )] -#[get("/hpo/sim/term-gene")] +#[get("/api/v1/hpo/sim/term-gene")] async fn handle( data: Data>, _path: Path<()>, - query: web::Query, -) -> actix_web::Result, CustomError> { + query: web::Query, +) -> actix_web::Result, CustomError> { let hpo: &Ontology = &data.ontology; // Translate strings from the query into an `HpoGroup`. @@ -119,7 +120,7 @@ mod test { pub async fn run_query( web_server_data: Arc, uri: &str, - ) -> Result { + ) -> Result { let app = actix_web::test::init_service( actix_web::App::new() .app_data(actix_web::web::Data::new(web_server_data)) @@ -127,7 +128,7 @@ mod test { ) .await; let req = actix_web::test::TestRequest::get().uri(uri).to_request(); - let resp: crate::query::query_result::Result = + let resp: crate::query::query_result::HpoSimTermGeneResult = actix_web::test::call_and_read_body_json(&app, req).await; Ok(resp) diff --git a/src/server/run/hpo_sim/term_term.rs b/src/server/run/hpo_sim/term_term.rs index 1f572a1..6d63308 100644 --- a/src/server/run/hpo_sim/term_term.rs +++ b/src/server/run/hpo_sim/term_term.rs @@ -22,7 +22,7 @@ use crate::server::{run::CustomError, run::WebServerData}; /// /// - `lhs` -- first set of terms to compute similarity for /// - `rhs` -- econd set of terms to compute similarity for -#[derive(serde::Serialize, serde::Deserialize, utoipa::IntoParams, Default, Debug, Clone)] +#[derive(Default, Debug, Clone, serde::Serialize, serde::Deserialize, utoipa::IntoParams)] pub struct RequestQuery { /// The one set of HPO terms to compute similarity for. #[serde(deserialize_with = "super::super::vec_str_deserialize")] @@ -44,9 +44,8 @@ pub struct RequestQuery { /// Request as sent together with the response. /// /// The difference is that the `lhs` and `rhs` fields are replaced by vecs. -#[derive(serde::Serialize, serde::Deserialize, utoipa::ToSchema, Default, Debug, Clone)] -#[schema(title = "HpoSimTermTermQuery")] -pub struct ResponseQuery { +#[derive(Default, Debug, Clone, serde::Serialize, serde::Deserialize, utoipa::ToSchema)] +pub struct HpoSimTermTermQuery { /// The one set of HPO terms to compute similarity for. pub lhs: Vec, /// The second set of HPO terms to compute similarity for. @@ -63,30 +62,28 @@ pub struct ResponseQuery { } /// Result container. -#[derive(serde::Serialize, serde::Deserialize, utoipa::ToSchema, Default, Debug, Clone)] -#[schema(title = "HpoSimTermTermResult")] -pub struct Result { +#[derive(Default, Debug, Clone, serde::Serialize, serde::Deserialize, utoipa::ToSchema)] +pub struct HpoSimTermTermResult { /// Version information. pub version: Version, /// The original query records. - pub query: ResponseQuery, + pub query: HpoSimTermTermQuery, /// The resulting records for the scored genes. - pub result: Vec, + pub result: Vec, } /// Result entry for `handle`. #[derive( - serde::Serialize, - serde::Deserialize, - utoipa::ToSchema, Default, Debug, Clone, PartialEq, PartialOrd, + serde::Serialize, + serde::Deserialize, + utoipa::ToSchema, )] -#[schema(title = "HpoSimTermTermResultEntry")] -pub struct ResultEntry { +pub struct HpoSimTermTermResultEntry { /// The lhs entry. pub lhs: String, /// The rhs entry. @@ -102,20 +99,21 @@ pub struct ResultEntry { /// # Errors /// /// In the case that there is an error running the server. -#[allow(clippy::unused_async)] #[utoipa::path( - operation_id = "hpo_sim_term_term", + get, + operation_id = "hpoSimTermTerm", params(RequestQuery), responses( - (status = 200, description = "The query was successful.", body = Result), + (status = 200, description = "The query was successful.", body = HpoSimTermTermResult), + (status = 500, description = "The server encountered an error.", body = CustomError) ) )] -#[get("/hpo/sim/term-term")] +#[get("/api/v1/hpo/sim/term-term")] async fn handle( data: Data>, _path: Path<()>, query: web::Query, -) -> actix_web::Result, CustomError> { +) -> actix_web::Result, CustomError> { let ontology: &Ontology = &data.ontology; let mut result = Vec::new(); @@ -136,7 +134,7 @@ async fn handle( // Compute the similarity for each pair. for (lhs, rhs) in lhs.iter().cartesian_product(rhs.iter()) { let similarity = ic.calculate(lhs, rhs); - let elem = ResultEntry { + let elem = HpoSimTermTermResultEntry { lhs: lhs.id().to_string(), rhs: rhs.id().to_string(), score: similarity, @@ -160,9 +158,9 @@ async fn handle( combiner, } = query.into_inner(); - let result = Result { + let result = HpoSimTermTermResult { version: Version::new(&data.ontology.hpo_version()), - query: ResponseQuery { + query: HpoSimTermTermQuery { lhs, rhs, ic_base, @@ -188,7 +186,7 @@ mod test { pub async fn run_query( web_server_data: Arc, uri: &str, - ) -> Result { + ) -> Result { let app = actix_web::test::init_service( actix_web::App::new() .app_data(actix_web::web::Data::new(web_server_data)) @@ -196,7 +194,8 @@ mod test { ) .await; let req = actix_web::test::TestRequest::get().uri(uri).to_request(); - let resp: super::Result = actix_web::test::call_and_read_body_json(&app, req).await; + let resp: super::HpoSimTermTermResult = + actix_web::test::call_and_read_body_json(&app, req).await; Ok(resp) } diff --git a/src/server/run/hpo_terms.rs b/src/server/run/hpo_terms.rs index 0b92750..26f17a9 100644 --- a/src/server/run/hpo_terms.rs +++ b/src/server/run/hpo_terms.rs @@ -23,10 +23,9 @@ use super::{CustomError, ResultGene}; /// - `max_results` -- the maximum number of records to return /// - `genes` -- whether to include `"genes"` in result #[derive( - serde::Serialize, serde::Deserialize, utoipa::ToSchema, utoipa::IntoParams, Debug, Clone, + Debug, Clone, serde::Serialize, serde::Deserialize, utoipa::ToSchema, utoipa::IntoParams, )] -#[schema(title = "HpoTermsQuery")] -pub struct Query { +pub struct HpoTermsQuery { /// The term ID to search for. pub term_id: Option, /// The term name to search for. @@ -50,9 +49,8 @@ fn _default_genes() -> bool { } /// Result entry for `fetch_hpo_genes`. -#[derive(serde::Serialize, serde::Deserialize, utoipa::ToSchema, Debug, Clone)] -#[schema(title = "HpoTermsResultEntry")] -pub struct ResultEntry { +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, utoipa::ToSchema)] +pub struct HpoTermsResultEntry { /// The HPO term's ID. pub term_id: String, /// The HPO term's name. @@ -68,21 +66,21 @@ pub struct ResultEntry { pub genes: Option>, } -impl PartialEq for ResultEntry { +impl PartialEq for HpoTermsResultEntry { fn eq(&self, other: &Self) -> bool { (self.term_id == other.term_id) && (self.name == other.name) } } -impl Eq for ResultEntry {} +impl Eq for HpoTermsResultEntry {} -impl PartialOrd for ResultEntry { +impl PartialOrd for HpoTermsResultEntry { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } -impl Ord for ResultEntry { +impl Ord for HpoTermsResultEntry { fn cmp(&self, other: &Self) -> std::cmp::Ordering { match self.term_id.cmp(&other.term_id) { core::cmp::Ordering::Equal => {} @@ -92,7 +90,7 @@ impl Ord for ResultEntry { } } -impl ResultEntry { +impl HpoTermsResultEntry { /// Create a `ResultEntry` from an `HpoTerm`. /// /// # Errors @@ -181,7 +179,7 @@ impl ResultEntry { } else { None }; - Ok(ResultEntry { + Ok(HpoTermsResultEntry { term_id: term.id().to_string(), name: term.name().to_string(), genes, @@ -194,14 +192,13 @@ impl ResultEntry { /// Container for the result. #[derive(Debug, serde::Serialize, serde::Deserialize, utoipa::ToSchema)] -#[schema(title = "HpoTermsResult")] -pub struct Result_ { +pub struct HpoTermsResult { /// Version information. pub version: Version, /// The original query records. - pub query: Query, + pub query: HpoTermsQuery, /// The resulting records for the scored genes. - pub result: Vec, + pub result: Vec, } /// Query for terms in the HPO database. @@ -212,20 +209,22 @@ pub struct Result_ { #[allow(clippy::unused_async)] #[allow(clippy::too_many_lines)] #[utoipa::path( - operation_id = "hpo_terms", - params(Query), + get, + operation_id = "hpoTerms", + params(HpoTermsQuery), responses( - (status = 200, description = "The query was successful.", body = Result_), + (status = 200, description = "The query was successful.", body = HpoTermsResult), + (status = 500, description = "The server encountered an error.", body = CustomError) ) )] -#[get("/hpo/terms")] +#[get("/api/v1/hpo/terms")] async fn handle( data: Data>, _path: Path<()>, - query: web::Query, -) -> actix_web::Result, CustomError> { + query: web::Query, +) -> actix_web::Result, CustomError> { let ontology = &data.ontology; - let mut result: Vec = Vec::new(); + let mut result: Vec = Vec::new(); let field_term_id = data .full_text_index @@ -277,7 +276,7 @@ async fn handle( CustomError::new(anyhow::anyhow!("Term ID {} not found in HPO", term_id)) })?; result.push( - ResultEntry::from_term_with_ontology( + HpoTermsResultEntry::from_term_with_ontology( &term, ontology, query.genes, @@ -345,7 +344,7 @@ async fn handle( })?; result.push( - ResultEntry::from_term_with_ontology( + HpoTermsResultEntry::from_term_with_ontology( &term, ontology, query.genes, @@ -358,7 +357,7 @@ async fn handle( } }; - let result = Result_ { + let result = HpoTermsResult { version: Version::new(&data.ontology.hpo_version()), query: query.into_inner(), result, @@ -378,7 +377,7 @@ mod test { pub async fn run_query( web_server_data: Arc, uri: &str, - ) -> Result { + ) -> Result { let app = actix_web::test::init_service( actix_web::App::new() .app_data(actix_web::web::Data::new(web_server_data)) @@ -386,7 +385,7 @@ mod test { ) .await; let req = actix_web::test::TestRequest::get().uri(uri).to_request(); - let resp: super::Result_ = actix_web::test::call_and_read_body_json(&app, req).await; + let resp: super::HpoTermsResult = actix_web::test::call_and_read_body_json(&app, req).await; Ok(resp) } diff --git a/src/server/run/mod.rs b/src/server/run/mod.rs index 2885121..cf54365 100644 --- a/src/server/run/mod.rs +++ b/src/server/run/mod.rs @@ -45,9 +45,9 @@ pub struct Args { pub listen_port: u16, } -#[derive(Debug)] +#[derive(Debug, serde::Serialize, serde::Deserialize, utoipa::ToSchema)] struct CustomError { - err: anyhow::Error, + err: String, } impl std::fmt::Display for CustomError { @@ -57,8 +57,11 @@ impl std::fmt::Display for CustomError { } impl CustomError { + #[allow(clippy::needless_pass_by_value)] fn new(err: anyhow::Error) -> Self { - CustomError { err } + CustomError { + err: err.to_string(), + } } } @@ -163,23 +166,23 @@ where hpo_sim::term_gene::handle, ), components(schemas( - hpo_genes::Query, - hpo_genes::Result, - hpo_genes::ResultEntry, - hpo_omims::Query, - hpo_omims::Result, - hpo_omims::ResultEntry, - hpo_terms::Query, - hpo_terms::Result_, - hpo_terms::ResultEntry, - hpo_sim::term_gene::Query, - crate::query::query_result::Result, - crate::query::query_result::ResultEntry, - crate::query::query_result::TermDetails, + hpo_genes::HpoGenesQuery, + hpo_genes::HpoGenesResult, + hpo_genes::HpoGenesResultEntry, + hpo_omims::HpoOmimsQuery, + hpo_omims::HpoOmimsResult, + hpo_omims::HpoOmimsResultEntry, + hpo_terms::HpoTermsQuery, + hpo_terms::HpoTermsResult, + hpo_terms::HpoTermsResultEntry, + hpo_sim::term_gene::HpoSimTermGeneQuery, + crate::query::query_result::HpoSimTermGeneResult, + crate::query::query_result::HpoSimTermGeneResultEntry, + crate::query::query_result::HpoSimTermGeneTermDetails, crate::query::HpoTerm, - hpo_sim::term_term::ResponseQuery, - hpo_sim::term_term::Result, - hpo_sim::term_term::ResultEntry, + hpo_sim::term_term::HpoSimTermTermQuery, + hpo_sim::term_term::HpoSimTermTermResult, + hpo_sim::term_term::HpoSimTermTermResultEntry, ResultGene, ResultHpoTerm, Match,