openapi: rework the allowedValues to allow for imported variables

Previously I was assuming all variables were declared in the same file.
Turns out that imports exists too, and we need to recurse one more time
in those imports./

Also "clean" up a bit the function to do the parsing in 2 steps:
- first find out the actual matching node in js
- then convert it based on its type
rinse wash repeat.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
This commit is contained in:
Benjamin Tissoires 2021-04-14 11:26:00 +02:00
parent 15bddbc86a
commit 39f8a138d7
2 changed files with 100 additions and 35 deletions

View file

@ -466,6 +466,7 @@ class SchemaProperty(object):
self.type = 'object' self.type = 'object'
self.blackbox = False self.blackbox = False
self.required = True self.required = True
imports = {}
for p in statement.value.properties: for p in statement.value.properties:
try: try:
if p.key.name == 'type': if p.key.name == 'type':
@ -477,41 +478,79 @@ class SchemaProperty(object):
elif p.key.name == 'allowedValues': elif p.key.name == 'allowedValues':
self.type = 'enum' self.type = 'enum'
if p.value.type == 'ArrayExpression': self.enum = []
self.enum = [e.value.lower() for e in p.value.elements]
elif p.value.type == 'Identifier': def parse_enum(value, enum):
# tree wide lookout for the identifier if value.type == 'ArrayExpression':
def find_variable(elem, match): for e in value.elements:
if isinstance(elem, list): parse_enum(e, enum)
for value in elem: elif value.type == 'Literal':
ret = find_variable(value, match) enum.append(value.value.lower())
return
elif value.type == 'Identifier':
# tree wide lookout for the identifier
def find_variable(elem, match):
if isinstance(elem, list):
for value in elem:
ret = find_variable(value, match)
if ret is not None:
return ret
try:
items = elem.items()
except AttributeError:
return None
except TypeError:
return None
if (elem.type == 'VariableDeclarator' and
elem.id.name == match):
return elem
elif (elem.type == 'ImportSpecifier' and
elem.local.name == match):
# we have to treat that case in the caller, because we lack
# information of the source of the import at that point
return elem
elif (elem.type == 'ExportNamedDeclaration' and
elem.declaration.type == 'VariableDeclaration'):
ret = find_variable(elem.declaration.declarations, match)
if ret is not None: if ret is not None:
return ret return ret
try: for type, value in items:
items = elem.items() ret = find_variable(value, match)
except AttributeError: if ret is not None:
return None if ret.type == 'ImportSpecifier':
except TypeError: # first open and read the import source, if
# we haven't already done so
path = elem.source.value
if elem.source.value.startswith('/'):
script_dir = os.path.dirname(os.path.realpath(__file__))
path = os.path.abspath(os.path.join('{}/..'.format(script_dir), elem.source.value.lstrip('/')))
else:
path = os.path.abspath(os.path.join(os.path.dirname(context.path), elem.source.value))
path += '.js'
if path not in imports:
imported_context = parse_file(path)
imports[path] = imported_context
imported_context = imports[path]
# and then re-run the find in the imported file
return find_variable(imported_context.program.body, match)
return ret
return None return None
if (elem.type == 'VariableDeclarator' and elem = find_variable(context.program.body, value.name)
elem.id.name == match):
return elem
for type, value in items: if elem is None:
ret = find_variable(value, match) raise TypeError('can not find "{}"'.format(value.name))
if ret is not None:
return ret
return None return parse_enum(elem.init, enum)
elem = find_variable(context.program.body, p.value.name) parse_enum(p.value, self.enum)
if elem.init.type != 'ArrayExpression':
raise TypeError('can not find "{}"'.format(p.value.name))
self.enum = [e.value.lower() for e in elem.init.elements]
elif p.key.name == 'blackbox': elif p.key.name == 'blackbox':
self.blackbox = True self.blackbox = True
@ -757,6 +796,15 @@ class Context(object):
return ''.join(self._txt[begin - 1:end]) return ''.join(self._txt[begin - 1:end])
def parse_file(path):
try:
# if the file failed, it's likely it doesn't contain a schema
context = Context(path)
except:
return
return context
def parse_schemas(schemas_dir): def parse_schemas(schemas_dir):
schemas = {} schemas = {}
@ -766,12 +814,7 @@ def parse_schemas(schemas_dir):
files.sort() files.sort()
for filename in files: for filename in files:
path = os.path.join(root, filename) path = os.path.join(root, filename)
try: context = parse_file(path)
# if the file failed, it's likely it doesn't contain a schema
context = Context(path)
except:
continue
program = context.program program = context.program
current_schema = None current_schema = None
@ -1012,7 +1055,7 @@ def main():
script_dir = os.path.dirname(os.path.realpath(__file__)) script_dir = os.path.dirname(os.path.realpath(__file__))
parser.add_argument('--release', default='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`') help='the current version of the API, can be retrieved by running `git describe --tags --abbrev=0`')
parser.add_argument('dir', default='{}/../models'.format(script_dir), nargs='?', parser.add_argument('dir', default=os.path.abspath('{}/../models'.format(script_dir)), nargs='?',
help='the directory where to look for schemas') help='the directory where to look for schemas')
args = parser.parse_args() args = parser.parse_args()

