Skip to content

Commit

Permalink
Refactor HelpMethod and HelpInfo classes to include HTTP methods and …
Browse files Browse the repository at this point in the history
…improve documentation rendering
  • Loading branch information
dkmstr committed Feb 4, 2025
1 parent 458b4d3 commit 8130afa
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 67 deletions.
128 changes: 80 additions & 48 deletions server/src/uds/REST/documentation.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,10 @@

@dataclasses.dataclass
class HelpMethodInfo:

method: str
text: str
methods: list[types.rest.HelpNode.Methods]

def __str__(self) -> str:
return f'{self.method}: {self.text}'
Expand All @@ -63,27 +65,69 @@ def __repr__(self) -> str:


class HelpMethod(enum.Enum):
ITEM = HelpMethodInfo('<uuid>', 'Retrieves an item by its UUID')
LOG = HelpMethodInfo(f'<uuid>/{consts.rest.LOG}', 'Retrieves the log of an item')
OVERVIEW = HelpMethodInfo(consts.rest.OVERVIEW, 'General Overview of all items (a list')
TABLEINFO = HelpMethodInfo(consts.rest.TABLEINFO, 'Table visualization information (types, etc..)')
TYPES = HelpMethodInfo(consts.rest.TYPES, 'Retrieves a list of types available')
TYPES_TYPE = HelpMethodInfo(f'{consts.rest.TYPES}/<type>', 'Retrieves a type information')
GUI = HelpMethodInfo(consts.rest.GUI, 'GUI information')
GUI_TYPES = HelpMethodInfo(f'{consts.rest.GUI}/<type>', 'GUI Types information')


@dataclasses.dataclass
ITEM = HelpMethodInfo(
'<uuid>',
'Retrieves an item by its UUID',
[
types.rest.HelpNode.Methods.GET,
types.rest.HelpNode.Methods.PUT,
types.rest.HelpNode.Methods.DELETE,
],
)
LOG = HelpMethodInfo(
f'<uuid>/{consts.rest.LOG}',
'Retrieves the logs of an element by its UUID',
[
types.rest.HelpNode.Methods.GET,
],
)
OVERVIEW = HelpMethodInfo(
consts.rest.OVERVIEW, 'General Overview of all items (a list', [types.rest.HelpNode.Methods.GET]
)
TABLEINFO = HelpMethodInfo(
consts.rest.TABLEINFO,
'Table visualization information (types, etc..)',
[
types.rest.HelpNode.Methods.GET,
],
)
TYPES = HelpMethodInfo(
consts.rest.TYPES,
'Retrieves a list of types available',
[
types.rest.HelpNode.Methods.GET,
],
)
TYPES_TYPE = HelpMethodInfo(
f'{consts.rest.TYPES}/<type>',
'Retrieves a type information',
[
types.rest.HelpNode.Methods.GET,
],
)
GUI = HelpMethodInfo(consts.rest.GUI, 'GUI information', [types.rest.HelpNode.Methods.GET])
GUI_TYPES = HelpMethodInfo(
f'{consts.rest.GUI}/<type>', 'GUI Types information', [types.rest.HelpNode.Methods.GET]
)


@dataclasses.dataclass(frozen=True)
class HelpInfo:
level: int
path: str
text: str
methods: list[HelpMethod]
method: types.rest.HelpNode.Methods = types.rest.HelpNode.Methods.GET

@property
def is_empty(self) -> bool:
return not self.path

def as_dict(self) -> dict[str, str]:
return {
'path': self.path,
'text': self.text,
'method': self.method.value,
}


class Documentation(View):

