2021-05-10 18:42:13 +02:00
|
|
|
import importlib
|
|
|
|
import json
|
|
|
|
import os
|
|
|
|
import re
|
|
|
|
import sys
|
2021-10-24 11:52:08 +02:00
|
|
|
from random import randint
|
2021-05-10 18:42:13 +02:00
|
|
|
from typing import Union, List
|
|
|
|
|
|
|
|
from docutils import nodes
|
|
|
|
from docutils.parsers.rst import Directive
|
2021-10-24 11:52:08 +02:00
|
|
|
from marshmallow import fields
|
2021-05-10 18:42:13 +02:00
|
|
|
|
|
|
|
|
|
|
|
class SchemaDirective(Directive):
|
|
|
|
"""
|
|
|
|
Support for response/message schemas in the docs. Format: ``.. schema:: rel_path.SchemaClass(arg1=value1, ...)``,
|
|
|
|
where ``rel_path`` is the path of the schema relative to ``platypush/schemas``.
|
|
|
|
"""
|
|
|
|
has_content = True
|
|
|
|
_schema_regex = re.compile(r'^\s*(.+?)\s*(\((.+?)\))?\s*$')
|
|
|
|
_schemas_path = os.path.abspath(
|
|
|
|
os.path.join(
|
|
|
|
os.path.dirname(os.path.relpath(__file__)), '..', '..', '..', 'platypush', 'schemas'))
|
|
|
|
|
|
|
|
sys.path.insert(0, _schemas_path)
|
|
|
|
|
2021-10-24 11:52:08 +02:00
|
|
|
@classmethod
|
|
|
|
def _get_field_value(cls, field):
|
2021-05-10 18:42:13 +02:00
|
|
|
metadata = getattr(field, 'metadata', {})
|
2021-10-24 11:52:08 +02:00
|
|
|
if metadata.get('example'):
|
|
|
|
return metadata['example']
|
|
|
|
if metadata.get('description'):
|
|
|
|
return metadata['description']
|
|
|
|
|
|
|
|
if isinstance(field, fields.Number):
|
|
|
|
return randint(1, 99)
|
|
|
|
if isinstance(field, fields.Boolean):
|
|
|
|
return bool(randint(0, 1))
|
|
|
|
if isinstance(field, fields.URL):
|
|
|
|
return 'https://example.org'
|
|
|
|
if isinstance(field, fields.Nested):
|
|
|
|
ret = {
|
|
|
|
name: cls._get_field_value(f)
|
|
|
|
for name, f in field.nested().fields.items()
|
|
|
|
}
|
|
|
|
|
|
|
|
return [ret] if field.many else ret
|
|
|
|
|
|
|
|
return str(field.__class__.__name__).lower()
|
2021-05-10 18:42:13 +02:00
|
|
|
|
|
|
|
def _parse_schema(self) -> Union[dict, List[dict]]:
|
|
|
|
m = self._schema_regex.match('\n'.join(self.content))
|
|
|
|
schema_module_name = '.'.join(['platypush.schemas', *(m.group(1).split('.')[:-1])])
|
|
|
|
schema_module = importlib.import_module(schema_module_name)
|
|
|
|
schema_class = getattr(schema_module, m.group(1).split('.')[-1])
|
2021-07-17 01:32:18 +02:00
|
|
|
schema_args = eval(f'dict({m.group(3)})') if m.group(3) else {}
|
2021-05-10 18:42:13 +02:00
|
|
|
schema = schema_class(**schema_args)
|
|
|
|
output = {
|
|
|
|
name: self._get_field_value(field)
|
|
|
|
for name, field in schema.fields.items()
|
|
|
|
if not field.load_only
|
|
|
|
}
|
|
|
|
|
|
|
|
return [output] if schema.many else output
|
|
|
|
|
|
|
|
def run(self):
|
|
|
|
content = json.dumps(self._parse_schema(), sort_keys=True, indent=2)
|
|
|
|
block = nodes.literal_block(content, content)
|
|
|
|
block['language'] = 'json'
|
|
|
|
return [block]
|
|
|
|
|
|
|
|
|
|
|
|
def setup(app):
|
|
|
|
app.add_directive('schema', SchemaDirective)
|
|
|
|
|
|
|
|
return {
|
|
|
|
'version': '0.1',
|
|
|
|
'parallel_read_safe': True,
|
|
|
|
'parallel_write_safe': True,
|
|
|
|
}
|