View file

@ -1,7 +1,7 @@
swagger: '2.0' swagger: '2.0'
info: info:
title: Wekan REST API title: Wekan REST API
version: v5.17 version: v5.19
description: | description: |
The REST API allows you to control and extend Wekan with ease. The REST API allows you to control and extend Wekan with ease.
@ -2757,6 +2757,10 @@ definitions:
description: | description: |
Does the board allows labels? Does the board allows labels?
type: boolean type: boolean
allowsCreator:
description: |
Does the board allow creator?
type: boolean
allowsAssignee: allowsAssignee:
description: | description: |
Does the board allows assignee? Does the board allows assignee?
@ -2834,7 +2838,12 @@ definitions:
type: type:
description: | description: |
The type of board The type of board
possible values: board, template-board, template-container
type: string type: string
enum:
- board
- template-board
- template-container
sort: sort:
description: | description: |
Sort value Sort value
@ -2857,6 +2866,7 @@ definitions:
- allowsDescriptionText - allowsDescriptionText
- allowsActivities - allowsActivities
- allowsLabels - allowsLabels
- allowsCreator
- allowsAssignee - allowsAssignee
- allowsMembers - allowsMembers
- allowsRequestedBy - allowsRequestedBy
@ -2889,6 +2899,7 @@ definitions:
`saddlebrown`, `paleturquoise`, `mistyrose`, `indigo` `saddlebrown`, `paleturquoise`, `mistyrose`, `indigo`
type: string type: string
enum: enum:
- white
- green - green
- yellow - yellow
- orange - orange
@ -3154,6 +3165,10 @@ definitions:
description: | description: |
type of the card type of the card
type: string type: string
enum:
- cardtype-card
- cardtype-linkedcard
- cardtype-linkedboard
linkedId: linkedId:
description: | description: |
ID of the linked card ID of the linked card
@ -3298,6 +3313,7 @@ definitions:
- dropdown - dropdown
- checkbox - checkbox
- currency - currency
- stringtemplate
settings: settings:
description: | description: |
settings of the custom field settings of the custom field
@ -3344,6 +3360,10 @@ definitions:
type: array type: array
items: items:
$ref: "#/definitions/CustomFieldsSettingsDropdownitems" $ref: "#/definitions/CustomFieldsSettingsDropdownitems"
stringtemplateFormat:
type: string
stringtemplateSeparator:
type: string
CustomFieldsSettingsDropdownitems: CustomFieldsSettingsDropdownitems:
type: object type: object
Integrations: Integrations:
@ -3467,6 +3487,7 @@ definitions:
- lime - lime
- pink - pink
- black - black
- silver
- peachpuff - peachpuff
- crimson - crimson
- plum - plum
@ -3558,6 +3579,7 @@ definitions:
- lime - lime
- pink - pink
- black - black
- silver
- peachpuff - peachpuff
- crimson - crimson
- plum - plum