docs(CLI): filled README.md

All possible documentation was added in a quality sufficient for
a first release. After all, everything there is is documented.

Fixes #50
This commit is contained in:
Sebastian Thiel
2015-04-26 11:22:28 +02:00
parent 607ba745d1
commit 944e04bd12
6 changed files with 122 additions and 27 deletions

View File

@@ -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:

View File

@@ -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}" />\
</%block>
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.
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-<scope-hash>.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} <resource> <method> [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/

View File

@@ -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 = '<file>'
MIME_ARG = '<mime>'
@@ -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))

View File

@@ -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: <URL to github.io docs here, see #51>
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} <url>
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} <folder>
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
</%def>

View File

@@ -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>
<%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)
})

View File

@@ -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