envelope.mojo (4839B)
1 from std.collections import Optional 2 3 from json import Value, loads 4 from json.deserialize import Deserializable, get_string 5 6 from hyf_core.metadata import hyf_protocol_version 7 from hyf_core.request_context import RequestContext, parse_request_context 8 from hyf_stdio.errors import WireError 9 10 11 def _require_object(value: Value, context: String) raises: 12 if not value.is_object(): 13 raise Error(context + " must be a JSON object") 14 15 16 def _require_non_empty(value: String, field_name: String) raises: 17 if value == "": 18 raise Error( 19 "request envelope field '" + field_name + "' must not be empty" 20 ) 21 22 23 def _require_request_keys(value: Value) raises: 24 for key in value.object_keys(): 25 if ( 26 key != "version" 27 and key != "request_id" 28 and key != "trace_id" 29 and key != "capability" 30 and key != "context" 31 and key != "input" 32 ): 33 raise Error( 34 "request envelope contains unexpected field '" + key + "'" 35 ) 36 37 38 def _has_key(value: Value, key: String) -> Bool: 39 for candidate in value.object_keys(): 40 if candidate == key: 41 return True 42 return False 43 44 45 def _require_protocol_version(json: Value) raises -> Int: 46 if not _has_key(json, "version"): 47 raise Error("request envelope field 'version' is required") 48 49 var version = json["version"] 50 if not version.is_int(): 51 raise Error("request envelope field 'version' must be an integer") 52 53 var version_value = Int(version.int_value()) 54 if version_value != hyf_protocol_version(): 55 raise Error( 56 "request envelope version " 57 + String(version_value) 58 + " is unsupported; expected " 59 + String(hyf_protocol_version()) 60 ) 61 return version_value 62 63 64 def _parse_optional_trace_id(json: Value) raises -> Optional[String]: 65 if not _has_key(json, "trace_id"): 66 return None 67 68 var trace_id = get_string(json, "trace_id") 69 _require_non_empty(trace_id, "trace_id") 70 return String(trace_id) 71 72 73 def _require_input_value(json: Value) raises -> Value: 74 if not _has_key(json, "input"): 75 raise Error("request envelope field 'input' is required") 76 77 var input = json["input"] 78 if not input.is_object(): 79 raise Error("request envelope field 'input' must be a JSON object") 80 return input.clone() 81 82 83 @fieldwise_init 84 struct WireRequest(Deserializable, Copyable, Movable): 85 var version: Int 86 var request_id: String 87 var trace_id: Optional[String] 88 var capability: String 89 var context: RequestContext 90 var input: Value 91 92 @staticmethod 93 def from_json(json: Value) raises -> Self: 94 _require_object(json, "request envelope") 95 _require_request_keys(json) 96 var version = _require_protocol_version(json) 97 98 var request_id = get_string(json, "request_id") 99 _require_non_empty(request_id, "request_id") 100 101 var trace_id = _parse_optional_trace_id(json) 102 103 var capability = get_string(json, "capability") 104 _require_non_empty(capability, "capability") 105 106 var context_json = Value(None) 107 if _has_key(json, "context"): 108 context_json = json["context"].clone() 109 110 var context = parse_request_context(context_json) 111 var input = _require_input_value(json) 112 113 return Self( 114 version=version, 115 request_id=request_id, 116 trace_id=trace_id^, 117 capability=capability, 118 context=context^, 119 input=input^, 120 ) 121 122 123 @fieldwise_init 124 struct WireSuccessResponse(Copyable, Movable): 125 var version: Int 126 var request_id: String 127 var trace_id: Optional[String] 128 var output: Value 129 var meta: Optional[Value] 130 131 def to_json_value(self) raises -> Value: 132 var value = loads("{}") 133 value.set("version", Value(self.version)) 134 value.set("request_id", Value(String(self.request_id))) 135 if self.trace_id: 136 value.set("trace_id", Value(String(self.trace_id.value()))) 137 value.set("ok", Value(True)) 138 value.set("output", self.output.clone()) 139 if self.meta: 140 value.set("meta", self.meta.value().clone()) 141 return value^ 142 143 144 @fieldwise_init 145 struct WireErrorResponse(Copyable, Movable): 146 var version: Int 147 var request_id: String 148 var trace_id: Optional[String] 149 var error: WireError 150 151 def to_json_value(self) raises -> Value: 152 var value = loads("{}") 153 value.set("version", Value(self.version)) 154 value.set("request_id", Value(String(self.request_id))) 155 if self.trace_id: 156 value.set("trace_id", Value(String(self.trace_id.value()))) 157 value.set("ok", Value(False)) 158 value.set("error", self.error.to_json_value()) 159 return value^