# JMAP Interface Usage Examples This document demonstrates how to construct and use all JMAP message types using **only the module interfaces**, without any manual JSON parsing or construction. All examples use the accessor functions and constructors (`v`) provided by the module signatures. ## Table of Contents 1. [Core Protocol Examples](#core-protocol-examples) 2. [Mail Protocol Examples](#mail-protocol-examples) 3. [Client Usage Examples](#client-usage-examples) 4. [Complete Workflow Examples](#complete-workflow-examples) --- ## Core Protocol Examples ### Creating a JMAP Request ```ocaml open Jmap_core (* Create capability URNs *) let core_cap = Jmap_capability.core let mail_cap = Jmap_capability.mail (* Create a request with method calls *) let request = Jmap_request.make ~using:[core_cap; mail_cap] [ (* Method calls go here - see below *) ] (* Accessing request fields *) let caps = Jmap_request.using request let calls = Jmap_request.method_calls request let created = Jmap_request.created_ids request ``` ### Working with Primitives ```ocaml open Jmap_primitives (* Create IDs *) let account_id = Jmap_id.of_string "account123" let mailbox_id = Jmap_id.of_string "mailbox456" (* Create integers *) let limit = UnsignedInt.of_int 50 let offset = Int53.of_int 0 (* Create dates *) let now = UTCDate.now () let sent_date = Date.of_string "2024-01-15T10:30:00Z" (* Access values *) let limit_int = UnsignedInt.to_int limit let date_str = UTCDate.to_string now ``` ### Building Filters ```ocaml open Jmap_filter (* Simple condition filter *) let simple_filter = condition { field = "value" } (* Complex AND filter *) let and_filter = and_ [ condition { has_keyword = Some "$flagged" }; condition { after = Some now }; ] (* Complex nested filter *) let complex_filter = and_ [ or_ [ condition { from = Some "alice@example.com" }; condition { from = Some "bob@example.com" }; ]; not_ (condition { has_keyword = Some "$seen" }) ] ``` ### Building Sort Comparators ```ocaml open Jmap_comparator (* Simple ascending sort *) let sort_by_date = v ~property:"receivedAt" () (* Descending sort with collation *) let sort_by_name = v ~property:"name" ~is_ascending:false ~collation:"i;unicode-casemap" () ``` ### Standard Method Requests #### Get Request ```ocaml open Jmap_standard_methods.Get (* Get specific objects *) let get_req = v ~account_id ~ids:[mailbox_id; Jmap_id.of_string "mailbox789"] ~properties:["id"; "name"; "totalEmails"] () (* Get all objects with all properties *) let get_all_req = v ~account_id () (* Access request fields *) let acc_id = account_id get_req let ids_opt = ids get_req let props_opt = properties get_req ``` #### Query Request ```ocaml open Jmap_standard_methods.Query (* Complex query *) let query_req = v ~account_id ~filter:(Some complex_filter) ~sort:(Some [sort_by_date; sort_by_name]) ~position:(Some (Int53.of_int 0)) ~limit:(Some (UnsignedInt.of_int 50)) ~calculate_total:(Some true) () ``` #### Set Request ```ocaml open Jmap_standard_methods.Set (* Create new objects *) let set_create_req = v ~account_id ~create:(Some [ (Jmap_id.of_string "temp1", new_obj1); (Jmap_id.of_string "temp2", new_obj2); ]) () (* Update objects using PatchObject *) let set_update_req = v ~account_id ~update:(Some [ (existing_id, [ ("name", Some (`String "New Name")); ("archived", Some (`Bool true)); ]); ]) () (* Destroy objects *) let set_destroy_req = v ~account_id ~destroy:(Some [id1; id2; id3]) () (* Combined create/update/destroy with state check *) let set_combined_req = v ~account_id ~if_in_state:(Some "expectedState123") ~create:(Some [...]) ~update:(Some [...]) ~destroy:(Some [...]) () ``` --- ## Mail Protocol Examples ### Creating a Mailbox ```ocaml open Jmap_mail.Jmap_mailbox (* Create mailbox rights *) let full_rights = Rights.v ~may_read_items:true ~may_add_items:true ~may_remove_items:true ~may_set_seen:true ~may_set_keywords:true ~may_create_child:true ~may_rename:true ~may_delete:true ~may_submit:true (* Create a mailbox *) let inbox = v ~id:(Jmap_id.of_string "inbox123") ~name:"Inbox" ~role:(Some "inbox") ~sort_order:(UnsignedInt.of_int 10) ~total_emails:(UnsignedInt.of_int 1542) ~unread_emails:(UnsignedInt.of_int 127) ~total_threads:(UnsignedInt.of_int 890) ~unread_threads:(UnsignedInt.of_int 95) ~my_rights:full_rights ~is_subscribed:true () (* Create a sub-mailbox *) let archive = v ~id:(Jmap_id.of_string "archive456") ~name:"Archive" ~parent_id:(Some (Jmap_id.of_string "inbox123")) ~role:(Some "archive") ~sort_order:(UnsignedInt.of_int 20) ~total_emails:(UnsignedInt.of_int 5231) ~unread_emails:(UnsignedInt.of_int 0) ~total_threads:(UnsignedInt.of_int 3452) ~unread_threads:(UnsignedInt.of_int 0) ~my_rights:full_rights ~is_subscribed:false () (* Access mailbox fields *) let mailbox_name = name inbox let parent = parent_id archive let unread_count = UnsignedInt.to_int (unread_emails inbox) let can_delete = Rights.may_delete (my_rights inbox) ``` ### Mailbox Query with Filters ```ocaml open Jmap_mail.Jmap_mailbox (* Create mailbox filter *) let filter = Filter.v ~parent_id:(Some inbox_id) ~has_any_role:(Some false) ~is_subscribed:(Some true) () (* Query with tree sorting *) let query_req = Query.v ~account_id ~filter:(Some (Jmap_filter.condition filter)) ~sort_as_tree:(Some true) ~filter_as_tree:(Some true) () ``` ### Creating an Email ```ocaml open Jmap_mail.Jmap_email (* Create email addresses *) let from_addr = EmailAddress.v ~name:(Some "Alice Smith") ~email:"alice@example.com" let to_addr = EmailAddress.v ~email:"bob@example.com" (* name is optional *) (* Create a simple text email *) let text_body = BodyPart.v ~part_id:(Some "1") ~blob_id:(Some blob_id) ~size:(UnsignedInt.of_int 1234) ~headers:[] ~type_:"text/plain" ~charset:(Some "utf-8") () (* Create email with all common fields *) let email = v ~id:(Jmap_id.of_string "email123") ~blob_id:(Jmap_id.of_string "blob456") ~thread_id:(Jmap_id.of_string "thread789") ~mailbox_ids:[(inbox_id, true); (drafts_id, true)] ~keywords:[("$seen", true); ("$flagged", true)] ~size:(UnsignedInt.of_int 15234) ~received_at:(UTCDate.now ()) ~from:(Some [from_addr]) ~to_:(Some [to_addr]) ~subject:(Some "Important Meeting") ~sent_at:(Some sent_date) ~text_body:(Some [text_body]) ~has_attachment:false ~preview:"This is a preview of the email..." () (* Access email fields *) let subject = subject email let sender = from email let is_flagged = List.mem ("$flagged", true) (keywords email) ``` ### Email with Attachments ```ocaml open Jmap_mail.Jmap_email (* Create multipart structure *) let text_part = BodyPart.v ~part_id:(Some "1") ~size:(UnsignedInt.of_int 500) ~headers:[] ~type_:"text/plain" ~charset:(Some "utf-8") () let attachment = BodyPart.v ~part_id:(Some "2") ~blob_id:(Some attachment_blob_id) ~size:(UnsignedInt.of_int 45000) ~headers:[] ~name:(Some "document.pdf") ~type_:"application/pdf" ~disposition:(Some "attachment") () let multipart = BodyPart.v ~size:(UnsignedInt.of_int 45500) ~headers:[] ~type_:"multipart/mixed" ~sub_parts:(Some [text_part; attachment]) () let email_with_attachment = v ~id ~blob_id ~thread_id ~mailbox_ids:[(inbox_id, true)] ~size:(UnsignedInt.of_int 45500) ~received_at:(UTCDate.now ()) ~body_structure:(Some multipart) ~attachments:(Some [attachment]) ~has_attachment:true ~preview:"Email with PDF attachment" () ``` ### Email Query with Complex Filters ```ocaml open Jmap_mail.Jmap_email (* Create email filter *) let email_filter = Filter.v ~in_mailbox:(Some inbox_id) ~after:(Some (UTCDate.of_string "2024-01-01T00:00:00Z")) ~has_keyword:(Some "$flagged") ~not_keyword:(Some "$seen") ~from:(Some "important@example.com") ~has_attachment:(Some true) () (* Create query with filter *) let email_query = Query.v ~account_id ~filter:(Some (Jmap_filter.condition email_filter)) ~sort:(Some [ Jmap_comparator.v ~property:"receivedAt" ~is_ascending:false () ]) ~collapse_threads:(Some true) ~limit:(Some (UnsignedInt.of_int 25)) () ``` ### Email Import ```ocaml open Jmap_mail.Jmap_email.Import (* Create import email object *) let import_email = import_email_v ~blob_id:raw_message_blob_id ~mailbox_ids:[(inbox_id, true)] ~keywords:[("$seen", true)] () (* Create import request *) let import_req = request_v ~account_id ~emails:[ (Jmap_id.of_string "import1", import_email); ] () ``` ### Creating an Identity ```ocaml open Jmap_mail.Jmap_identity let signature_html = "
Best regards,
Alice Smith
CEO
I'm away until January. Will respond when I return.
") () (* Check if active *) let is_active = is_enabled vacation ``` --- ## Client Usage Examples ### Creating a Client Connection ```ocaml open Jmap_client open Jmap_connection (* Create custom config *) let config = config_v ~max_retries:5 ~timeout:60.0 ~user_agent:"MyApp/1.0" () (* Create connection with basic auth *) let conn = v ~config ~auth:(basic "user@example.com" "password123") () (* Create connection with bearer token *) let oauth_conn = v ~config ~auth:(bearer "oauth_token_here") () (* Access connection properties *) let max_retries = config_max_retries (config conn) let auth_method = auth conn ``` --- ## Complete Workflow Examples ### Example 1: Fetch and Display Mailboxes ```ocaml open Lwt.Syntax open Jmap_core open Jmap_mail.Jmap_mailbox let display_mailboxes client account_id = (* Create get request *) let get_req = Get.v ~account_id () (* Create JMAP request *) let request = Jmap_request.make ~using:[Jmap_capability.core; Jmap_capability.mail] [Jmap_invocation.Packed { (* Would need to properly construct invocation *) }] in (* Execute request *) let* response = Jmap_client.call client request in (* Process response *) let method_responses = Jmap_response.method_responses response in (* Extract mailboxes and display *) Lwt.return_unit ``` ### Example 2: Search and Display Emails ```ocaml open Jmap_mail.Jmap_email let search_flagged_emails client account_id inbox_id = (* Build filter *) let filter = Filter.v ~in_mailbox:(Some inbox_id) ~has_keyword:(Some "$flagged") ~not_keyword:(Some "$seen") () (* Build query *) let query = Query.v ~account_id ~filter:(Some (Jmap_filter.condition filter)) ~sort:(Some [ Jmap_comparator.v ~property:"receivedAt" ~is_ascending:false () ]) ~limit:(Some (UnsignedInt.of_int 20)) () (* Execute query and fetch results *) (* ... *) ``` ### Example 3: Create and Send Email ```ocaml open Jmap_mail let send_email client account_id identity_id = (* Create email addresses *) let from = Jmap_email.EmailAddress.v ~name:(Some "Alice") ~email:"alice@example.com" in let to_ = Jmap_email.EmailAddress.v ~email:"bob@example.com" in (* Create email body *) let body = Jmap_email.BodyPart.v ~part_id:(Some "1") ~size:(UnsignedInt.of_int 100) ~headers:[] ~type_:"text/plain" ~charset:(Some "utf-8") () (* Create draft email *) let email = Jmap_email.v ~id:(Jmap_id.of_string "draft1") ~blob_id:(Jmap_id.of_string "blob1") ~thread_id:(Jmap_id.of_string "thread1") ~mailbox_ids:[(Jmap_id.of_string "drafts", true)] ~keywords:[("$draft", true)] ~size:(UnsignedInt.of_int 100) ~received_at:(UTCDate.now ()) ~from:(Some [from]) ~to_:(Some [to_]) ~subject:(Some "Hello") ~text_body:(Some [body]) ~has_attachment:false ~preview:"Hello, how are you?" () (* Create submission *) let submission = Jmap_email_submission.v ~id:(Jmap_id.of_string "sub1") ~identity_id ~email_id:(Jmap_email.id email) ~thread_id:(Jmap_email.thread_id email) ~send_at:(UTCDate.now ()) ~undo_status:"pending" () (* Execute send *) (* ... *) ``` ### Example 4: Update Mailbox Hierarchy ```ocaml open Jmap_mail.Jmap_mailbox let reorganize_mailboxes client account_id = (* Create new archive folder *) let new_archive = v ~id:(Jmap_id.of_string "temp1") ~name:"Archive 2024" ~parent_id:(Some (Jmap_id.of_string "archive")) ~sort_order:(UnsignedInt.of_int 10) ~total_emails:(UnsignedInt.of_int 0) ~unread_emails:(UnsignedInt.of_int 0) ~total_threads:(UnsignedInt.of_int 0) ~unread_threads:(UnsignedInt.of_int 0) ~my_rights:full_rights ~is_subscribed:true () (* Create set request *) let set_req = Jmap_standard_methods.Set.v ~account_id ~create:(Some [(Jmap_id.of_string "temp1", new_archive)]) () (* Execute *) (* ... *) ``` --- ## Key Patterns ### 1. **All Required Fields First** ```ocaml let obj = v ~required1 ~required2 ~optional1:(Some value) () ``` ### 2. **Optional Fields Default to None** ```ocaml let obj = v ~id ~name () (* All other fields are None/[] *) ``` ### 3. **Response Field Access** ```ocaml (* For request/response pairs *) let req_id = account_id request let resp_id = response_account_id response ``` ### 4. **Submodule Pattern** ```ocaml let rights = Rights.v ~may_read_items:true ~may_delete:false (...) let mailbox = v ~id ~name ~my_rights:rights () ``` ### 5. **Filter Composition** ```ocaml let filter = Jmap_filter.and_ [ condition { field1 = value1 }; or_ [ condition { field2 = value2 }; condition { field3 = value3 }; ] ] ``` --- ## Summary This document shows that **all JMAP message types can be constructed using only the module interfaces** with: - ✅ Type-safe constructors with labeled arguments - ✅ Clear separation of required vs optional fields - ✅ Accessor functions for all fields - ✅ No manual JSON manipulation required - ✅ Composable filters and queries - ✅ Complete coverage of core and mail protocols The interface design ensures compile-time safety while remaining flexible and ergonomic for all JMAP use cases.