diff --git a/etc/api/type-cli.yaml b/etc/api/type-cli.yaml index 316e17894d..5aedc05bb4 100644 --- a/etc/api/type-cli.yaml +++ b/etc/api/type-cli.yaml @@ -16,7 +16,7 @@ make: templates: - source: ../LICENSE.md - source: ../Cargo.toml - - source: docs/commands.yml + - source: docs/commands.md - source: mkdocs.yml - source: README.md - source: main.rs diff --git a/src/mako/api/lib/mbuild.mako b/src/mako/api/lib/mbuild.mako index d9b1c91283..2df3d5f5f8 100644 --- a/src/mako/api/lib/mbuild.mako +++ b/src/mako/api/lib/mbuild.mako @@ -10,7 +10,7 @@ CALL_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, - method_name_to_variant, unique_type_name, size_to_bytes) + method_name_to_variant, unique_type_name, size_to_bytes, method_default_scope) def get_parts(part_prop): if not part_prop: @@ -427,15 +427,7 @@ match result { auth_call = 'self.hub.auth.borrow_mut()' if supports_scopes(auth): - all_scopes = sorted(auth.oauth2.scopes.keys()) - default_scope = all_scopes[0] - if m.httpMethod in ('HEAD', 'GET', 'OPTIONS', 'TRACE'): - for scope in all_scopes: - if 'readonly' in scope: - default_scope = scope - break - # end for each scope - # end try to find read-only default scope + default_scope = method_default_scope(m) # end handle default scope # s = '{foo}' -> ('{foo}', 'foo') -> (find_this, replace_with) diff --git a/src/mako/cli/docs/commands.md.mako b/src/mako/cli/docs/commands.md.mako new file mode 100644 index 0000000000..e2d16908a5 --- /dev/null +++ b/src/mako/cli/docs/commands.md.mako @@ -0,0 +1,38 @@ +<%namespace name="util" file="../../lib/util.mako"/>\ +<% + from util import (hash_comment, new_context, method_default_scope) + from cli import (subcommand_md_filename, new_method_context, SPLIT_START, SPLIT_END, pretty, SCOPE_FLAG, + mangle_subcommand) + + c = new_context(schemas, resources, context.get('methods')) +%>\ +% for resource in sorted(c.rta_map.keys()): +% for method in sorted(c.rta_map[resource]): +<% + mc = new_method_context(resource, method, c) +%>\ +${SPLIT_START} ${subcommand_md_filename(resource, method)} +# ${pretty(resource)}: ${pretty(method)} +% if mc.m.description: +${mc.m.description} +% endif # show method description +% if mc.m.get('scopes'): +# Scopes + +You will need authorization for \ +% if len(mc.m.scopes) > 1: +at least one of the following scopes to make a valid call: + +% for s in mc.m.scopes: +* *${s}* +% endfor +% else: +the *${mc.m.scopes[0]}* scope to make a valid call. +% endif # len(scopes) > 1 + +If unset, the scope for this method defaults to *${method_default_scope(mc.m)}*. +You can set the scope for this method like this: `${util.program_name()} --${SCOPE_FLAG} ${mangle_subcommand(resource)} ${mangle_subcommand(method)} ...` +% endif # have method scopes +${SPLIT_END} +% endfor # each method +% endfor # each resource diff --git a/src/mako/cli/docs/commands.yml.mako b/src/mako/cli/docs/commands.yml.mako deleted file mode 100644 index 6c981b6668..0000000000 --- a/src/mako/cli/docs/commands.yml.mako +++ /dev/null @@ -1,16 +0,0 @@ -<%namespace name="util" file="../../lib/util.mako"/>\ -<% - from util import (hash_comment, new_context) - from cli import (subcommand_md_filename, SPLIT_START, SPLIT_END) - - c = new_context(schemas, resources, context.get('methods')) -%>\ -% for resource in sorted(c.rta_map.keys()): -% for method in sorted(c.rta_map[resource]): -${SPLIT_START} ${subcommand_md_filename(resource, method)} -${resource.upper()} DOCS - -${method.upper()} -${SPLIT_END} -% endfor # each method -% endfor # each resource \ No newline at end of file diff --git a/src/mako/cli/lib/cli.py b/src/mako/cli/lib/cli.py index 13d42e674f..22e59e6f0c 100644 --- a/src/mako/cli/lib/cli.py +++ b/src/mako/cli/lib/cli.py @@ -2,6 +2,7 @@ import util import os import re +import collections SPLIT_START = '>>>>>>>' SPLIT_END = '<<<<<<<' @@ -11,12 +12,34 @@ STRUCT_FLAG = 'r' UPLOAD_FLAG = 'u' OUTPUT_FLAG = 'o' VALUE_ARG = 'v' +SCOPE_FLAG = 'scope' + +CONFIG_DIR = '~/.google-service-cli' re_splitters = re.compile(r"%s ([\w\-\.]+)\n(.*?)\n%s" % (SPLIT_START, SPLIT_END), re.MULTILINE|re.DOTALL) +MethodContext = collections.namedtuple('MethodContext', ['m', 'response_schema', 'params', 'request_value', + 'media_params' ,'required_props', 'optional_props', + 'part_prop']) + +def new_method_context(resource, method, c): + m = c.fqan_map[util.to_fqan(c.rtc_map[resource], resource, method)] + response_schema = util.method_response(c, m) + params, request_value = util.build_all_params(c, m) + media_params = util.method_media_params(m) + required_props, optional_props, part_prop = util.organize_params(params, request_value) + + return MethodContext(m, response_schema, params, request_value, media_params, + required_props, optional_props, part_prop) + + +def pretty(n): + return ' '.join(s.capitalize() for s in mangle_subcommand(n).split('-')) + + # transform name to be a suitable subcommand def mangle_subcommand(name): - return util.camel_to_under(util.singular(name)).replace('_', '-').replace('.', '-') + return util.camel_to_under(name).replace('_', '-').replace('.', '-') # transform the resource name into a suitable filename to contain the markdown documentation for it diff --git a/src/mako/cli/lib/docopt.mako b/src/mako/cli/lib/docopt.mako index e9abc26fd3..9224d734d6 100644 --- a/src/mako/cli/lib/docopt.mako +++ b/src/mako/cli/lib/docopt.mako @@ -1,7 +1,8 @@ <%namespace name="util" file="../../lib/util.mako"/>\ <%! - from util import (put_and, to_fqan, method_response, build_all_params, organize_params, method_media_params) - from cli import (mangle_subcommand, PARAM_FLAG, STRUCT_FLAG, UPLOAD_FLAG, OUTPUT_FLAG, VALUE_ARG) + from util import (put_and, supports_scopes) + from cli import (mangle_subcommand, new_method_context, PARAM_FLAG, STRUCT_FLAG, UPLOAD_FLAG, OUTPUT_FLAG, VALUE_ARG, + CONFIG_DIR, SCOPE_FLAG) v_arg = '<%s>' % VALUE_ARG file_arg = '' @@ -20,41 +21,37 @@ Usage: % for resource in sorted(c.rta_map.keys()): % for method in sorted(c.rta_map[resource]): <% - m = c.fqan_map[to_fqan(c.rtc_map[resource], resource, method)] - response_schema = method_response(c, m) - params, request_value = build_all_params(c, m) - media_params = method_media_params(m) - required_props, optional_props, part_prop = organize_params(params, request_value) + mc = new_method_context(resource, method, c) args = list() - if optional_props or parameters is not UNDEFINED: + if mc.optional_props or parameters is not UNDEFINED: args.append('[-%s %s]...' % (PARAM_FLAG, v_arg)) param_used = True # end paramters - if request_value: + if mc.request_value: args.append('-%s %s...' % (STRUCT_FLAG, v_arg)) struct_used = True # end request_value - if media_params: - upload_protocols = [mp.protocol for mp in media_params] + if mc.media_params: + upload_protocols = [mp.protocol for mp in mc.media_params] mode = '|'.join(upload_protocols) - if len(media_params) > 1: + if len(mc.media_params) > 1: mode = '(%s)' % mode args.append('-%s %s %s %s' % (UPLOAD_FLAG, mode, file_arg, mime_arg)) upload_protocols_used = upload_protocols_used|set(upload_protocols) # end upload handling - if response_schema: + if mc.response_schema: args.append('[-%s %s]' % (OUTPUT_FLAG, out_arg)) output_used = True # handle output %>\ - ${util.program_name()} ${mangle_subcommand(resource)} ${mangle_subcommand(method)} ${' '.join(args)} + ${util.program_name()} [config] ${mangle_subcommand(resource)} ${mangle_subcommand(method)} ${' '.join(args)} % endfor # each method % endfor # end for each resource - ${util.program_name()} --help + ${util.program_name()} --help % if param_used|struct_used|output_used or upload_protocols_used: Options: @@ -83,6 +80,18 @@ Options: a filepath that is to contain the received bytes. If unset, it defaults to standard output. % endif + % endif # any special option is used +Config: +% 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. + If unset, it defaults to the shortest scope url for a particular method. +% endif scopes + --config-dir + 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}] "); \ No newline at end of file diff --git a/src/mako/cli/mkdocs.yml.mako b/src/mako/cli/mkdocs.yml.mako index 3a65b7acae..b67c278b7f 100644 --- a/src/mako/cli/mkdocs.yml.mako +++ b/src/mako/cli/mkdocs.yml.mako @@ -1,11 +1,8 @@ <% from util import (put_and, new_context) - from cli import (subcommand_md_filename, mangle_subcommand) + from cli import (subcommand_md_filename, mangle_subcommand, pretty) c = new_context(schemas, resources, context.get('methods')) - - def pretty(n): - return ' '.join(s.capitalize() for s in mangle_subcommand(n).split('-')) %>\ <%namespace name="util" file="../lib/util.mako"/>\ site_name: ${util.canonical_name()} v${util.crate_version()} diff --git a/src/mako/lib/util.py b/src/mako/lib/util.py index 0a837558c6..1c72edf8c3 100644 --- a/src/mako/lib/util.py +++ b/src/mako/lib/util.py @@ -834,6 +834,18 @@ def hub_type_bounds(): def supports_scopes(auth): return bool(auth) and bool(auth.oauth2) +# Returns th desired scope for the given method. It will use read-only scopes for read-only methods +def method_default_scope(m): + default_scope = sorted(m.scopes)[0] + if m.httpMethod in ('HEAD', 'GET', 'OPTIONS', 'TRACE'): + for scope in m.scopes: + if 'readonly' in scope: + default_scope = scope + break + # end for each scope + # end try to find read-only default scope + return default_scope + # return list of type bounds required by method builder def mb_type_bounds(): return hub_type_bounds()