Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Provide a generic Tree.dump() function #1366

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions src/viur/core/prototypes/tree.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import logging
import json
import typing as t
from deprecated.sphinx import deprecated
from viur.core import utils, errors, db, current
Expand All @@ -7,6 +8,7 @@
from viur.core.cache import flushCache
from viur.core.skeleton import Skeleton, SkeletonInstance
from viur.core.tasks import CallDeferred
from viur.core.render.json.default import CustomJsonEncoder
from .skelmodule import SkelModule


Expand Down Expand Up @@ -1050,6 +1052,88 @@ def onCloned(self, skelType: SkelType, skel: SkeletonInstance, src_skel: Skeleto
if self.leafSkelCls:
self._clone_recursive("leaf", src_skel["key"], skel["key"], skel["parentrepo"])

# Helpers

@exposed
def dump(
self,
parententry: t.Optional[db.Key | int | str] = None,
depth: int = 3,
limit: int = 30,
**kwargs
) -> str:
"""
Dumps a Tree's content as JSON, based on a parententry.

This function is very limited in its bounds, and shall only be used for quick retrieval of small tree structures
and for debugging purposes.

:param parententry: Parententry to dump; If not given, the fuction tries to figure out a single parent entry.
:param depth: Depth to dump children. Can only be a maxiumum of 3.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
:param depth: Depth to dump children. Can only be a maxiumum of 3.
:param depth: Depth to dump children. Can only be a maximum of 3.

:param limit: Limit of entries on each level, can be maximum of 30 per level and type.
"""
if not utils.string.is_prefix(self.render.kind, "json"):
raise errors.BadRequest("Can only use this function on JSON-based renders")
Comment on lines +1086 to +1087
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Methods in a module should be render independent IMO. That's exactly the purpose of the renderer, to transform data from a generic to a specific output.

I would just fetch the render instances here and in dump() pass the result through the renderer.

This method could also be used for internal purposes to fetch data for non-json-renderers.


current.request.get().response.headers["Content-Type"] = "application/json"

if depth < 1 or depth > 3:
raise errors.NotAcceptable("'depth' out of bounds")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why we not print out the borders?


if limit < 1 or limit > 30:
raise errors.NotAcceptable("'limit' out of bounds")

if not parententry:
repos = self.getAvailableRootNodes(**kwargs)

match len(repos):
case 0:
raise errors.Unauthorized()
case 1:
parententry = repos[0]["key"]
case _:
raise errors.NotAcceptable(f"Missing required parameter {'parententry'!r}")

def query(parententry, _depth: int = 1):
# fetch the nodes
q = self.viewSkel("node").all()
q.mergeExternalFilter(kwargs | {"parententry": parententry})

if not (q := self.listFilter(q)):
raise errors.Unauthorized()

self._apply_default_order(q)

ret = []
for skel in q.fetch(limit=limit):
node = self.render.renderSkelValues(skel)
node["_skeltype"] = "node"

# recurse into children
if _depth < depth:
node["_children"] = query(skel["key"], _depth + 1)

ret.append(node)

# fetch the leafs (when this is a tree)
if self.leafSkelCls:
q = self.viewSkel("leaf").all()
q.mergeExternalFilter(kwargs | {"parententry": parententry})

if not (q := self.listFilter(q)):
raise errors.Unauthorized()

self._apply_default_order(q)

ret += [
self.render.renderSkelValues(skel) | {"_skeltype": "leaf"}
for skel in q.fetch(limit=limit)
]

return ret

return json.dumps(query(parententry), cls=CustomJsonEncoder)


Tree.vi = True
Tree.admin = True
Loading