Mercurial > lbo > hg > async-google-apis
changeset 28:366924ffc5c2
Support media download (e.g. Drive: Files.Export)
author | Lewin Bormann <lbo@spheniscida.de> |
---|---|
date | Sat, 17 Oct 2020 21:31:13 +0200 |
parents | b02856cbbd8e |
children | a83949096306 |
files | drive_example/src/drive_v3_types.rs generate/generate.py generate/templates.py |
diffstat | 3 files changed, 131 insertions(+), 39 deletions(-) [+] |
line wrap: on
line diff
--- a/drive_example/src/drive_v3_types.rs Sat Oct 17 20:43:04 2020 +0200 +++ b/drive_example/src/drive_v3_types.rs Sat Oct 17 21:31:13 2020 +0200 @@ -3,6 +3,7 @@ use chrono::{DateTime, Utc}; use anyhow::{Error, Result}; use std::collections::HashMap; +use tokio::stream::{Stream, StreamExt}; use percent_encoding::{percent_encode, NON_ALPHANUMERIC}; pub type TlsConnr = hyper_rustls::HttpsConnector<hyper::client::HttpConnector>; @@ -3485,9 +3486,9 @@ /// Exports a Google Doc to the requested MIME type and returns the exported content. Please note that the exported content is limited to 10MB. pub async fn export( - &mut self, params: &FilesExportParams) -> Result<()> { - - let rel_path = format!("files/{fileId}/export", fileId=params.file_id); + &mut self, params: &FilesExportParams, dst: &mut std::io::Write) -> Result<()> { + + let rel_path = format!("files/trash", ); let path = "https://www.googleapis.com/drive/v3/".to_string() + &rel_path; let mut scopes = &self.scopes; if scopes.is_empty() { @@ -3513,10 +3514,12 @@ if !resp.status().is_success() { return Err(anyhow::Error::new(ApiError::HTTPError(resp.status()))); } - let resp_body = hyper::body::to_bytes(resp.into_body()).await?; - let bodystr = String::from_utf8(resp_body.to_vec())?; - let decoded = serde_json::from_str(&bodystr)?; - Ok(decoded) + let resp_body = resp.into_body(); + let write_result = resp_body.map(move |chunk| { dst.write(chunk?.as_ref()); Ok(()) }).collect::<Vec<Result<()>>>().await; + if let Some(e) = write_result.into_iter().find(|r| r.is_err()) { + return e; + } + Ok(()) }
--- a/generate/generate.py Sat Oct 17 20:43:04 2020 +0200 +++ b/generate/generate.py Sat Oct 17 21:31:13 2020 +0200 @@ -228,7 +228,10 @@ # Types of the function in_type = method['request']['$ref'] if 'request' in method else '()' out_type = method['response']['$ref'] if 'response' in method else '()' + + is_download = method.get('supportsMediaDownload', False) and not method.get('useMediaDownloadService', False) is_upload = 'mediaUpload' in method + media_upload = method.get('mediaUpload', None) if media_upload and 'simple' in media_upload['protocols']: upload_path = media_upload['protocols']['simple']['path'] @@ -236,39 +239,14 @@ upload_path = '' http_method = method['httpMethod'] - formatted_path, required_params = resolve_parameters(method['path']) - data_normal = { - 'name': snake_case(methodname), - 'param_type': params_type_name, - 'in_type': in_type, - 'out_type': out_type, - 'base_path': discdoc['baseUrl'], - 'rel_path_expr': formatted_path, - 'params': [{ - 'param': p, - 'snake_param': sp - } for (p, sp) in parameters.items()], - 'required_params': [{ - 'param': p, - 'snake_param': sp - } for (p, sp) in required_parameters.items()], - 'scopes': [{ - 'scope': s - } for s in method['scopes']], - 'description': method.get('description', ''), - 'http_method': http_method - } - if in_type == '()': - data_normal.pop('in_type') - method_fragments.append(chevron.render(NormalMethodTmpl, data_normal)) - - if is_upload: - data_upload = { + if is_download: + data_download = { 'name': snake_case(methodname), 'param_type': params_type_name, + 'in_type': in_type, 'out_type': out_type, - 'base_path': discdoc['rootUrl'], - 'rel_path_expr': '"' + upload_path.lstrip('/') + '"', + 'base_path': discdoc['baseUrl'], + 'rel_path_expr': formatted_path, 'params': [{ 'param': p, 'snake_param': sp @@ -281,9 +259,61 @@ 'scope': s } for s in method['scopes']], 'description': method.get('description', ''), - 'http_method': http_method, + 'http_method': http_method } - method_fragments.append(chevron.render(UploadMethodTmpl, data_upload)) + if in_type == '()': + data_download.pop('in_type') + method_fragments.append(chevron.render(DownloadMethodTmpl, data_download)) + + else: + formatted_path, required_params = resolve_parameters(method['path']) + data_normal = { + 'name': snake_case(methodname), + 'param_type': params_type_name, + 'in_type': in_type, + 'out_type': out_type, + 'base_path': discdoc['baseUrl'], + 'rel_path_expr': formatted_path, + 'params': [{ + 'param': p, + 'snake_param': sp + } for (p, sp) in parameters.items()], + 'required_params': [{ + 'param': p, + 'snake_param': sp + } for (p, sp) in required_parameters.items()], + 'scopes': [{ + 'scope': s + } for s in method['scopes']], + 'description': method.get('description', ''), + 'http_method': http_method + } + if in_type == '()': + data_normal.pop('in_type') + method_fragments.append(chevron.render(NormalMethodTmpl, data_normal)) + + if is_upload: + data_upload = { + 'name': snake_case(methodname), + 'param_type': params_type_name, + 'out_type': out_type, + 'base_path': discdoc['rootUrl'], + 'rel_path_expr': '"' + upload_path.lstrip('/') + '"', + 'params': [{ + 'param': p, + 'snake_param': sp + } for (p, sp) in parameters.items()], + 'required_params': [{ + 'param': p, + 'snake_param': sp + } for (p, sp) in required_parameters.items()], + 'scopes': [{ + 'scope': s + } for s in method['scopes']], + 'description': method.get('description', ''), + 'http_method': http_method, + } + method_fragments.append(chevron.render(UploadMethodTmpl, data_upload)) return chevron.render(ServiceImplementationTmpl, { 'service': service,
--- a/generate/templates.py Sat Oct 17 20:43:04 2020 +0200 +++ b/generate/templates.py Sat Oct 17 21:31:13 2020 +0200 @@ -4,6 +4,7 @@ use chrono::{DateTime, Utc}; use anyhow::{Error, Result}; use std::collections::HashMap; +use tokio::stream::{Stream, StreamExt}; use percent_encoding::{percent_encode, NON_ALPHANUMERIC}; pub type TlsConnr = hyper_rustls::HttpsConnector<hyper::client::HttpConnector>; @@ -167,3 +168,61 @@ Ok(decoded) } ''' + +# Takes: +# name, param_type, in_type, out_type +# base_path, rel_path_expr +# params: [{param, snake_param}] +# http_method +DownloadMethodTmpl = ''' +/// {{{description}}} +pub async fn {{{name}}}( + &mut self, params: &{{{param_type}}}, {{#in_type}}{{{in_type}}},{{/in_type}} dst: &mut std::io::Write) -> Result<{{out_type}}> { + + let rel_path = {{{rel_path_expr}}}; + let path = "{{{base_path}}}".to_string() + &rel_path; + let mut scopes = &self.scopes; + if scopes.is_empty() { + scopes = &vec![{{#scopes}}"{{{scope}}}".to_string(), + {{/scopes}}]; + } + let tok = self.authenticator.token(&self.scopes).await?; + let mut url_params = format!("?oauth_token={token}&fields=*", token=tok.as_str()); + {{#params}} + if let Some(ref val) = ¶ms.{{{snake_param}}} { + url_params.push_str(&format!("&{{{param}}}={}", + percent_encode(format!("{}", val).as_bytes(), NON_ALPHANUMERIC).to_string())); + } + {{/params}} + {{#required_params}} + url_params.push_str(&format!("&{{{param}}}={}", + percent_encode(format!("{}", params.{{{snake_param}}}).as_bytes(), NON_ALPHANUMERIC).to_string())); + {{/required_params}} + + let full_uri = path + &url_params; + let reqb = hyper::Request::builder() + .uri(full_uri) + .method("{{{http_method}}}") + .header("Content-Type", "application/json"); + + let body = hyper::Body::from(""); + {{#in_type}} + let mut body_str = serde_json::to_string(req)?; + if body_str == "null" { + body_str.clear(); + } + let body = hyper::Body::from(body_str); + {{/in_type}} + let request = reqb.body(body)?; + let resp = self.client.request(request).await?; + if !resp.status().is_success() { + return Err(anyhow::Error::new(ApiError::HTTPError(resp.status()))); + } + let resp_body = resp.into_body(); + let write_result = resp_body.map(move |chunk| { dst.write(chunk?.as_ref()); Ok(()) }).collect::<Vec<Result<()>>>().await; + if let Some(e) = write_result.into_iter().find(|r| r.is_err()) { + return e; + } + Ok(()) + } +'''