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: endpoint /api/v1/genes/lookup with OpenAPI (#587) #589

Merged
merged 2 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
53 changes: 53 additions & 0 deletions openapi.schema.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,34 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/CustomError'
/api/v1/genes/lookup:
get:
tags:
- genes_lookup
summary: Search for genes.
operationId: genesLookup
parameters:
- name: q
in: query
description: The strings to search for.
required: true
schema:
type: array
items:
type: string
responses:
'200':
description: Genes search results.
content:
application/json:
schema:
$ref: '#/components/schemas/GenesLookupResponse'
'500':
description: Internal server error.
content:
application/json:
schema:
$ref: '#/components/schemas/CustomError'
/api/v1/genes/search:
get:
tags:
Expand Down Expand Up @@ -1886,6 +1914,31 @@ components:
- string
- 'null'
description: The disorder name.
GenesLookupResponse:
type: object
description: Result for `async fn handle_with_openapi(
required:
- genes
properties:
genes:
type: array
items:
$ref: '#/components/schemas/GenesLookupResultEntry'
description: The resulting gene information.
GenesLookupResultEntry:
type: object
description: One result entry in the response.
required:
- query
properties:
query:
type: string
description: The query string,
gene_names:
oneOf:
- type: 'null'
- $ref: '#/components/schemas/GeneNames'
description: The gene names information.
GenesNcbiRecord:
type: object
description: A record from the NCBI gene database.
Expand Down
86 changes: 74 additions & 12 deletions src/server/run/genes_lookup.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
//! Implementation of `/genes/lookup` that allows to lookup genes by symbol or identifier.
//! Implementation of endpoint `/api/v1/genes/lookup`.
//!
//! Also includes the implementation of the `/genes/lookup` endpoint (deprecated).
//!
//! In contrast to gene search, more than one query may be given but this must match exactly
//! the symbol or HGNC/NCBI/ENSEMBL identifier.
use actix_web::{
get,
web::{self, Data, Json, Path},
Responder,
};

use crate::server::run::GeneNames;
Expand All @@ -16,9 +17,11 @@ use serde_with::{formats::CommaSeparator, StringWithSeparator};
/// Parameters for `handle`.
#[serde_with::skip_serializing_none]
#[serde_with::serde_as]
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
#[derive(
serde::Serialize, serde::Deserialize, Debug, Clone, utoipa::ToSchema, utoipa::IntoParams,
)]
#[serde(rename_all = "snake_case")]
struct Request {
pub(crate) struct GenesLookupQuery {
/// The strings to search for.
#[serde_as(as = "StringWithSeparator::<CommaSeparator, String>")]
pub q: Vec<String>,
Expand All @@ -33,14 +36,12 @@ struct Container {
pub genes: indexmap::IndexMap<String, Option<GeneNames>>,
}

/// Query for annotations for one variant.
#[allow(clippy::option_map_unit_fn)]
#[get("/genes/lookup")]
async fn handle(
/// Implementation of both endpoints.
async fn handle_impl(
data: Data<crate::server::run::WebServerData>,
_path: Path<()>,
query: web::Query<Request>,
) -> actix_web::Result<impl Responder, CustomError> {
query: web::Query<GenesLookupQuery>,
) -> actix_web::Result<Container, CustomError> {
let genes_db = data.genes.as_ref().ok_or(CustomError::new(anyhow::anyhow!(
"genes database not available"
)))?;
Expand All @@ -54,9 +55,70 @@ async fn handle(
(q.clone(), v)
}));

Ok(Json(Container {
Ok(Container {
// server_version: VERSION.to_string(),
// builder_version,
genes,
}))
})
}

/// Query for annotations for one variant.
#[get("/genes/lookup")]
async fn handle(
data: Data<crate::server::run::WebServerData>,
path: Path<()>,
query: web::Query<GenesLookupQuery>,
) -> actix_web::Result<Json<Container>, CustomError> {
Ok(Json(handle_impl(data, path, query).await?))
}

/// One result entry in the response.
#[derive(
Debug, Clone, serde::Serialize, serde::Deserialize, utoipa::ToSchema, utoipa::ToResponse,
)]
pub(crate) struct GenesLookupResultEntry {
/// The query string,
pub query: String,
/// The gene names information.
pub gene_names: Option<GeneNames>,
}

/// Result for `async fn handle_with_openapi(
#[derive(
Debug, Clone, serde::Serialize, serde::Deserialize, utoipa::ToSchema, utoipa::ToResponse,
)]
pub(crate) struct GenesLookupResponse {
/// The resulting gene information.
pub genes: Vec<GenesLookupResultEntry>,
}

impl From<Container> for GenesLookupResponse {
fn from(container: Container) -> Self {
Self {
genes: container
.genes
.into_iter()
.map(|(query, gene_names)| GenesLookupResultEntry { query, gene_names })
.collect(),
}
}
}

/// Search for genes.
#[utoipa::path(
get,
operation_id = "genesLookup",
params(GenesLookupQuery),
responses(
(status = 200, description = "Genes search results.", body = GenesLookupResponse),
(status = 500, description = "Internal server error.", body = CustomError)
)
)]
#[get("/api/v1/genes/lookup")]
async fn handle_with_openapi(
data: Data<crate::server::run::WebServerData>,
path: Path<()>,
query: web::Query<GenesLookupQuery>,
) -> actix_web::Result<Json<GenesLookupResponse>, CustomError> {
Ok(Json(handle_impl(data, path, query).await?.into()))
}
5 changes: 5 additions & 0 deletions src/server/run/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ pub mod openapi {
use crate::{
common::cli::GenomeRelease,
server::run::genes_info::{self, response::*},
server::run::genes_lookup::{self, GenesLookupResponse, GenesLookupResultEntry},
server::run::genes_search::{
self, GenesFields, GenesScoredGeneNames, GenesSearchQuery, GenesSearchResponse,
},
Expand All @@ -54,6 +55,7 @@ pub mod openapi {
paths(
versions::handle,
genes_info::handle_with_openapi,
genes_lookup::handle_with_openapi,
genes_search::handle_with_openapi
),
components(schemas(
Expand Down Expand Up @@ -115,6 +117,8 @@ pub mod openapi {
GenesSearchResponse,
GenesScoredGeneNames,
GeneNames,
GenesLookupResponse,
GenesLookupResultEntry
))
)]
pub struct ApiDoc;
Expand Down Expand Up @@ -142,6 +146,7 @@ pub async fn main(args: &Args, dbs: Data<WebServerData>) -> std::io::Result<()>
.service(genes_search::handle)
.service(genes_search::handle_with_openapi)
.service(genes_lookup::handle)
.service(genes_lookup::handle_with_openapi)
.service(versions::handle)
.service(
utoipa_swagger_ui::SwaggerUi::new("/swagger-ui/{_:.*}")
Expand Down
Loading