Skip to content

Commit c7b776a

Browse files
committed
feat: add contacts support
1 parent 3a973a6 commit c7b776a

23 files changed

Lines changed: 1458 additions & 9 deletions

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Microsoft Graph API client library for Rust.
44

55
https://learn.microsoft.com/en-us/graph/api/overview
66

7-
It currently covers the mail surface; see [API coverage](#api-coverage) for the supported and planned Graph domains.
7+
It currently covers the mail and contacts surfaces; see [API coverage](#api-coverage) for the supported and planned Graph domains.
88

99
## Table of contents
1010

@@ -19,13 +19,13 @@ It currently covers the mail surface; see [API coverage](#api-coverage) for the
1919

2020
## API coverage
2121

22-
Microsoft Graph is a single API spanning many domains; io-msgraph covers them incrementally and feature-gates each so consumers compile only what they use. This table tracks what is implemented today.
22+
Microsoft Graph is a single API spanning many domains; io-msgraph covers them incrementally. This table tracks what is implemented today.
2323

2424
| Domain | Coverage | Status |
2525
|------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------|
2626
| [Mail] | mail folders (list, get, create, update, delete); messages (list, get, get raw MIME, create draft from JSON or MIME, update, delete, move, copy, send); attachments (list, get raw content, delete); sendMail (JSON and MIME); signed-in user (`me`) | Supported |
2727
| [Calendar] | events and calendars | Soon |
28-
| [Contacts] | contacts and contact folders | Soon |
28+
| [Contacts] | contact folders (list, get, create, update, delete, list child folders); contacts (list, get, create, update, delete) | Supported |
2929

3030
[Mail]: https://learn.microsoft.com/en-us/graph/outlook-mail-concept-overview
3131
[Calendar]: https://learn.microsoft.com/en-us/graph/outlook-calendar-concept-overview

src/v1/client.rs

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,18 @@ use crate::{
5151
coroutine::*,
5252
v1::rest::users::{
5353
MsgraphUser,
54+
contact_folders::{
55+
MsgraphContactFolder, MsgraphContactFoldersListResponse,
56+
child_folders::MsgraphContactChildFoldersList, create::MsgraphContactFolderCreate,
57+
delete::MsgraphContactFolderDelete, get::MsgraphContactFolderGet,
58+
list::MsgraphContactFoldersList, list::MsgraphContactFoldersListParams,
59+
update::MsgraphContactFolderUpdate,
60+
},
61+
contacts::{
62+
MsgraphContact, MsgraphContactsListResponse, create::MsgraphContactCreate,
63+
delete::MsgraphContactDelete, get::MsgraphContactGet, list::MsgraphContactsList,
64+
list::MsgraphContactsListParams, update::MsgraphContactUpdate,
65+
},
5466
get::MsgraphUserGet,
5567
mail_folders::{
5668
MsgraphMailFolder, MsgraphMailFoldersListResponse,
@@ -304,6 +316,100 @@ impl MsgraphClientStd {
304316
self.run(coroutine)
305317
}
306318

319+
pub fn contact_folders_list(
320+
&mut self,
321+
params: &MsgraphContactFoldersListParams,
322+
) -> Result<MsgraphSendOutput<MsgraphContactFoldersListResponse>, MsgraphClientStdError> {
323+
let coroutine = MsgraphContactFoldersList::new(&self.auth, &self.user_id, params)?;
324+
self.run(coroutine)
325+
}
326+
327+
pub fn contact_folder_get(
328+
&mut self,
329+
id: &str,
330+
) -> Result<MsgraphSendOutput<MsgraphContactFolder>, MsgraphClientStdError> {
331+
let coroutine = MsgraphContactFolderGet::new(&self.auth, &self.user_id, id)?;
332+
self.run(coroutine)
333+
}
334+
335+
pub fn contact_folder_create(
336+
&mut self,
337+
folder: &MsgraphContactFolder,
338+
) -> Result<MsgraphSendOutput<MsgraphContactFolder>, MsgraphClientStdError> {
339+
let coroutine = MsgraphContactFolderCreate::new(&self.auth, &self.user_id, folder)?;
340+
self.run(coroutine)
341+
}
342+
343+
pub fn contact_folder_update(
344+
&mut self,
345+
id: &str,
346+
folder: &MsgraphContactFolder,
347+
) -> Result<MsgraphSendOutput<MsgraphContactFolder>, MsgraphClientStdError> {
348+
let coroutine = MsgraphContactFolderUpdate::new(&self.auth, &self.user_id, id, folder)?;
349+
self.run(coroutine)
350+
}
351+
352+
pub fn contact_folder_delete(
353+
&mut self,
354+
id: &str,
355+
) -> Result<MsgraphSendOutput<MsgraphNoResponse>, MsgraphClientStdError> {
356+
let coroutine = MsgraphContactFolderDelete::new(&self.auth, &self.user_id, id)?;
357+
self.run(coroutine)
358+
}
359+
360+
pub fn contact_child_folders_list(
361+
&mut self,
362+
id: &str,
363+
params: &MsgraphContactFoldersListParams,
364+
) -> Result<MsgraphSendOutput<MsgraphContactFoldersListResponse>, MsgraphClientStdError> {
365+
let coroutine = MsgraphContactChildFoldersList::new(&self.auth, &self.user_id, id, params)?;
366+
self.run(coroutine)
367+
}
368+
369+
pub fn contacts_list(
370+
&mut self,
371+
folder: Option<&str>,
372+
params: &MsgraphContactsListParams,
373+
) -> Result<MsgraphSendOutput<MsgraphContactsListResponse>, MsgraphClientStdError> {
374+
let coroutine = MsgraphContactsList::new(&self.auth, &self.user_id, folder, params)?;
375+
self.run(coroutine)
376+
}
377+
378+
pub fn contact_get(
379+
&mut self,
380+
id: &str,
381+
expand: Option<&str>,
382+
) -> Result<MsgraphSendOutput<MsgraphContact>, MsgraphClientStdError> {
383+
let coroutine = MsgraphContactGet::new(&self.auth, &self.user_id, id, expand)?;
384+
self.run(coroutine)
385+
}
386+
387+
pub fn contact_create(
388+
&mut self,
389+
folder: Option<&str>,
390+
contact: &MsgraphContact,
391+
) -> Result<MsgraphSendOutput<MsgraphContact>, MsgraphClientStdError> {
392+
let coroutine = MsgraphContactCreate::new(&self.auth, &self.user_id, folder, contact)?;
393+
self.run(coroutine)
394+
}
395+
396+
pub fn contact_update(
397+
&mut self,
398+
id: &str,
399+
contact: &MsgraphContact,
400+
) -> Result<MsgraphSendOutput<MsgraphContact>, MsgraphClientStdError> {
401+
let coroutine = MsgraphContactUpdate::new(&self.auth, &self.user_id, id, contact)?;
402+
self.run(coroutine)
403+
}
404+
405+
pub fn contact_delete(
406+
&mut self,
407+
id: &str,
408+
) -> Result<MsgraphSendOutput<MsgraphNoResponse>, MsgraphClientStdError> {
409+
let coroutine = MsgraphContactDelete::new(&self.auth, &self.user_id, id)?;
410+
self.run(coroutine)
411+
}
412+
307413
pub fn messages_list(
308414
&mut self,
309415
folder: Option<&str>,

src/v1/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
//! HTTP/JSON transport every coroutine delegates to, and `query` turns a
55
//! params struct into OData query pairs.
66
7+
mod types;
8+
#[doc(inline)]
9+
pub use types::*;
10+
711
#[cfg(feature = "client")]
812
pub mod client;
913
pub mod query;

src/v1/rest/mod.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! Microsoft Graph REST API, mirroring the reference resource tree. The
2-
//! mail surface hangs off the `users` resource (`/me` or
3-
//! `/users/{id}`): mail folders, messages and the sendMail action.
2+
//! mail and contacts surfaces hang off the `users` resource (`/me` or
3+
//! `/users/{id}`): mail folders, messages, the sendMail action, contact
4+
//! folders and contacts.
45
//!
56
//! <https://learn.microsoft.com/en-us/graph/api/overview>
67
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
//! List the child folders of a Microsoft Graph contact folder
2+
//! (`GET /me/contactFolders/{id}/childFolders`).
3+
//!
4+
//! <https://learn.microsoft.com/en-us/graph/api/contactfolder-list-childfolders>
5+
6+
use alloc::format;
7+
8+
use io_http::rfc6750::bearer::HttpAuthBearer;
9+
use log::{debug, trace};
10+
use url::Url;
11+
12+
use crate::{
13+
coroutine::*,
14+
msgraph_try,
15+
v1::{
16+
query::to_query_pairs,
17+
rest::users::contact_folders::{
18+
MsgraphContactFoldersListResponse, list::MsgraphContactFoldersListParams,
19+
},
20+
send::{MSGRAPH_API_BASE, MsgraphSend, MsgraphSendError, MsgraphSendOutput, user_path},
21+
},
22+
};
23+
24+
/// Lists the child folders of a Microsoft Graph contact folder.
25+
pub struct MsgraphContactChildFoldersList {
26+
send: MsgraphSend<MsgraphContactFoldersListResponse>,
27+
}
28+
29+
impl MsgraphContactChildFoldersList {
30+
pub fn new(
31+
auth: &HttpAuthBearer,
32+
user_id: &str,
33+
id: &str,
34+
params: &MsgraphContactFoldersListParams,
35+
) -> Result<Self, MsgraphSendError> {
36+
debug!("prepare microsoft graph contact child folders listing");
37+
trace!("id: {id:?}");
38+
trace!("params: {params:?}");
39+
40+
let user = user_path(user_id);
41+
let mut url = Url::parse(MSGRAPH_API_BASE)?
42+
.join(&format!("{user}/contactFolders/{id}/childFolders"))?;
43+
url.query_pairs_mut().extend_pairs(to_query_pairs(params));
44+
45+
let send = MsgraphSend::get(auth, url);
46+
47+
Ok(Self { send })
48+
}
49+
}
50+
51+
impl MsgraphCoroutine for MsgraphContactChildFoldersList {
52+
type Yield = MsgraphYield;
53+
type Return = Result<MsgraphSendOutput<MsgraphContactFoldersListResponse>, MsgraphSendError>;
54+
55+
fn resume(&mut self, arg: Option<&[u8]>) -> MsgraphCoroutineState<Self::Yield, Self::Return> {
56+
let out = msgraph_try!(&mut self.send, arg);
57+
debug!("microsoft graph contact child folders listed");
58+
trace!("out: {out:?}");
59+
MsgraphCoroutineState::Complete(Ok(out))
60+
}
61+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
//! Create a Microsoft Graph contact folder
2+
//! (`POST /me/contactFolders`).
3+
//!
4+
//! <https://learn.microsoft.com/en-us/graph/api/user-post-contactfolders>
5+
6+
use alloc::format;
7+
8+
use io_http::rfc6750::bearer::HttpAuthBearer;
9+
use log::{debug, trace};
10+
use url::Url;
11+
12+
use crate::{
13+
coroutine::*,
14+
msgraph_try,
15+
v1::{
16+
rest::users::contact_folders::MsgraphContactFolder,
17+
send::{MSGRAPH_API_BASE, MsgraphSend, MsgraphSendError, MsgraphSendOutput, user_path},
18+
},
19+
};
20+
21+
pub struct MsgraphContactFolderCreate {
22+
send: MsgraphSend<MsgraphContactFolder>,
23+
}
24+
25+
impl MsgraphContactFolderCreate {
26+
pub fn new(
27+
auth: &HttpAuthBearer,
28+
user_id: &str,
29+
folder: &MsgraphContactFolder,
30+
) -> Result<Self, MsgraphSendError> {
31+
debug!("prepare microsoft graph contact folder for creation");
32+
trace!("folder: {folder:?}");
33+
34+
if folder.display_name.trim().is_empty() {
35+
let err =
36+
MsgraphSendError::InvalidRequest("Contact folder name cannot be empty".into());
37+
return Err(err);
38+
}
39+
40+
let user = user_path(user_id);
41+
let url = Url::parse(MSGRAPH_API_BASE)?.join(&format!("{user}/contactFolders"))?;
42+
let send = MsgraphSend::post_json(auth, url, folder)?;
43+
44+
Ok(Self { send })
45+
}
46+
}
47+
48+
impl MsgraphCoroutine for MsgraphContactFolderCreate {
49+
type Yield = MsgraphYield;
50+
type Return = Result<MsgraphSendOutput<MsgraphContactFolder>, MsgraphSendError>;
51+
52+
fn resume(&mut self, arg: Option<&[u8]>) -> MsgraphCoroutineState<Self::Yield, Self::Return> {
53+
let out = msgraph_try!(&mut self.send, arg);
54+
debug!("microsoft graph contact folder created");
55+
trace!("out: {out:?}");
56+
MsgraphCoroutineState::Complete(Ok(out))
57+
}
58+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//! Delete a Microsoft Graph contact folder
2+
//! (`DELETE /me/contactFolders/{id}`).
3+
//!
4+
//! <https://learn.microsoft.com/en-us/graph/api/contactfolder-delete>
5+
6+
use alloc::format;
7+
8+
use io_http::rfc6750::bearer::HttpAuthBearer;
9+
use log::{debug, trace};
10+
use url::Url;
11+
12+
use crate::{
13+
coroutine::*,
14+
msgraph_try,
15+
v1::send::{
16+
MSGRAPH_API_BASE, MsgraphNoResponse, MsgraphSend, MsgraphSendError, MsgraphSendOutput,
17+
user_path,
18+
},
19+
};
20+
21+
pub struct MsgraphContactFolderDelete {
22+
send: MsgraphSend<MsgraphNoResponse>,
23+
}
24+
25+
impl MsgraphContactFolderDelete {
26+
pub fn new(auth: &HttpAuthBearer, user_id: &str, id: &str) -> Result<Self, MsgraphSendError> {
27+
debug!("prepare microsoft graph contact folder deletion");
28+
trace!("id: {id:?}");
29+
30+
let user = user_path(user_id);
31+
let url = Url::parse(MSGRAPH_API_BASE)?.join(&format!("{user}/contactFolders/{id}"))?;
32+
let send = MsgraphSend::delete(auth, url);
33+
34+
Ok(Self { send })
35+
}
36+
}
37+
38+
impl MsgraphCoroutine for MsgraphContactFolderDelete {
39+
type Yield = MsgraphYield;
40+
type Return = Result<MsgraphSendOutput<MsgraphNoResponse>, MsgraphSendError>;
41+
42+
fn resume(&mut self, arg: Option<&[u8]>) -> MsgraphCoroutineState<Self::Yield, Self::Return> {
43+
let out = msgraph_try!(&mut self.send, arg);
44+
debug!("microsoft graph contact folder deleted");
45+
trace!("out: {out:?}");
46+
MsgraphCoroutineState::Complete(Ok(out))
47+
}
48+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//! Get a Microsoft Graph contact folder
2+
//! (`GET /me/contactFolders/{id}`).
3+
//!
4+
//! <https://learn.microsoft.com/en-us/graph/api/contactfolder-get>
5+
6+
use alloc::format;
7+
8+
use io_http::rfc6750::bearer::HttpAuthBearer;
9+
use log::{debug, trace};
10+
use url::Url;
11+
12+
use crate::{
13+
coroutine::*,
14+
msgraph_try,
15+
v1::{
16+
rest::users::contact_folders::MsgraphContactFolder,
17+
send::{MSGRAPH_API_BASE, MsgraphSend, MsgraphSendError, MsgraphSendOutput, user_path},
18+
},
19+
};
20+
21+
pub struct MsgraphContactFolderGet {
22+
send: MsgraphSend<MsgraphContactFolder>,
23+
}
24+
25+
impl MsgraphContactFolderGet {
26+
pub fn new(auth: &HttpAuthBearer, user_id: &str, id: &str) -> Result<Self, MsgraphSendError> {
27+
debug!("prepare microsoft graph contact folder retrieval");
28+
trace!("id: {id:?}");
29+
30+
let user = user_path(user_id);
31+
let url = Url::parse(MSGRAPH_API_BASE)?.join(&format!("{user}/contactFolders/{id}"))?;
32+
let send = MsgraphSend::get(auth, url);
33+
34+
Ok(Self { send })
35+
}
36+
}
37+
38+
impl MsgraphCoroutine for MsgraphContactFolderGet {
39+
type Yield = MsgraphYield;
40+
type Return = Result<MsgraphSendOutput<MsgraphContactFolder>, MsgraphSendError>;
41+
42+
fn resume(&mut self, arg: Option<&[u8]>) -> MsgraphCoroutineState<Self::Yield, Self::Return> {
43+
let out = msgraph_try!(&mut self.send, arg);
44+
debug!("microsoft graph contact folder retrieved");
45+
trace!("out: {out:?}");
46+
MsgraphCoroutineState::Complete(Ok(out))
47+
}
48+
}

0 commit comments

Comments
 (0)