feat(delegate): begin()/finished() calls

During `begin()`, the delegate receives additional information about the
current call, which can be useful for performance tracking, among
other things.

Fixes #25
This commit is contained in:
Sebastian Thiel
2015-03-19 15:29:50 +01:00
parent a05426e79b
commit 508d14eafb
3 changed files with 57 additions and 16 deletions

View File

@@ -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:
@@ -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):
@@ -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:
<%
@@ -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);
}
}
@@ -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 {
@@ -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);
@@ -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):
@@ -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:
@@ -743,6 +755,7 @@ if enable_resource_parsing \
% else:
let result_value = res;
% endif
${delegate_finish}
return cmn::Result::Success(result_value)
}
}

View File

@@ -12,7 +12,7 @@ re_desc_parts = re.compile("((the part (names|properties) that you can include i
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',
@@ -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)

View File

@@ -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.
@@ -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.
@@ -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)]
@@ -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