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: Header to render only specific bones in the json renderer #1376

Open
wants to merge 2 commits into
base: 3.6
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
49 changes: 37 additions & 12 deletions src/viur/core/render/json/default.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import fnmatch
import json
import typing as t
from datetime import datetime
from enum import Enum

from viur.core import bones, utils, db, current
from viur.core.skeleton import SkeletonInstance
from viur.core.i18n import translate
from viur.core import bones, current, db
from viur.core.config import conf
from datetime import datetime
import typing as t
from viur.core.i18n import translate
from viur.core.skeleton import SkeletonInstance


class CustomJsonEncoder(json.JSONEncoder):
Expand Down Expand Up @@ -86,12 +86,12 @@ def renderSingleBoneValue(self, value: t.Any,
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))
"dest": self.renderSkelValues(value["dest"], isinstance(bone, bones.FileBone), True),
"rel": (self.renderSkelValues(value["rel"], isinstance(bone, bones.FileBone), True)
if value["rel"] else None),
}
elif isinstance(bone, bones.RecordBone):
return self.renderSkelValues(value)
return self.renderSkelValues(value, sub_skel=True)
elif isinstance(bone, bones.PasswordBone):
return ""
else:
Expand Down Expand Up @@ -120,21 +120,46 @@ def renderBoneValue(self, bone: bones.BaseBone, skel: SkeletonInstance, key: str
res = self.renderSingleBoneValue(boneVal, bone, skel, key)
return res

def renderSkelValues(self, skel: SkeletonInstance, injectDownloadURL: bool = False) -> t.Optional[dict]:
def renderSkelValues(
self,
skel: SkeletonInstance,
injectDownloadURL: bool = False,
sub_skel: 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.
:param injectDownloadURL: If True 'downloadUrl' is injected in the result.
:param sub_skel: Skeleton inside a Relation- or Recordbone.
"""
if skel is None:
return None
elif isinstance(skel, dict):
return skel

res = {}
request_data = current.request_data.get()
if not request_data.get("viur-bone_list"):
if bone_list := current.request.get().request.headers.get("x-viur-bonelist"):
bone_list = bone_list.split(",")
if "key" not in bone_list:
bone_list.append("key")
if "name" not in bone_list:
bone_list.append("name")
request_data["viur-bone_list"] = bone_list
else:
request_data["viur-bone_list"] = None

if request_data["viur-bone_list"] and not sub_skel:
for bone_name, bone in skel.items():
for accept_bone_name in request_data["viur-bone_list"]:
if fnmatch.fnmatch(bone_name, accept_bone_name):
res[bone_name] = self.renderBoneValue(bone, skel, bone_name)

for key, bone in skel.items():
res[key] = self.renderBoneValue(bone, skel, key)
else:
for bone_name, bone in skel.items():
res[bone_name] = self.renderBoneValue(bone, skel, bone_name)

if (
injectDownloadURL
Expand Down
13 changes: 12 additions & 1 deletion src/viur/core/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ def _select_language(self, path: str) -> str:
return path

def _process(self):
if self.method not in ("get", "post", "head"):
if self.method not in ("get", "post", "head", "options"):
logging.error(f"{self.method=} not supported")
return

Expand Down Expand Up @@ -537,6 +537,10 @@ def _route(self, path: str) -> None:
if caller.exposed is False and not self.internalRequest:
raise errors.NotFound()

# Fill the Allow header of the response with the allowed HTTP methods
if self.method == "options":
self.response.headers["Allow"] = ", ".join(sorted(caller.methods)).upper()

# Check for @force_ssl flag
if not self.internalRequest \
and caller.ssl \
Expand Down Expand Up @@ -573,6 +577,13 @@ def _route(self, path: str) -> None:
# Now call the routed method!
res = caller(*self.args, **kwargs)

if self.method == "options":
# OPTIONS request doesn't have a body
del self.response.app_iter
del self.response.content_type
self.response.status = "204 No Content"
return

if not isinstance(res, bytes): # Convert the result to bytes if it is not already!
res = str(res).encode("UTF-8")
self.response.write(res)
Expand Down
Loading