|
@@ -23,12 +23,12 @@ def get_req_body_elems(obj, elems):
|
|
|
right = obj.property.name
|
|
|
if left == 'req.body' and right not in elems:
|
|
|
elems.append(right)
|
|
|
- return f'{left}.{right}'
|
|
|
+ return '{}.{}'.format(left, right)
|
|
|
elif obj.type == 'VariableDeclaration':
|
|
|
for s in obj.declarations:
|
|
|
get_req_body_elems(s, elems)
|
|
|
elif obj.type == 'VariableDeclarator':
|
|
|
- if obj.id.type == "ObjectPattern":
|
|
|
+ if obj.id.type == 'ObjectPattern':
|
|
|
# get_req_body_elems() can't be called directly here:
|
|
|
# const {isAdmin, isNoComments, isCommentOnly} = req.body;
|
|
|
right = get_req_body_elems(obj.init, elems)
|
|
@@ -158,12 +158,14 @@ class EntryPoint(object):
|
|
|
|
|
|
def error(self, message):
|
|
|
if self._raw_doc is None:
|
|
|
- sys.stderr.write(f'in {self.schema.name},\n')
|
|
|
- sys.stderr.write(f'{message}\n')
|
|
|
+ sys.stderr.write('in {},\n'.format(self.schema.name))
|
|
|
+ sys.stderr.write('{}\n'.format(message))
|
|
|
return
|
|
|
- sys.stderr.write(f'in {self.schema.name}, lines {self._raw_doc.loc.start.line}-{self._raw_doc.loc.end.line}\n')
|
|
|
- sys.stderr.write(f'{self._raw_doc.value}\n')
|
|
|
- sys.stderr.write(f'{message}\n')
|
|
|
+ sys.stderr.write('in {}, lines {}-{}\n'.format(self.schema.name,
|
|
|
+ self._raw_doc.loc.start.line,
|
|
|
+ self._raw_doc.loc.end.line))
|
|
|
+ sys.stderr.write('{}\n'.format(self._raw_doc.value))
|
|
|
+ sys.stderr.write('{}\n'.format(message))
|
|
|
|
|
|
@property
|
|
|
def doc(self):
|
|
@@ -233,7 +235,7 @@ class EntryPoint(object):
|
|
|
if name.startswith('{'):
|
|
|
param_type = name.strip('{}')
|
|
|
if param_type not in ['string', 'number', 'boolean', 'integer', 'array', 'file']:
|
|
|
- self.error(f'Warning, unknown type {param_type}\n allowed values: string, number, boolean, integer, array, file')
|
|
|
+ self.error('Warning, unknown type {}\n allowed values: string, number, boolean, integer, array, file'.format(param_type))
|
|
|
try:
|
|
|
name, desc = desc.split(maxsplit=1)
|
|
|
except ValueError:
|
|
@@ -246,7 +248,7 @@ class EntryPoint(object):
|
|
|
|
|
|
# we should not have 2 identical parameter names
|
|
|
if tag in params:
|
|
|
- self.error(f'Warning, overwriting parameter {name}')
|
|
|
+ self.error('Warning, overwriting parameter {}'.format(name))
|
|
|
|
|
|
params[name] = (param_type, optional, desc)
|
|
|
|
|
@@ -276,7 +278,7 @@ class EntryPoint(object):
|
|
|
|
|
|
# we should not have 2 identical tags but @param or @tag
|
|
|
if tag in self._doc:
|
|
|
- self.error(f'Warning, overwriting tag {tag}')
|
|
|
+ self.error('Warning, overwriting tag {}'.format(tag))
|
|
|
|
|
|
self._doc[tag] = data
|
|
|
|
|
@@ -299,7 +301,7 @@ class EntryPoint(object):
|
|
|
current_data = ''
|
|
|
line = data
|
|
|
else:
|
|
|
- self.error(f'Unknown tag {tag}, ignoring')
|
|
|
+ self.error('Unknown tag {}, ignoring'.format(tag))
|
|
|
|
|
|
current_data += line + '\n'
|
|
|
|
|
@@ -321,24 +323,24 @@ class EntryPoint(object):
|
|
|
def print_openapi_param(self, name, indent):
|
|
|
ptype, poptional, pdesc = self.doc_param(name)
|
|
|
if pdesc is not None:
|
|
|
- print(f'{" " * indent}description: |')
|
|
|
- print(f'{" " * (indent + 2)}{pdesc}')
|
|
|
+ print('{}description: |'.format(' ' * indent))
|
|
|
+ print('{}{}'.format(' ' * (indent + 2), pdesc))
|
|
|
else:
|
|
|
- print(f'{" " * indent}description: the {name} value')
|
|
|
+ print('{}description: the {} value'.format(' ' * indent, name))
|
|
|
if ptype is not None:
|
|
|
- print(f'{" " * indent}type: {ptype}')
|
|
|
+ print('{}type: {}'.format(' ' * indent, ptype))
|
|
|
else:
|
|
|
- print(f'{" " * indent}type: string')
|
|
|
+ print('{}type: string'.format(' ' * indent))
|
|
|
if poptional:
|
|
|
- print(f'{" " * indent}required: false')
|
|
|
+ print('{}required: false'.format(' ' * indent))
|
|
|
else:
|
|
|
- print(f'{" " * indent}required: true')
|
|
|
+ print('{}required: true'.format(' ' * indent))
|
|
|
|
|
|
@property
|
|
|
def operationId(self):
|
|
|
if 'operation' in self._doc:
|
|
|
return self._doc['operation']
|
|
|
- return f'{self.method_name}_{self.reduced_function_name}'
|
|
|
+ return '{}_{}'.format(self.method_name, self.reduced_function_name)
|
|
|
|
|
|
@property
|
|
|
def description(self):
|
|
@@ -363,49 +365,49 @@ class EntryPoint(object):
|
|
|
|
|
|
def print_openapi_return(self, obj, indent):
|
|
|
if isinstance(obj, dict):
|
|
|
- print(f'{" " * indent}type: object')
|
|
|
- print(f'{" " * indent}properties:')
|
|
|
+ print('{}type: object'.format(' ' * indent))
|
|
|
+ print('{}properties:'.format(' ' * indent))
|
|
|
for k, v in obj.items():
|
|
|
- print(f'{" " * (indent + 2)}{k}:')
|
|
|
+ print('{}{}:'.format(' ' * (indent + 2), k))
|
|
|
self.print_openapi_return(v, indent + 4)
|
|
|
|
|
|
elif isinstance(obj, list):
|
|
|
if len(obj) > 1:
|
|
|
self.error('Error while parsing @return tag, an array should have only one type')
|
|
|
- print(f'{" " * indent}type: array')
|
|
|
- print(f'{" " * indent}items:')
|
|
|
+ print('{}type: array'.format(' ' * indent))
|
|
|
+ print('{}items:'.format(' ' * indent))
|
|
|
self.print_openapi_return(obj[0], indent + 2)
|
|
|
|
|
|
elif isinstance(obj, str) or isinstance(obj, unicode):
|
|
|
rtype = 'type: ' + obj
|
|
|
if obj == self.schema.name:
|
|
|
- rtype = f'$ref: "#/definitions/{obj}"'
|
|
|
- print(f'{" " * indent}{rtype}')
|
|
|
+ rtype = '$ref: "#/definitions/{}"'.format(obj)
|
|
|
+ print('{}{}'.format(' ' * indent, rtype))
|
|
|
|
|
|
def print_openapi(self):
|
|
|
parameters = [token[1:-2] if token.endswith('Id') else token[1:]
|
|
|
for token in self.path.split('/')
|
|
|
if token.startswith(':')]
|
|
|
|
|
|
- print(f' {self.method_name}:')
|
|
|
+ print(' {}:'.format(self.method_name))
|
|
|
|
|
|
- print(f' operationId: {self.operationId}')
|
|
|
+ print(' operationId: {}'.format(self.operationId))
|
|
|
|
|
|
if self.summary is not None:
|
|
|
- print(f' summary: {self.summary}')
|
|
|
+ print(' summary: {}'.format(self.summary))
|
|
|
|
|
|
if self.description is not None:
|
|
|
- print(f' description: |')
|
|
|
+ print(' description: |')
|
|
|
for line in self.description.split('\n'):
|
|
|
if line.strip():
|
|
|
- print(f' {line}')
|
|
|
+ print(' {}'.format(line))
|
|
|
else:
|
|
|
print('')
|
|
|
|
|
|
if len(self.tags) > 0:
|
|
|
- print(f' tags:')
|
|
|
+ print(' tags:')
|
|
|
for tag in self.tags:
|
|
|
- print(f' - {tag}')
|
|
|
+ print(' - {}'.format(tag))
|
|
|
|
|
|
# export the parameters
|
|
|
if self.method_name in ('post', 'put'):
|
|
@@ -416,14 +418,14 @@ class EntryPoint(object):
|
|
|
print(' parameters:')
|
|
|
if self.method_name in ('post', 'put'):
|
|
|
for f in self.body_params:
|
|
|
- print(f''' - name: {f}
|
|
|
- in: formData''')
|
|
|
+ print(''' - name: {}
|
|
|
+ in: formData'''.format(f))
|
|
|
self.print_openapi_param(f, 10)
|
|
|
for p in parameters:
|
|
|
if p in self.body_params:
|
|
|
self.error(' '.join((p, self.path, self.method_name)))
|
|
|
- print(f''' - name: {p}
|
|
|
- in: path''')
|
|
|
+ print(''' - name: {}
|
|
|
+ in: path'''.format(p))
|
|
|
self.print_openapi_param(p, 10)
|
|
|
print(''' produces:
|
|
|
- application/json
|
|
@@ -485,7 +487,9 @@ class SchemaProperty(object):
|
|
|
return
|
|
|
|
|
|
def __repr__(self):
|
|
|
- return f'SchemaProperty({self.name}{"*" if self.required else ""}, {self.doc})'
|
|
|
+ return 'SchemaProperty({}{}, {})'.format(self.name,
|
|
|
+ '*' if self.required else '',
|
|
|
+ self.doc)
|
|
|
|
|
|
def print_openapi(self, indent, current_schema, required_properties):
|
|
|
schema_name = self.schema.name
|
|
@@ -501,11 +505,11 @@ class SchemaProperty(object):
|
|
|
if required_properties is not None and required_properties:
|
|
|
print(' required:')
|
|
|
for f in required_properties:
|
|
|
- print(f' - {f}')
|
|
|
+ print(' - {}'.format(f))
|
|
|
required_properties.clear()
|
|
|
|
|
|
- print(f''' {subschema}:
|
|
|
- type: object''')
|
|
|
+ print(''' {}:
|
|
|
+ type: object'''.format(subschema))
|
|
|
return current_schema
|
|
|
|
|
|
subschema = name.split('.')[0]
|
|
@@ -516,23 +520,23 @@ class SchemaProperty(object):
|
|
|
if required_properties is not None and required_properties:
|
|
|
print(' required:')
|
|
|
for f in required_properties:
|
|
|
- print(f' - {f}')
|
|
|
+ print(' - {}'.format(f))
|
|
|
required_properties.clear()
|
|
|
|
|
|
- print(f''' {schema_name}:
|
|
|
+ print(''' {}:
|
|
|
type: object
|
|
|
- properties:''')
|
|
|
+ properties:'''.format(schema_name))
|
|
|
|
|
|
if required_properties is not None and self.required:
|
|
|
required_properties.append(name)
|
|
|
|
|
|
- print(f'{" "*indent}{name}:')
|
|
|
+ print('{}{}:'.format(' ' * indent, name))
|
|
|
|
|
|
if self.doc is not None:
|
|
|
- print(f'{" "*indent} description: |')
|
|
|
+ print('{} description: |'.format(' ' * indent))
|
|
|
for line in self.doc:
|
|
|
if line.strip():
|
|
|
- print(f'{" "*indent} {line}')
|
|
|
+ print('{} {}'.format(' ' * indent, line))
|
|
|
else:
|
|
|
print('')
|
|
|
|
|
@@ -540,31 +544,31 @@ class SchemaProperty(object):
|
|
|
if ptype in ('enum', 'date'):
|
|
|
ptype = 'string'
|
|
|
if ptype != 'object':
|
|
|
- print(f'{" "*indent} type: {ptype}')
|
|
|
+ print('{} type: {}'.format(' ' * indent, ptype))
|
|
|
|
|
|
if self.type == 'array':
|
|
|
- print(f'{" "*indent} items:')
|
|
|
+ print('{} items:'.format(' ' * indent))
|
|
|
for elem in self.elements:
|
|
|
if elem == 'object':
|
|
|
- print(f'{" "*indent} $ref: "#/definitions/{schema_name + name.capitalize()}"')
|
|
|
+ print('{} $ref: "#/definitions/{}"'.format(' ' * indent, schema_name + name.capitalize()))
|
|
|
else:
|
|
|
- print(f'{" "*indent} type: {elem}')
|
|
|
+ print('{} type: {}'.format(' ' * indent, elem))
|
|
|
if not self.required:
|
|
|
- print(f'{" "*indent} x-nullable: true')
|
|
|
+ print('{} x-nullable: true'.format(' ' * indent))
|
|
|
|
|
|
elif self.type == 'object':
|
|
|
if self.blackbox:
|
|
|
- print(f'{" "*indent} type: object')
|
|
|
+ print('{} type: object'.format(' ' * indent))
|
|
|
else:
|
|
|
- print(f'{" "*indent} $ref: "#/definitions/{schema_name + name.capitalize()}"')
|
|
|
+ print('{} $ref: "#/definitions/{}"'.format(' ' * indent, schema_name + name.capitalize()))
|
|
|
|
|
|
elif self.type == 'enum':
|
|
|
- print(f'{" "*indent} enum:')
|
|
|
+ print('{} enum:'.format(' ' * indent))
|
|
|
for enum in self.enum:
|
|
|
- print(f'{" "*indent} - {enum}')
|
|
|
+ print('{} - {}'.format(' ' * indent, enum))
|
|
|
|
|
|
if '.' not in self.name and not self.required:
|
|
|
- print(f'{" "*indent} x-nullable: true')
|
|
|
+ print('{} x-nullable: true'.format(' ' * indent))
|
|
|
|
|
|
return schema_name
|
|
|
|
|
@@ -620,10 +624,10 @@ class Schemas(object):
|
|
|
if self.fields is None:
|
|
|
return
|
|
|
|
|
|
- print(f' {self.name}:')
|
|
|
+ print(' {}:'.format(self.name))
|
|
|
print(' type: object')
|
|
|
if self.doc is not None:
|
|
|
- print(f' description: {self.doc}')
|
|
|
+ print(' description: {}'.format(self.doc))
|
|
|
|
|
|
print(' properties:')
|
|
|
|
|
@@ -636,7 +640,7 @@ class Schemas(object):
|
|
|
if required_properties:
|
|
|
print(' required:')
|
|
|
for f in required_properties:
|
|
|
- print(f' - {f}')
|
|
|
+ print(' - {}'.format(f))
|
|
|
|
|
|
# then print the references
|
|
|
current = None
|
|
@@ -648,7 +652,7 @@ class Schemas(object):
|
|
|
if required_properties:
|
|
|
print(' required:')
|
|
|
for f in required_properties:
|
|
|
- print(f' - {f}')
|
|
|
+ print(' - {}'.format(f))
|
|
|
|
|
|
required_properties = []
|
|
|
# then print the references in the references
|
|
@@ -658,7 +662,7 @@ class Schemas(object):
|
|
|
if required_properties:
|
|
|
print(' required:')
|
|
|
for f in required_properties:
|
|
|
- print(f' - {f}')
|
|
|
+ print(' - {}'.format(f))
|
|
|
|
|
|
|
|
|
def parse_schemas(schemas_dir):
|
|
@@ -731,10 +735,10 @@ def parse_schemas(schemas_dir):
|
|
|
|
|
|
|
|
|
def generate_openapi(schemas, entry_points, version):
|
|
|
- print(f'''swagger: '2.0'
|
|
|
+ print('''swagger: '2.0'
|
|
|
info:
|
|
|
title: Wekan REST API
|
|
|
- version: {version}
|
|
|
+ version: {0}
|
|
|
description: |
|
|
|
The REST API allows you to control and extend Wekan with ease.
|
|
|
|
|
@@ -866,7 +870,7 @@ paths:
|
|
|
default:
|
|
|
description: |
|
|
|
Error in registration
|
|
|
-''')
|
|
|
+'''.format(version))
|
|
|
|
|
|
# GET and POST on the same path are valid, we need to reshuffle the paths
|
|
|
# with the path as the sorting key
|
|
@@ -880,7 +884,7 @@ paths:
|
|
|
sorted_paths.sort()
|
|
|
|
|
|
for path in sorted_paths:
|
|
|
- print(f' {methods[path][0].url}:')
|
|
|
+ print(' {}:'.format(methods[path][0].url))
|
|
|
|
|
|
for ep in methods[path]:
|
|
|
ep.print_openapi()
|
|
@@ -897,9 +901,9 @@ paths:
|
|
|
def main():
|
|
|
parser = argparse.ArgumentParser(description='Generate an OpenAPI 2.0 from the given JS schemas.')
|
|
|
script_dir = os.path.dirname(os.path.realpath(__file__))
|
|
|
- parser.add_argument('--release', default=f'git-master', nargs=1,
|
|
|
+ parser.add_argument('--release', default='git-master', nargs=1,
|
|
|
help='the current version of the API, can be retrieved by running `git describe --tags --abbrev=0`')
|
|
|
- parser.add_argument('dir', default=f'{script_dir}/../models', nargs='?',
|
|
|
+ parser.add_argument('dir', default='{}/../models'.format(script_dir), nargs='?',
|
|
|
help='the directory where to look for schemas')
|
|
|
|
|
|
args = parser.parse_args()
|