Expand All @@ -99,48 +143,36 @@ def dispatch(

help_data: list[HelpInfo] = []

def _process_node(node: 'types.rest.HelpNode', path: str, level: int) -> None:
if node.kind == types.rest.HelpNode.HelpNodeType.MODEL:
methods = [
HelpMethod.OVERVIEW,
HelpMethod.GUI,
HelpMethod.GUI_TYPES,
HelpMethod.TYPES,
HelpMethod.TYPES_TYPE,
HelpMethod.TABLEINFO,
HelpMethod.ITEM,
HelpMethod.LOG,
]
elif node.kind == types.rest.HelpNode.HelpNodeType.DETAIL:
methods = [
HelpMethod.OVERVIEW,
HelpMethod.GUI,
HelpMethod.GUI_TYPES,
HelpMethod.TYPES,
HelpMethod.TYPES_TYPE,
HelpMethod.TABLEINFO,
HelpMethod.ITEM,
HelpMethod.LOG,
]
else:
methods = []

if node.kind != types.rest.HelpNode.HelpNodeType.PATH:
help_data.append(HelpInfo(level, path, node.help.text, methods))
def _process_node(node: 'types.rest.HelpNode', path: str) -> None:
match node.kind:
case types.rest.HelpNode.Type.PATH:
pass
case types.rest.HelpNode.Type.MODEL | types.rest.HelpNode.Type.DETAIL:
for func in [
HelpMethod.OVERVIEW,
HelpMethod.GUI,
HelpMethod.GUI_TYPES,
HelpMethod.TYPES,
HelpMethod.TYPES_TYPE,
HelpMethod.TABLEINFO,
HelpMethod.ITEM,
HelpMethod.LOG,
]:
for method in func.value.methods:
help_data.append(HelpInfo(f'{path}/{func.value.method}', func.value.text, method))
case _:
for method in node.methods:
help_data.append(HelpInfo(path, node.help.text, method))

for child in node.children:
_process_node(
child,
child.help.path,
level + (0 if node.kind == types.rest.HelpNode.HelpNodeType.PATH else 1),
)
_process_node(child, child.help.path)

_process_node(Dispatcher.base_handler_node.help_node(), '', 0)
_process_node(Dispatcher.base_handler_node.help_node(), '')

response = render(
request=request,
template_name='uds/modern/documentation.html',
context={'help': [h for h in help_data if not h.is_empty]},
context={'help': [h.as_dict() for h in help_data]},
)

return response
Expand Down
28 changes: 18 additions & 10 deletions server/src/uds/core/types/rest.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,22 +159,30 @@ def process_help(help: str) -> str:

@dataclasses.dataclass(frozen=True)
class HelpNode:
class HelpNodeType(enum.StrEnum):
class Type(enum.StrEnum):
MODEL = 'model'
DETAIL = 'detail'
CUSTOM = 'custom'
PATH = 'path'

class Methods(enum.StrEnum):
GET = 'GET'
POST = 'POST'
PUT = 'PUT'
DELETE = 'DELETE'
PATCH = 'PATCH'

help: HelpPath
children: list['HelpNode'] # Children nodes
kind: HelpNodeType
kind: Type
methods: set[Methods] = dataclasses.field(default_factory=lambda: {HelpNode.Methods.GET})

def __hash__(self) -> int:
return hash(self.help.path)
return hash(self.help.path + ''.join(method for method in self.methods))

def __eq__(self, other: object) -> bool:
if isinstance(other, HelpNode):
return self.help.path == other.help.path
return self.help.path == other.help.path and self.methods == other.methods
if not isinstance(other, HelpPath):
return False

Expand Down Expand Up @@ -237,12 +245,12 @@ def help_node(self) -> HelpNode:

custom_help: set[HelpNode] = set()

help_node_type = HelpNode.HelpNodeType.PATH
help_node_type = HelpNode.Type.PATH

if self.handler:
help_node_type = HelpNode.HelpNodeType.CUSTOM
help_node_type = HelpNode.Type.CUSTOM
if issubclass(self.handler, ModelHandler):
help_node_type = HelpNode.HelpNodeType.MODEL
help_node_type = HelpNode.Type.MODEL
# Add custom_methods
for method in self.handler.custom_methods:
# Method is a Me CustomModelMethod,
Expand All @@ -252,7 +260,7 @@ def help_node(self) -> HelpNode:
HelpNode(
HelpPath(path=self.full_path() + '/' + method.name, help=doc),
[],
HelpNode.HelpNodeType.CUSTOM,
HelpNode.Type.CUSTOM,
)
)

Expand All @@ -263,7 +271,7 @@ def help_node(self) -> HelpNode:
HelpNode(
HelpPath(path=self.full_path() + '/' + method_name, help=''),
[],
HelpNode.HelpNodeType.DETAIL,
HelpNode.Type.DETAIL,
)
)
# Add custom_methods
Expand All @@ -282,7 +290,7 @@ def help_node(self) -> HelpNode:
help=doc,
),
[],
HelpNode.HelpNodeType.CUSTOM,
HelpNode.Type.CUSTOM,
)
)

Expand Down
10 changes: 1 addition & 9 deletions server/src/uds/templates/uds/modern/documentation.html
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,7 @@ <h1 data-i18n="uds:documentation">Documentation</h1>
{% for h in help %}
<div>{{ h }}</div>
<div class="doc-item">
{% if h.methods %}
<div class="doc-item-methods">
{% for m in h.methods %}
<div class="doc-item-method">{{ h.path }}/{{ m.value.method }} : {{ m.value.text }}</div>
{% endfor %}
</div>
{% else %}
<div>{{ h.path }} : {{ h.text }}</div>
{% endif %}
<div>{{ h.path }} : {{ h.text }} {{ h.method }}</div>
</div>
{% endfor %}
</div>
Expand Down

0 comments on commit 8130afa

Please sign in to comment.