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: fuzon-http implementation #20

Merged
merged 18 commits into from
Oct 2, 2024
Merged
Show file tree
Hide file tree
Changes from 8 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
17 changes: 9 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions fuzon-http/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ edition.workspace = true

[dependencies]
actix-web = "4.9.0"
clap = { version = "4.5.18", features = ["derive"] }
fuzon = { version = "0.2.2", path = "../fuzon" }
serde = { version = "1.0.210", features = ["derive"] }
serde_json = "1.0.128"
23 changes: 23 additions & 0 deletions fuzon-http/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# fuzon-http

This is a web-server to deploy fuzon as a web-service.
All ontologies are loaded once on server startup, and the indices are kept in memory.

cmdoret marked this conversation as resolved.
Show resolved Hide resolved
## Configuration

The server takes a configuration file as input to determine what ontologies to load, and which collections to load them into. Collections are individual matchers which can be queried independently.

## Installation

```shell
cd fuzon-http
cargo build --release
../target/release/fuzon-http --config config/example.json
cmdoret marked this conversation as resolved.
Show resolved Hide resolved
```

## Usage
cmdoret marked this conversation as resolved.
Show resolved Hide resolved

Fuzzy matching queries should use `GET /top?collection={collection}&top={top}&query={query}`.

To discover available collections, use `GET /list`.

7 changes: 7 additions & 0 deletions fuzon-http/config/example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"collections": {
"cell_type": "https://purl.obolibrary.org/obo/cl.owl",
"source_material": "https://purl.obolibrary.org/obo/uberon.owl",
"taxon_id": "https://purl.obolibrary.org/obo/ncbitaxon/subsets/taxslim.owl"
}
}
44 changes: 25 additions & 19 deletions fuzon-http/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use std::collections::HashMap;
use actix_web::{get, web, App, HttpServer, Responder, Result};
use clap::Parser;
use fuzon::{TermMatcher};
use serde::{Deserialize, Serialize};
use serde_json;
use std::sync::Arc;
use std::fs::File;

// URL query parameters when requesting matching codes
#[derive(Debug, Deserialize)]
Expand All @@ -28,6 +30,7 @@ struct Config {
}

// Shared app state built from config and used by services
#[derive(Clone, Debug)]
struct AppState {
collections: Arc<HashMap<String, TermMatcher>>,
}
Expand All @@ -37,16 +40,13 @@ impl AppState {
let collections = data
.collections
.into_iter()
.map(|(k, v)| (k, TermMatcher::from_paths(vec![&v]).unwrap())).collect();
.inspect(|(k, _)| println!("Loading {}...", k))
cmdoret marked this conversation as resolved.
Show resolved Hide resolved
.map(|(k, v)| (k, TermMatcher::from_paths(vec![&v]).unwrap()))
.collect();
AppState { collections: Arc::new(collections) }
}
}

// Used for debugging
#[get("/hello/{name}")]
async fn greet(name: web::Path<String>) -> impl Responder {
format!("Hello {name}!")
}

// list collections: /list
#[get("/list")]
Expand Down Expand Up @@ -75,28 +75,34 @@ async fn top(data: web::Data<AppState>, req: web::Query<CodeRequest>) -> Result<
Ok(web::Json(top_terms))
}

/// http server to serve the fuzon terminology matching api
#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
struct Args {
/// Path to the configuration file.
#[clap(short, long)]
config: String,
}


#[actix_web::main] // or #[tokio::main]
async fn main() -> std::io::Result<()> {
let args = Args::parse();
let config_path = args.config;

let config: Config = serde_json::from_reader(
File::open(config_path).expect("Failed to open config file.")
).expect("Failed to parse config.");

// NOTE: config as inline json for debugging, later this will be a config file
let config_data = r#"
{
"collections": {
"cell_type": "https://purl.obolibrary.org/obo/cl.owl",
"source_material": "https://purl.obolibrary.org/obo/uberon.owl",
"taxon_id": "https://purl.obolibrary.org/obo/ncbitaxon/subsets/taxslim.owl"
}
}"#;
let data = web::block(move ||
serde_json::from_str::<Config>(config_data).unwrap()
AppState::from_config(config)
cmdoret marked this conversation as resolved.
Show resolved Hide resolved
)
.await
.expect("Failed to parse config");
.expect("Failed to initialize state from config.");

HttpServer::new(move || {
App::new()
.app_data(web::Data::new(AppState::from_config(data.clone())))
.service(greet)
.app_data(web::Data::new(data.clone()))
.service(list)
.service(top)
})
Expand Down
1 change: 1 addition & 0 deletions fuzon/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ lazy_static! {
};
}

#[derive(Debug, Clone)]
pub struct TermMatcher {
pub terms: Vec<Term>,
}
Expand Down
Loading