feat(downloads): alt 'media' handling to allow dls

This also includes documentation to state which methods actually support
media download, and how to achieve that.

Added TODO to not forget we should tell the user how to achieve these
kinds of things.

Fixes #21
This commit is contained in:
Sebastian Thiel
2015-03-19 09:31:11 +01:00
parent 4a27ac7e1d
commit 02d7a06fdf
30 changed files with 838 additions and 655 deletions

View File

@@ -11,8 +11,8 @@
doc_root = directories.output + '/doc'
doc_index = doc_root + '/index.html'
to_doc_root = lambda gen_root, api_name: gen_root + '/target/doc/' + api_name
central_api_index = lambda api_name: doc_root + '/' + api_name + '/index.html'
to_doc_root = lambda gen_root, crate_name: gen_root + '/target/doc/' + crate_name
central_api_index = lambda crate_name: doc_root + '/' + crate_name + '/index.html'
discovery_url = 'https://www.googleapis.com/discovery/v1/'
apis = json.loads(urllib2.urlopen(discovery_url + "apis").read())
@@ -27,6 +27,7 @@
import util
import os
api_name = util.library_name(an, version)
crate_name = util.library_to_crate_name(api_name)
gen_root = directories.output + '/' + api_name
gen_root_stamp = gen_root + '/.timestamp'
api_common = gen_root + '/src/cmn.rs'
@@ -34,7 +35,7 @@
api_cargo = api_name + '-cargo'
api_doc = api_name + '-doc'
api_doc_root = to_doc_root(gen_root, api_name)
api_doc_root = to_doc_root(gen_root, crate_name)
api_doc_index = api_doc_root + '/index.html'
# source, destination of individual output files
@@ -70,9 +71,9 @@ ${api_doc_index}: ${api_name}
${api_doc}: ${api_doc_index}
${central_api_index(api_name)}: ${api_doc_index}
${central_api_index(crate_name)}: ${api_doc_index}
@mkdir -p ${doc_root}
cp -Rf ${os.path.dirname(to_doc_root(gen_root, api_name))}/* ${doc_root}
cp -Rf ${os.path.dirname(to_doc_root(gen_root, crate_name))}/* ${doc_root}
${api_clean}:
-rm -Rf ${gen_root}
@@ -83,7 +84,7 @@ clean-apis: ${space_join(1)} docs-clean
cargo: ${space_join(2)}
apis: ${space_join(0)}
${doc_index}: ${' '.join(central_api_index(a[0]) for a in api_info)} $(MAKO_STANDARD_DEPENDENCIES)
${doc_index}: ${' '.join(central_api_index(util.library_to_crate_name(a[0])) for a in api_info)} $(MAKO_STANDARD_DEPENDENCIES)
$(MAKO) --var DOC_ROOT=${doc_root} -io $(MAKO_SRC)/index.html.mako=$@ --data-files $(API_SHARED_INFO) $(API_LIST)
@echo Documentation index created at '$@'

View File

@@ -132,6 +132,10 @@ ${link('Hub Delegate', delegate_url)}, or the ${link('Authenticator Delegate', u
When delegates handle errors or intermediate values, they may have a chance to instruct the system to retry. This
makes the system potentially resilient to all kinds of errors.
${'##'} About Uploads and Downlods
TODO: 'alt' media for downloads, custom methods for uploads (simple, resumable)
${'##'} About Customization/Callbacks
You may alter the way an `${api.terms.action}()` method is called by providing a ${link('delegate', delegate_url)} to the

View File

@@ -35,6 +35,8 @@
fn_name = 'add_' + fn_name
return fn_name
add_param_fn = 'param'
%>\
<%namespace name="util" file="util.mako"/>\
<%namespace name="lib" file="lib.mako"/>\
@@ -46,6 +48,8 @@
<%
hub_type_name = hub_type(schemas,util.canonical_name())
m = c.fqan_map[to_fqan(c.rtc_map[resource], resource, method)]
response_schema = method_response(c, m)
# an identifier for a property. We prefix them to prevent clashes with the setters
mb_tparams = mb_type_params_s(m)
ThisType = mb_type(resource, method) + mb_tparams
@@ -60,6 +64,15 @@
${m.description | rust_doc_comment}
///
% endif
% if m.get('supportsMediaDownload', False):
/// This method supports **media download**. To enable it, set the *alt* parameter to *media*, .i.e.
/// `.${add_param_fn}("alt", "media")`.
% if response_schema:
/// Please note that due to missing multi-part support on the server side, you will only receive the media,
/// but not the `${response_schema.id}` structure that you would usually get. The latter will be a default value.
% endif
///
% endif ## supports media download
/// A builder for the *${method}* method supported by a *${singular(resource)}* resource.
/// It is not used directly, but through a `${rb_type(resource)}`.
///
@@ -135,7 +148,7 @@ ${self._setter_fn(resource, method, m, p, part_prop, ThisType, c)}\
/// * *${opn}* (${op.location}-${op.type}) - ${op.description}
% endfor
% endif
pub fn param<T>(mut self, name: T, value: T) -> ${ThisType}
pub fn ${add_param_fn}<T>(mut self, name: T, value: T) -> ${ThisType}
where T: Str {
self.${api.properties.params}.insert(name.as_slice().to_string(), value.as_slice().to_string());
self
@@ -370,10 +383,12 @@ match result {
rtype = 'cmn::Result<hyper::client::Response>'
response_schema = method_response(c, m)
supports_download = m.get('supportsMediaDownload', False);
reserved_params = []
if response_schema:
if not supports_download:
reserved_params = ['alt']
rtype = 'cmn::Result<(hyper::client::Response, %s)>' % (response_schema.id)
reserved_params = ['alt']
mtype_param = 'RS'
mtype_where = 'ReadSeek'
@@ -462,12 +477,8 @@ match result {
## "the trait `core::marker::Sized` is not implemented for the type `std::io::Read`"
use hyper::client::IntoBody;
use std::io::{Read, Seek};
use hyper::header::{ContentType, ContentLength};
let mut params = Vec::new();
params.reserve_exact((${len(params) + len(reserved_params)} + ${paddfields}.len()));
% if response_schema:
params.push(("alt", "json".to_string()));
% endif
use hyper::header::{ContentType, ContentLength, Authorization, UserAgent};
let mut params: Vec<(&str, String)> = Vec::with_capacity((${len(params) + len(reserved_params)} + ${paddfields}.len()));
% for p in field_params:
<%
pname = 'self.' + property(p.name) # property identifier
@@ -510,6 +521,30 @@ match result {
params.push((&name, value.clone()));
}
% if response_schema:
% if supports_download:
let (json_field_missing, enable_resource_parsing) = {
let mut enable = true;
let mut field_present = true;
for &(name, ref value) in params.iter() {
if name == "alt" {
field_present = false;
if value.as_slice() != "json" {
enable = false;
}
break;
}
}
(field_present, enable)
};
if json_field_missing {
params.push(("alt", "json".to_string()));
}
% else:
params.push(("alt", "json".to_string()));
% endif ## supportsMediaDownload
% endif ## response schema
% if media_params:
let (mut url, protocol) = \
% for mp in media_params:
@@ -613,7 +648,7 @@ else {
if token.is_none() {
return cmn::Result::MissingToken
}
let auth_header = hyper::header::Authorization(token.unwrap().access_token);
let auth_header = Authorization(token.unwrap().access_token);
% endif
% if request_value:
request_value_reader.seek(io::SeekFrom::Start(0)).unwrap();
@@ -634,7 +669,7 @@ else {
% endif
let mut req = client.borrow_mut().request(hyper::method::Method::Extension("${m.httpMethod}".to_string()), url.as_slice())
.header(hyper::header::UserAgent(self.hub._user_agent.clone()))\
.header(UserAgent(self.hub._user_agent.clone()))\
% if supports_scopes(auth):
.header(auth_header)\
@@ -690,9 +725,20 @@ else {
return cmn::Result::Failure(res)
}
% if response_schema:
let mut json_response = String::new();
res.read_to_string(&mut json_response).unwrap();
let result_value = (res, json::decode(&json_response).unwrap());
## If 'alt' is not json, we cannot attempt to decode the response
let result_value = \
% if supports_download:
if enable_resource_parsing \
% endif
{
let mut json_response = String::new();
res.read_to_string(&mut json_response).unwrap();
(res, json::decode(&json_response).unwrap())
}\
% if supports_download:
else { (res, Default::default()) }\
% endif
;
% else:
let result_value = res;
% endif

View File

@@ -24,7 +24,7 @@ ${util.library_name(name, version)}\
</%def>
<%def name="crate_name()" buffered="True">\
google-${self.library_name()}\
${util.library_to_crate_name(util.library_name(name, version))}\
</%def>
## All crates and standard `use` declaration, required for all examples

View File

@@ -748,6 +748,10 @@ def library_name(name, version):
version = 'v' + version
return normalize_library_name(name) + version
# return crate name for given result of `library_name()`
def library_to_crate_name(name):
return 'google-' + name
# return type name of a resource method builder, from a resource name
def rb_type(r):
return "%sMethodsBuilder" % singular(canonical_type_name(r))