implement api routing

This commit is contained in:
Sam Rijs 2018-01-27 12:01:17 +11:00
parent 2ce0218218
commit 28f5cf7ba2
3 changed files with 91 additions and 39 deletions

View file

@ -7,6 +7,7 @@ authors = ["Sam Rijs <srijs@airpost.net>"]
futures = "0.1.18" futures = "0.1.18"
hyper = "0.11.15" hyper = "0.11.15"
hyper-tls = "0.1.2" hyper-tls = "0.1.2"
route-recognizer = "0.1.12"
semver = { version = "0.9.0", features = ["serde"] } semver = { version = "0.9.0", features = ["serde"] }
serde = "1.0.27" serde = "1.0.27"
serde_derive = "1.0.27" serde_derive = "1.0.27"

View file

@ -1,8 +1,10 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::sync::Arc;
use futures::{Future, future}; use futures::{Future, IntoFuture, future};
use hyper::{Error as HyperError, Request, Response, StatusCode}; use hyper::{Error as HyperError, Method, Request, Response, StatusCode};
use hyper::header::ContentType; use hyper::header::ContentType;
use route_recognizer::{Params, Router};
use semver::{Version, VersionReq}; use semver::{Version, VersionReq};
use serde_json; use serde_json;
use slog::Logger; use slog::Logger;
@ -11,8 +13,23 @@ use tokio_service::Service;
use ::models::repo::RepoPath; use ::models::repo::RepoPath;
use ::engine::Engine; use ::engine::Engine;
enum Route {
AnalyzeDependencies
}
#[derive(Clone)]
pub struct Api { pub struct Api {
pub engine: Engine engine: Engine,
router: Arc<Router<Route>>
}
impl Api {
pub fn new(engine: Engine) -> Api {
let mut router = Router::new();
router.add("/api/v1/analyze/:site/:qual/:name", Route::AnalyzeDependencies);
Api { engine, router: Arc::new(router) }
}
} }
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
@ -38,45 +55,77 @@ impl Service for Api {
type Future = Box<Future<Item=Response, Error=HyperError>>; type Future = Box<Future<Item=Response, Error=HyperError>>;
fn call(&self, req: Request) -> Self::Future { fn call(&self, req: Request) -> Self::Future {
let repo_path = RepoPath::from_parts("github.com", "hyperium", "hyper").unwrap(); if let Ok(route_match) = self.router.recognize(req.uri().path()) {
match route_match.handler {
&Route::AnalyzeDependencies => {
if *req.method() == Method::Get {
return Box::new(self.analyze_dependencies(req, route_match.params));
}
}
}
}
let future = self.engine.analyze_dependencies(repo_path).then(|result| { let mut response = Response::new();
match result { response.set_status(StatusCode::NotFound);
Box::new(future::ok(response))
}
}
impl Api {
fn analyze_dependencies<'r>(&self, _req: Request, params: Params) -> impl Future<Item=Response, Error=HyperError> {
let engine = self.engine.clone();
let site = params.find("site").expect("route param 'site' not found");
let qual = params.find("qual").expect("route param 'qual' not found");
let name = params.find("name").expect("route param 'name' not found");
RepoPath::from_parts(site, qual, name).into_future().then(move |repo_path_result| {
match repo_path_result {
Err(err) => { Err(err) => {
let mut response = Response::new(); let mut response = Response::new();
response.set_status(StatusCode::InternalServerError); response.set_status(StatusCode::BadRequest);
response.set_body(format!("{:?}", err)); response.set_body(format!("{:?}", err));
future::Either::A(future::ok(response)) future::Either::A(future::ok(response))
}, },
Ok(dependencies) => { Ok(repo_path) => {
let response_struct = AnalyzeDependenciesResponse { future::Either::B(engine.analyze_dependencies(repo_path).then(|analyze_result| {
dependencies: dependencies.main.into_iter() match analyze_result {
.map(|(name, analyzed)| (name.into(), AnalyzeDependenciesResponseDetail { Err(err) => {
outdated: analyzed.is_outdated(), let mut response = Response::new();
required: analyzed.required, response.set_status(StatusCode::InternalServerError);
latest: analyzed.latest response.set_body(format!("{:?}", err));
})).collect(), future::Either::A(future::ok(response))
dev_dependencies: dependencies.dev.into_iter() },
.map(|(name, analyzed)| (name.into(), AnalyzeDependenciesResponseDetail { Ok(dependencies) => {
outdated: analyzed.is_outdated(), let response_struct = AnalyzeDependenciesResponse {
required: analyzed.required, dependencies: dependencies.main.into_iter()
latest: analyzed.latest .map(|(name, analyzed)| (name.into(), AnalyzeDependenciesResponseDetail {
})).collect(), outdated: analyzed.is_outdated(),
build_dependencies: dependencies.build.into_iter() required: analyzed.required,
.map(|(name, analyzed)| (name.into(), AnalyzeDependenciesResponseDetail { latest: analyzed.latest
outdated: analyzed.is_outdated(), })).collect(),
required: analyzed.required, dev_dependencies: dependencies.dev.into_iter()
latest: analyzed.latest .map(|(name, analyzed)| (name.into(), AnalyzeDependenciesResponseDetail {
})).collect() outdated: analyzed.is_outdated(),
}; required: analyzed.required,
let mut response = Response::new() latest: analyzed.latest
.with_header(ContentType::json()) })).collect(),
.with_body(serde_json::to_string(&response_struct).unwrap()); build_dependencies: dependencies.build.into_iter()
future::Either::B(future::ok(response)) .map(|(name, analyzed)| (name.into(), AnalyzeDependenciesResponseDetail {
outdated: analyzed.is_outdated(),
required: analyzed.required,
latest: analyzed.latest
})).collect()
};
let mut response = Response::new()
.with_header(ContentType::json())
.with_body(serde_json::to_string(&response_struct).unwrap());
future::Either::B(future::ok(response))
}
}
}))
} }
} }
}); })
Box::new(future)
} }
} }

View file

@ -4,6 +4,7 @@
extern crate futures; extern crate futures;
extern crate hyper; extern crate hyper;
extern crate hyper_tls; extern crate hyper_tls;
extern crate route_recognizer;
extern crate semver; extern crate semver;
#[macro_use] extern crate serde_derive; #[macro_use] extern crate serde_derive;
extern crate serde; extern crate serde;
@ -61,9 +62,10 @@ fn main() {
logger: logger.clone() logger: logger.clone()
}; };
let serve = http.serve_addr_handle(&addr, &handle, move || { let api = Api::new(engine);
Ok(Api { engine: engine.clone() })
}).expect("failed to bind server"); let serve = http.serve_addr_handle(&addr, &handle, move || Ok(api.clone()))
.expect("failed to bind server");
let serving = serve.for_each(move |conn| { let serving = serve.for_each(move |conn| {
let conn_logger = logger.clone(); let conn_logger = logger.clone();