summaryrefslogtreecommitdiffstats
path: root/src/pybind/mgr/dashboard/controllers/_docs.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/pybind/mgr/dashboard/controllers/_docs.py')
-rw-r--r--src/pybind/mgr/dashboard/controllers/_docs.py128
1 files changed, 128 insertions, 0 deletions
diff --git a/src/pybind/mgr/dashboard/controllers/_docs.py b/src/pybind/mgr/dashboard/controllers/_docs.py
new file mode 100644
index 000000000..5bd7a5a7a
--- /dev/null
+++ b/src/pybind/mgr/dashboard/controllers/_docs.py
@@ -0,0 +1,128 @@
+from typing import Any, Dict, List, Optional, Tuple, Union
+
+from ..api.doc import SchemaInput, SchemaType
+
+
+class EndpointDoc: # noqa: N802
+ DICT_TYPE = Union[Dict[str, Any], Dict[int, Any]]
+
+ def __init__(self, description: str = "", group: str = "",
+ parameters: Optional[Union[DICT_TYPE, List[Any], Tuple[Any, ...]]] = None,
+ responses: Optional[DICT_TYPE] = None) -> None:
+ self.description = description
+ self.group = group
+ self.parameters = parameters
+ self.responses = responses
+
+ self.validate_args()
+
+ if not self.parameters:
+ self.parameters = {} # type: ignore
+
+ self.resp = {}
+ if self.responses:
+ for status_code, response_body in self.responses.items():
+ schema_input = SchemaInput()
+ schema_input.type = SchemaType.ARRAY if \
+ isinstance(response_body, list) else SchemaType.OBJECT
+ schema_input.params = self._split_parameters(response_body)
+
+ self.resp[str(status_code)] = schema_input
+
+ def validate_args(self) -> None:
+ if not isinstance(self.description, str):
+ raise Exception("%s has been called with a description that is not a string: %s"
+ % (EndpointDoc.__name__, self.description))
+ if not isinstance(self.group, str):
+ raise Exception("%s has been called with a groupname that is not a string: %s"
+ % (EndpointDoc.__name__, self.group))
+ if self.parameters and not isinstance(self.parameters, dict):
+ raise Exception("%s has been called with parameters that is not a dict: %s"
+ % (EndpointDoc.__name__, self.parameters))
+ if self.responses and not isinstance(self.responses, dict):
+ raise Exception("%s has been called with responses that is not a dict: %s"
+ % (EndpointDoc.__name__, self.responses))
+
+ def _split_param(self, name: str, p_type: Union[type, DICT_TYPE, List[Any], Tuple[Any, ...]],
+ description: str, optional: bool = False, default_value: Any = None,
+ nested: bool = False) -> Dict[str, Any]:
+ param = {
+ 'name': name,
+ 'description': description,
+ 'required': not optional,
+ 'nested': nested,
+ }
+ if default_value:
+ param['default'] = default_value
+ if isinstance(p_type, type):
+ param['type'] = p_type
+ else:
+ nested_params = self._split_parameters(p_type, nested=True)
+ if nested_params:
+ param['type'] = type(p_type)
+ param['nested_params'] = nested_params
+ else:
+ param['type'] = p_type
+ return param
+
+ # Optional must be set to True in order to set default value and parameters format must be:
+ # 'name: (type or nested parameters, description, [optional], [default value])'
+ def _split_dict(self, data: DICT_TYPE, nested: bool) -> List[Any]:
+ splitted = []
+ for name, props in data.items():
+ if isinstance(name, str) and isinstance(props, tuple):
+ if len(props) == 2:
+ param = self._split_param(name, props[0], props[1], nested=nested)
+ elif len(props) == 3:
+ param = self._split_param(
+ name, props[0], props[1], optional=props[2], nested=nested)
+ if len(props) == 4:
+ param = self._split_param(name, props[0], props[1], props[2], props[3], nested)
+ splitted.append(param)
+ else:
+ raise Exception(
+ """Parameter %s in %s has not correct format. Valid formats are:
+ <name>: (<type>, <description>, [optional], [default value])
+ <name>: (<[type]>, <description>, [optional], [default value])
+ <name>: (<[nested parameters]>, <description>, [optional], [default value])
+ <name>: (<{nested parameters}>, <description>, [optional], [default value])"""
+ % (name, EndpointDoc.__name__))
+ return splitted
+
+ def _split_list(self, data: Union[List[Any], Tuple[Any, ...]], nested: bool) -> List[Any]:
+ splitted = [] # type: List[Any]
+ for item in data:
+ splitted.extend(self._split_parameters(item, nested))
+ return splitted
+
+ # nested = True means parameters are inside a dict or array
+ def _split_parameters(self, data: Optional[Union[DICT_TYPE, List[Any], Tuple[Any, ...]]],
+ nested: bool = False) -> List[Any]:
+ param_list = [] # type: List[Any]
+ if isinstance(data, dict):
+ param_list.extend(self._split_dict(data, nested))
+ elif isinstance(data, (list, tuple)):
+ param_list.extend(self._split_list(data, True))
+ return param_list
+
+ def __call__(self, func: Any) -> Any:
+ func.doc_info = {
+ 'summary': self.description,
+ 'tag': self.group,
+ 'parameters': self._split_parameters(self.parameters),
+ 'response': self.resp
+ }
+ return func
+
+
+class APIDoc(object):
+ def __init__(self, description="", group=""):
+ self.tag = group
+ self.tag_descr = description
+
+ def __call__(self, cls):
+ cls.doc_info = {
+ 'tag': self.tag,
+ 'tag_descr': self.tag_descr
+ }
+ return cls