1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
use valico::json_dsl;
use valico::json_schema;

use server::{method, mime};
use backend;
use errors;
use json::{JsonValue};
use batteries::schemes;
use framework;
use framework::path;

pub type EndpointHandler = Box<for<'a> Fn(framework::Client<'a>, &JsonValue) -> backend::HandleResult<framework::Client<'a>> + 'static + Sync>;

#[allow(missing_copy_implementations)]
pub enum EndpointHandlerPresent {
    HandlerPresent
}

pub type EndpointBuilder = FnOnce(&mut Endpoint) -> EndpointHandlerPresent + 'static;

pub struct Endpoint {
    pub method: method::Method,
    pub path: path::Path,
    pub summary: Option<String>,
    pub desc: Option<String>,
    pub coercer: Option<json_dsl::Builder>,
    pub consumes: Option<Vec<mime::Mime>>,
    pub produces: Option<Vec<mime::Mime>>,
    handler: Option<EndpointHandler>,
}

unsafe impl Send for Endpoint {}

impl Endpoint {

    pub fn new(method: method::Method, path: &str) -> Endpoint {
        Endpoint {
            method: method,
            path: path::Path::parse(path, true).unwrap(),
            summary: None,
            desc: None,
            coercer: None,
            consumes: None,
            produces: None,
            handler: None,
        }
    }

    pub fn build<F>(method: method::Method, path: &str, builder: F) -> Endpoint
    where F: FnOnce(&mut Endpoint) -> EndpointHandlerPresent {
        let mut endpoint = Endpoint::new(method, path);
        builder(&mut endpoint);

        endpoint
    }

    pub fn summary(&mut self, summary: &str) {
        self.summary = Some(summary.to_string());
    }

    pub fn desc(&mut self, desc: &str) {
        self.desc = Some(desc.to_string());
    }

    pub fn consumes(&mut self, mimes: Vec<mime::Mime>) {
        self.consumes = Some(mimes);
    }

    pub fn produces(&mut self, mimes: Vec<mime::Mime>) {
        self.produces = Some(mimes);
    }

    pub fn params<F>(&mut self, builder: F) where F: FnOnce(&mut json_dsl::Builder) + 'static {
        self.coercer = Some(json_dsl::Builder::build(builder));
    }

    pub fn handle<F: 'static>(&mut self, handler: F) -> EndpointHandlerPresent
    where F: for<'a> Fn(framework::Client<'a>, &JsonValue) -> backend::HandleResult<framework::Client<'a>> + Sync+Send {
        self.handler = Some(Box::new(handler));
        EndpointHandlerPresent::HandlerPresent
    }

    pub fn handle_boxed(&mut self, handler: EndpointHandler) -> EndpointHandlerPresent {
        self.handler = Some(handler);
        EndpointHandlerPresent::HandlerPresent
    }

    fn validate(&self, params: &mut JsonValue, scope: Option<&json_schema::Scope>) -> backend::HandleResult<()> {
        // Validate namespace params with valico
        if self.coercer.is_some() {
            // validate and coerce params
            let coercer = self.coercer.as_ref().unwrap();
            let state = coercer.process(params, &scope);
            if state.is_strictly_valid() {
                Ok(())
            } else {
                if state.missing.len() > 0 {
                    warn!("There are some missing JSON schemes: {:?}", state.missing);
                }
                Err(error_response!(errors::Validation{ reason: state.errors }))
            }
        } else {
            Ok(())
        }
    }

    pub fn call_endpoint<'a>(&self,
        params: &mut JsonValue,
        req: &'a mut (backend::Request + 'a),
        info: &mut framework::CallInfo) -> backend::HandleResult<backend::Response> {

        let mut client = framework::Client::new(info.app, self, req, &info.media);

        for parent in info.parents.iter() {
            try!(Endpoint::call_callbacks(parent.get_before(), &mut client, params));
        }

        for parent in info.parents.iter() {
            try!(Endpoint::call_callbacks(parent.get_before_validation(), &mut client, params));
        }

        try!(self.validate(params, info.app.ext.get::<schemes::SchemesScope>()));

        for parent in info.parents.iter() {
            try!(Endpoint::call_callbacks(parent.get_after_validation(), &mut client, params));
        }

        let handler = self.handler.as_ref();
        let mut client = try!((handler.unwrap())(client, params));

        for parent in info.parents.iter() {
            try!(Endpoint::call_callbacks(parent.get_after(), &mut client, params));
        }

        Ok(client.move_response())
    }

    fn call_callbacks(
        cbs: &Vec<framework::Callback>,
        client: &mut framework::Client,
        params: &mut JsonValue) -> backend::HandleSuccessResult {

        for cb in cbs.iter() {
            try!(cb(client, params));
        }

        Ok(())
    }

}

impl framework::ApiHandler for Endpoint {
    fn api_call<'r>(&self,
        rest_path: &str,
        params: &mut JsonValue,
        req: &'r mut (backend::Request + 'r),
        info: &mut framework::CallInfo) -> backend::HandleResult<backend::Response> {

        // method::Method guard
        if req.method() != &self.method {
            return Err(error_response!(errors::NotMatch))
        }

        match self.path.is_match(rest_path) {
            Some(captures) =>  {
                self.path.apply_captures(params, captures);
                self.call_endpoint(params, req, info)
            },
            None => Err(error_response!(errors::NotMatch))
        }

    }
}