Skip to content

Commit e82a60c

Browse files
committed
Add pagination logic. Add missing API methods
1 parent bd9a51e commit e82a60c

File tree

7 files changed

+109
-24
lines changed

7 files changed

+109
-24
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ See [Get access on behalf of a user](https://docs.microsoft.com/en-us/graph/auth
1313
## Breaking changes if you're upgrading prior 1.0.0
1414
- Added structure to library to match API documentation for e.g. `client.get_me()` => `client.users.get_me()`.
1515
- Renamed several methods to match API documentation for e.g. `client.get_me_events()` => `client.calendar.list_events()`.
16-
- Result from calling a method is not longer a dictionary but a Response object. To access the dict response as before then call `.data` property for e.g `r = client.users.get_me()` then `r.data`.
16+
- Result from calling a method is not longer a dictionary but a Response object. To access the dict response as before then call `.data` attribute for e.g `r = client.users.get_me()` then `r.data`.
1717
- Previous API calls made through beta endpoints are now pointing to v1.0 by default. This can be changed to beta if needed with the parameter `api_version` in the client instantiation.
1818
- Removed Office 365 endpoints as they were merged with the Microsoft Graph API. See [Office 365 APIs](https://docs.microsoft.com/en-us/previous-versions/office/office-365-api/).
1919
## New in 1.0.0

microsoftgraph/calendar.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,34 +17,36 @@ def __init__(self, client) -> None:
1717
self._client = client
1818

1919
@token_required
20-
def list_events(self, calendar_id: str = None) -> Response:
20+
def list_events(self, calendar_id: str = None, params: dict = None) -> Response:
2121
"""Get a list of event objects in the user's mailbox. The list contains single instance meetings and series
2222
masters.
2323
2424
https://docs.microsoft.com/en-us/graph/api/user-list-events?view=graph-rest-1.0&tabs=http
2525
2626
Args:
2727
calendar_id (str): Calendar ID.
28+
params (dict, optional): Query. Defaults to None.
2829
2930
Returns:
3031
Response: Microsoft Graph Response.
3132
"""
3233
url = "me/calendars/{}/events".format(calendar_id) if calendar_id else "me/events"
33-
return self._client._get(self._client.base_url + url)
34+
return self._client._get(self._client.base_url + url, params=params)
3435

3536
@token_required
36-
def get_event(self, event_id: str) -> Response:
37+
def get_event(self, event_id: str, params: dict = None) -> Response:
3738
"""Get the properties and relationships of the specified event object.
3839
3940
https://docs.microsoft.com/en-us/graph/api/event-get?view=graph-rest-1.0&tabs=http
4041
4142
Args:
4243
event_id (str): Event ID.
44+
params (dict, optional): Query. Defaults to None.
4345
4446
Returns:
4547
Response: Microsoft Graph Response.
4648
"""
47-
return self._client._get(self._client.base_url + "me/events/{}".format(event_id))
49+
return self._client._get(self._client.base_url + "me/events/{}".format(event_id), params=params)
4850

4951
@token_required
5052
def create_event(
@@ -106,16 +108,19 @@ def create_event(
106108
return self._client._post(self._client.base_url + url, json=body)
107109

108110
@token_required
109-
def list_calendars(self) -> Response:
111+
def list_calendars(self, params: dict = None) -> Response:
110112
"""Get all the user's calendars (/calendars navigation property), get the calendars from the default calendar
111113
group or from a specific calendar group.
112114
113115
https://docs.microsoft.com/en-us/graph/api/user-list-calendars?view=graph-rest-1.0&tabs=http
114116
117+
Args:
118+
params (dict, optional): Query. Defaults to None.
119+
115120
Returns:
116121
Response: Microsoft Graph Response.
117122
"""
118-
return self._client._get(self._client.base_url + "me/calendars")
123+
return self._client._get(self._client.base_url + "me/calendars", params=params)
119124

120125
@token_required
121126
def create_calendar(self, name: str) -> Response:

microsoftgraph/client.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from urllib.parse import urlencode
22

33
import requests
4-
4+
import copy
55
from microsoftgraph import exceptions
66
from microsoftgraph.calendar import Calendar
77
from microsoftgraph.contacts import Contacts
@@ -27,6 +27,7 @@ def __init__(
2727
api_version: str = "v1.0",
2828
account_type: str = "common",
2929
requests_hooks: dict = None,
30+
paginate: bool = True,
3031
) -> None:
3132
"""Instantiates library.
3233
@@ -47,6 +48,7 @@ def __init__(
4748

4849
self.base_url = self.RESOURCE + self.api_version + "/"
4950
self.token = None
51+
self.paginate = paginate
5052

5153
self.calendar = Calendar(self)
5254
self.contacts = Contacts(self)
@@ -164,8 +166,30 @@ def set_token(self, token: dict) -> None:
164166
"""
165167
self.token = token
166168

169+
def _paginate_response(self, response: dict, **kwargs) -> dict:
170+
"""Some queries against Microsoft Graph return multiple pages of data either due to server-side paging or due to
171+
the use of the $top query parameter to specifically limit the page size in a request. When a result set spans
172+
multiple pages, Microsoft Graph returns an @odata.nextLink property in the response that contains a URL to the
173+
next page of results.
174+
175+
https://docs.microsoft.com/en-us/graph/paging?context=graph%2Fapi%2F1.0&view=graph-rest-1.0
176+
177+
Args:
178+
response (dict): Graph API Response.
179+
180+
Returns:
181+
dict: Graph API Response.
182+
"""
183+
if not self.paginate:
184+
return response
185+
while "@odata.nextLink" in response.data:
186+
data = response.data["value"]
187+
response = self._get(response.data["@odata.nextLink"], **kwargs)
188+
response.data["value"] += data
189+
return response
190+
167191
def _get(self, url, **kwargs):
168-
return self._request("GET", url, **kwargs)
192+
return self._paginate_response(self._request("GET", url, **kwargs), **kwargs)
169193

170194
def _post(self, url, **kwargs):
171195
return self._request("POST", url, **kwargs)

microsoftgraph/contacts.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import json
21
from microsoftgraph.decorators import token_required
32
from microsoftgraph.response import Response
43

@@ -30,18 +29,20 @@ def get_contact(self, contact_id: str, params: dict = None) -> Response:
3029
return self._client._get(self._client.base_url + "me/contacts/{}".format(contact_id), params=params)
3130

3231
@token_required
33-
def list_contacts(self, params: dict = None) -> Response:
32+
def list_contacts(self, folder_id: str = None, params: dict = None) -> Response:
3433
"""Get a contact collection from the default contacts folder of the signed-in user.
3534
3635
https://docs.microsoft.com/en-us/graph/api/user-list-contacts?view=graph-rest-1.0&tabs=http
3736
3837
Args:
38+
folder_id (str): Folder ID.
3939
params (dict, optional): Query. Defaults to None.
4040
4141
Returns:
4242
Response: Microsoft Graph Response.
4343
"""
44-
return self._client._get(self._client.base_url + "me/contacts", params=params)
44+
url = "me/contactfolders/{}/contacts".format(folder_id) if folder_id else "me/contacts"
45+
return self._client._get(self._client.base_url + url, params=params)
4546

4647
@token_required
4748
def create_contact(

microsoftgraph/mail.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,22 @@ def __init__(self, client) -> None:
1616
"""
1717
self._client = client
1818

19+
@token_required
20+
def list_messages(self, folder_id: str = None, params: dict = None) -> Response:
21+
"""Get the messages in the signed-in user's mailbox (including the Deleted Items and Clutter folders).
22+
23+
https://docs.microsoft.com/en-us/graph/api/user-list-messages?view=graph-rest-1.0&tabs=http
24+
25+
Args:
26+
folder_id (str, optional): Mail Folder ID.
27+
params (dict, optional): Query. Defaults to None.
28+
29+
Returns:
30+
Response: Microsoft Graph Response.
31+
"""
32+
url = "me/mailFolders/{id}/messages".format(folder_id) if folder_id else "me/messages"
33+
return self._client._get(self._client.base_url + url, params=params)
34+
1935
@token_required
2036
def get_message(self, message_id: str, params: dict = None) -> Response:
2137
"""Retrieve the properties and relationships of a message object.
@@ -107,3 +123,40 @@ def send_mail(
107123

108124
# Do a POST to Graph's sendMail API and return the response.
109125
return self._client._post(self._client.base_url + "me/sendMail", json=email_msg)
126+
127+
@token_required
128+
def list_mail_folders(self, params: dict = None) -> Response:
129+
"""Get the mail folder collection directly under the root folder of the signed-in user. The returned collection
130+
includes any mail search folders directly under the root.
131+
132+
By default, this operation does not return hidden folders. Use a query parameter includeHiddenFolders to include
133+
them in the response.
134+
135+
https://docs.microsoft.com/en-us/graph/api/user-list-mailfolders?view=graph-rest-1.0&tabs=http
136+
137+
Args:
138+
params (dict, optional): Query. Defaults to None.
139+
140+
Returns:
141+
Response: Microsoft Graph Response.
142+
"""
143+
return self._client._get(self._client.base_url + "me/mailFolders", params=params)
144+
145+
@token_required
146+
def create_mail_folder(self, display_name: str, is_hidden: bool = False) -> Response:
147+
"""Use this API to create a new mail folder in the root folder of the user's mailbox.
148+
149+
https://docs.microsoft.com/en-us/graph/api/user-post-mailfolders?view=graph-rest-1.0&tabs=http
150+
151+
Args:
152+
display_name (str): Query.
153+
is_hidden (bool, optional): Is the folder hidden. Defaults to False.
154+
155+
Returns:
156+
Response: Microsoft Graph Response.
157+
"""
158+
data = {
159+
"displayName": display_name,
160+
"isHidden": is_hidden,
161+
}
162+
return self._client._post(self._client.base_url + "me/mailFolders", json=data)

microsoftgraph/notes.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
class Notes(object):
66
def __init__(self, client) -> None:
77
"""Use the OneNote REST API.
8-
8+
99
https://docs.microsoft.com/en-us/graph/api/resources/onenote-api-overview?view=graph-rest-1.0
1010
1111
Args:
@@ -28,32 +28,35 @@ def list_notebooks(self, params: dict = None) -> Response:
2828
return self._client._get(self._client.base_url + "me/onenote/notebooks", params=params)
2929

3030
@token_required
31-
def get_notebook(self, notebook_id: str) -> Response:
31+
def get_notebook(self, notebook_id: str, params: dict = None) -> Response:
3232
"""Retrieve the properties and relationships of a notebook object.
3333
3434
https://docs.microsoft.com/en-us/graph/api/notebook-get?view=graph-rest-1.0&tabs=http
3535
3636
Args:
3737
notebook_id (str): The unique identifier of the notebook.
38+
params (dict, optional): Query. Defaults to None.
3839
3940
Returns:
4041
Response: Microsoft Graph Response.
4142
"""
42-
return self._client._get(self._client.base_url + "me/onenote/notebooks/" + notebook_id)
43+
return self._client._get(self._client.base_url + "me/onenote/notebooks/" + notebook_id, params=params)
4344

4445
@token_required
45-
def list_sections(self, notebook_id: str) -> Response:
46+
def list_sections(self, notebook_id: str, params: dict = None) -> Response:
4647
"""Retrieve a list of onenoteSection objects from the specified notebook.
4748
4849
https://docs.microsoft.com/en-us/graph/api/notebook-list-sections?view=graph-rest-1.0&tabs=http
4950
5051
Args:
5152
notebook_id (str): The unique identifier of the notebook.
53+
params (dict, optional): Query. Defaults to None.
5254
5355
Returns:
5456
Response: Microsoft Graph Response.
5557
"""
56-
return self._client._get(self._client.base_url + "me/onenote/notebooks/{}/sections".format(notebook_id))
58+
url = "me/onenote/notebooks/{}/sections".format(notebook_id)
59+
return self._client._get(self._client.base_url + url, params=params)
5760

5861
@token_required
5962
def list_pages(self, params: dict = None) -> Response:

microsoftgraph/response.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,13 @@ class Response:
55
def __init__(self, original) -> None:
66
self.original = original
77

8-
def __repr__(self) -> str:
9-
return "<Response [{}] ({})>".format(self.status_code, self.data)
10-
11-
@property
12-
def data(self):
138
if "application/json" in self.original.headers.get("Content-Type", ""):
14-
return self.original.json()
15-
return self.original.content
9+
self.data = self.original.json()
10+
else:
11+
self.data = self.original.content
12+
13+
def __repr__(self) -> str:
14+
return "<Response [{}]>".format(self.status_code)
1615

1716
@property
1817
def status_code(self):

0 commit comments

Comments
 (0)