Upgraded to actix_web 1.0+
* Some refactoring * Adding CORS support * Updating API & tests with 405 for the relevent calls * Fixing test so they can be run
This commit is contained in:
20
Cargo.toml
20
Cargo.toml
@@ -18,16 +18,21 @@ license = "MIT"
|
||||
|
||||
include = ["Cargo.toml", "README.md", "LICENSE", "ACKNOWLEDGEMENTS", "src/**/*.rs"]
|
||||
|
||||
[dependencies]
|
||||
mercator_db = "^0.1"
|
||||
mercator_parser = "^0.1"
|
||||
[features]
|
||||
static-error-pages = []
|
||||
|
||||
actix = "^0.7"
|
||||
actix-web = "^0.7"
|
||||
[dependencies]
|
||||
actix-web = "^1.0"
|
||||
actix-files = "^0.1"
|
||||
actix-service = "^0.4"
|
||||
actix-cors = "^0.1"
|
||||
|
||||
measure_time = "^0.6"
|
||||
memmap = "^0.7"
|
||||
|
||||
mercator_db = "^0.1"
|
||||
mercator_parser = "^0.1"
|
||||
|
||||
serde = "^1.0"
|
||||
serde_derive = "^1.0"
|
||||
serde_json = "^1.0"
|
||||
@@ -37,3 +42,8 @@ bincode = "^1.1"
|
||||
#log = { version = "^0.4", features = ["max_level_trace", "release_max_level_info"] }
|
||||
log = { version = "^0.4", features = ["max_level_trace", "release_max_level_trace"] }
|
||||
pretty_env_logger = "^0.3" # Logger implementation
|
||||
|
||||
[dev-dependencies]
|
||||
# Only for tests
|
||||
actix-server-config = "^0.1"
|
||||
actix-http = "^0.2"
|
||||
2140
api/swagger.yaml
2140
api/swagger.yaml
File diff suppressed because it is too large
Load Diff
33
src/main.rs
33
src/main.rs
@@ -8,11 +8,10 @@ mod rest_api;
|
||||
mod shared_state;
|
||||
|
||||
use std::process::exit;
|
||||
use std::sync::Arc;
|
||||
use std::sync::RwLock;
|
||||
|
||||
use actix_web::web::Data;
|
||||
use mercator_db::json::model;
|
||||
use mercator_db::json::storage;
|
||||
use mercator_db::DataBase;
|
||||
|
||||
use shared_state::SharedState;
|
||||
@@ -54,8 +53,6 @@ fn main() {
|
||||
|
||||
let hostname;
|
||||
let port;
|
||||
let base;
|
||||
let allowed_origins: Vec<String>;
|
||||
//let data;
|
||||
|
||||
match std::env::var("MERCATOR_HOST") {
|
||||
@@ -80,27 +77,6 @@ fn main() {
|
||||
}
|
||||
};
|
||||
|
||||
match std::env::var("MERCATOR_BASE") {
|
||||
Ok(val) => base = val,
|
||||
Err(val) => {
|
||||
error!("Could not fetch {} : `{}`", "MERCATOR_BASE", val);
|
||||
exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
match std::env::var("MERCATOR_ALLOWED_ORIGINS") {
|
||||
Ok(val) => {
|
||||
allowed_origins = val
|
||||
.split(',')
|
||||
.map(|s| s.trim().to_string())
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
Err(val) => {
|
||||
error!("Could not fetch {} : `{}`", "MERCATOR_ALLOWED_ORIGINS", val);
|
||||
exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
/* UNUSED FOR NOW
|
||||
match std::env::var("MERCATOR_DATA") {
|
||||
Ok(val) => data = val,
|
||||
@@ -134,13 +110,10 @@ fn main() {
|
||||
}
|
||||
// END of Temporary bloc
|
||||
}
|
||||
let state = SharedState::new(db);
|
||||
|
||||
rest_api::run(
|
||||
hostname,
|
||||
&hostname,
|
||||
port,
|
||||
base,
|
||||
allowed_origins,
|
||||
Arc::new(RwLock::new(state)),
|
||||
Data::new(RwLock::new(SharedState::new(db))),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,47 +1,86 @@
|
||||
use actix_web::HttpRequest;
|
||||
use std::sync::RwLock;
|
||||
|
||||
use actix_web::web;
|
||||
use actix_web::web::Data;
|
||||
use actix_web::web::Json;
|
||||
use actix_web::HttpResponse;
|
||||
use actix_web::Json;
|
||||
|
||||
use super::error_400;
|
||||
use super::AppState;
|
||||
use super::Filters;
|
||||
use super::StringOrStaticFileResult;
|
||||
use crate::shared_state::SharedState;
|
||||
|
||||
pub fn health(_req: &HttpRequest<AppState>) -> HttpResponse {
|
||||
use super::error_422;
|
||||
use super::ok_200;
|
||||
use super::HandlerResult;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Query {
|
||||
query: String,
|
||||
}
|
||||
|
||||
// Also used for the root service.
|
||||
pub fn health() -> HttpResponse {
|
||||
HttpResponse::Ok().finish()
|
||||
}
|
||||
|
||||
pub fn query(
|
||||
(_parameters, _state): (Option<Json<Filters>>, HttpRequest<AppState>),
|
||||
) -> StringOrStaticFileResult {
|
||||
fn query((parameters, state): (Json<Query>, Data<RwLock<SharedState>>)) -> HandlerResult {
|
||||
trace!("query Triggered!");
|
||||
error_400()
|
||||
let context = state.read().unwrap();
|
||||
let query = ¶meters.query;
|
||||
|
||||
if query.is_empty() {
|
||||
error_422(format!("Invalid query in '{:?}'", query))
|
||||
} else {
|
||||
// FIXME: MANAGE PROJECTIONS
|
||||
let results = context
|
||||
.db()
|
||||
.core_keys()
|
||||
.iter()
|
||||
// FIXME: Specify from json output space + threshold volume
|
||||
.flat_map(|core| match context.query(query, core, None, None) {
|
||||
Err(_) => vec![], // FIXME: Return errors here instead!!
|
||||
Ok(r) => {
|
||||
let mut r = r.into_iter().map(|o| o.space_id).collect::<Vec<_>>();
|
||||
r.sort_unstable();
|
||||
r.dedup();
|
||||
r
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
ok_200(&results)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn config(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(web::resource("/health").route(web::get().to(health)));
|
||||
cfg.service(web::resource("/query").route(web::post().to(query)));
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::tests::*;
|
||||
mod routing {
|
||||
use super::super::tests_utils::*;
|
||||
|
||||
#[test]
|
||||
fn health() {
|
||||
let ep = get_path("/health".into());
|
||||
expect_200(http::Method::GET, ep.clone());
|
||||
let ep = &get_path("/health");
|
||||
|
||||
expect_400(http::Method::POST, ep.clone());
|
||||
expect_400(http::Method::PUT, ep.clone());
|
||||
expect_400(http::Method::PATCH, ep.clone());
|
||||
expect_400(http::Method::DELETE, ep.clone());
|
||||
expect_200(Method::GET, ep);
|
||||
|
||||
expect_405(Method::POST, ep);
|
||||
expect_405(Method::PUT, ep);
|
||||
expect_405(Method::PATCH, ep);
|
||||
expect_405(Method::DELETE, ep);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn query() {
|
||||
let ep = get_path("/query".into());
|
||||
expect_200(http::Method::POST, ep.clone());
|
||||
expect_422(http::Method::POST, ep.clone());
|
||||
let ep = &get_path("/query");
|
||||
|
||||
expect_400(http::Method::GET, ep.clone());
|
||||
expect_400(http::Method::PUT, ep.clone());
|
||||
expect_400(http::Method::PATCH, ep.clone());
|
||||
expect_400(http::Method::DELETE, ep.clone());
|
||||
expect_200(Method::POST, ep);
|
||||
expect_422(Method::POST, ep);
|
||||
|
||||
expect_405(Method::GET, ep);
|
||||
expect_405(Method::PUT, ep);
|
||||
expect_405(Method::PATCH, ep);
|
||||
expect_405(Method::DELETE, ep);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
use actix_web::HttpRequest;
|
||||
use actix_web::Json;
|
||||
use actix_web::Path;
|
||||
use std::sync::RwLock;
|
||||
|
||||
use actix_web::web;
|
||||
use actix_web::web::Data;
|
||||
use actix_web::web::Path;
|
||||
|
||||
use crate::shared_state::SharedState;
|
||||
|
||||
use super::error_400;
|
||||
use super::error_404;
|
||||
use super::ok_200;
|
||||
use super::AppState;
|
||||
use super::StringOrStaticFileResult;
|
||||
use super::HandlerResult;
|
||||
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
pub struct Core {
|
||||
@@ -15,92 +18,86 @@ pub struct Core {
|
||||
scales: Vec<Vec<i32>>,
|
||||
}
|
||||
|
||||
pub fn put(
|
||||
(_path, _core, _state): (Path<String>, Json<Core>, HttpRequest<AppState>),
|
||||
) -> StringOrStaticFileResult {
|
||||
trace!("PUT Triggered!");
|
||||
fn put(path: Path<String>) -> HandlerResult {
|
||||
trace!("PUT Triggered on {}", path);
|
||||
error_400()
|
||||
}
|
||||
|
||||
pub fn get((core, state): (Path<String>, HttpRequest<AppState>)) -> StringOrStaticFileResult {
|
||||
fn get((core, state): (Path<String>, Data<RwLock<SharedState>>)) -> HandlerResult {
|
||||
trace!("GET Triggered!");
|
||||
let core = core.to_string();
|
||||
let context = state.state().shared.read().unwrap();
|
||||
let context = state.read().unwrap();
|
||||
|
||||
match context.db().core(core) {
|
||||
Ok(core) => ok_200(&Core {
|
||||
name: core.name().clone(),
|
||||
version: core.version().clone(),
|
||||
scales: vec![vec![0, 0, 0]],
|
||||
//FIXME: Report the actual values. Might need to change the format
|
||||
// FIXME: Report the actual values. Might need to change the format
|
||||
// to per reference space.
|
||||
}),
|
||||
Err(_) => error_404(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn patch(
|
||||
(_path, _core, _state): (Path<String>, Json<Core>, HttpRequest<AppState>),
|
||||
) -> StringOrStaticFileResult {
|
||||
trace!("PATCH Triggered!");
|
||||
fn patch(path: Path<String>) -> HandlerResult {
|
||||
trace!("PATCH Triggered on {}", path);
|
||||
error_400()
|
||||
}
|
||||
|
||||
pub fn delete((_path, _state): (Path<String>, HttpRequest<AppState>)) -> StringOrStaticFileResult {
|
||||
trace!("DELETE Triggered!");
|
||||
fn delete(path: Path<String>) -> HandlerResult {
|
||||
trace!("DELETE Triggered on {}", path);
|
||||
error_400()
|
||||
}
|
||||
|
||||
pub fn config(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(
|
||||
web::resource("/cores/{name}")
|
||||
.route(web::get().to(get))
|
||||
.route(web::put().to(put))
|
||||
.route(web::patch().to(patch))
|
||||
.route(web::delete().to(delete)),
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::tests::*;
|
||||
mod routing {
|
||||
use super::super::tests_utils::*;
|
||||
|
||||
const INSTANCE_EXISTS: &str = "/cores/42";
|
||||
const INSTANCE_INVALID: &str = "/cores/21";
|
||||
const INSTANCE_EXISTS: &str = CORE;
|
||||
const INSTANCE_INVALID: &str = "/41-doesnotexists";
|
||||
|
||||
// FIXME: Add Body to request to see difference between (in)valid bodied requests
|
||||
|
||||
#[test]
|
||||
fn put() {
|
||||
json::expect_200(http::Method::PUT, get_path(INSTANCE_EXISTS), "".to_string());
|
||||
json::expect_422(http::Method::PUT, get_path(INSTANCE_EXISTS), "".to_string());
|
||||
json::expect_200(
|
||||
http::Method::PUT,
|
||||
get_path(INSTANCE_INVALID),
|
||||
"".to_string(),
|
||||
);
|
||||
json::expect_200(Method::PUT, &get_core(INSTANCE_EXISTS), "".to_string());
|
||||
json::expect_422(Method::PUT, &get_core(INSTANCE_EXISTS), "".to_string());
|
||||
json::expect_200(Method::PUT, &get_core(INSTANCE_INVALID), "".to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn patch() {
|
||||
json::expect_200(
|
||||
http::Method::PATCH,
|
||||
get_path(INSTANCE_EXISTS),
|
||||
"".to_string(),
|
||||
);
|
||||
json::expect_422(
|
||||
http::Method::PATCH,
|
||||
get_path(INSTANCE_EXISTS),
|
||||
"".to_string(),
|
||||
);
|
||||
expect_400(http::Method::PATCH, get_path(INSTANCE_INVALID));
|
||||
json::expect_200(Method::PATCH, &get_core(INSTANCE_EXISTS), "".to_string());
|
||||
json::expect_422(Method::PATCH, &get_core(INSTANCE_EXISTS), "".to_string());
|
||||
expect_404(Method::PATCH, &get_core(INSTANCE_INVALID));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get() {
|
||||
expect_200(http::Method::GET, get_path(INSTANCE_EXISTS));
|
||||
expect_404(http::Method::GET, get_path(INSTANCE_INVALID));
|
||||
expect_200(Method::GET, &get_core(INSTANCE_EXISTS));
|
||||
expect_404(Method::GET, &get_core(INSTANCE_INVALID));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delete() {
|
||||
expect_200(http::Method::DELETE, get_path(INSTANCE_EXISTS));
|
||||
expect_404(http::Method::DELETE, get_path(INSTANCE_INVALID));
|
||||
expect_200(Method::DELETE, &get_core(INSTANCE_EXISTS));
|
||||
expect_404(Method::DELETE, &get_core(INSTANCE_INVALID));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn post() {
|
||||
expect_400(http::Method::POST, get_path(INSTANCE_EXISTS));
|
||||
expect_400(http::Method::POST, get_path(INSTANCE_INVALID));
|
||||
expect_405(Method::POST, &get_core(INSTANCE_EXISTS));
|
||||
expect_405(Method::POST, &get_core(INSTANCE_INVALID));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
use actix_web::HttpRequest;
|
||||
use actix_web::Json;
|
||||
use std::sync::RwLock;
|
||||
|
||||
use actix_web::web;
|
||||
use actix_web::web::Data;
|
||||
use actix_web::web::Json;
|
||||
|
||||
use crate::shared_state::SharedState;
|
||||
|
||||
use super::error_400;
|
||||
use super::ok_200;
|
||||
use super::AppState;
|
||||
use super::Filters;
|
||||
use super::StringOrStaticFileResult;
|
||||
use super::HandlerResult;
|
||||
|
||||
pub fn post(
|
||||
(parameters, state): (Option<Json<Filters>>, HttpRequest<AppState>),
|
||||
) -> StringOrStaticFileResult {
|
||||
fn post((parameters, state): (Option<Json<Filters>>, Data<RwLock<SharedState>>)) -> HandlerResult {
|
||||
trace!("POST Triggered!");
|
||||
let context = state.state().shared.read().unwrap();
|
||||
let context = state.read().unwrap();
|
||||
let parameters = Filters::get(parameters);
|
||||
|
||||
let mut results = match parameters.filters {
|
||||
@@ -20,8 +22,9 @@ pub fn post(
|
||||
.db()
|
||||
.core_keys()
|
||||
.iter()
|
||||
//FIXME: Specify from json output space + threshold volume
|
||||
.filter_map(|core| match context.filter(&filter, core, None, None) {
|
||||
Err(_) => None, //FIXME: Return errors here instead!!
|
||||
Err(_) => None, // FIXME: Return errors here instead!!
|
||||
Ok(_) => Some(core.to_string()),
|
||||
})
|
||||
.collect(),
|
||||
@@ -33,30 +36,34 @@ pub fn post(
|
||||
ok_200(&results)
|
||||
}
|
||||
|
||||
pub fn put(
|
||||
(_parameters, _state): (Option<Json<Filters>>, HttpRequest<AppState>),
|
||||
) -> StringOrStaticFileResult {
|
||||
fn put() -> HandlerResult {
|
||||
trace!("PUT Triggered!");
|
||||
error_400()
|
||||
}
|
||||
|
||||
pub fn patch(
|
||||
(_parameters, _state): (Option<Json<Filters>>, HttpRequest<AppState>),
|
||||
) -> StringOrStaticFileResult {
|
||||
fn patch() -> HandlerResult {
|
||||
trace!("PATCH Triggered!");
|
||||
error_400()
|
||||
}
|
||||
|
||||
pub fn delete(
|
||||
(_parameters, _state): (Option<Json<Filters>>, HttpRequest<AppState>),
|
||||
) -> StringOrStaticFileResult {
|
||||
fn delete() -> HandlerResult {
|
||||
trace!("DELETE Triggered!");
|
||||
error_400()
|
||||
}
|
||||
|
||||
pub fn config(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(
|
||||
web::resource("/cores")
|
||||
.route(web::post().to(post))
|
||||
.route(web::put().to(put))
|
||||
.route(web::patch().to(patch))
|
||||
.route(web::delete().to(delete)),
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::tests::*;
|
||||
mod routing {
|
||||
use super::super::tests_utils::*;
|
||||
|
||||
const COLLECTION: &str = "/cores";
|
||||
|
||||
@@ -64,43 +71,43 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn post() {
|
||||
expect_200(http::Method::POST, get_path(COLLECTION));
|
||||
json::expect_200(http::Method::POST, get_path(COLLECTION), "".to_string());
|
||||
expect_200(Method::POST, &get_core(""));
|
||||
json::expect_200(Method::POST, &get_core(""), "".to_string());
|
||||
|
||||
json::expect_422(http::Method::POST, get_path(COLLECTION), "".to_string());
|
||||
json::expect_422(Method::POST, &get_core(""), "".to_string());
|
||||
|
||||
expect_400(http::Method::POST, get_path(COLLECTION));
|
||||
expect_400(Method::POST, &get_core(""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn put() {
|
||||
json::expect_200(http::Method::PUT, get_path(COLLECTION), "".to_string());
|
||||
json::expect_200(Method::PUT, &get_core(""), "".to_string());
|
||||
|
||||
json::expect_422(http::Method::PUT, get_path(COLLECTION), "".to_string());
|
||||
json::expect_422(Method::PUT, &get_core(""), "".to_string());
|
||||
|
||||
expect_400(http::Method::PUT, get_path(COLLECTION));
|
||||
expect_400(Method::PUT, &get_core(""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn patch() {
|
||||
json::expect_200(http::Method::PATCH, get_path(COLLECTION), "".to_string());
|
||||
json::expect_200(Method::PATCH, &get_core(""), "".to_string());
|
||||
|
||||
json::expect_422(http::Method::PATCH, get_path(COLLECTION), "".to_string());
|
||||
json::expect_422(Method::PATCH, &get_core(""), "".to_string());
|
||||
|
||||
expect_400(http::Method::PATCH, get_path(COLLECTION));
|
||||
expect_400(Method::PATCH, &get_core(""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delete() {
|
||||
json::expect_200(http::Method::DELETE, get_path(COLLECTION), "".to_string());
|
||||
json::expect_200(Method::DELETE, &get_core(""), "".to_string());
|
||||
|
||||
json::expect_422(http::Method::DELETE, get_path(COLLECTION), "".to_string());
|
||||
json::expect_422(Method::DELETE, &get_core(""), "".to_string());
|
||||
|
||||
expect_400(http::Method::DELETE, get_path(COLLECTION));
|
||||
expect_400(Method::DELETE, &get_core(""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get() {
|
||||
expect_400(http::Method::GET, get_path(COLLECTION));
|
||||
expect_405(Method::GET, &get_core(""));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
use super::AppState;
|
||||
|
||||
use actix_web::fs;
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::HttpRequest;
|
||||
use actix_web::Path;
|
||||
use actix_web::Result;
|
||||
|
||||
pub fn page_400(_req: &HttpRequest<AppState>) -> Result<fs::NamedFile> {
|
||||
trace!("400 Triggered!");
|
||||
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
|
||||
}
|
||||
|
||||
pub fn page_400_no_state(_req: &HttpRequest) -> Result<fs::NamedFile> {
|
||||
trace!("400 Triggered!");
|
||||
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
|
||||
}
|
||||
pub fn page_404(_req: &HttpRequest) -> Result<fs::NamedFile> {
|
||||
trace!("404 Triggered!");
|
||||
Ok(fs::NamedFile::open("static/404.html")?.set_status_code(StatusCode::NOT_FOUND))
|
||||
}
|
||||
|
||||
pub fn static_file((path, _req): (Path<String>, HttpRequest)) -> Result<fs::NamedFile> {
|
||||
trace!("static/{} Triggered!", path);
|
||||
|
||||
match fs::NamedFile::open(format!("static/{}", path).as_str()) {
|
||||
Ok(o) => Ok(o),
|
||||
Err(_) => {
|
||||
Ok(fs::NamedFile::open("static/404.html")?.set_status_code(StatusCode::NOT_FOUND))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::sync::RwLock;
|
||||
|
||||
use actix_web::http;
|
||||
use actix_web::test::TestRequest;
|
||||
|
||||
#[test]
|
||||
fn page_400() {
|
||||
let response = TestRequest::with_state(AppState {
|
||||
shared: Arc::new(RwLock::new(0)),
|
||||
})
|
||||
.run(&super::page_400)
|
||||
.unwrap();
|
||||
assert_eq!(response.status(), http::StatusCode::BAD_REQUEST);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn page_400_no_state() {
|
||||
let response = TestRequest::default()
|
||||
.run(&super::page_400_no_state)
|
||||
.unwrap();
|
||||
assert_eq!(response.status(), http::StatusCode::BAD_REQUEST);
|
||||
}
|
||||
#[test]
|
||||
fn page_404() {
|
||||
let response = TestRequest::default().run(&super::page_404).unwrap();
|
||||
assert_eq!(response.status(), http::StatusCode::NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod routing {
|
||||
use super::super::tests::*;
|
||||
|
||||
#[test]
|
||||
fn default_no_path() {
|
||||
expect_404(http::Method::GET, "".into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_slash() {
|
||||
expect_404(http::Method::GET, "/".into());
|
||||
expect_404(http::Method::GET, "//".into());
|
||||
expect_404(http::Method::GET, "/ /".into());
|
||||
expect_404(http::Method::GET, "/ //".into());
|
||||
expect_404(http::Method::GET, "// ".into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_invalid_prefix() {
|
||||
expect_404(http::Method::GET, "/test".into());
|
||||
expect_404(http::Method::GET, format!("{}test", PREFIX));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_prefix_no_slash() {
|
||||
expect_400(http::Method::GET, PREFIX.into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_prefix_final_slash() {
|
||||
expect_400(http::Method::GET, format!("{}/", PREFIX));
|
||||
}
|
||||
}
|
||||
88
src/rest_api/helpers.rs
Normal file
88
src/rest_api/helpers.rs
Normal file
@@ -0,0 +1,88 @@
|
||||
use std::fmt::Debug;
|
||||
use std::io::Error;
|
||||
use std::io::ErrorKind;
|
||||
|
||||
use actix_files::NamedFile;
|
||||
use actix_web::Either;
|
||||
use actix_web::HttpResponse;
|
||||
use serde::Serialize;
|
||||
|
||||
use super::HandlerResult;
|
||||
use super::*;
|
||||
|
||||
pub fn ok_200<T>(data: &T) -> HandlerResult
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
match serde_json::to_string(data) {
|
||||
Ok(response) => Ok(Either::A(HttpResponse::Ok().body(response))),
|
||||
Err(e) => error_500(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn error_422<S>(reason: S) -> HandlerResult
|
||||
where
|
||||
S: Debug,
|
||||
{
|
||||
Ok(Either::A(HttpResponse::UnprocessableEntity().body(
|
||||
format!("422 - Unprocessable Entity:\n{:?}", reason),
|
||||
)))
|
||||
}
|
||||
|
||||
pub fn error_500<S>(reason: S) -> HandlerResult
|
||||
where
|
||||
S: Debug,
|
||||
{
|
||||
Err(Error::new(
|
||||
ErrorKind::Other,
|
||||
format!("500 - Internal Server Error: {:?}", reason),
|
||||
))
|
||||
}
|
||||
|
||||
//pub fn page_400() -> HandlerResult {
|
||||
// trace!("400 Triggered!");
|
||||
// error_400()
|
||||
//}
|
||||
|
||||
pub fn page_404() -> HandlerResult {
|
||||
trace!("404 Triggered!");
|
||||
error_404()
|
||||
}
|
||||
|
||||
//pub fn page_405() -> HandlerResult {
|
||||
// trace!("405 Triggered!");
|
||||
// error_405()
|
||||
//}
|
||||
|
||||
pub fn api(path: Path<String>) -> Result<NamedFile, Error> {
|
||||
trace!("api/{} Triggered!", path);
|
||||
|
||||
match NamedFile::open(format!("static/api/{}", path).as_str()) {
|
||||
Ok(o) => Ok(o),
|
||||
Err(_) => {
|
||||
Ok(NamedFile::open("static/errors/404.html")?.set_status_code(StatusCode::NOT_FOUND))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn static_file(path: Path<String>) -> Result<NamedFile, Error> {
|
||||
trace!("static/{} Triggered!", path);
|
||||
|
||||
match NamedFile::open(format!("static/{}", path).as_str()) {
|
||||
Ok(o) => Ok(o),
|
||||
Err(_) => {
|
||||
Ok(NamedFile::open("static/errors/404.html")?.set_status_code(StatusCode::NOT_FOUND))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::tests_utils::*;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn page_400() {
|
||||
// expect_400(Method::PATCH, get_core(INSTANCE_INVALID));
|
||||
}
|
||||
}
|
||||
23
src/rest_api/helpers_dynamic_pages.rs
Normal file
23
src/rest_api/helpers_dynamic_pages.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
#![cfg(not(feature = "static-error-pages"))]
|
||||
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::Either;
|
||||
use actix_web::HttpResponse;
|
||||
|
||||
use super::HandlerResult;
|
||||
|
||||
fn error(code: StatusCode) -> HandlerResult {
|
||||
Ok(Either::A(HttpResponse::build(code).finish()))
|
||||
}
|
||||
|
||||
pub fn error_400() -> HandlerResult {
|
||||
error(StatusCode::BAD_REQUEST)
|
||||
}
|
||||
|
||||
pub fn error_404() -> HandlerResult {
|
||||
error(StatusCode::NOT_FOUND)
|
||||
}
|
||||
|
||||
//pub fn error_405() -> HandlerResult {
|
||||
// error(StatusCode::METHOD_NOT_ALLOWED)
|
||||
//}
|
||||
25
src/rest_api/helpers_static_pages.rs
Normal file
25
src/rest_api/helpers_static_pages.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
#![cfg(feature = "static-error-pages")]
|
||||
|
||||
use actix_files::NamedFile;
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::Either;
|
||||
|
||||
use super::HandlerResult;
|
||||
|
||||
fn error(code: StatusCode) -> HandlerResult {
|
||||
let path = format!("static/errors/{}.html", u16::from(code));
|
||||
|
||||
Ok(Either::B(NamedFile::open(path)?.set_status_code(code)))
|
||||
}
|
||||
|
||||
pub fn error_400() -> HandlerResult {
|
||||
error(StatusCode::BAD_REQUEST)
|
||||
}
|
||||
|
||||
pub fn error_404() -> HandlerResult {
|
||||
error(StatusCode::NOT_FOUND)
|
||||
}
|
||||
|
||||
//pub fn error_405() -> HandlerResult {
|
||||
// error(StatusCode::METHOD_NOT_ALLOWED)
|
||||
//}
|
||||
@@ -9,37 +9,46 @@ mod cores;
|
||||
mod spatial_object;
|
||||
mod spatial_objects;
|
||||
|
||||
mod default;
|
||||
mod helpers;
|
||||
mod helpers_dynamic_pages;
|
||||
mod helpers_static_pages;
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::io::Error;
|
||||
use std::process::exit;
|
||||
use std::sync::RwLock;
|
||||
|
||||
use actix_web::fs;
|
||||
use actix_cors::Cors;
|
||||
use actix_files::NamedFile;
|
||||
use actix_web::http;
|
||||
use actix_web::http::Method;
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::middleware;
|
||||
use actix_web::middleware::cors::Cors;
|
||||
use actix_web::pred;
|
||||
use actix_web::server;
|
||||
use actix_web::server::HttpHandler;
|
||||
use actix_web::server::HttpHandlerTask;
|
||||
use actix_web::web;
|
||||
use actix_web::web::Data;
|
||||
use actix_web::web::Json;
|
||||
use actix_web::web::Path;
|
||||
use actix_web::App;
|
||||
use actix_web::Either;
|
||||
use actix_web::Json;
|
||||
use serde::Serialize;
|
||||
use actix_web::HttpResponse;
|
||||
use actix_web::HttpServer;
|
||||
|
||||
#[cfg(feature = "static-error-pages")]
|
||||
pub use helpers_static_pages::*;
|
||||
|
||||
#[cfg(not(feature = "static-error-pages"))]
|
||||
pub use helpers_dynamic_pages::*;
|
||||
|
||||
pub use helpers::*;
|
||||
|
||||
use crate::SharedState;
|
||||
|
||||
// Application shared state
|
||||
pub struct AppState {
|
||||
shared: Arc<RwLock<SharedState>>,
|
||||
}
|
||||
pub type HandlerResult = Result<Either<HttpResponse, NamedFile>, Error>;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Filters {
|
||||
filters: Option<String>,
|
||||
ids_only: Option<bool>,
|
||||
// resolution: Option<Vec<u64>>, // None means automatic selection, based on ViewPort
|
||||
// view_port: Option<u64>,
|
||||
}
|
||||
|
||||
impl Filters {
|
||||
@@ -50,40 +59,13 @@ impl Filters {
|
||||
None => Filters {
|
||||
filters: None,
|
||||
ids_only: Some(true),
|
||||
//resolution: None,
|
||||
},
|
||||
Some(p) => p.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type StringOrStaticFileResult = Either<String, fs::NamedFile>;
|
||||
|
||||
pub fn ok_200<T>(data: &T) -> StringOrStaticFileResult
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
Either::A(
|
||||
serde_json::to_string_pretty(data)
|
||||
.unwrap_or_else(|e| format!("Internal Error 500: {:?}", e)),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn error_400() -> StringOrStaticFileResult {
|
||||
Either::B(
|
||||
fs::NamedFile::open("static/400.html")
|
||||
.unwrap()
|
||||
.set_status_code(StatusCode::BAD_REQUEST),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn error_404() -> StringOrStaticFileResult {
|
||||
Either::B(
|
||||
fs::NamedFile::open("static/404.html")
|
||||
.unwrap()
|
||||
.set_status_code(StatusCode::NOT_FOUND),
|
||||
)
|
||||
}
|
||||
|
||||
// From: https://stackoverflow.com/a/52367953
|
||||
fn into_static<S>(s: S) -> &'static str
|
||||
where
|
||||
@@ -92,218 +74,252 @@ where
|
||||
Box::leak(s.into().into_boxed_str())
|
||||
}
|
||||
|
||||
fn get_app<S>(
|
||||
prefix: S,
|
||||
allowed_origins: &[&'static str],
|
||||
state: Arc<RwLock<SharedState>>,
|
||||
) -> Vec<Box<HttpHandler<Task = Box<HttpHandlerTask>>>>
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
vec![
|
||||
App::with_state(AppState { shared: state })
|
||||
.prefix(into_static(prefix).to_string())
|
||||
.middleware(middleware::Logger::new(
|
||||
r#"%a "%r" %s %b "%{Referer}i" "%{User-Agent}i" %T[s] %D[ms]"#,
|
||||
))
|
||||
// ACTIONS -------------------------------------------------------------------
|
||||
.resource("/health", |r| {
|
||||
r.method(Method::GET).f(actions::health);
|
||||
r.route()
|
||||
.filter(pred::Not(pred::Get()))
|
||||
.f(default::page_400);
|
||||
})
|
||||
// DEFAULT -------------------------------------------------------------------
|
||||
.default_resource(|r| {
|
||||
r.f(default::page_400);
|
||||
})
|
||||
// REQUIRES CORS Support ---------------------------------------------------------------
|
||||
.configure(|app| {
|
||||
let mut cors = Cors::for_app(app);
|
||||
for origin in allowed_origins {
|
||||
cors.allowed_origin(origin);
|
||||
fn config_v1(cfg: &mut web::ServiceConfig) {
|
||||
// Warning: Order matters, as a more generic path would catch calls for a
|
||||
// more specific one when registered first.
|
||||
space::config(cfg);
|
||||
spaces::config(cfg);
|
||||
|
||||
core::config(cfg);
|
||||
cores::config(cfg);
|
||||
|
||||
spatial_object::config(cfg);
|
||||
spatial_objects::config(cfg);
|
||||
|
||||
actions::config(cfg);
|
||||
|
||||
cfg.route("/static/{file:.*}", web::get().to(static_file));
|
||||
cfg.route("/api/{file:.*}", web::get().to(api));
|
||||
cfg.route("/", web::to(page_404));
|
||||
}
|
||||
|
||||
pub fn config(cfg: &mut web::ServiceConfig) {
|
||||
let prefix;
|
||||
|
||||
match std::env::var("MERCATOR_BASE") {
|
||||
Ok(val) => prefix = val,
|
||||
Err(val) => {
|
||||
error!("Could not fetch {} : `{}`", "MERCATOR_BASE", val);
|
||||
exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
cfg.service(web::scope(into_static(format!("{}/v1", prefix))).configure(config_v1))
|
||||
.service(web::scope(into_static(prefix)).configure(config_v1))
|
||||
.route("/health", web::get().to(actions::health))
|
||||
.route("/static/{file:.*}", web::get().to(static_file));
|
||||
}
|
||||
|
||||
pub fn get_cors() -> Cors {
|
||||
// Setup CORS support.
|
||||
let mut cors = Cors::new();
|
||||
|
||||
match std::env::var("MERCATOR_ALLOWED_ORIGINS") {
|
||||
Ok(val) => {
|
||||
let allowed_origins = val.split(',').map(|s| s.trim()).collect::<Vec<_>>();
|
||||
|
||||
for origin in allowed_origins {
|
||||
if !origin.is_empty() {
|
||||
cors = cors.allowed_origin(into_static(origin));
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(val) => {
|
||||
warn!(
|
||||
"Could not fetch {} : `{}`, allowing all origins",
|
||||
"MERCATOR_ALLOWED_ORIGINS", val
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
cors.allowed_methods(vec!["GET", "POST", "UPDATE", "PATCH", "DELETE", "OPTIONS"])
|
||||
.allowed_headers(vec![http::header::AUTHORIZATION, http::header::ACCEPT])
|
||||
.allowed_header(http::header::CONTENT_TYPE)
|
||||
.max_age(600)
|
||||
.resource("/queries", |r| {
|
||||
r.method(Method::POST).with(actions::query);
|
||||
r.route()
|
||||
.filter(pred::Not(pred::Post()))
|
||||
.f(default::page_400);
|
||||
})
|
||||
// SPACES -------------------------------------------------------------------
|
||||
.resource("/spaces", |r| {
|
||||
r.method(Method::POST).with(spaces::post);
|
||||
r.method(Method::PUT).with(spaces::put);
|
||||
r.method(Method::PATCH).with(spaces::patch);
|
||||
r.method(Method::DELETE).with(spaces::delete);
|
||||
})
|
||||
.resource("/spaces/{name}", |r| {
|
||||
r.method(Method::PUT).with(space::put);
|
||||
r.method(Method::PATCH).with(space::patch);
|
||||
r.method(Method::GET).with(space::get);
|
||||
r.method(Method::DELETE).with(space::delete);
|
||||
})
|
||||
// DATASETS -------------------------------------------------------------------
|
||||
.resource("/cores", |r| {
|
||||
r.method(Method::POST).with(&cores::post);
|
||||
r.method(Method::PUT).with(&cores::put);
|
||||
r.method(Method::PATCH).with(&cores::patch);
|
||||
r.method(Method::DELETE).with(&cores::delete);
|
||||
})
|
||||
.resource("/cores/{name}", |r| {
|
||||
r.method(Method::PUT).with(core::put);
|
||||
r.method(Method::GET).with(core::get);
|
||||
r.method(Method::PATCH).with(core::patch);
|
||||
r.method(Method::DELETE).with(core::delete);
|
||||
})
|
||||
// SPATIAL OBJECTS -------------------------------------------------------------------
|
||||
.resource("/cores/{name}/spatial_objects", |r| {
|
||||
r.method(Method::POST).with(spatial_objects::post);
|
||||
r.method(Method::PUT).with(spatial_objects::put);
|
||||
r.method(Method::PATCH).with(spatial_objects::patch);
|
||||
r.method(Method::DELETE).with(spatial_objects::delete);
|
||||
})
|
||||
.resource("/cores/{name}/spatial_objects/{id}", |r| {
|
||||
r.method(Method::PUT).with(spatial_object::put);
|
||||
r.method(Method::GET).with(spatial_object::get);
|
||||
r.method(Method::PATCH).with(spatial_object::patch);
|
||||
r.method(Method::DELETE).with(spatial_object::delete);
|
||||
})
|
||||
.register()
|
||||
})
|
||||
.boxed(),
|
||||
App::new()
|
||||
.resource("/static/{file}", |r| {
|
||||
r.method(Method::GET).with(default::static_file)
|
||||
})
|
||||
.default_resource(|r| {
|
||||
// 404 for GET request
|
||||
r.method(Method::GET).f(default::page_404);
|
||||
|
||||
// all requests that are not `GET`
|
||||
r.route()
|
||||
.filter(pred::Not(pred::Get()))
|
||||
.f(default::page_400_no_state);
|
||||
})
|
||||
.boxed(),
|
||||
]
|
||||
}
|
||||
|
||||
pub fn run<S>(
|
||||
host: S,
|
||||
port: u16,
|
||||
prefix: S,
|
||||
allowed_origins: Vec<S>,
|
||||
state: Arc<RwLock<SharedState>>,
|
||||
) where
|
||||
S: Into<String>,
|
||||
{
|
||||
info!("Initializing server...");
|
||||
macro_rules! get_app {
|
||||
($state:expr) => {
|
||||
App::new()
|
||||
.register_data($state.clone())
|
||||
.wrap(middleware::Logger::new(
|
||||
r#"%a "%r" %s %b "%{Referer}i" "%{User-Agent}i" %T[s] %D[ms]"#,
|
||||
))
|
||||
.wrap(get_cors())
|
||||
.configure(config)
|
||||
.default_service(
|
||||
web::resource("/")
|
||||
// 404 for GET request
|
||||
.route(web::to(page_404)),
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
let sys = actix::System::new("spatial-search");
|
||||
let prefix = into_static(prefix);
|
||||
let host = host.into();
|
||||
pub fn run(host: &str, port: u16, state: Data<RwLock<SharedState>>) {
|
||||
info!("Starting http server: {}:{}", host, port);
|
||||
|
||||
let mut origins = Vec::with_capacity(allowed_origins.len());
|
||||
for origin in allowed_origins {
|
||||
origins.push(into_static(origin));
|
||||
}
|
||||
|
||||
server::new(move || get_app(prefix, &origins, state.clone()))
|
||||
// Create & run the server.
|
||||
match HttpServer::new(move || get_app!(state))
|
||||
.bind(format!("{}:{}", host, port))
|
||||
.unwrap()
|
||||
.start();
|
||||
|
||||
info!("Started http server: {}:{}{}", host, port, prefix);
|
||||
|
||||
let _ = sys.run();
|
||||
.run()
|
||||
{
|
||||
Ok(_) => info!("Server Stopped!"),
|
||||
Err(e) => error!("Error running the server: {}", e),
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::get_app;
|
||||
use super::{Arc, RwLock, SharedState};
|
||||
mod tests_utils {
|
||||
use super::*;
|
||||
|
||||
pub use actix_web::http;
|
||||
pub use actix_web::http::Method;
|
||||
pub use actix_web::test::TestServer;
|
||||
//use actix_server_config::ServerConfig;
|
||||
//use actix_service::IntoNewService;
|
||||
//use actix_service::NewService;
|
||||
use actix_service::Service;
|
||||
//use actix_web::dev::ServiceResponse;
|
||||
use actix_web::test;
|
||||
//use actix_web::test::TestRequest;
|
||||
use mercator_db::DataBase;
|
||||
|
||||
pub const PREFIX: &str = "spatial-search";
|
||||
pub const CORE_ID: &str = "10k";
|
||||
|
||||
fn get_start_state() -> Arc<RwLock<SharedState>> {
|
||||
Arc::new(RwLock::new(0))
|
||||
}
|
||||
pub const PREFIX: &str = "/spatial-search";
|
||||
pub const CORE: &str = "/10k";
|
||||
pub const SPACE: &str = "/std";
|
||||
pub const SPATIAL_OBJECT: &str = "/oid0.44050628835072825";
|
||||
|
||||
pub fn get_test_server() -> TestServer {
|
||||
TestServer::with_factory(move || get_app(PREFIX, get_start_state().clone()))
|
||||
pub enum Method {
|
||||
GET,
|
||||
POST,
|
||||
PUT,
|
||||
PATCH,
|
||||
DELETE,
|
||||
}
|
||||
|
||||
pub fn get_path(path: &str) -> String {
|
||||
format!("{}{}", PREFIX, path)
|
||||
}
|
||||
|
||||
pub fn expect_200(method: Method, path: String) -> () {
|
||||
let mut srv = get_test_server();
|
||||
let req = srv.client(method, path.as_str()).finish().unwrap();
|
||||
let response = srv.execute(req.send()).unwrap();
|
||||
assert_eq!(http::StatusCode::OK, response.status());
|
||||
pub fn get_space(name: &str) -> String {
|
||||
format!("{}{}", get_path("/spaces"), name)
|
||||
}
|
||||
|
||||
pub fn expect_400(method: Method, path: String) -> () {
|
||||
let mut srv = get_test_server();
|
||||
let req = srv.client(method, path.as_str()).finish().unwrap();
|
||||
let response = srv.execute(req.send()).unwrap();
|
||||
assert_eq!(http::StatusCode::BAD_REQUEST, response.status());
|
||||
pub fn get_core(name: &str) -> String {
|
||||
format!("{}{}", get_path("/cores"), name)
|
||||
}
|
||||
|
||||
pub fn expect_404(method: Method, path: String) -> () {
|
||||
let mut srv = get_test_server();
|
||||
let req = srv.client(method, path.as_str()).finish().unwrap();
|
||||
let response = srv.execute(req.send()).unwrap();
|
||||
assert_eq!(http::StatusCode::NOT_FOUND, response.status());
|
||||
pub fn get_objects(name: &str) -> String {
|
||||
format!("{}{}{}", get_core(CORE), "/spatial_objects", name)
|
||||
}
|
||||
|
||||
pub fn expect_422(method: Method, path: String) -> () {
|
||||
let mut srv = get_test_server();
|
||||
let req = srv.client(method, path.as_str()).finish().unwrap();
|
||||
let response = srv.execute(req.send()).unwrap();
|
||||
assert_eq!(http::StatusCode::UNPROCESSABLE_ENTITY, response.status());
|
||||
pub fn expect(method: Method, path: &str, code: http::StatusCode) {
|
||||
std::env::set_var("MERCATOR_BASE", PREFIX);
|
||||
|
||||
let mut app = test::init_service(get_app!(Data::new(RwLock::new(SharedState::new(
|
||||
DataBase::load(CORE_ID).unwrap()
|
||||
)))));
|
||||
|
||||
let request = match method {
|
||||
Method::GET => test::TestRequest::get(),
|
||||
Method::POST => test::TestRequest::post(),
|
||||
Method::PUT => test::TestRequest::put(),
|
||||
Method::PATCH => test::TestRequest::patch(),
|
||||
Method::DELETE => test::TestRequest::delete(),
|
||||
};
|
||||
|
||||
let request = request.uri(&path).to_request();
|
||||
let response = test::block_on(app.call(request)).unwrap();
|
||||
|
||||
assert_eq!(response.status(), code);
|
||||
}
|
||||
|
||||
pub fn expect_200(method: Method, path: &str) {
|
||||
expect(method, path, http::StatusCode::OK);
|
||||
}
|
||||
|
||||
pub fn expect_400(method: Method, path: &str) {
|
||||
expect(method, path, http::StatusCode::BAD_REQUEST);
|
||||
}
|
||||
|
||||
pub fn expect_404(method: Method, path: &str) {
|
||||
expect(method, path, http::StatusCode::NOT_FOUND);
|
||||
}
|
||||
|
||||
pub fn expect_405(method: Method, path: &str) {
|
||||
expect(method, path, http::StatusCode::METHOD_NOT_ALLOWED);
|
||||
}
|
||||
|
||||
pub fn expect_422(method: Method, path: &str) {
|
||||
expect(method, path, http::StatusCode::UNPROCESSABLE_ENTITY);
|
||||
}
|
||||
|
||||
pub mod json {
|
||||
use super::*;
|
||||
|
||||
pub fn expect_200(method: Method, path: String, json: String) -> () {
|
||||
let mut srv = get_test_server();
|
||||
let req = srv.client(method, path.as_str()).json(json).unwrap();
|
||||
let response = srv.execute(req.send()).unwrap();
|
||||
assert_eq!(http::StatusCode::OK, response.status());
|
||||
pub fn expect_200(method: Method, path: &str, json: String) {
|
||||
expect(method, path, http::StatusCode::OK);
|
||||
}
|
||||
|
||||
/*
|
||||
pub fn expect_400(method: Method, path: String, json: String) -> () {
|
||||
let mut srv = get_test_server();
|
||||
let req = srv.client(method, path.as_str()).json(json).unwrap();
|
||||
let response = srv.execute(req.send()).unwrap();
|
||||
assert_eq!(http::StatusCode::BAD_REQUEST, response.status());
|
||||
}
|
||||
*/
|
||||
|
||||
pub fn expect_404(method: Method, path: String, json: String) -> () {
|
||||
let mut srv = get_test_server();
|
||||
let req = srv.client(method, path.as_str()).json(json).unwrap();
|
||||
let response = srv.execute(req.send()).unwrap();
|
||||
assert_eq!(http::StatusCode::NOT_FOUND, response.status());
|
||||
pub fn expect_404(method: Method, path: &str, json: String) {
|
||||
expect(method, path, http::StatusCode::NOT_FOUND);
|
||||
}
|
||||
|
||||
pub fn expect_422(method: Method, path: String, json: String) -> () {
|
||||
let mut srv = get_test_server();
|
||||
let req = srv.client(method, path.as_str()).json(json).unwrap();
|
||||
let response = srv.execute(req.send()).unwrap();
|
||||
assert_eq!(http::StatusCode::UNPROCESSABLE_ENTITY, response.status());
|
||||
pub fn expect_422(method: Method, path: &str, json: String) {
|
||||
expect(method, path, http::StatusCode::UNPROCESSABLE_ENTITY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod routing {
|
||||
use std::panic;
|
||||
|
||||
use super::tests_utils::*;
|
||||
|
||||
#[test]
|
||||
fn default_no_path() {
|
||||
// FIXME: Currently the string is validated by the URI constructor which
|
||||
// simply unwraps, thus we have to resort to this ugly workaround.
|
||||
// The goal is to catch if that behavior changes in the future.
|
||||
let result = panic::catch_unwind(|| {
|
||||
expect_404(Method::GET, "");
|
||||
});
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_slash() {
|
||||
// We have to manually URL-encode spaces.
|
||||
expect_404(Method::GET, "/");
|
||||
expect_404(Method::GET, "//");
|
||||
expect_404(Method::GET, "/%20/");
|
||||
expect_404(Method::GET, "/%20//");
|
||||
expect_404(Method::GET, "//%20");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_invalid_prefix() {
|
||||
expect_404(Method::GET, "/test");
|
||||
expect_404(Method::GET, &format!("{}test", PREFIX));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_prefix_no_slash() {
|
||||
expect_404(Method::PUT, PREFIX);
|
||||
expect_404(Method::GET, PREFIX);
|
||||
expect_404(Method::POST, PREFIX);
|
||||
expect_404(Method::PATCH, PREFIX);
|
||||
expect_404(Method::DELETE, PREFIX);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_prefix_final_slash() {
|
||||
let path = &format!("{}/", PREFIX);
|
||||
expect_404(Method::PUT, path);
|
||||
expect_404(Method::GET, path);
|
||||
expect_404(Method::POST, path);
|
||||
expect_404(Method::PATCH, path);
|
||||
expect_404(Method::DELETE, path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,26 @@
|
||||
use actix_web::HttpRequest;
|
||||
use actix_web::Path;
|
||||
use std::sync::RwLock;
|
||||
|
||||
use actix_web::web;
|
||||
use actix_web::web::Data;
|
||||
use actix_web::web::Path;
|
||||
|
||||
use crate::model;
|
||||
use crate::shared_state::SharedState;
|
||||
|
||||
use super::error_400;
|
||||
use super::error_404;
|
||||
use super::ok_200;
|
||||
use super::AppState;
|
||||
use super::StringOrStaticFileResult;
|
||||
use super::HandlerResult;
|
||||
|
||||
pub fn put((path, _state): (Path<String>, HttpRequest<AppState>)) -> StringOrStaticFileResult {
|
||||
fn put(path: Path<String>) -> HandlerResult {
|
||||
trace!("PUT Triggered on {}", path);
|
||||
error_400()
|
||||
}
|
||||
|
||||
pub fn get((path, state): (Path<String>, HttpRequest<AppState>)) -> StringOrStaticFileResult {
|
||||
fn get((path, state): (Path<String>, Data<RwLock<SharedState>>)) -> HandlerResult {
|
||||
trace!("GET Triggered on '{}'", path);
|
||||
let name = path.to_string();
|
||||
let context = state.state().shared.read().unwrap();
|
||||
let context = state.read().unwrap();
|
||||
|
||||
match context.db().space(name) {
|
||||
Ok(space) => {
|
||||
@@ -28,66 +31,64 @@ pub fn get((path, state): (Path<String>, HttpRequest<AppState>)) -> StringOrStat
|
||||
}
|
||||
}
|
||||
|
||||
pub fn patch((path, _state): (Path<String>, HttpRequest<AppState>)) -> StringOrStaticFileResult {
|
||||
fn patch(path: Path<String>) -> HandlerResult {
|
||||
trace!("PATCH Triggered on {}", path);
|
||||
error_400()
|
||||
}
|
||||
|
||||
pub fn delete((path, _state): (Path<String>, HttpRequest<AppState>)) -> StringOrStaticFileResult {
|
||||
fn delete(path: Path<String>) -> HandlerResult {
|
||||
trace!("DELETE Triggered on {}", path);
|
||||
error_400()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::tests::*;
|
||||
pub fn config(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(
|
||||
web::resource("/spaces/{name}")
|
||||
.route(web::get().to(get))
|
||||
.route(web::put().to(put))
|
||||
.route(web::patch().to(patch))
|
||||
.route(web::delete().to(delete)),
|
||||
);
|
||||
}
|
||||
|
||||
const INSTANCE_EXISTS: &str = "/spaces/42";
|
||||
const INSTANCE_INVALID: &str = "/spaces/21";
|
||||
#[cfg(test)]
|
||||
mod routing {
|
||||
use super::super::tests_utils::*;
|
||||
|
||||
const INSTANCE_EXISTS: &str = SPACE;
|
||||
const INSTANCE_INVALID: &str = "/21-doesnotexists";
|
||||
|
||||
// FIXME: Add Body to request to see difference between (in)valid bodied requests
|
||||
|
||||
#[test]
|
||||
fn put() {
|
||||
json::expect_200(http::Method::PUT, get_path(INSTANCE_EXISTS), "".to_string());
|
||||
json::expect_422(http::Method::PUT, get_path(INSTANCE_EXISTS), "".to_string());
|
||||
json::expect_200(
|
||||
http::Method::PUT,
|
||||
get_path(INSTANCE_INVALID),
|
||||
"".to_string(),
|
||||
);
|
||||
json::expect_200(Method::PUT, &get_space(INSTANCE_EXISTS), "".to_string());
|
||||
json::expect_422(Method::PUT, &get_space(INSTANCE_EXISTS), "".to_string());
|
||||
json::expect_200(Method::PUT, &get_space(INSTANCE_INVALID), "".to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn patch() {
|
||||
json::expect_200(
|
||||
http::Method::PATCH,
|
||||
get_path(INSTANCE_EXISTS),
|
||||
"".to_string(),
|
||||
);
|
||||
json::expect_422(
|
||||
http::Method::PATCH,
|
||||
get_path(INSTANCE_EXISTS),
|
||||
"".to_string(),
|
||||
);
|
||||
expect_400(http::Method::PATCH, get_path(INSTANCE_INVALID));
|
||||
json::expect_200(Method::PATCH, &get_space(INSTANCE_EXISTS), "".to_string());
|
||||
json::expect_422(Method::PATCH, &get_space(INSTANCE_EXISTS), "".to_string());
|
||||
expect_400(Method::PATCH, &get_space(INSTANCE_INVALID));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get() {
|
||||
expect_200(http::Method::GET, get_path(INSTANCE_EXISTS));
|
||||
expect_404(http::Method::GET, get_path(INSTANCE_INVALID));
|
||||
expect_200(Method::GET, &get_space(INSTANCE_EXISTS));
|
||||
expect_404(Method::GET, &get_space(INSTANCE_INVALID));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delete() {
|
||||
expect_200(http::Method::DELETE, get_path(INSTANCE_EXISTS));
|
||||
expect_404(http::Method::DELETE, get_path(INSTANCE_INVALID));
|
||||
expect_200(Method::DELETE, &get_space(INSTANCE_EXISTS));
|
||||
expect_404(Method::DELETE, &get_space(INSTANCE_INVALID));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn post() {
|
||||
expect_400(http::Method::POST, get_path(INSTANCE_EXISTS));
|
||||
expect_400(http::Method::POST, get_path(INSTANCE_INVALID));
|
||||
expect_405(Method::POST, &get_space(INSTANCE_EXISTS));
|
||||
expect_405(Method::POST, &get_space(INSTANCE_INVALID));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
use actix_web::HttpRequest;
|
||||
use actix_web::Json;
|
||||
use std::sync::RwLock;
|
||||
|
||||
use actix_web::web;
|
||||
use actix_web::web::Data;
|
||||
use actix_web::web::Json;
|
||||
|
||||
use crate::shared_state::SharedState;
|
||||
|
||||
use super::error_400;
|
||||
use super::ok_200;
|
||||
use super::AppState;
|
||||
use super::Filters;
|
||||
use super::StringOrStaticFileResult;
|
||||
use super::HandlerResult;
|
||||
|
||||
pub fn post(
|
||||
(parameters, state): (Option<Json<Filters>>, HttpRequest<AppState>),
|
||||
) -> StringOrStaticFileResult {
|
||||
fn post((parameters, state): (Option<Json<Filters>>, Data<RwLock<SharedState>>)) -> HandlerResult {
|
||||
trace!("POST Triggered!");
|
||||
let context = state.state().shared.read().unwrap();
|
||||
let context = state.read().unwrap();
|
||||
let parameters = Filters::get(parameters);
|
||||
|
||||
let mut results = match parameters.filters {
|
||||
@@ -20,8 +22,9 @@ pub fn post(
|
||||
.db()
|
||||
.core_keys()
|
||||
.iter()
|
||||
// FIXME: Specify from json output space + threshold volume
|
||||
.flat_map(|core| match context.filter(&filter, core, None, None) {
|
||||
Err(_) => vec![], //FIXME: Return errors here instead!!
|
||||
Err(_) => vec![], // FIXME: Return errors here instead!!
|
||||
Ok(r) => {
|
||||
let mut r = r.into_iter().map(|o| o.space_id).collect::<Vec<_>>();
|
||||
r.sort_unstable();
|
||||
@@ -37,74 +40,76 @@ pub fn post(
|
||||
ok_200(&results)
|
||||
}
|
||||
|
||||
pub fn put(
|
||||
(_parameters, _state): (Option<Json<Filters>>, HttpRequest<AppState>),
|
||||
) -> StringOrStaticFileResult {
|
||||
fn put() -> HandlerResult {
|
||||
trace!("PUT Triggered!");
|
||||
error_400()
|
||||
}
|
||||
|
||||
pub fn patch(
|
||||
(_parameters, _state): (Option<Json<Filters>>, HttpRequest<AppState>),
|
||||
) -> StringOrStaticFileResult {
|
||||
fn patch() -> HandlerResult {
|
||||
trace!("PATCH Triggered!");
|
||||
error_400()
|
||||
}
|
||||
|
||||
pub fn delete(
|
||||
(_parameters, _state): (Option<Json<Filters>>, HttpRequest<AppState>),
|
||||
) -> StringOrStaticFileResult {
|
||||
fn delete() -> HandlerResult {
|
||||
trace!("DELETE Triggered!");
|
||||
error_400()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::tests::*;
|
||||
pub fn config(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(
|
||||
web::resource("/spaces")
|
||||
.route(web::post().to(post))
|
||||
.route(web::put().to(put))
|
||||
.route(web::patch().to(patch))
|
||||
.route(web::delete().to(delete)),
|
||||
);
|
||||
}
|
||||
|
||||
const COLLECTION: &str = "/spaces";
|
||||
#[cfg(test)]
|
||||
mod routing {
|
||||
use super::super::tests_utils::*;
|
||||
|
||||
// FIXME: Add Body to request to see difference between (in)valid bodied requests
|
||||
|
||||
#[test]
|
||||
fn post() {
|
||||
expect_200(http::Method::POST, get_path(COLLECTION));
|
||||
json::expect_200(http::Method::POST, get_path(COLLECTION), "".to_string());
|
||||
expect_200(Method::POST, &get_space(""));
|
||||
json::expect_200(Method::POST, &get_space(""), "".to_string());
|
||||
|
||||
json::expect_422(http::Method::POST, get_path(COLLECTION), "".to_string());
|
||||
json::expect_422(Method::POST, &get_space(""), "".to_string());
|
||||
|
||||
expect_400(http::Method::POST, get_path(COLLECTION));
|
||||
expect_400(Method::POST, &get_space(""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn put() {
|
||||
json::expect_200(http::Method::PUT, get_path(COLLECTION), "".to_string());
|
||||
json::expect_200(Method::PUT, &get_space(""), "".to_string());
|
||||
|
||||
json::expect_422(http::Method::PUT, get_path(COLLECTION), "".to_string());
|
||||
json::expect_422(Method::PUT, &get_space(""), "".to_string());
|
||||
|
||||
expect_400(http::Method::PUT, get_path(COLLECTION));
|
||||
expect_400(Method::PUT, &get_space(""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn patch() {
|
||||
json::expect_200(http::Method::PATCH, get_path(COLLECTION), "".to_string());
|
||||
json::expect_200(Method::PATCH, &get_space(""), "".to_string());
|
||||
|
||||
json::expect_422(http::Method::PATCH, get_path(COLLECTION), "".to_string());
|
||||
json::expect_422(Method::PATCH, &get_space(""), "".to_string());
|
||||
|
||||
expect_400(http::Method::PATCH, get_path(COLLECTION));
|
||||
expect_400(Method::PATCH, &get_space(""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delete() {
|
||||
json::expect_200(http::Method::DELETE, get_path(COLLECTION), "".to_string());
|
||||
json::expect_200(Method::DELETE, &get_space(""), "".to_string());
|
||||
|
||||
json::expect_422(http::Method::DELETE, get_path(COLLECTION), "".to_string());
|
||||
json::expect_422(Method::DELETE, &get_space(""), "".to_string());
|
||||
|
||||
expect_400(http::Method::DELETE, get_path(COLLECTION));
|
||||
expect_400(Method::DELETE, &get_space(""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get() {
|
||||
expect_400(http::Method::GET, get_path(COLLECTION));
|
||||
expect_405(Method::GET, &get_space(""));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,104 +1,104 @@
|
||||
use actix_web::HttpRequest;
|
||||
use actix_web::Json;
|
||||
use actix_web::Path;
|
||||
use std::sync::RwLock;
|
||||
|
||||
use actix_web::web;
|
||||
use actix_web::web::Data;
|
||||
use actix_web::web::Path;
|
||||
|
||||
use crate::model::to_spatial_objects;
|
||||
use crate::shared_state::SharedState;
|
||||
|
||||
use super::error_400;
|
||||
use super::error_404;
|
||||
use super::ok_200;
|
||||
use super::AppState;
|
||||
use super::StringOrStaticFileResult;
|
||||
use crate::model::to_spatial_objects;
|
||||
use super::HandlerResult;
|
||||
|
||||
pub fn put(
|
||||
(core, id, state): (Path<String>, Path<String>, HttpRequest<AppState>),
|
||||
) -> StringOrStaticFileResult {
|
||||
trace!("PUT Triggered!");
|
||||
fn put(path: Path<String>) -> HandlerResult {
|
||||
trace!("PUT Triggered on {}", path);
|
||||
error_400()
|
||||
}
|
||||
|
||||
pub fn get(
|
||||
(path, state): (Path<(String, String)>, HttpRequest<AppState>),
|
||||
) -> StringOrStaticFileResult {
|
||||
fn get((path, state): (Path<(String, String)>, Data<RwLock<SharedState>>)) -> HandlerResult {
|
||||
trace!("GET Triggered!");
|
||||
let (core, id) = path.into_inner();
|
||||
let core = core.to_string();
|
||||
let id = id.to_string();
|
||||
let context = state.state().shared.read().unwrap();
|
||||
let context = state.read().unwrap();
|
||||
let db = context.db();
|
||||
|
||||
match db.core(core) {
|
||||
Ok(core) => match core.get_by_id(db, &id, None, 0.0) {
|
||||
Ok(objects) => ok_200(&to_spatial_objects(db, objects)),
|
||||
Ok(objects) => {
|
||||
let results = to_spatial_objects(db, objects);
|
||||
if results.is_empty() {
|
||||
error_404()
|
||||
} else {
|
||||
ok_200(&results)
|
||||
}
|
||||
}
|
||||
Err(_) => error_404(),
|
||||
},
|
||||
Err(_) => error_404(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn patch(
|
||||
(core, id, state): (Path<String>, Path<String>, HttpRequest<AppState>),
|
||||
) -> StringOrStaticFileResult {
|
||||
trace!("PATCH Triggered!");
|
||||
fn patch(path: Path<String>) -> HandlerResult {
|
||||
trace!("PATCH Triggered on {}", path);
|
||||
error_400()
|
||||
}
|
||||
|
||||
pub fn delete(
|
||||
(core, id, state): (Path<String>, Path<String>, HttpRequest<AppState>),
|
||||
) -> StringOrStaticFileResult {
|
||||
trace!("DELETE Triggered!");
|
||||
fn delete(path: Path<String>) -> HandlerResult {
|
||||
trace!("DELETE Triggered on {}", path);
|
||||
error_400()
|
||||
}
|
||||
|
||||
pub fn config(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(
|
||||
web::resource("/cores/{name}/spatial_objects/{id}")
|
||||
.route(web::get().to(get))
|
||||
.route(web::put().to(put))
|
||||
.route(web::patch().to(patch))
|
||||
.route(web::delete().to(delete)),
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::tests::*;
|
||||
mod routing {
|
||||
use super::super::tests_utils::*;
|
||||
|
||||
const INSTANCE_EXISTS: &str = "/cores/42/spatial_objects/42";
|
||||
const INSTANCE_INVALID: &str = "/cores/42/spatial_objects/21";
|
||||
const INSTANCE_EXISTS: &str = SPATIAL_OBJECT;
|
||||
const INSTANCE_INVALID: &str = "/21-doesnotexists";
|
||||
|
||||
// FIXME: Add Body to request to see difference between (in)valid bodied requests
|
||||
|
||||
#[test]
|
||||
fn put() {
|
||||
json::expect_200(http::Method::PUT, get_path(INSTANCE_EXISTS), "".to_string());
|
||||
json::expect_422(http::Method::PUT, get_path(INSTANCE_EXISTS), "".to_string());
|
||||
json::expect_200(
|
||||
http::Method::PUT,
|
||||
get_path(INSTANCE_INVALID),
|
||||
"".to_string(),
|
||||
);
|
||||
json::expect_200(Method::PUT, &get_objects(INSTANCE_EXISTS), "".to_string());
|
||||
json::expect_422(Method::PUT, &get_objects(INSTANCE_EXISTS), "".to_string());
|
||||
json::expect_200(Method::PUT, &get_objects(INSTANCE_INVALID), "".to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn patch() {
|
||||
json::expect_200(
|
||||
http::Method::PATCH,
|
||||
get_path(INSTANCE_EXISTS),
|
||||
"".to_string(),
|
||||
);
|
||||
json::expect_422(
|
||||
http::Method::PATCH,
|
||||
get_path(INSTANCE_EXISTS),
|
||||
"".to_string(),
|
||||
);
|
||||
expect_400(http::Method::PATCH, get_path(INSTANCE_INVALID));
|
||||
json::expect_200(Method::PATCH, &get_objects(INSTANCE_EXISTS), "".to_string());
|
||||
json::expect_422(Method::PATCH, &get_objects(INSTANCE_EXISTS), "".to_string());
|
||||
expect_400(Method::PATCH, &get_objects(INSTANCE_INVALID));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get() {
|
||||
expect_200(http::Method::GET, get_path(INSTANCE_EXISTS));
|
||||
expect_404(http::Method::GET, get_path(INSTANCE_INVALID));
|
||||
expect_200(Method::GET, &get_objects(INSTANCE_EXISTS));
|
||||
expect_404(Method::GET, &get_objects(INSTANCE_INVALID));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delete() {
|
||||
expect_200(http::Method::DELETE, get_path(INSTANCE_EXISTS));
|
||||
expect_404(http::Method::DELETE, get_path(INSTANCE_INVALID));
|
||||
expect_200(Method::DELETE, &get_objects(INSTANCE_EXISTS));
|
||||
expect_404(Method::DELETE, &get_objects(INSTANCE_INVALID));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn post() {
|
||||
expect_400(http::Method::POST, get_path(INSTANCE_EXISTS));
|
||||
expect_400(http::Method::POST, get_path(INSTANCE_INVALID));
|
||||
expect_405(Method::POST, &get_objects(INSTANCE_EXISTS));
|
||||
expect_405(Method::POST, &get_objects(INSTANCE_INVALID));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,28 @@
|
||||
use actix_web::HttpRequest;
|
||||
use actix_web::Json;
|
||||
use actix_web::Path;
|
||||
use std::sync::RwLock;
|
||||
|
||||
use actix_web::web;
|
||||
use actix_web::web::Data;
|
||||
use actix_web::web::Json;
|
||||
use actix_web::web::Path;
|
||||
|
||||
use crate::shared_state::SharedState;
|
||||
|
||||
use super::error_400;
|
||||
use super::error_404;
|
||||
use super::ok_200;
|
||||
use super::AppState;
|
||||
use super::Filters;
|
||||
use super::StringOrStaticFileResult;
|
||||
use super::HandlerResult;
|
||||
|
||||
pub fn post(
|
||||
(core_id, parameters, state): (Path<String>, Option<Json<Filters>>, HttpRequest<AppState>),
|
||||
) -> StringOrStaticFileResult {
|
||||
fn post(
|
||||
(core_id, parameters, state): (
|
||||
Path<String>,
|
||||
Option<Json<Filters>>,
|
||||
Data<RwLock<SharedState>>,
|
||||
),
|
||||
) -> HandlerResult {
|
||||
trace!("POST Triggered!");
|
||||
let core = core_id.to_string();
|
||||
let context = state.state().shared.read().unwrap();
|
||||
let context = state.read().unwrap();
|
||||
|
||||
match context.db().core(core) {
|
||||
Ok(core) => {
|
||||
@@ -23,8 +31,9 @@ pub fn post(
|
||||
// Generate a list of oid.
|
||||
let mut results = match parameters.filters {
|
||||
None => core.keys().iter().map(|o| o.id().clone()).collect(),
|
||||
// FIXME: Specify from json output space + threshold volume
|
||||
Some(filter) => match context.filter(&filter, &core_id, None, None) {
|
||||
Err(_) => vec![], //FIXME: Return errors here instead!!
|
||||
Err(_) => vec![], // FIXME: Return errors here instead!!
|
||||
Ok(objects) => objects.iter().map(|o| o.value.id().clone()).collect(),
|
||||
},
|
||||
};
|
||||
@@ -37,68 +46,76 @@ pub fn post(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn put((_path, _state): (Path<String>, HttpRequest<AppState>)) -> StringOrStaticFileResult {
|
||||
fn put() -> HandlerResult {
|
||||
trace!("PUT Triggered!");
|
||||
error_400()
|
||||
}
|
||||
|
||||
pub fn patch((_path, _state): (Path<String>, HttpRequest<AppState>)) -> StringOrStaticFileResult {
|
||||
fn patch() -> HandlerResult {
|
||||
trace!("PATCH Triggered!");
|
||||
error_400()
|
||||
}
|
||||
|
||||
pub fn delete((_path, _state): (Path<String>, HttpRequest<AppState>)) -> StringOrStaticFileResult {
|
||||
fn delete() -> HandlerResult {
|
||||
trace!("DELETE Triggered!");
|
||||
error_400()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::tests::*;
|
||||
pub fn config(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(
|
||||
web::resource("/cores/{name}/spatial_objects")
|
||||
.route(web::post().to(post))
|
||||
.route(web::put().to(put))
|
||||
.route(web::patch().to(patch))
|
||||
.route(web::delete().to(delete)),
|
||||
);
|
||||
}
|
||||
|
||||
const COLLECTION: &str = "/cores/42/spatial_objects";
|
||||
#[cfg(test)]
|
||||
mod routing {
|
||||
use super::super::tests_utils::*;
|
||||
|
||||
// FIXME: Add Body to request to see difference between (in)valid bodied requests
|
||||
|
||||
#[test]
|
||||
fn post() {
|
||||
expect_200(http::Method::POST, get_path(COLLECTION));
|
||||
json::expect_200(http::Method::POST, get_path(COLLECTION), "".to_string());
|
||||
expect_200(Method::POST, &get_objects(""));
|
||||
json::expect_200(Method::POST, &get_objects(""), "".to_string());
|
||||
|
||||
json::expect_422(http::Method::POST, get_path(COLLECTION), "".to_string());
|
||||
json::expect_422(Method::POST, &get_objects(""), "".to_string());
|
||||
|
||||
expect_400(http::Method::POST, get_path(COLLECTION));
|
||||
expect_400(Method::POST, &get_objects(""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn put() {
|
||||
json::expect_200(http::Method::PUT, get_path(COLLECTION), "".to_string());
|
||||
json::expect_200(Method::PUT, &get_objects(""), "".to_string());
|
||||
|
||||
json::expect_422(http::Method::PUT, get_path(COLLECTION), "".to_string());
|
||||
json::expect_422(Method::PUT, &get_objects(""), "".to_string());
|
||||
|
||||
expect_400(http::Method::PUT, get_path(COLLECTION));
|
||||
expect_400(Method::PUT, &get_objects(""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn patch() {
|
||||
json::expect_200(http::Method::PATCH, get_path(COLLECTION), "".to_string());
|
||||
json::expect_200(Method::PATCH, &get_objects(""), "".to_string());
|
||||
|
||||
json::expect_422(http::Method::PATCH, get_path(COLLECTION), "".to_string());
|
||||
json::expect_422(Method::PATCH, &get_objects(""), "".to_string());
|
||||
|
||||
expect_400(http::Method::PATCH, get_path(COLLECTION));
|
||||
expect_400(Method::PATCH, &get_objects(""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delete() {
|
||||
json::expect_200(http::Method::DELETE, get_path(COLLECTION), "".to_string());
|
||||
json::expect_200(Method::DELETE, &get_objects(""), "".to_string());
|
||||
|
||||
json::expect_422(http::Method::DELETE, get_path(COLLECTION), "".to_string());
|
||||
json::expect_422(Method::DELETE, &get_objects(""), "".to_string());
|
||||
|
||||
expect_400(http::Method::DELETE, get_path(COLLECTION));
|
||||
expect_400(Method::DELETE, &get_objects(""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get() {
|
||||
expect_400(http::Method::GET, get_path(COLLECTION));
|
||||
expect_405(Method::GET, &get_objects(""));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,13 +34,13 @@ paths:
|
||||
summary: >
|
||||
Health check of the service.
|
||||
description: >
|
||||
Please note that making anything but a **GET** call is a bad request.
|
||||
Please note that making anything but a **GET** call is a bad request, and will return a 405.
|
||||
operationId: get_health_check
|
||||
responses:
|
||||
'200':
|
||||
$ref: '#/responses/Standard200'
|
||||
default:
|
||||
$ref: '#/responses/Standard400'
|
||||
$ref: '#/responses/Standard405'
|
||||
|
||||
/queries:
|
||||
post:
|
||||
@@ -58,7 +58,7 @@ paths:
|
||||
'422':
|
||||
$ref: '#/responses/Standard422'
|
||||
default:
|
||||
$ref: '#/responses/Standard400'
|
||||
$ref: '#/responses/Standard405'
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# SPACES QUERIES
|
||||
@@ -748,6 +748,9 @@ responses:
|
||||
Standard422:
|
||||
description: >
|
||||
Unprocessable Entity
|
||||
Standard405:
|
||||
description: >
|
||||
Invalid Method
|
||||
Standard404:
|
||||
description: >
|
||||
Object not found
|
||||
8
static/errors/405.html
Normal file
8
static/errors/405.html
Normal file
@@ -0,0 +1,8 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>405 - Method not allowed</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>405 - Method not allowed</h1>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user