use jmap_base_client::{BearerAuth, ClientConfig, JmapClient, NoneAuth, Session};
use wiremock::matchers::{method, path};
use wiremock::{Mock, MockServer, ResponseTemplate};
const SESSION_FIXTURE: &str = r#"{
"capabilities": {
"urn:ietf:params:jmap:core": {
"maxSizeUpload": 50000000,
"maxConcurrentUpload": 8,
"maxSizeRequest": 10000000,
"maxConcurrentRequest": 8,
"maxCallsInRequest": 32,
"maxObjectsInGet": 256,
"maxObjectsInSet": 128,
"collationAlgorithms": ["i;ascii-numeric", "i;ascii-casemap", "i;unicode-casemap"]
},
"urn:ietf:params:jmap:mail": {}
},
"accounts": {
"A13824": {
"name": "john@example.com",
"isPersonal": true,
"isReadOnly": false,
"accountCapabilities": {
"urn:ietf:params:jmap:mail": { "maxMailboxesPerEmail": null, "maxMailboxDepth": 10 }
}
}
},
"primaryAccounts": {
"urn:ietf:params:jmap:core": "A13824",
"urn:ietf:params:jmap:mail": "A13824"
},
"username": "john@example.com",
"apiUrl": "https://jmap.example.com/api/",
"downloadUrl": "https://jmap.example.com/download/{accountId}/{blobId}/{name}?accept={type}",
"uploadUrl": "https://jmap.example.com/upload/{accountId}/",
"eventSourceUrl": "https://jmap.example.com/eventsource/?types={types}&closeafter={closeafter}&ping={ping}",
"state": "75128aab4b1b"
}"#;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let (base_url, _server) = match std::env::var("JMAP_TEST_URL") {
Ok(url) => (url, None),
Err(_) => {
let server = start_mock_server().await;
let url = server.uri();
(url, Some(server))
}
};
let session = match std::env::var("JMAP_TEST_TOKEN") {
Ok(token) => {
let auth = BearerAuth::new(&token)?;
JmapClient::new_plain(auth, &base_url, ClientConfig::default())?
.fetch_session()
.await?
}
Err(_) => {
JmapClient::new_plain(NoneAuth, &base_url, ClientConfig::default())?
.fetch_session()
.await?
}
};
print_session(&session);
Ok(())
}
fn print_session(session: &Session) {
println!("username: [REDACTED]");
println!("api_url: {}", session.api_url);
println!("upload_url: {}", session.upload_url);
println!("download_url: {}", session.download_url);
println!("event_source: {}", session.event_source_url);
println!("state: [opaque]");
let mut caps: Vec<&String> = session.capabilities.keys().collect();
caps.sort();
println!("\ncapabilities ({}):", caps.len());
for cap in caps {
println!(" - {cap}");
}
let mut account_ids: Vec<&String> = session.accounts.keys().collect();
account_ids.sort();
println!("\naccounts ({}):", account_ids.len());
for id in account_ids {
let info = &session.accounts[id];
println!(
" - {id} (personal={}, read_only={})",
info.is_personal, info.is_read_only,
);
}
let mut prims: Vec<(&String, &String)> = session.primary_accounts.iter().collect();
prims.sort();
println!("\nprimary accounts ({}):", prims.len());
for (cap, acct) in prims {
println!(" - {cap} => {acct}");
}
}
async fn start_mock_server() -> MockServer {
let body: serde_json::Value =
serde_json::from_str(SESSION_FIXTURE).expect("SESSION_FIXTURE must be valid JSON");
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/.well-known/jmap"))
.respond_with(ResponseTemplate::new(200).set_body_json(body))
.mount(&server)
.await;
server
}