this repo has no description
at main 12 kB view raw
1(*--------------------------------------------------------------------------- 2 Copyright (c) 2025 Anil Madhavapeddy. All rights reserved. 3 SPDX-License-Identifier: ISC 4 ---------------------------------------------------------------------------*) 5 6(** High-level JMAP client using Requests 7 8 This module provides a full-featured JMAP client with session management, 9 request execution, and blob upload/download capabilities. *) 10 11(** {1 Types} *) 12 13type t 14(** A JMAP client with session state and HTTP connection management. *) 15 16type error = 17 | Http_error of int * string 18 (** HTTP error with status code and message. *) 19 | Jmap_error of Jmap_proto.Error.Request_error.t 20 (** JMAP protocol error at request level. *) 21 | Json_error of Jsont.Error.t 22 (** JSON encoding/decoding error. *) 23 | Session_error of string 24 (** Session fetch or parse error. *) 25 | Connection_error of string 26 (** Network connection error. *) 27(** Error types that can occur during JMAP operations. *) 28 29val pp_error : Format.formatter -> error -> unit 30(** Pretty-print an error. *) 31 32val error_to_string : error -> string 33(** Convert an error to a string. *) 34 35exception Jmap_client_error of error 36(** Exception wrapper for JMAP client errors. *) 37 38(** {1 Client Creation} *) 39 40val create : 41 ?auth:Requests.Auth.t -> 42 session:Jmap_proto.Session.t -> 43 Requests.t -> 44 t 45(** [create ?auth ~session requests] creates a JMAP client from an existing 46 session and Requests instance. 47 48 @param auth Authentication to use for requests. 49 @param session A pre-fetched JMAP session. 50 @param requests The Requests instance for HTTP operations. *) 51 52val create_from_url : 53 ?auth:Requests.Auth.t -> 54 Requests.t -> 55 string -> 56 (t, error) result 57(** [create_from_url ?auth requests url] creates a JMAP client by fetching 58 the session from the given JMAP API URL or well-known URL. 59 60 The URL can be either: 61 - A direct JMAP API URL (e.g., "https://api.example.com/jmap/") 62 - A well-known URL (e.g., "https://example.com/.well-known/jmap") 63 64 @param auth Authentication to use for the session request and subsequent requests. 65 @param requests The Requests instance for HTTP operations. 66 @param url The JMAP API or well-known URL. *) 67 68val create_from_url_exn : 69 ?auth:Requests.Auth.t -> 70 Requests.t -> 71 string -> 72 t 73(** [create_from_url_exn ?auth requests url] is like {!create_from_url} but 74 raises {!Jmap_client_error} on failure. *) 75 76(** {1 Session Access} *) 77 78val session : t -> Jmap_proto.Session.t 79(** [session client] returns the current JMAP session. *) 80 81val refresh_session : t -> (unit, error) result 82(** [refresh_session client] fetches a fresh session from the server and 83 updates the client's session state. *) 84 85val refresh_session_exn : t -> unit 86(** [refresh_session_exn client] is like {!refresh_session} but raises on error. *) 87 88val api_url : t -> string 89(** [api_url client] returns the JMAP API URL for this client. *) 90 91val upload_url : t -> string 92(** [upload_url client] returns the blob upload URL template. *) 93 94val download_url : t -> string 95(** [download_url client] returns the blob download URL template. *) 96 97(** {1 Request Execution} *) 98 99val request : 100 t -> 101 Jmap_proto.Request.t -> 102 (Jmap_proto.Response.t, error) result 103(** [request client req] executes a JMAP request and returns the response. *) 104 105val request_exn : 106 t -> 107 Jmap_proto.Request.t -> 108 Jmap_proto.Response.t 109(** [request_exn client req] is like {!request} but raises on error. *) 110 111(** {1 Blob Operations} *) 112 113val upload : 114 t -> 115 account_id:Jmap_proto.Id.t -> 116 content_type:string -> 117 data:string -> 118 (Jmap_proto.Blob.upload_response, error) result 119(** [upload client ~account_id ~content_type ~data] uploads a blob. 120 121 @param account_id The account to upload to. 122 @param content_type MIME type of the blob. 123 @param data The blob data as a string. *) 124 125val upload_exn : 126 t -> 127 account_id:Jmap_proto.Id.t -> 128 content_type:string -> 129 data:string -> 130 Jmap_proto.Blob.upload_response 131(** [upload_exn client ~account_id ~content_type ~data] is like {!upload} 132 but raises on error. *) 133 134val download : 135 t -> 136 account_id:Jmap_proto.Id.t -> 137 blob_id:Jmap_proto.Id.t -> 138 ?name:string -> 139 ?accept:string -> 140 unit -> 141 (string, error) result 142(** [download client ~account_id ~blob_id ?name ?accept ()] downloads a blob. 143 144 @param account_id The account containing the blob. 145 @param blob_id The blob ID to download. 146 @param name Optional filename hint for Content-Disposition. 147 @param accept Optional Accept header value. *) 148 149val download_exn : 150 t -> 151 account_id:Jmap_proto.Id.t -> 152 blob_id:Jmap_proto.Id.t -> 153 ?name:string -> 154 ?accept:string -> 155 unit -> 156 string 157(** [download_exn] is like {!download} but raises on error. *) 158 159(** {1 Convenience Builders} 160 161 Helper functions for building common JMAP method invocations. *) 162 163module Build : sig 164 (** {2 Core Methods} *) 165 166 val echo : 167 call_id:string -> 168 Jsont.json -> 169 Jmap_proto.Invocation.t 170 (** [echo ~call_id data] builds a Core/echo invocation. *) 171 172 (** {2 Mailbox Methods} *) 173 174 val mailbox_get : 175 call_id:string -> 176 account_id:Jmap_proto.Id.t -> 177 ?ids:Jmap_proto.Id.t list -> 178 ?properties:string list -> 179 unit -> 180 Jmap_proto.Invocation.t 181 (** [mailbox_get ~call_id ~account_id ?ids ?properties ()] builds a 182 Mailbox/get invocation. *) 183 184 val mailbox_changes : 185 call_id:string -> 186 account_id:Jmap_proto.Id.t -> 187 since_state:string -> 188 ?max_changes:int64 -> 189 unit -> 190 Jmap_proto.Invocation.t 191 (** [mailbox_changes ~call_id ~account_id ~since_state ?max_changes ()] 192 builds a Mailbox/changes invocation. *) 193 194 val mailbox_query : 195 call_id:string -> 196 account_id:Jmap_proto.Id.t -> 197 ?filter:Jmap_mail.Mail_filter.mailbox_filter -> 198 ?sort:Jmap_proto.Filter.comparator list -> 199 ?position:int64 -> 200 ?limit:int64 -> 201 unit -> 202 Jmap_proto.Invocation.t 203 (** [mailbox_query ~call_id ~account_id ?filter ?sort ?position ?limit ()] 204 builds a Mailbox/query invocation. *) 205 206 (** {2 Email Methods} *) 207 208 val email_get : 209 call_id:string -> 210 account_id:Jmap_proto.Id.t -> 211 ?ids:Jmap_proto.Id.t list -> 212 ?properties:string list -> 213 ?body_properties:string list -> 214 ?fetch_text_body_values:bool -> 215 ?fetch_html_body_values:bool -> 216 ?fetch_all_body_values:bool -> 217 ?max_body_value_bytes:int64 -> 218 unit -> 219 Jmap_proto.Invocation.t 220 (** [email_get ~call_id ~account_id ?ids ?properties ...] builds an 221 Email/get invocation. *) 222 223 val email_changes : 224 call_id:string -> 225 account_id:Jmap_proto.Id.t -> 226 since_state:string -> 227 ?max_changes:int64 -> 228 unit -> 229 Jmap_proto.Invocation.t 230 (** [email_changes ~call_id ~account_id ~since_state ?max_changes ()] 231 builds an Email/changes invocation. *) 232 233 val email_query : 234 call_id:string -> 235 account_id:Jmap_proto.Id.t -> 236 ?filter:Jmap_mail.Mail_filter.email_filter -> 237 ?sort:Jmap_proto.Filter.comparator list -> 238 ?position:int64 -> 239 ?limit:int64 -> 240 ?collapse_threads:bool -> 241 unit -> 242 Jmap_proto.Invocation.t 243 (** [email_query ~call_id ~account_id ?filter ?sort ?position ?limit 244 ?collapse_threads ()] builds an Email/query invocation. *) 245 246 (** {2 Thread Methods} *) 247 248 val thread_get : 249 call_id:string -> 250 account_id:Jmap_proto.Id.t -> 251 ?ids:Jmap_proto.Id.t list -> 252 unit -> 253 Jmap_proto.Invocation.t 254 (** [thread_get ~call_id ~account_id ?ids ()] builds a Thread/get invocation. *) 255 256 val thread_changes : 257 call_id:string -> 258 account_id:Jmap_proto.Id.t -> 259 since_state:string -> 260 ?max_changes:int64 -> 261 unit -> 262 Jmap_proto.Invocation.t 263 (** [thread_changes ~call_id ~account_id ~since_state ?max_changes ()] 264 builds a Thread/changes invocation. *) 265 266 (** {2 Identity Methods} *) 267 268 val identity_get : 269 call_id:string -> 270 account_id:Jmap_proto.Id.t -> 271 ?ids:Jmap_proto.Id.t list -> 272 ?properties:string list -> 273 unit -> 274 Jmap_proto.Invocation.t 275 (** [identity_get ~call_id ~account_id ?ids ?properties ()] builds an 276 Identity/get invocation. *) 277 278 (** {2 Submission Methods} *) 279 280 val email_submission_get : 281 call_id:string -> 282 account_id:Jmap_proto.Id.t -> 283 ?ids:Jmap_proto.Id.t list -> 284 ?properties:string list -> 285 unit -> 286 Jmap_proto.Invocation.t 287 (** [email_submission_get ~call_id ~account_id ?ids ?properties ()] 288 builds an EmailSubmission/get invocation. *) 289 290 val email_submission_query : 291 call_id:string -> 292 account_id:Jmap_proto.Id.t -> 293 ?filter:Jmap_mail.Mail_filter.submission_filter -> 294 ?sort:Jmap_proto.Filter.comparator list -> 295 ?position:int64 -> 296 ?limit:int64 -> 297 unit -> 298 Jmap_proto.Invocation.t 299 (** [email_submission_query ~call_id ~account_id ?filter ?sort ?position 300 ?limit ()] builds an EmailSubmission/query invocation. *) 301 302 (** {2 Vacation Response Methods} *) 303 304 val vacation_response_get : 305 call_id:string -> 306 account_id:Jmap_proto.Id.t -> 307 unit -> 308 Jmap_proto.Invocation.t 309 (** [vacation_response_get ~call_id ~account_id ()] builds a 310 VacationResponse/get invocation. The singleton ID is automatically used. *) 311 312 (** {2 Request Building} *) 313 314 val make_request : 315 ?created_ids:(Jmap_proto.Id.t * Jmap_proto.Id.t) list -> 316 capabilities:string list -> 317 Jmap_proto.Invocation.t list -> 318 Jmap_proto.Request.t 319 (** [make_request ?created_ids ~capabilities invocations] builds a JMAP request. 320 321 @param created_ids Optional client-created ID mappings. 322 @param capabilities List of capability URIs to use. 323 @param invocations List of method invocations. *) 324end 325 326(** {1 Response Parsing} 327 328 Helper functions for parsing typed responses from JMAP invocations. *) 329 330module Parse : sig 331 val find_invocation : 332 call_id:string -> 333 Jmap_proto.Response.t -> 334 Jmap_proto.Invocation.t option 335 (** [find_invocation ~call_id response] finds an invocation by call ID. *) 336 337 val get_invocation_exn : 338 call_id:string -> 339 Jmap_proto.Response.t -> 340 Jmap_proto.Invocation.t 341 (** [get_invocation_exn ~call_id response] finds an invocation by call ID. 342 @raise Failure if not found. *) 343 344 val parse_invocation : 345 'a Jsont.t -> 346 Jmap_proto.Invocation.t -> 347 ('a, Jsont.Error.t) result 348 (** [parse_invocation jsont inv] decodes the invocation's arguments. *) 349 350 val parse_response : 351 call_id:string -> 352 'a Jsont.t -> 353 Jmap_proto.Response.t -> 354 ('a, Jsont.Error.t) result 355 (** [parse_response ~call_id jsont response] finds and parses an invocation. *) 356 357 (** {2 Typed Response Codecs} *) 358 359 val get_response : 'a Jsont.t -> 'a Jmap_proto.Method.get_response Jsont.t 360 (** [get_response obj_jsont] creates a Foo/get response codec. *) 361 362 val query_response : Jmap_proto.Method.query_response Jsont.t 363 (** Codec for Foo/query responses. *) 364 365 val changes_response : Jmap_proto.Method.changes_response Jsont.t 366 (** Codec for Foo/changes responses. *) 367 368 val set_response : 'a Jsont.t -> 'a Jmap_proto.Method.set_response Jsont.t 369 (** [set_response obj_jsont] creates a Foo/set response codec. *) 370 371 (** {2 Mail-specific Codecs} *) 372 373 val mailbox_get_response : Jmap_mail.Mailbox.t Jmap_proto.Method.get_response Jsont.t 374 val email_get_response : Jmap_mail.Email.t Jmap_proto.Method.get_response Jsont.t 375 val thread_get_response : Jmap_mail.Thread.t Jmap_proto.Method.get_response Jsont.t 376 val identity_get_response : Jmap_mail.Identity.t Jmap_proto.Method.get_response Jsont.t 377 378 (** {2 Convenience Parsers} *) 379 380 val parse_mailbox_get : 381 call_id:string -> 382 Jmap_proto.Response.t -> 383 (Jmap_mail.Mailbox.t Jmap_proto.Method.get_response, Jsont.Error.t) result 384 385 val parse_email_get : 386 call_id:string -> 387 Jmap_proto.Response.t -> 388 (Jmap_mail.Email.t Jmap_proto.Method.get_response, Jsont.Error.t) result 389 390 val parse_email_query : 391 call_id:string -> 392 Jmap_proto.Response.t -> 393 (Jmap_proto.Method.query_response, Jsont.Error.t) result 394 395 val parse_thread_get : 396 call_id:string -> 397 Jmap_proto.Response.t -> 398 (Jmap_mail.Thread.t Jmap_proto.Method.get_response, Jsont.Error.t) result 399 400 val parse_changes : 401 call_id:string -> 402 Jmap_proto.Response.t -> 403 (Jmap_proto.Method.changes_response, Jsont.Error.t) result 404end