hyf

Context-aware query service for Radroots
git clone https://radroots.dev/git/hyf.git
Log | Files | Refs | README | LICENSE

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^