mirror of
https://github.com/wekan/wekan.git
synced 2025-09-22 01:50:48 +02:00
openapi: make the code python 3.5 compatible
It is common to use Ubuntu 16.04 to build snaps. For example, the official docker container to build snaps is using this old distribution. However, Ubuntu 16.04 ships Python 3.5.X which is not compatible with the f-strings in generate_openapi.py. This is sad, because we need to use the `.format()` syntax to make it compatible.
This commit is contained in:
parent
acc4493517
commit
8be7eec2ca
1 changed files with 72 additions and 68 deletions
|
@ -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()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue