Skip to content

Commit

Permalink
feat: proper OpenAPI-documented and versioned endpoints (#227)
Browse files Browse the repository at this point in the history
  • Loading branch information
holtgrewe committed Nov 20, 2024
1 parent a6b46ff commit 5d04666
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 163 deletions.
30 changes: 13 additions & 17 deletions src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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<HpoTerm>,
/// The gene list to score.
Expand All @@ -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<ResultEntry>,
pub result: Vec<HpoSimTermGeneResultEntry>,
}

/// 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<Vec<TermDetails>>,
pub terms: Option<Vec<HpoSimTermGeneTermDetails>>,
}

/// 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<HpoTerm>,
/// The gene's HPO term.
Expand Down Expand Up @@ -149,11 +145,11 @@ pub fn run_query<S>(
genes: &Vec<&hpo::annotations::Gene>,
hpo: &Ontology,
ncbi_to_hgnc: &HashMap<u32, String, S>,
) -> Result<query_result::Result, anyhow::Error>
) -> Result<query_result::HpoSimTermGeneResult, anyhow::Error>
where
S: std::hash::BuildHasher,
{
let query = query_result::Query {
let query = query_result::HpoSimTermGeneQuery {
terms: patient
.iter()
.map(|t| {
Expand All @@ -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(),
Expand Down Expand Up @@ -217,7 +213,7 @@ where
None
};

TermDetails {
HpoSimTermGeneTermDetails {
term_query,
term_gene: HpoTerm {
term_id: gene_term.id().to_string(),
Expand All @@ -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),
Expand Down
59 changes: 30 additions & 29 deletions src/server/run/hpo_genes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>,
/// The gene symbol to search for.
Expand Down Expand Up @@ -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.
Expand All @@ -84,7 +81,7 @@ pub struct ResultEntry {
pub hpo_terms: Option<Vec<ResultHpoTerm>>,
}

impl ResultEntry {
impl HpoGenesResultEntry {
/// Create a `ResultEntry` from a `Gene` with an `Ontology`.
pub fn from_gene_with_ontology(
gene: &Gene,
Expand All @@ -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(),
Expand All @@ -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<ResultEntry>,
pub result: Vec<HpoGenesResultEntry>,
}

/// 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<Arc<WebServerData>>,
_path: Path<()>,
query: web::Query<Query>,
) -> actix_web::Result<Json<Result>, CustomError> {
query: web::Query<HpoGenesQuery>,
) -> actix_web::Result<Json<HpoGenesResult>, CustomError> {
let ontology = &data.ontology;
let match_ = query.match_.unwrap_or_default();
let mut result: Vec<ResultEntry> = Vec::new();
let mut result: Vec<HpoGenesResultEntry> = Vec::new();

if match_ == Match::Exact {
let gene = if let Some(gene_id) = &query.gene_id {
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -234,15 +235,15 @@ pub(crate) mod test {
pub async fn run_query(
web_server_data: Arc<crate::server::run::WebServerData>,
uri: &str,
) -> Result<super::Result, anyhow::Error> {
) -> Result<super::HpoGenesResult, anyhow::Error> {
let app = actix_web::test::init_service(
actix_web::App::new()
.app_data(actix_web::web::Data::new(web_server_data))
.service(super::handle),
)
.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)
}
Expand Down
Loading

0 comments on commit 5d04666

Please sign in to comment.