(** JMAP Request Object A Request object represents a single HTTP POST to the JMAP API endpoint. It contains capabilities the client wants to use and a list of method calls. Reference: RFC 8620 Section 3.3 Test files: - test/data/core/request_echo.json - test/data/core/request_get.json - All request_*.json files *) (** Main request type *) type t = { using : Jmap_capability.t list; method_calls : Jmap_invocation.invocation_list; created_ids : (Jmap_id.t * Jmap_id.t) list option; } (** Accessors *) let using t = t.using let method_calls t = t.method_calls let created_ids t = t.created_ids (** Create a request *) let make ?(created_ids=None) ~using method_calls = { using; method_calls; created_ids } (** Parser submodule *) module Parser = struct (** Parse request from JSON value. Test files: test/data/core/request_*.json *) let of_json json = match json with | `O fields -> let get_field name = match List.assoc_opt name fields with | Some v -> v | None -> raise (Jmap_error.Parse_error (Printf.sprintf "Missing field: %s" name)) in (* Parse using *) let using = match get_field "using" with | `A caps -> List.map (function | `String cap -> Jmap_capability.of_string cap | _ -> raise (Jmap_error.Parse_error "using values must be strings") ) caps | _ -> raise (Jmap_error.Parse_error "using must be an array") in (* Parse methodCalls *) let method_calls = match get_field "methodCalls" with | `A calls -> List.map Jmap_invocation.of_json calls | _ -> raise (Jmap_error.Parse_error "methodCalls must be an array") in (* Parse createdIds (optional) *) let created_ids = match List.assoc_opt "createdIds" fields with | Some (`O ids) -> Some (List.map (fun (k, v) -> match v with | `String id -> (Jmap_id.of_string k, Jmap_id.of_string id) | _ -> raise (Jmap_error.Parse_error "createdIds values must be strings") ) ids) | Some _ -> raise (Jmap_error.Parse_error "createdIds must be an object") | None -> None in { using; method_calls; created_ids } | _ -> raise (Jmap_error.Parse_error "Request must be a JSON object") (** Parse request from JSON string *) let of_string s = try of_json (Ezjsonm.from_string s) with | Ezjsonm.Parse_error (_, msg) -> raise (Jmap_error.Parse_error ("Invalid JSON: " ^ msg)) (** Parse request from input channel *) let of_channel ic = try of_json (Ezjsonm.from_channel ic) with | Ezjsonm.Parse_error (_, msg) -> raise (Jmap_error.Parse_error ("Invalid JSON: " ^ msg)) end (** Serialization *) let to_json t = let using_json = `A (List.map (fun cap -> `String (Jmap_capability.to_string cap) ) t.using) in let method_calls_json = `A (List.map (fun (Jmap_invocation.Packed inv) -> Jmap_invocation.to_json inv ) t.method_calls) in let fields = [ ("using", using_json); ("methodCalls", method_calls_json); ] in let fields = match t.created_ids with | Some ids -> let ids_json = `O (List.map (fun (k, v) -> (Jmap_id.to_string k, `String (Jmap_id.to_string v)) ) ids) in fields @ [("createdIds", ids_json)] | None -> fields in `O fields