Skip to content

Commit

Permalink
feat(delegate): begin()/finished() calls
Browse files Browse the repository at this point in the history
During `begin()`, the delegate receives additional information about the
current call, which can be useful for performance tracking, among
other things.

Fixes #25
  • Loading branch information
Byron committed Mar 19, 2015
1 parent a05426e commit 508d14e
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 16 deletions.
41 changes: 27 additions & 14 deletions src/mako/lib/mbuild.mako
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
hub_type_params_s, method_media_params, enclose_in, mb_type_bounds, method_response,
METHOD_BUILDER_MARKERT_TRAIT, pass_through, markdown_rust_block, parts_from_params,
DELEGATE_PROPERTY_NAME, struct_type_bounds_s, supports_scopes, scope_url_to_variant,
re_find_replacements, ADD_PARAM_FN, ADD_PARAM_MEDIA_EXAMPLE, upload_action_fn, METHODS_RESOURCE)
re_find_replacements, ADD_PARAM_FN, ADD_PARAM_MEDIA_EXAMPLE, upload_action_fn, METHODS_RESOURCE,
method_name_to_variant)
def get_parts(part_prop):
if not part_prop:
Expand Down Expand Up @@ -419,6 +420,7 @@ match result {
paddfields = 'self.' + api.properties.params
delegate = 'self.' + property(DELEGATE_PROPERTY_NAME)
delegate_finish = 'if let Some(ref mut d) = ' + delegate + ' { d.finished(); }'
auth_call = 'self.hub.auth.borrow_mut()'
if supports_scopes(auth):
Expand Down Expand Up @@ -479,6 +481,10 @@ match result {
use hyper::client::IntoBody;
use std::io::{Read, Seek};
use hyper::header::{ContentType, ContentLength, Authorization, UserAgent};
if let Some(ref mut d) = ${delegate} {
d.begin(cmn::MethodInfo { id: "${m.id}",
http_method: ${method_name_to_variant(m.httpMethod)} });
}
let mut params: Vec<(&str, String)> = Vec::with_capacity((${len(params) + len(reserved_params)} + ${paddfields}.len()));
% for p in field_params:
<%
Expand Down Expand Up @@ -515,6 +521,7 @@ match result {
## Additional params - may not overlap with optional params
for &field in [${', '.join(enclose_in('"', reserved_params + [p.name for p in field_params]))}].iter() {
if ${paddfields}.contains_key(field) {
${delegate_finish}
return cmn::Result::FieldClash(field);
}
}
Expand Down Expand Up @@ -570,12 +577,15 @@ else {
assert 'key' in parameters, "Expected 'key' parameter if there are no scopes"
%>
let mut key = ${auth_call}.api_key();
if key.is_none() { if let Some(ref mut dlg) = ${delegate} {
key = dlg.api_key();
if key.is_none() { if let Some(ref mut d) = ${delegate} {
key = d.api_key();
}}
match key {
Some(value) => params.push(("key", value)),
None => return cmn::Result::MissingAPIKey,
None => {
${delegate_finish}
return cmn::Result::MissingAPIKey
}
}
% else:
if self.${api.properties.scopes}.len() == 0 {
Expand Down Expand Up @@ -643,10 +653,11 @@ else {
loop {
% if supports_scopes(auth):
let mut token = ${auth_call}.token(self.${api.properties.scopes}.keys());
if token.is_none() { if let Some(ref mut dlg) = ${delegate} {
token = dlg.token();
if token.is_none() { if let Some(ref mut d) = ${delegate} {
token = d.token();
}}
if token.is_none() {
${delegate_finish}
return cmn::Result::MissingToken
}
let auth_header = Authorization(token.unwrap().access_token);
Expand All @@ -669,7 +680,7 @@ else {
};
% endif
let mut req = client.borrow_mut().request(hyper::method::Method::Extension("${m.httpMethod}".to_string()), url.as_slice())
let mut req = client.borrow_mut().request(${method_name_to_variant(m.httpMethod)}, url.as_slice())
.header(UserAgent(self.hub._user_agent.clone()))\
% if supports_scopes(auth):
Expand Down Expand Up @@ -697,32 +708,33 @@ else {
}
% endif ## media upload handling
match ${delegate} {
Some(ref mut d) => d.pre_request("${m.id}"),
None => {}
if let Some(ref mut d) = ${delegate} {
d.pre_request();
}
match req.send() {
Err(err) => {
if let Some(ref mut dlg) = ${delegate} {
if let oauth2::Retry::After(d) = dlg.http_error(&err) {
if let Some(ref mut d) = ${delegate} {
if let oauth2::Retry::After(d) = d.http_error(&err) {
sleep(d);
continue;
}
}
${delegate_finish}
return cmn::Result::HttpError(err)
}
Ok(mut res) => {
if !res.status.is_success() {
if let Some(ref mut dlg) = ${delegate} {
if let Some(ref mut d) = ${delegate} {
let mut json_err = String::new();
res.read_to_string(&mut json_err).unwrap();
let error_info: cmn::JsonServerError = json::decode(&json_err).unwrap();
if let oauth2::Retry::After(d) = dlg.http_failure(&res, error_info) {
if let oauth2::Retry::After(d) = d.http_failure(&res, error_info) {
sleep(d);
continue;
}
}
${delegate_finish}
return cmn::Result::Failure(res)
}
% if response_schema:
Expand All @@ -743,6 +755,7 @@ if enable_resource_parsing \
% else:
let result_value = res;
% endif
${delegate_finish}
return cmn::Result::Success(result_value)
}
}
Expand Down
8 changes: 7 additions & 1 deletion src/mako/lib/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

re_find_replacements = re.compile("\{[/\+]?\w+\*?\}")


HTTP_METHODS = set(("OPTIONS", "GET", "POST", "PUT", "DELETE", "HEAD", "TRACE", "CONNECT", "PATCH" ))

USE_FORMAT = 'use_format_field'
TYPE_MAP = {'boolean' : 'bool',
Expand Down Expand Up @@ -879,6 +879,12 @@ def scope_url_to_variant(name, url, fully_qualified=True):
return fqvn(FULL)
return fqvn(dot_sep_to_canonical_type_name(repl(base)))

def method_name_to_variant(name):
fmt = 'hyper::method::Method::Extension("%s")'
if name in HTTP_METHODS:
name = name.capitalize()
fmt = 'hyper::method::Method::%s'
return fmt % name.capitalize()

# given a rust type-name (no optional, as from to_rust_type), you will get a suitable random default value
# as string suitable to be passed as reference (or copy, where applicable)
Expand Down
24 changes: 23 additions & 1 deletion src/rust/cmn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use oauth2;
use hyper;
use hyper::header::{ContentType, ContentLength, Headers};
use hyper::http::LINE_ENDING;
use hyper::method::Method;

/// Identifies the Hub. There is only one per library, this trait is supposed
/// to make intended use more explicit.
Expand Down Expand Up @@ -56,6 +57,14 @@ pub struct JsonServerError {
/// uploading media
pub trait Delegate {

/// Called at the beginning of any API request. The delegate should store the method
/// information if he is interesting in knowing more context when further calls to it
/// are made.
/// The matching `finished()` call will always be made, no matter whether or not the API
/// request was sucessfull. That way, the delgate may easily maintain a clean state
/// between various API calls.
fn begin(&mut self, MethodInfo) {}

/// Called whenever there is an [HttpError](http://hyperium.github.io/hyper/hyper/error/enum.HttpError.html), usually if there are network problems.
///
/// Return retry information.
Expand Down Expand Up @@ -88,7 +97,14 @@ pub trait Delegate {

/// Called prior to sending the main request of the given method. It can be used to time
/// the call or to print progress information.
fn pre_request(&mut self, method_name: &str) { let _ = method_name; }
/// It's also useful as you can be sure that a request will definitely be made.
fn pre_request(&mut self) { }


/// Called before the API request method returns, in every case. It can be used to clean up
/// internal state between calls to the API.
/// This call always has a matching call to `begin(...)`.
fn finished(&mut self) {}
}

#[derive(Default)]
Expand Down Expand Up @@ -119,6 +135,12 @@ pub enum Result<T = ()> {
Success(T),
}

/// Contains information about an API request.
pub struct MethodInfo {
pub id: &'static str,
pub http_method: Method,
}

const BOUNDARY: &'static str = "MDuXWGyeE33QFXGchb2VFWc4Z7945d";

/// Provides a `Read` interface that converts multiple parts into the protocol
Expand Down

0 comments on commit 508d14e

Please sign in to comment.