Coverage for /home/runner/work/viur-core/viur-core/viur/src/viur/core/render/json/default.py: 0%

82 statements  

« prev     ^ index     » next       coverage.py v7.10.7, created at 2025-09-29 09:00 +0000

1import json 

2import typing as t 

3import logging 

4from enum import Enum 

5from viur.core import db, current 

6from viur.core.render.abstract import AbstractRenderer 

7from viur.core.skeleton import SkeletonInstance, SkelList 

8from viur.core.i18n import translate 

9from viur.core.config import conf 

10from datetime import datetime 

11from deprecated.sphinx import deprecated 

12 

13 

14# VIUR4: Remove this piece of sh.. 

15class CustomJsonEncoder(json.JSONEncoder): 

16 """ 

17 This custom JSON-Encoder for this json-render ensures that translations are evaluated and can be dumped. 

18 """ 

19 

20 def default(self, o: t.Any) -> t.Any: 

21 

22 if isinstance(o, translate): 

23 return str(o) 

24 elif isinstance(o, datetime): 

25 return o.isoformat() 

26 elif isinstance(o, db.Key): 

27 return str(o) 

28 elif isinstance(o, Enum): 

29 return o.value 

30 elif isinstance(o, set): 

31 return tuple(o) 

32 elif isinstance(o, SkeletonInstance): 

33 return {bone_name: o[bone_name] for bone_name in o} 

34 return json.JSONEncoder.default(self, o) 

35 

36 

37class DefaultRender(AbstractRenderer): 

38 kind = "json" 

39 

40 @staticmethod 

41 def render_structure(structure: dict): 

42 """ 

43 Performs structure rewriting according to VIUR2/3 compatibility flags. 

44 #FIXME: Remove this entire function with VIUR4 

45 """ 

46 for struct in structure.values(): 

47 # Optionally replace new-key by a copy of the value under the old-key 

48 if "json.bone.structure.camelcasenames" in conf.compatibility: 

49 for find, replace in { 

50 "boundslat": "boundsLat", 

51 "boundslng": "boundsLng", 

52 "emptyvalue": "emptyValue", 

53 "max": "maxAmount", 

54 "maxlength": "maxLength", 

55 "min": "minAmount", 

56 "preventduplicates": "preventDuplicates", 

57 "readonly": "readOnly", 

58 "valid_html": "validHtml", 

59 "valid_mime_types": "validMimeTypes", 

60 }.items(): 

61 if find in struct: 

62 struct[replace] = struct[find] 

63 

64 # Call render_structure() recursively on "using" and "relskel" members. 

65 for substruct in ("using", "relskel"): 

66 if substruct in struct and struct[substruct]: 

67 struct[substruct] = DefaultRender.render_structure(struct[substruct]) 

68 

69 # Optionally return list of tuples instead of dict 

70 if "json.bone.structure.keytuples" in conf.compatibility: 

71 return [(key, struct) for key, struct in structure.items()] 

72 

73 return structure 

74 

75 @deprecated(version="3.8.0", reason="Just use `skel.dump()` for this now") 

76 def renderSkelValues(self, skel: SkeletonInstance): 

77 logging.warning( 

78 "DefaultRender.renderSkelValues() is obsolete, just use `skel.dump()` for it now!", 

79 stacklevel=3, 

80 ) 

81 return skel.dump() 

82 

83 def renderEntry(self, skel: SkeletonInstance, actionName, params=None, *, next_url: t.Optional[str] = None): 

84 structure = None 

85 errors = None 

86 

87 if isinstance(skel, SkeletonInstance): 

88 vals = skel.dump() 

89 structure = DefaultRender.render_structure(skel.structure()) 

90 errors = [{ 

91 "error": error.severity.name.upper(), 

92 "errorMessage": error.errorMessage, 

93 "fieldPath": error.fieldPath, 

94 "invalidatedFields": error.invalidatedFields, 

95 "severity": error.severity.value, 

96 } for error in skel.errors] 

97 

98 else: 

99 # VIUR4 DEPRECATION 

100 logging.warning(f"Passing a {type(skel)!r} here is invalid. It should be a SkeletonInstance.") 

101 if isinstance(skel, (list, tuple)): 

102 raise ValueError("Cannot handle lists here") 

103 

104 vals = skel # VIUR4 DEPRECATION!!! 

105 

106 res = { 

107 "action": actionName, 

108 "values": vals, 

109 "structure": structure, 

110 "errors": errors, 

111 "next_url": next_url, 

112 "params": params, 

113 } 

114 

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

116 return json.dumps(res, cls=CustomJsonEncoder) 

117 

118 def view(self, skel: SkeletonInstance, action: str = "view", params=None, **kwargs): 

119 return self.renderEntry(skel, action, params) 

120 

121 def list(self, skellist: SkelList, action: str = "list", params=None, **kwargs): 

122 if not isinstance(skellist, SkelList): 

123 raise ValueError("Function requires a SkelList") 

124 

125 res = { 

126 "action": action, 

127 "cursor": skellist.getCursor() if skellist else None, 

128 "params": params, 

129 "skellist": [item.dump() for item in skellist], 

130 "structure": 

131 DefaultRender.render_structure(skellist[0].structure()) 

132 if skellist and "json.bone.structure.inlists" in conf.compatibility 

133 else None, 

134 "orders": skellist.get_orders() if skellist else None, 

135 } 

136 

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

138 return json.dumps(res, cls=CustomJsonEncoder) 

139 

140 def add(self, skel: SkeletonInstance, action: str = "add", params=None, **kwargs): 

141 return self.renderEntry(skel, action, params) 

142 

143 def edit(self, skel: SkeletonInstance, action: str = "edit", params=None, **kwargs): 

144 return self.renderEntry(skel, action, params) 

145 

146 def editSuccess(self, skel: SkeletonInstance, action: str = "editSuccess", params=None, **kwargs): 

147 return self.renderEntry(skel, action, params) 

148 

149 def addSuccess(self, skel: SkeletonInstance, action: str = "addSuccess", params=None, **kwargs): 

150 return self.renderEntry(skel, action, params) 

151 

152 def deleteSuccess(self, skel: SkeletonInstance, params=None, *args, **kwargs): 

153 return json.dumps("OKAY") 

154 

155 def listRootNodes(self, rootNodes, *args, **kwargs): 

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

157 return json.dumps(rootNodes, cls=CustomJsonEncoder) 

158 

159 def render( 

160 self, 

161 action: str, 

162 skel: t.Optional[SkeletonInstance] = None, 

163 *, 

164 next_url: t.Optional[str] = None, 

165 **kwargs 

166 ): 

167 """ 

168 Universal rendering function. 

169 

170 Handles an action and a skeleton. It shall be used by any action, in future. 

171 """ 

172 return self.renderEntry(skel, action, next_url=next_url, params=kwargs)