My agentic slop goes here. Not intended for anyone else!
1# JMAP Interface Usage Examples 2 3This document demonstrates how to construct and use all JMAP message types using **only the module interfaces**, without any manual JSON parsing or construction. 4 5All examples use the accessor functions and constructors (`v`) provided by the module signatures. 6 7## Table of Contents 8 91. [Core Protocol Examples](#core-protocol-examples) 102. [Mail Protocol Examples](#mail-protocol-examples) 113. [Client Usage Examples](#client-usage-examples) 124. [Complete Workflow Examples](#complete-workflow-examples) 13 14--- 15 16## Core Protocol Examples 17 18### Creating a JMAP Request 19 20```ocaml 21open Jmap_core 22 23(* Create capability URNs *) 24let core_cap = Jmap_capability.core 25let mail_cap = Jmap_capability.mail 26 27(* Create a request with method calls *) 28let request = Jmap_request.make 29 ~using:[core_cap; mail_cap] 30 [ 31 (* Method calls go here - see below *) 32 ] 33 34(* Accessing request fields *) 35let caps = Jmap_request.using request 36let calls = Jmap_request.method_calls request 37let created = Jmap_request.created_ids request 38``` 39 40### Working with Primitives 41 42```ocaml 43open Jmap_primitives 44 45(* Create IDs *) 46let account_id = Jmap_id.of_string "account123" 47let mailbox_id = Jmap_id.of_string "mailbox456" 48 49(* Create integers *) 50let limit = UnsignedInt.of_int 50 51let offset = Int53.of_int 0 52 53(* Create dates *) 54let now = UTCDate.now () 55let sent_date = Date.of_string "2024-01-15T10:30:00Z" 56 57(* Access values *) 58let limit_int = UnsignedInt.to_int limit 59let date_str = UTCDate.to_string now 60``` 61 62### Building Filters 63 64```ocaml 65open Jmap_filter 66 67(* Simple condition filter *) 68let simple_filter = condition { field = "value" } 69 70(* Complex AND filter *) 71let and_filter = and_ [ 72 condition { has_keyword = Some "$flagged" }; 73 condition { after = Some now }; 74] 75 76(* Complex nested filter *) 77let complex_filter = and_ [ 78 or_ [ 79 condition { from = Some "alice@example.com" }; 80 condition { from = Some "bob@example.com" }; 81 ]; 82 not_ (condition { has_keyword = Some "$seen" }) 83] 84``` 85 86### Building Sort Comparators 87 88```ocaml 89open Jmap_comparator 90 91(* Simple ascending sort *) 92let sort_by_date = v ~property:"receivedAt" () 93 94(* Descending sort with collation *) 95let sort_by_name = v 96 ~property:"name" 97 ~is_ascending:false 98 ~collation:"i;unicode-casemap" 99 () 100``` 101 102### Standard Method Requests 103 104#### Get Request 105 106```ocaml 107open Jmap_standard_methods.Get 108 109(* Get specific objects *) 110let get_req = v 111 ~account_id 112 ~ids:[mailbox_id; Jmap_id.of_string "mailbox789"] 113 ~properties:["id"; "name"; "totalEmails"] 114 () 115 116(* Get all objects with all properties *) 117let get_all_req = v ~account_id () 118 119(* Access request fields *) 120let acc_id = account_id get_req 121let ids_opt = ids get_req 122let props_opt = properties get_req 123``` 124 125#### Query Request 126 127```ocaml 128open Jmap_standard_methods.Query 129 130(* Complex query *) 131let query_req = v 132 ~account_id 133 ~filter:(Some complex_filter) 134 ~sort:(Some [sort_by_date; sort_by_name]) 135 ~position:(Some (Int53.of_int 0)) 136 ~limit:(Some (UnsignedInt.of_int 50)) 137 ~calculate_total:(Some true) 138 () 139``` 140 141#### Set Request 142 143```ocaml 144open Jmap_standard_methods.Set 145 146(* Create new objects *) 147let set_create_req = v 148 ~account_id 149 ~create:(Some [ 150 (Jmap_id.of_string "temp1", new_obj1); 151 (Jmap_id.of_string "temp2", new_obj2); 152 ]) 153 () 154 155(* Update objects using PatchObject *) 156let set_update_req = v 157 ~account_id 158 ~update:(Some [ 159 (existing_id, [ 160 ("name", Some (`String "New Name")); 161 ("archived", Some (`Bool true)); 162 ]); 163 ]) 164 () 165 166(* Destroy objects *) 167let set_destroy_req = v 168 ~account_id 169 ~destroy:(Some [id1; id2; id3]) 170 () 171 172(* Combined create/update/destroy with state check *) 173let set_combined_req = v 174 ~account_id 175 ~if_in_state:(Some "expectedState123") 176 ~create:(Some [...]) 177 ~update:(Some [...]) 178 ~destroy:(Some [...]) 179 () 180``` 181 182--- 183 184## Mail Protocol Examples 185 186### Creating a Mailbox 187 188```ocaml 189open Jmap_mail.Jmap_mailbox 190 191(* Create mailbox rights *) 192let full_rights = Rights.v 193 ~may_read_items:true 194 ~may_add_items:true 195 ~may_remove_items:true 196 ~may_set_seen:true 197 ~may_set_keywords:true 198 ~may_create_child:true 199 ~may_rename:true 200 ~may_delete:true 201 ~may_submit:true 202 203(* Create a mailbox *) 204let inbox = v 205 ~id:(Jmap_id.of_string "inbox123") 206 ~name:"Inbox" 207 ~role:(Some "inbox") 208 ~sort_order:(UnsignedInt.of_int 10) 209 ~total_emails:(UnsignedInt.of_int 1542) 210 ~unread_emails:(UnsignedInt.of_int 127) 211 ~total_threads:(UnsignedInt.of_int 890) 212 ~unread_threads:(UnsignedInt.of_int 95) 213 ~my_rights:full_rights 214 ~is_subscribed:true 215 () 216 217(* Create a sub-mailbox *) 218let archive = v 219 ~id:(Jmap_id.of_string "archive456") 220 ~name:"Archive" 221 ~parent_id:(Some (Jmap_id.of_string "inbox123")) 222 ~role:(Some "archive") 223 ~sort_order:(UnsignedInt.of_int 20) 224 ~total_emails:(UnsignedInt.of_int 5231) 225 ~unread_emails:(UnsignedInt.of_int 0) 226 ~total_threads:(UnsignedInt.of_int 3452) 227 ~unread_threads:(UnsignedInt.of_int 0) 228 ~my_rights:full_rights 229 ~is_subscribed:false 230 () 231 232(* Access mailbox fields *) 233let mailbox_name = name inbox 234let parent = parent_id archive 235let unread_count = UnsignedInt.to_int (unread_emails inbox) 236let can_delete = Rights.may_delete (my_rights inbox) 237``` 238 239### Mailbox Query with Filters 240 241```ocaml 242open Jmap_mail.Jmap_mailbox 243 244(* Create mailbox filter *) 245let filter = Filter.v 246 ~parent_id:(Some inbox_id) 247 ~has_any_role:(Some false) 248 ~is_subscribed:(Some true) 249 () 250 251(* Query with tree sorting *) 252let query_req = Query.v 253 ~account_id 254 ~filter:(Some (Jmap_filter.condition filter)) 255 ~sort_as_tree:(Some true) 256 ~filter_as_tree:(Some true) 257 () 258``` 259 260### Creating an Email 261 262```ocaml 263open Jmap_mail.Jmap_email 264 265(* Create email addresses *) 266let from_addr = EmailAddress.v 267 ~name:(Some "Alice Smith") 268 ~email:"alice@example.com" 269 270let to_addr = EmailAddress.v 271 ~email:"bob@example.com" (* name is optional *) 272 273(* Create a simple text email *) 274let text_body = BodyPart.v 275 ~part_id:(Some "1") 276 ~blob_id:(Some blob_id) 277 ~size:(UnsignedInt.of_int 1234) 278 ~headers:[] 279 ~type_:"text/plain" 280 ~charset:(Some "utf-8") 281 () 282 283(* Create email with all common fields *) 284let email = v 285 ~id:(Jmap_id.of_string "email123") 286 ~blob_id:(Jmap_id.of_string "blob456") 287 ~thread_id:(Jmap_id.of_string "thread789") 288 ~mailbox_ids:[(inbox_id, true); (drafts_id, true)] 289 ~keywords:[("$seen", true); ("$flagged", true)] 290 ~size:(UnsignedInt.of_int 15234) 291 ~received_at:(UTCDate.now ()) 292 ~from:(Some [from_addr]) 293 ~to_:(Some [to_addr]) 294 ~subject:(Some "Important Meeting") 295 ~sent_at:(Some sent_date) 296 ~text_body:(Some [text_body]) 297 ~has_attachment:false 298 ~preview:"This is a preview of the email..." 299 () 300 301(* Access email fields *) 302let subject = subject email 303let sender = from email 304let is_flagged = List.mem ("$flagged", true) (keywords email) 305``` 306 307### Email with Attachments 308 309```ocaml 310open Jmap_mail.Jmap_email 311 312(* Create multipart structure *) 313let text_part = BodyPart.v 314 ~part_id:(Some "1") 315 ~size:(UnsignedInt.of_int 500) 316 ~headers:[] 317 ~type_:"text/plain" 318 ~charset:(Some "utf-8") 319 () 320 321let attachment = BodyPart.v 322 ~part_id:(Some "2") 323 ~blob_id:(Some attachment_blob_id) 324 ~size:(UnsignedInt.of_int 45000) 325 ~headers:[] 326 ~name:(Some "document.pdf") 327 ~type_:"application/pdf" 328 ~disposition:(Some "attachment") 329 () 330 331let multipart = BodyPart.v 332 ~size:(UnsignedInt.of_int 45500) 333 ~headers:[] 334 ~type_:"multipart/mixed" 335 ~sub_parts:(Some [text_part; attachment]) 336 () 337 338let email_with_attachment = v 339 ~id 340 ~blob_id 341 ~thread_id 342 ~mailbox_ids:[(inbox_id, true)] 343 ~size:(UnsignedInt.of_int 45500) 344 ~received_at:(UTCDate.now ()) 345 ~body_structure:(Some multipart) 346 ~attachments:(Some [attachment]) 347 ~has_attachment:true 348 ~preview:"Email with PDF attachment" 349 () 350``` 351 352### Email Query with Complex Filters 353 354```ocaml 355open Jmap_mail.Jmap_email 356 357(* Create email filter *) 358let email_filter = Filter.v 359 ~in_mailbox:(Some inbox_id) 360 ~after:(Some (UTCDate.of_string "2024-01-01T00:00:00Z")) 361 ~has_keyword:(Some "$flagged") 362 ~not_keyword:(Some "$seen") 363 ~from:(Some "important@example.com") 364 ~has_attachment:(Some true) 365 () 366 367(* Create query with filter *) 368let email_query = Query.v 369 ~account_id 370 ~filter:(Some (Jmap_filter.condition email_filter)) 371 ~sort:(Some [ 372 Jmap_comparator.v ~property:"receivedAt" ~is_ascending:false () 373 ]) 374 ~collapse_threads:(Some true) 375 ~limit:(Some (UnsignedInt.of_int 25)) 376 () 377``` 378 379### Email Import 380 381```ocaml 382open Jmap_mail.Jmap_email.Import 383 384(* Create import email object *) 385let import_email = import_email_v 386 ~blob_id:raw_message_blob_id 387 ~mailbox_ids:[(inbox_id, true)] 388 ~keywords:[("$seen", true)] 389 () 390 391(* Create import request *) 392let import_req = request_v 393 ~account_id 394 ~emails:[ 395 (Jmap_id.of_string "import1", import_email); 396 ] 397 () 398``` 399 400### Creating an Identity 401 402```ocaml 403open Jmap_mail.Jmap_identity 404 405let signature_html = "<p>Best regards,<br>Alice Smith<br><i>CEO</i></p>" 406 407let identity = v 408 ~id:(Jmap_id.of_string "identity123") 409 ~name:"Work Identity" 410 ~email:"alice@company.com" 411 ~reply_to:(Some [EmailAddress.v ~email:"noreply@company.com"]) 412 ~bcc:(Some [EmailAddress.v ~email:"archive@company.com"]) 413 ~text_signature:"Best regards,\nAlice Smith\nCEO" 414 ~html_signature:signature_html 415 ~may_delete:true 416 () 417``` 418 419### Email Submission 420 421```ocaml 422open Jmap_mail.Jmap_email_submission 423 424(* Create SMTP envelope *) 425let envelope = Envelope.v 426 ~mail_from:(Address.v ~email:"alice@example.com" ()) 427 ~rcpt_to:[ 428 Address.v ~email:"bob@example.com" (); 429 Address.v ~email:"carol@example.com" (); 430 ] 431 432(* Create email submission *) 433let submission = v 434 ~id:(Jmap_id.of_string "submission123") 435 ~identity_id 436 ~email_id 437 ~thread_id 438 ~envelope:(Some envelope) 439 ~send_at:(UTCDate.now ()) 440 ~undo_status:"pending" 441 () 442 443(* Access submission state *) 444let can_undo = (undo_status submission) = "pending" 445let send_time = send_at submission 446``` 447 448### Vacation Response 449 450```ocaml 451open Jmap_mail.Jmap_vacation_response 452 453let vacation = v 454 ~id:(Jmap_id.of_string "singleton") 455 ~is_enabled:true 456 ~from_date:(Some (UTCDate.of_string "2024-12-20T00:00:00Z")) 457 ~to_date:(Some (UTCDate.of_string "2024-12-31T23:59:59Z")) 458 ~subject:(Some "Out of Office") 459 ~text_body:(Some "I'm away until January. Will respond when I return.") 460 ~html_body:(Some "<p>I'm away until January. Will respond when I return.</p>") 461 () 462 463(* Check if active *) 464let is_active = is_enabled vacation 465``` 466 467--- 468 469## Client Usage Examples 470 471### Creating a Client Connection 472 473```ocaml 474open Jmap_client 475open Jmap_connection 476 477(* Create custom config *) 478let config = config_v 479 ~max_retries:5 480 ~timeout:60.0 481 ~user_agent:"MyApp/1.0" 482 () 483 484(* Create connection with basic auth *) 485let conn = v 486 ~config 487 ~auth:(basic "user@example.com" "password123") 488 () 489 490(* Create connection with bearer token *) 491let oauth_conn = v 492 ~config 493 ~auth:(bearer "oauth_token_here") 494 () 495 496(* Access connection properties *) 497let max_retries = config_max_retries (config conn) 498let auth_method = auth conn 499``` 500 501--- 502 503## Complete Workflow Examples 504 505### Example 1: Fetch and Display Mailboxes 506 507```ocaml 508open Lwt.Syntax 509open Jmap_core 510open Jmap_mail.Jmap_mailbox 511 512let display_mailboxes client account_id = 513 (* Create get request *) 514 let get_req = Get.v ~account_id () 515 516 (* Create JMAP request *) 517 let request = Jmap_request.make 518 ~using:[Jmap_capability.core; Jmap_capability.mail] 519 [Jmap_invocation.Packed { 520 (* Would need to properly construct invocation *) 521 }] 522 in 523 524 (* Execute request *) 525 let* response = Jmap_client.call client request in 526 527 (* Process response *) 528 let method_responses = Jmap_response.method_responses response in 529 (* Extract mailboxes and display *) 530 531 Lwt.return_unit 532``` 533 534### Example 2: Search and Display Emails 535 536```ocaml 537open Jmap_mail.Jmap_email 538 539let search_flagged_emails client account_id inbox_id = 540 (* Build filter *) 541 let filter = Filter.v 542 ~in_mailbox:(Some inbox_id) 543 ~has_keyword:(Some "$flagged") 544 ~not_keyword:(Some "$seen") 545 () 546 547 (* Build query *) 548 let query = Query.v 549 ~account_id 550 ~filter:(Some (Jmap_filter.condition filter)) 551 ~sort:(Some [ 552 Jmap_comparator.v ~property:"receivedAt" ~is_ascending:false () 553 ]) 554 ~limit:(Some (UnsignedInt.of_int 20)) 555 () 556 557 (* Execute query and fetch results *) 558 (* ... *) 559``` 560 561### Example 3: Create and Send Email 562 563```ocaml 564open Jmap_mail 565 566let send_email client account_id identity_id = 567 (* Create email addresses *) 568 let from = Jmap_email.EmailAddress.v 569 ~name:(Some "Alice") 570 ~email:"alice@example.com" in 571 let to_ = Jmap_email.EmailAddress.v 572 ~email:"bob@example.com" in 573 574 (* Create email body *) 575 let body = Jmap_email.BodyPart.v 576 ~part_id:(Some "1") 577 ~size:(UnsignedInt.of_int 100) 578 ~headers:[] 579 ~type_:"text/plain" 580 ~charset:(Some "utf-8") 581 () 582 583 (* Create draft email *) 584 let email = Jmap_email.v 585 ~id:(Jmap_id.of_string "draft1") 586 ~blob_id:(Jmap_id.of_string "blob1") 587 ~thread_id:(Jmap_id.of_string "thread1") 588 ~mailbox_ids:[(Jmap_id.of_string "drafts", true)] 589 ~keywords:[("$draft", true)] 590 ~size:(UnsignedInt.of_int 100) 591 ~received_at:(UTCDate.now ()) 592 ~from:(Some [from]) 593 ~to_:(Some [to_]) 594 ~subject:(Some "Hello") 595 ~text_body:(Some [body]) 596 ~has_attachment:false 597 ~preview:"Hello, how are you?" 598 () 599 600 (* Create submission *) 601 let submission = Jmap_email_submission.v 602 ~id:(Jmap_id.of_string "sub1") 603 ~identity_id 604 ~email_id:(Jmap_email.id email) 605 ~thread_id:(Jmap_email.thread_id email) 606 ~send_at:(UTCDate.now ()) 607 ~undo_status:"pending" 608 () 609 610 (* Execute send *) 611 (* ... *) 612``` 613 614### Example 4: Update Mailbox Hierarchy 615 616```ocaml 617open Jmap_mail.Jmap_mailbox 618 619let reorganize_mailboxes client account_id = 620 (* Create new archive folder *) 621 let new_archive = v 622 ~id:(Jmap_id.of_string "temp1") 623 ~name:"Archive 2024" 624 ~parent_id:(Some (Jmap_id.of_string "archive")) 625 ~sort_order:(UnsignedInt.of_int 10) 626 ~total_emails:(UnsignedInt.of_int 0) 627 ~unread_emails:(UnsignedInt.of_int 0) 628 ~total_threads:(UnsignedInt.of_int 0) 629 ~unread_threads:(UnsignedInt.of_int 0) 630 ~my_rights:full_rights 631 ~is_subscribed:true 632 () 633 634 (* Create set request *) 635 let set_req = Jmap_standard_methods.Set.v 636 ~account_id 637 ~create:(Some [(Jmap_id.of_string "temp1", new_archive)]) 638 () 639 640 (* Execute *) 641 (* ... *) 642``` 643 644--- 645 646## Key Patterns 647 648### 1. **All Required Fields First** 649```ocaml 650let obj = v 651 ~required1 652 ~required2 653 ~optional1:(Some value) 654 () 655``` 656 657### 2. **Optional Fields Default to None** 658```ocaml 659let obj = v ~id ~name () (* All other fields are None/[] *) 660``` 661 662### 3. **Response Field Access** 663```ocaml 664(* For request/response pairs *) 665let req_id = account_id request 666let resp_id = response_account_id response 667``` 668 669### 4. **Submodule Pattern** 670```ocaml 671let rights = Rights.v ~may_read_items:true ~may_delete:false (...) 672let mailbox = v ~id ~name ~my_rights:rights () 673``` 674 675### 5. **Filter Composition** 676```ocaml 677let filter = Jmap_filter.and_ [ 678 condition { field1 = value1 }; 679 or_ [ 680 condition { field2 = value2 }; 681 condition { field3 = value3 }; 682 ] 683] 684``` 685 686--- 687 688## Summary 689 690This document shows that **all JMAP message types can be constructed using only the module interfaces** with: 691 692- ✅ Type-safe constructors with labeled arguments 693- ✅ Clear separation of required vs optional fields 694- ✅ Accessor functions for all fields 695- ✅ No manual JSON manipulation required 696- ✅ Composable filters and queries 697- ✅ Complete coverage of core and mail protocols 698 699The interface design ensures compile-time safety while remaining flexible and ergonomic for all JMAP use cases.