diff --git a/src/mako/api/lib/mbuild.mako b/src/mako/api/lib/mbuild.mako index dbb5f9b335..b86c299347 100644 --- a/src/mako/api/lib/mbuild.mako +++ b/src/mako/api/lib/mbuild.mako @@ -493,9 +493,7 @@ match result { dlg.begin(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: <% - pname = 'self.' + property(p.name) # property identifier if media_params and 'mediaUpload' in m: upload_type_map = dict() for mp in media_params: @@ -504,6 +502,10 @@ match result { break # for each meadia param # end build media param map +%>\ + % for p in field_params: +<% + pname = 'self.' + property(p.name) # property identifier %>\ ## parts can also be derived from the request, but we do that only if it's not set % if p.name == 'part' and request_value: diff --git a/src/mako/cli/README.md.mako b/src/mako/cli/README.md.mako index 6684015cb0..deb0e4d1e1 100644 --- a/src/mako/cli/README.md.mako +++ b/src/mako/cli/README.md.mako @@ -1,5 +1,87 @@ -# HELLO ${id.upper()} +<% + from util import (markdown_comment, new_context) + from cli import (CONFIG_DIR, CONFIG_DIR_FLAG, SCOPE_FLAG, application_secret_path, DEBUG_FLAG, DEBUG_AUTH_FLAG) -## TODO: About authentication + c = new_context(schemas, resources, context.get('methods')) +%>\ +<%namespace name="util" file="../lib/util.mako"/>\ +<%namespace name="docopt" file="lib/docopt.mako"/>\ +<%block filter="markdown_comment">\ +<%util:gen_info source="${self.uri}" />\ + +The `${util.program_name()}` command-line interface *(CLI)* allows to use most features of the *Google ${util.canonical_name()}* service from the comfort of your terminal. -Include information about application secret files, and how we automatically write a default one. \ No newline at end of file +By default all output is printed to standard out, but flags can be set to direct it into a file independent of your shell's +capabilities. Errors will be printed to standard error, and cause the program's exit code to be non-zero. + +If data-structures are requested, these will be returned as pretty-printed JSON, to be useful as input to other tools. + +# Usage + +This documentation was generated from the *${util.canonical_name()}* API at revision *${revision is UNDEFINED and '00000000' or revision}*. The CLI is at version *${cargo.build_version}*. + +```bash +${docopt.new(c, usage_only=True)} +``` + +# Configuration + +The program will store all persistent data in the `${CONFIG_DIR}` directory in *JSON* files prefixed with `${util.program_name()}-`. You can change the directory used to store configuration with the `--${CONFIG_DIR_FLAG}` flag on a per-invocation basis. + +More information about the various kinds of persistent data are given in the following paragraphs. + +# Authentication + +Most APIs require a user to authenticate any request. If this is the case, the [scope][scopes] determines the +set of permissions granted. The granularity of these is usually no more than *read-only* or *full-access*. + +If not set, the system will automatically select the smallest feasible scope, e.g. when invoking a +method that is read-only, it will ask only for a read-only scope. +You may use the `--${SCOPE_FLAG}` flag to specify a scope directly. +All applicable scopes are documented in the respective method's CLI documentation. + +The first time a scope is used, the user is asked for permission. Follow the instructions given +by the CLI to grant permissions, or to decline. + +If a scope was authenticated by the user, the respective information will be stored as *JSON* in the configuration +directory, e.g. `${CONFIG_DIR}/${util.program_name()}-token-.json`. No manual management of these tokens +is necessary. + +To revoke granted authentication, please refer to the [official documentation][revoke-access]. + +# Application Secrets + +In order to allow any application to use Google services, it will need to be registered using the +[Google Developer Console][google-dev-console]. APIs the application may use are then enabled for it +one by one. Most APIs can be used for free and have a daily quota. + +To allow more comfortable usage of the CLI without forcing anyone to register an own application, the CLI +comes with a default application secret that is configured accordingly. This also means that heavy usage +all around the world may deplete the daily quota. + +You can workaround this limitation by putting your own secrets file at this location: +`${CONFIG_DIR}/${application_secret_path(util.program_name())}`, assuming that the required *${name}* API +was enabled for it. Such a secret file can be downloaded in the *Google Developer Console* at +*APIs & auth -> Credentials -> Download JSON* and used as is. + +Learn more about how to setup Google projects and enable APIs using the [official documentation][google-project-new]. + + +# Debugging + +Even though the CLI does its best to provide usable error messages, sometimes it might be desirable to know +what exactly led to a particular issue. This is done by allowing all client-server communication to be +output to standard error *as-is*. + +The `--${DEBUG_FLAG}` flag will print all client-server communication to standard error, whereas the `--${DEBUG_AUTH_FLAG}` flag +will cause all communication related to authentication to standard error. +If the `--${DEBUG_FLAG}` flag is set, error-results will be debug-printed, possibly yielding more information about the +issue at hand. + +You may consider redirecting standard error into a file for ease of use, e.g. `${util.program_name()} --${DEBUG_FLAG} [options] 2>debug.txt`. + + +[scopes]: https://developers.google.com/+/api/oauth#scopes +[revoke-access]: http://webapps.stackexchange.com/a/30849 +[google-dev-console]: https://console.developers.google.com/ +[google-project-new]: https://developers.google.com/console/help/new/ \ No newline at end of file diff --git a/src/mako/cli/lib/cli.py b/src/mako/cli/lib/cli.py index 8e51c23173..a2a909c00e 100644 --- a/src/mako/cli/lib/cli.py +++ b/src/mako/cli/lib/cli.py @@ -17,6 +17,8 @@ VALUE_ARG = 'v' KEY_VALUE_ARG = 'kv' SCOPE_FLAG = 'scope' CONFIG_DIR_FLAG = 'config-dir' +DEBUG_FLAG = 'debug' +DEBUG_AUTH_FLAG = 'debug-auth' FILE_ARG = '' MIME_ARG = '' @@ -100,6 +102,9 @@ def arg_ident(name): def flag_ident(name): return 'flag_' + ident(name) +def application_secret_path(program_name): + return program_name + '-secret.json' + # Returns identifier for method dealing with options for the given resource-method pair def call_method_ident(resource, method): return '_%s_%s' % (ident(resource), ident(method)) diff --git a/src/mako/cli/lib/docopt.mako b/src/mako/cli/lib/docopt.mako index 9c7aec7958..5d68c9a207 100644 --- a/src/mako/cli/lib/docopt.mako +++ b/src/mako/cli/lib/docopt.mako @@ -1,19 +1,21 @@ <%namespace name="util" file="../../lib/util.mako"/>\ <%! - from util import (put_and, supports_scopes) + from util import (put_and, supports_scopes, api_index) from cli import (mangle_subcommand, new_method_context, PARAM_FLAG, STRUCT_FLAG, UPLOAD_FLAG, OUTPUT_FLAG, VALUE_ARG, CONFIG_DIR, SCOPE_FLAG, is_request_value_property, FIELD_SEP, docopt_mode, FILE_ARG, MIME_ARG, OUT_ARG, - CONFIG_DIR_FLAG, KEY_VALUE_ARG, to_docopt_arg) + CONFIG_DIR_FLAG, KEY_VALUE_ARG, to_docopt_arg, DEBUG_FLAG, DEBUG_AUTH_FLAG) %>\ -<%def name="new(c)">\ +<%def name="new(c, usage_only=False)">\ <% param_used = False struct_used = False upload_protocols_used = set() output_used = False %>\ +% if not usage_only: docopt!(Options derive Debug, " Usage: +% endif % for resource in sorted(c.rta_map.keys()): % for method in sorted(c.rta_map[resource]): <% @@ -53,24 +55,27 @@ Usage: % endfor # end for each resource ${util.program_name()} --help -All documentation details can be found TODO: +All documentation details can be found at +${cargo.doc_base_url + '/' + api_index(cargo.doc_base_url, name, version, make, check_exists=False)} Configuration: % if supports_scopes(auth): --${SCOPE_FLAG} - Specify the authentication a method should be executed in. Each scope requires - the user to grant this application permission to use it. + Specify the authentication a method should be executed in. Each scope + requires the user to grant this application permission to use it. If unset, it defaults to the shortest scope url for a particular method. % endif scopes --${CONFIG_DIR_FLAG} - A directory into which we will store our persistent data. Defaults to a user-writable - directory that we will create during the first invocation. + A directory into which we will store our persistent data. Defaults to + a user-writable directory that we will create during the first invocation. [default: ${CONFIG_DIR}] - --debug - Output all server communication to standard error. `tx` and `rx` are placed into - the same stream. - --debug-auth - Output all communication related to authentication to standard error. `tx` and `rx` are placed into - the same stream. + --${DEBUG_FLAG} + Output all server communication to standard error. `tx` and `rx` are placed + into the same stream. + --${DEBUG_AUTH_FLAG} + Output all communication related to authentication to standard error. `tx` + and `rx` are placed into the same stream. +% if not usage_only: "); +% endif \ No newline at end of file diff --git a/src/mako/cli/lib/engine.mako b/src/mako/cli/lib/engine.mako index a042f29ff2..8667bbc547 100644 --- a/src/mako/cli/lib/engine.mako +++ b/src/mako/cli/lib/engine.mako @@ -6,7 +6,8 @@ from cli import (mangle_subcommand, new_method_context, PARAM_FLAG, STRUCT_FLAG, UPLOAD_FLAG, OUTPUT_FLAG, VALUE_ARG, CONFIG_DIR, SCOPE_FLAG, is_request_value_property, FIELD_SEP, docopt_mode, FILE_ARG, MIME_ARG, OUT_ARG, cmd_ident, call_method_ident, arg_ident, POD_TYPES, flag_ident, ident, JSON_TYPE_VALUE_MAP, - KEY_VALUE_ARG, to_cli_schema, SchemaEntry, CTYPE_POD, actual_json_type, CTYPE_MAP, CTYPE_ARRAY) + KEY_VALUE_ARG, to_cli_schema, SchemaEntry, CTYPE_POD, actual_json_type, CTYPE_MAP, CTYPE_ARRAY, + application_secret_path, DEBUG_FLAG, DEBUG_AUTH_FLAG) v_arg = '<%s>' % VALUE_ARG SOPT = 'self.opt.' @@ -101,7 +102,7 @@ self.opt.${cmd_ident(method)} { Ok(p) => p, }; - match cmn::application_secret_from_directory(&config_dir, "${util.program_name()}-secret.json", + match cmn::application_secret_from_directory(&config_dir, "${application_secret_path(util.program_name())}", "${api.credentials.replace('"', r'\"')}") { Ok(secret) => (config_dir, secret), Err(e) => return Err(InvalidOptionsError::single(e, 4)) @@ -109,14 +110,14 @@ self.opt.${cmd_ident(method)} { }; let auth = Authenticator::new( &secret, DefaultAuthenticatorDelegate, - ${self._debug_client('debug_auth') | indent_all_but_first_by(10)}, + ${self._debug_client(DEBUG_AUTH_FLAG) | indent_all_but_first_by(10)}, JsonTokenStorage { program_name: "${util.program_name()}", db_dir: config_dir.clone(), }, None); let client = - ${self._debug_client('debug') | indent_all_but_first_by(3)}; + ${self._debug_client(DEBUG_FLAG) | indent_all_but_first_by(3)}; let engine = Engine { opt: opt, hub: ${hub_type_name}::new(client, auth), @@ -137,7 +138,7 @@ self.opt.${cmd_ident(method)} { <%def name="_debug_client(flag_name)" buffered="True">\ -if opt.flag_${flag_name} { +if opt.flag_${mangle_ident(flag_name)} { hyper::Client::with_connector(mock::TeeConnector { connector: hyper::net::HttpConnector(None) }) diff --git a/src/mako/lib/util.py b/src/mako/lib/util.py index 3aedd8b87a..259cc344b7 100644 --- a/src/mako/lib/util.py +++ b/src/mako/lib/util.py @@ -832,13 +832,13 @@ def to_extern_crate_name(crate_name): def gen_crate_dir(name, version, ti): return to_extern_crate_name(library_to_crate_name(library_name(name, version), ti.target_suffix)) -def api_index(DOC_ROOT, name, version, ti): +def api_index(DOC_ROOT, name, version, ti, check_exists=True): crate_dir = gen_crate_dir(name, version, ti) if ti.documentation_engine == 'rustdoc': index_file_path = crate_dir + '/' + crate_dir + '/index.html' else: - index_file_path = crate_dir + '/' + '/index.html' - if os.path.isfile(DOC_ROOT + '/' + index_file_path): + index_file_path = crate_dir + '/' + 'index.html' + if not check_exists or os.path.isfile(os.path.join(DOC_ROOT, index_file_path)): return index_file_path return None