diff --git a/src/viur/core/bones/base.py b/src/viur/core/bones/base.py index b44f23b2b..c7f63d7af 100644 --- a/src/viur/core/bones/base.py +++ b/src/viur/core/bones/base.py @@ -958,6 +958,7 @@ def _hashValueForUniquePropertyIndex(self, value: str | int) -> list[str]: :return: A list containing a string representation of the hashed value. If the bone is multiple, the list may contain more than one hashed value. """ + def hashValue(value: str | int) -> str: h = hashlib.sha256() h.update(str(value).encode("UTF-8")) @@ -1260,3 +1261,29 @@ def structure(self) -> dict: ret["compute"]["lifetime"] = self.compute.interval.lifetime.total_seconds() return ret + + def render_value(self, skel: "SkeletonInstance", bone_name: str): + ret = {} + bone_value = skel[bone_name] + if self.languages and self.multiple: + res = {} + for language in self.languages: + if bone_value and language in bone_value and bone_value[language]: + ret[language] = [self.render_single_value(value) for value in bone_value[language]] + else: + res[language] = [] + elif self.languages: + for language in self.languages: + if bone_value and language in bone_value and bone_value[language]: + ret[language] = self.render_single_value(bone_value[language]) + else: + ret[language] = None + elif self.multiple: + ret = [self.render_single_value(value) for value in bone_value] + + else: + ret = self.render_single_value(bone_value) + return ret + + def render_single_value(self, value): + return value diff --git a/src/viur/core/bones/file.py b/src/viur/core/bones/file.py index 1769a07b8..abf0587f3 100644 --- a/src/viur/core/bones/file.py +++ b/src/viur/core/bones/file.py @@ -8,7 +8,7 @@ from hashlib import sha256 from time import time import typing as t -from viur.core import conf, db +from viur.core import conf, db, utils from viur.core.bones.treeleaf import TreeLeafBone from viur.core.tasks import CallDeferred @@ -306,3 +306,12 @@ def structure(self) -> dict: return super().structure() | { "valid_mime_types": self.validMimeTypes } + + def render_single_value(self, value: dict[str, "SkeletonInstance"]) -> dict | None: + res = super().render_single_value(value) + if res is not None: + for key, value in res.items(): + if value is not None: + res[key]["downloadUrl"] = utils.downloadUrlFor(value["dlkey"], value["name"], derived=False, + expires=conf.render_json_download_url_expiration) + return res diff --git a/src/viur/core/bones/password.py b/src/viur/core/bones/password.py index 1aa9d876d..2db0c7361 100644 --- a/src/viur/core/bones/password.py +++ b/src/viur/core/bones/password.py @@ -196,3 +196,6 @@ def structure(self) -> dict: "tests": self.tests, "test_threshold": self.test_threshold, } + + def render_single_value(self, value): + return "" diff --git a/src/viur/core/bones/record.py b/src/viur/core/bones/record.py index 0ce8800fa..c6efe88dd 100644 --- a/src/viur/core/bones/record.py +++ b/src/viur/core/bones/record.py @@ -181,3 +181,7 @@ def structure(self) -> dict: return super().structure() | { "format": self.format, "using": self.using().structure()} + + def render_single_value(self, value: "SkeletonInstance") -> dict | None: + if value is not None: + return value.render_bone_values() diff --git a/src/viur/core/bones/relational.py b/src/viur/core/bones/relational.py index be98a3501..90123f1e6 100644 --- a/src/viur/core/bones/relational.py +++ b/src/viur/core/bones/relational.py @@ -1331,3 +1331,10 @@ def structure(self) -> dict: "using": self.using().structure() if self.using else None, "relskel": self._refSkelCache().structure(), } + + def render_single_value(self, value: dict[str, "SkeletonInstance"]) -> dict | None: + if isinstance(value, dict): + return { + "dest": value["dest"].render_bone_values(), + "rel": value["rel"].render_bone_values() if value["rel"] else None, + } diff --git a/src/viur/core/render/json/default.py b/src/viur/core/render/json/default.py index 9ca9e4940..f4bcd15eb 100644 --- a/src/viur/core/render/json/default.py +++ b/src/viur/core/render/json/default.py @@ -1,8 +1,8 @@ import json +import warnings from enum import Enum - -from viur.core import bones, utils, db, current -from viur.core.skeleton import SkeletonInstance +from viur.core import db, current, logging +from viur.core.skeleton import SkelList, SkeletonInstance from viur.core.i18n import translate from viur.core.config import conf from datetime import datetime @@ -68,87 +68,17 @@ def render_structure(structure: dict): return structure - def renderSingleBoneValue(self, value: t.Any, - bone: bones.BaseBone, - skel: SkeletonInstance, - key - ) -> dict | str | None: - """ - Renders the value of a bone. - - It can be overridden and super-called from a custom renderer. - - :param bone: The bone which value should be rendered. - :type bone: Any bone that inherits from :class:`server.bones.base.BaseBone`. - - :return: A dict containing the rendered attributes. - """ - if isinstance(bone, bones.RelationalBone): - if isinstance(value, dict): - return { - "dest": self.renderSkelValues(value["dest"], injectDownloadURL=isinstance(bone, bones.FileBone)), - "rel": (self.renderSkelValues(value["rel"], injectDownloadURL=isinstance(bone, bones.FileBone)) - if value["rel"] else None), - } - elif isinstance(bone, bones.RecordBone): - return self.renderSkelValues(value) - elif isinstance(bone, bones.PasswordBone): - return "" - else: - return value - return None - - def renderBoneValue(self, bone: bones.BaseBone, skel: SkeletonInstance, key: str) -> list | dict | None: - boneVal = skel[key] - if bone.languages and bone.multiple: - res = {} - for language in bone.languages: - if boneVal and language in boneVal and boneVal[language]: - res[language] = [self.renderSingleBoneValue(v, bone, skel, key) for v in boneVal[language]] - else: - res[language] = [] - elif bone.languages: - res = {} - for language in bone.languages: - if boneVal and language in boneVal and boneVal[language]: - res[language] = self.renderSingleBoneValue(boneVal[language], bone, skel, key) - else: - res[language] = None - elif bone.multiple: - res = [self.renderSingleBoneValue(v, bone, skel, key) for v in boneVal] if boneVal else None - else: - res = self.renderSingleBoneValue(boneVal, bone, skel, key) - return res - - def renderSkelValues(self, skel: SkeletonInstance, injectDownloadURL: bool = False) -> t.Optional[dict]: - """ - Prepares values of one :class:`viur.core.skeleton.Skeleton` or a list of skeletons for output. - - :param skel: Skeleton which contents will be processed. - """ - if skel is None: - return None - elif isinstance(skel, dict): - return skel - res = {} - for key, bone in skel.items(): - res[key] = self.renderBoneValue(bone, skel, key) - if injectDownloadURL and "dlkey" in skel and "name" in skel: - res["downloadUrl"] = utils.downloadUrlFor(skel["dlkey"], skel["name"], derived=False, - expires=conf.render_json_download_url_expiration) - return res - - def renderEntry(self, skel: SkeletonInstance, actionName, params=None): + def renderEntry(self, skel: SkeletonInstance | list[SkeletonInstance], actionName, params=None): structure = None errors = None if isinstance(skel, list): - vals = [self.renderSkelValues(x) for x in skel] + vals = [_skel.render_bone_values() for _skel in skel] if isinstance(skel[0], SkeletonInstance): structure = DefaultRender.render_structure(skel[0].structure()) elif isinstance(skel, SkeletonInstance): - vals = self.renderSkelValues(skel) + vals = skel.render_bone_values() structure = DefaultRender.render_structure(skel.structure()) errors = [{"severity": x.severity.value, "fieldPath": x.fieldPath, "errorMessage": x.errorMessage, "invalidatedFields": x.invalidatedFields} for x in skel.errors] @@ -170,7 +100,8 @@ def renderEntry(self, skel: SkeletonInstance, actionName, params=None): def view(self, skel: SkeletonInstance, action: str = "view", params=None, **kwargs): return self.renderEntry(skel, action, params) - def list(self, skellist, action: str = "list", params=None, **kwargs): + def list(self, skellist: SkelList, action: str = "list", params=None, **kwargs): + # Rendering the structure in lists is flagged as deprecated structure = None cursor = None @@ -184,7 +115,7 @@ def list(self, skellist, action: str = "list", params=None, **kwargs): cursor = skellist.getCursor() orders = skellist.get_orders() - skellist = [self.renderSkelValues(skel) for skel in skellist] + skellist = [skel.render_bone_values() for skel in skellist] else: skellist = [] diff --git a/src/viur/core/skeleton.py b/src/viur/core/skeleton.py index ba14d4b07..5e7c571ce 100644 --- a/src/viur/core/skeleton.py +++ b/src/viur/core/skeleton.py @@ -303,6 +303,11 @@ def structure(self) -> dict: for i, (key, bone) in enumerate(self.items()) } + def render_bone_values(self): + return { + bone_name: bone.render_value(self, bone_name) for bone_name, bone in self.items() + } + def __deepcopy__(self, memodict): res = self.clone() memodict[id(self)] = res