mirror of
https://github.com/mimblewimble/grin.git
synced 2025-02-01 17:01:09 +03:00
Custom REST API operations in addition to CRUD
Allows for custom operations associated with POST requests under the main resource path. For example, in addition to POST on /user to create a user, allow easy support for /user/login and /user/logout.
This commit is contained in:
parent
d8deba15ee
commit
a402f39633
4 changed files with 52 additions and 28 deletions
|
@ -11,5 +11,4 @@ iron = "~0.5.1"
|
||||||
log = "~0.3"
|
log = "~0.3"
|
||||||
router = "~0.5.1"
|
router = "~0.5.1"
|
||||||
serde = "~0.9.10"
|
serde = "~0.9.10"
|
||||||
serde_derive = "~0.9.10"
|
|
||||||
serde_json = "~0.9.8"
|
serde_json = "~0.9.8"
|
||||||
|
|
|
@ -21,12 +21,9 @@
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
use std::net::ToSocketAddrs;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
use iron::method::Method;
|
|
||||||
|
|
||||||
use chain::{self, Tip};
|
use chain::{self, Tip};
|
||||||
use rest::*;
|
use rest::*;
|
||||||
|
|
||||||
|
@ -42,8 +39,8 @@ impl ApiEndpoint for ChainApi {
|
||||||
type ID = String;
|
type ID = String;
|
||||||
type T = Tip;
|
type T = Tip;
|
||||||
|
|
||||||
fn methods(&self) -> Vec<Method> {
|
fn operations(&self) -> Vec<Operation> {
|
||||||
vec![Method::Get]
|
vec![Operation::Get]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get(&self, id: String) -> ApiResult<Tip> {
|
fn get(&self, id: String) -> ApiResult<Tip> {
|
||||||
|
|
|
@ -19,8 +19,6 @@ extern crate log;
|
||||||
extern crate iron;
|
extern crate iron;
|
||||||
extern crate router;
|
extern crate router;
|
||||||
extern crate serde;
|
extern crate serde;
|
||||||
#[macro_use]
|
|
||||||
extern crate serde_derive;
|
|
||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
|
|
||||||
mod endpoints;
|
mod endpoints;
|
||||||
|
|
|
@ -22,7 +22,6 @@ use std::error::Error;
|
||||||
use std::fmt::{self, Display, Debug, Formatter};
|
use std::fmt::{self, Display, Debug, Formatter};
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::net::ToSocketAddrs;
|
use std::net::ToSocketAddrs;
|
||||||
use std::ops::Index;
|
|
||||||
use std::string::ToString;
|
use std::string::ToString;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
@ -68,6 +67,28 @@ impl From<ApiError> for IronError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub enum Operation {
|
||||||
|
Create,
|
||||||
|
Delete,
|
||||||
|
Update,
|
||||||
|
Get,
|
||||||
|
Custom(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Operation {
|
||||||
|
fn to_method(&self) -> Method {
|
||||||
|
match *self {
|
||||||
|
Operation::Create => Method::Post,
|
||||||
|
Operation::Delete => Method::Delete,
|
||||||
|
Operation::Update => Method::Put,
|
||||||
|
Operation::Get => Method::Get,
|
||||||
|
Operation::Custom(_) => Method::Post,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub type ApiResult<T> = ::std::result::Result<T, ApiError>;
|
pub type ApiResult<T> = ::std::result::Result<T, ApiError>;
|
||||||
|
|
||||||
/// Trait to implement to expose a service as a RESTful HTTP endpoint. Each
|
/// Trait to implement to expose a service as a RESTful HTTP endpoint. Each
|
||||||
|
@ -90,7 +111,7 @@ pub trait ApiEndpoint: Clone + Send + Sync + 'static {
|
||||||
type ID: ToString + FromStr;
|
type ID: ToString + FromStr;
|
||||||
type T: Serialize + Deserialize;
|
type T: Serialize + Deserialize;
|
||||||
|
|
||||||
fn methods(&self) -> Vec<Method>;
|
fn operations(&self) -> Vec<Operation>;
|
||||||
|
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn create(&self, o: Self::T) -> ApiResult<Self::ID> {
|
fn create(&self, o: Self::T) -> ApiResult<Self::ID> {
|
||||||
|
@ -111,6 +132,14 @@ pub trait ApiEndpoint: Clone + Send + Sync + 'static {
|
||||||
fn get(&self, id: Self::ID) -> ApiResult<Self::T> {
|
fn get(&self, id: Self::ID) -> ApiResult<Self::T> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
fn operation<IN, OUT>(&self, op: String, input: IN) -> ApiResult<OUT>
|
||||||
|
where IN: Serialize + Deserialize,
|
||||||
|
OUT: Serialize + Deserialize
|
||||||
|
{
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrapper required to define the implementation below, Rust doesn't let us
|
// Wrapper required to define the implementation below, Rust doesn't let us
|
||||||
|
@ -194,28 +223,29 @@ impl ApiServer {
|
||||||
// declare a route for each method actually implemented by the endpoint
|
// declare a route for each method actually implemented by the endpoint
|
||||||
let route_postfix = &subpath[1..];
|
let route_postfix = &subpath[1..];
|
||||||
let root = self.root.clone() + &subpath;
|
let root = self.root.clone() + &subpath;
|
||||||
for m in endpoint.methods() {
|
for op in endpoint.operations() {
|
||||||
let full_path = match m {
|
let full_path = match op.clone() {
|
||||||
Method::Get => root.clone() + "/:id",
|
Operation::Get => root.clone() + "/:id",
|
||||||
Method::Put => root.clone() + "/:id",
|
Operation::Update => root.clone() + "/:id",
|
||||||
Method::Delete => root.clone() + "/:id",
|
Operation::Delete => root.clone() + "/:id",
|
||||||
Method::Post => root.clone(),
|
Operation::Create => root.clone(),
|
||||||
_ => panic!(format!("Unsupported method: {}.", m)),
|
Operation::Custom(op_s) => format!("{}/:{}", root.clone(), op_s),
|
||||||
};
|
};
|
||||||
self.router.route(m.clone(),
|
self.router.route(op.to_method(),
|
||||||
full_path,
|
full_path,
|
||||||
ApiWrapper(endpoint.clone()),
|
ApiWrapper(endpoint.clone()),
|
||||||
m.to_string() + "_" + route_postfix);
|
format!("{:?}_{}", op, route_postfix));
|
||||||
}
|
}
|
||||||
|
|
||||||
// support for the HTTP Options method by differentiating what's on the
|
// support for the HTTP Options method by differentiating what's on the
|
||||||
// root resource vs the id resource
|
// root resource vs the id resource
|
||||||
let (root_opts, sub_opts) = endpoint.methods().iter().fold((vec![], vec![]),
|
let (root_opts, sub_opts) =
|
||||||
|mut acc, m| {
|
endpoint.operations().iter().fold((vec![], vec![]), |mut acc, op| {
|
||||||
if *m == Method::Post {
|
let m = op.to_method();
|
||||||
acc.0.push(m.clone());
|
if m == Method::Post {
|
||||||
|
acc.0.push(m);
|
||||||
} else {
|
} else {
|
||||||
acc.1.push(m.clone());
|
acc.1.push(m);
|
||||||
}
|
}
|
||||||
acc
|
acc
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue