mirror of
https://github.com/OMGeeky/google-apis-rs.git
synced 2025-12-30 16:18:49 +01:00
* in APIs, scopes will now be per-method, and if no scope is given, we will assume only the API key has to be set. Previously there was a wild mix between globally mentioned scopes and method scopes. * assure CLI generation works so far, for all avaialable APIs Related to #48
168 lines
5.3 KiB
Python
168 lines
5.3 KiB
Python
import util
|
|
|
|
import os
|
|
import re
|
|
import collections
|
|
from copy import deepcopy
|
|
|
|
SPLIT_START = '>>>>>>>'
|
|
SPLIT_END = '<<<<<<<'
|
|
|
|
PARAM_FLAG = 'p'
|
|
STRUCT_FLAG = 'r'
|
|
UPLOAD_FLAG = 'u'
|
|
OUTPUT_FLAG = 'o'
|
|
VALUE_ARG = 'v'
|
|
SCOPE_FLAG = 'scope'
|
|
|
|
FILE_ARG = '<file>'
|
|
MIME_ARG = '<mime>'
|
|
OUT_ARG = '<out>'
|
|
|
|
FIELD_SEP = '.'
|
|
|
|
CONFIG_DIR = '~/.google-service-cli'
|
|
|
|
POD_TYPES = set(('boolean', 'integer', 'number', 'uint32', 'double', 'float', 'int32', 'int64', 'uint64', 'string'))
|
|
|
|
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'])
|
|
|
|
CTYPE_POD = 'pod'
|
|
CTYPE_ARRAY = 'list'
|
|
CTYPE_MAP = 'map'
|
|
SchemaEntry = collections.namedtuple('SchemaEntry', ['container_type', 'actual_property', 'property'])
|
|
|
|
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('-'))
|
|
|
|
|
|
def is_request_value_property(mc, p):
|
|
return mc.request_value and mc.request_value.id == p.get(util.TREF)
|
|
|
|
|
|
# transform name to be a suitable subcommand
|
|
def mangle_subcommand(name):
|
|
return util.camel_to_under(name).replace('_', '-').replace('.', '-')
|
|
|
|
|
|
# transform the resource name into a suitable filename to contain the markdown documentation for it
|
|
def subcommand_md_filename(resource, method):
|
|
return mangle_subcommand(resource) + '_' + mangle_subcommand(method) + '.md'
|
|
|
|
|
|
def docopt_mode(protocols):
|
|
mode = '|'.join(protocols)
|
|
if len(protocols) > 1:
|
|
mode = '(%s)' % mode
|
|
return mode
|
|
|
|
|
|
# Return schema' with fields dict: { 'field1' : SchemaField(...), 'SubSchema': schema' }
|
|
def to_cli_schema(c, schema):
|
|
res = deepcopy(schema)
|
|
fd = dict()
|
|
res['fields'] = fd
|
|
|
|
# util.nested_type_name
|
|
properties = schema.get('properties', dict())
|
|
if not properties and 'variant' in schema and 'map' in schema.variant:
|
|
for e in schema.variant.map:
|
|
assert util.TREF in e
|
|
properties[e.type_value] = e
|
|
# end handle enumerations
|
|
|
|
for pn, p in properties.iteritems():
|
|
def set_nested_schema(ns):
|
|
if ns.fields:
|
|
fd[pn] = ns
|
|
# end utility
|
|
|
|
def dup_property():
|
|
pc = deepcopy(p)
|
|
if 'type' in pc and pc.type == 'string' and 'Count' in pn:
|
|
pc.type = 'int64'
|
|
return pc
|
|
# end
|
|
|
|
if util.TREF in p:
|
|
if p[util.TREF] != schema.id: # prevent recursion (in case of self-referential schemas)
|
|
set_nested_schema(to_cli_schema(c, c.schemas[p[util.TREF]]))
|
|
elif p.type == 'array' and 'items' in p and 'type' in p.get('items') and p.get('items').type in POD_TYPES:
|
|
pc = dup_property()
|
|
fd[pn] = SchemaEntry(CTYPE_ARRAY, pc.get('items'), pc)
|
|
elif p.type == 'object':
|
|
if util.is_map_prop(p):
|
|
if 'type' in p.additionalProperties and p.additionalProperties.type in POD_TYPES:
|
|
pc = dup_property()
|
|
fd[pn] = SchemaEntry(CTYPE_MAP, pc.additionalProperties, pc)
|
|
else:
|
|
set_nested_schema(to_cli_schema(c, c.schemas[util.nested_type_name(schema.id, pn)]))
|
|
elif p.type in POD_TYPES:
|
|
pc = dup_property()
|
|
fd[pn] = SchemaEntry(CTYPE_POD, pc, pc)
|
|
# end handle property type
|
|
# end
|
|
|
|
return res
|
|
|
|
|
|
# Convert the given cli-schema (result from to_cli_schema(schema)) to a yaml-like string. It's suitable for
|
|
# documentation only
|
|
def cli_schema_to_yaml(schema, prefix=''):
|
|
if not prefix:
|
|
o = '%s%s:\n' % (prefix, util.unique_type_name(schema.id))
|
|
else:
|
|
o = ''
|
|
prefix += ' '
|
|
for fn, f in schema.fields.iteritems():
|
|
o += '%s%s:' % (prefix, mangle_subcommand(fn))
|
|
if not isinstance(f, SchemaEntry):
|
|
o += '\n' + cli_schema_to_yaml(f, prefix)
|
|
else:
|
|
t = f.actual_property.type
|
|
if f.container_type == CTYPE_ARRAY:
|
|
t = '[%s]' % t
|
|
elif f.container_type == CTYPE_MAP:
|
|
t = '{ string: %s }' % t
|
|
o += ' %s\n' % t
|
|
# end for each field
|
|
return o
|
|
|
|
|
|
# split the result along split segments
|
|
def process_template_result(r, output_file):
|
|
found = False
|
|
dir = None
|
|
if output_file:
|
|
dir = os.path.dirname(output_file)
|
|
if not os.path.isdir(dir):
|
|
os.makedirs(dir)
|
|
# end handle output directory
|
|
|
|
for m in re_splitters.finditer(r):
|
|
found = True
|
|
fh = open(os.path.join(dir, m.group(1)), 'wb')
|
|
fh.write(m.group(2).encode('UTF-8'))
|
|
fh.close()
|
|
# end for each match
|
|
|
|
if found:
|
|
r = None
|
|
|
|
return r
|