Skip to content

Commit 34d3b23

Browse files
committed
Office 365 support; Decorator token_required for methods
1 parent 58da059 commit 34d3b23

File tree

3 files changed

+67
-16
lines changed

3 files changed

+67
-16
lines changed

microsoftgraph/client.py

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import mimetypes
33
import requests
44
from microsoftgraph import exceptions
5+
from microsoftgraph.decorators import token_required
56
from urllib.parse import urlencode, urlparse
67

78

@@ -11,6 +12,10 @@ class Client(object):
1112
TOKEN_ENDPOINT = '/oauth2/v2.0/token'
1213
RESOURCE = 'https://graph.microsoft.com/'
1314

15+
OFFICE365_AUTHORITY_URL = 'https://login.live.com'
16+
OFFICE365_AUTH_ENDPOINT = '/oauth20_authorize.srf?'
17+
OFFICE365_TOKEN_ENDPOINT = '/oauth20_token.srf'
18+
1419
def __init__(self, client_id, client_secret, api_version='v1.0', account_type='common'):
1520
self.client_id = client_id
1621
self.client_secret = client_secret
@@ -19,8 +24,9 @@ def __init__(self, client_id, client_secret, api_version='v1.0', account_type='c
1924

2025
self.base_url = self.RESOURCE + self.api_version + '/'
2126
self.token = None
27+
self.office365_token = None
2228

23-
def authorization_url(self, redirect_uri, scope, state=None):
29+
def authorization_url(self, redirect_uri, scope, state=None, office365=False):
2430
"""
2531
2632
Args:
@@ -49,10 +55,13 @@ def authorization_url(self, redirect_uri, scope, state=None):
4955

5056
if state:
5157
params['state'] = None
58+
if office365:
59+
response = self.OFFICE365_AUTHORITY_URL + self.OFFICE365_AUTH_ENDPOINT + urlencode(params)
60+
else:
61+
response = self.AUTHORITY_URL + self.account_type + self.AUTH_ENDPOINT + urlencode(params)
62+
return response
5263

53-
return self.AUTHORITY_URL + self.account_type + self.AUTH_ENDPOINT + urlencode(params)
54-
55-
def exchange_code(self, redirect_uri, code):
64+
def exchange_code(self, redirect_uri, code, office365=False):
5665
"""Exchanges a code for a Token.
5766
5867
Args:
@@ -72,9 +81,13 @@ def exchange_code(self, redirect_uri, code):
7281
'code': code,
7382
'grant_type': 'authorization_code',
7483
}
75-
return requests.post(self.AUTHORITY_URL + self.account_type + self.TOKEN_ENDPOINT, data=data).json()
84+
if office365:
85+
response = requests.post(self.OFFICE365_AUTHORITY_URL + self.OFFICE365_TOKEN_ENDPOINT, data=data)
86+
else:
87+
response = requests.post(self.AUTHORITY_URL + self.account_type + self.TOKEN_ENDPOINT, data=data)
88+
return self._parse(response)
7689

77-
def refresh_token(self, redirect_uri, refresh_token):
90+
def refresh_token(self, redirect_uri, refresh_token, office365=True):
7891
"""
7992
8093
Args:
@@ -95,17 +108,25 @@ def refresh_token(self, redirect_uri, refresh_token):
95108
'refresh_token': refresh_token,
96109
'grant_type': 'refresh_token',
97110
}
98-
return requests.post(self.AUTHORITY_URL + self.account_type + self.TOKEN_ENDPOINT, data=data).json()
111+
if office365:
112+
response = requests.post(self.OFFICE365_AUTHORITY_URL + self.OFFICE365_TOKEN_ENDPOINT, data=data)
113+
else:
114+
response = requests.post(self.AUTHORITY_URL + self.account_type + self.TOKEN_ENDPOINT, data=data)
115+
return self._parse(response)
99116

100-
def set_token(self, token):
117+
def set_token(self, token, office365=True):
101118
"""Sets the Token for its use in this library.
102119
103120
Args:
104121
token: A string with the Token.
105122
106123
"""
107-
self.token = token
124+
if office365:
125+
self.office365_token = token
126+
else:
127+
self.token = token
108128

129+
@token_required
109130
def get_me(self, params=None):
110131
"""Retrieve the properties and relationships of user object.
111132
@@ -122,6 +143,7 @@ def get_me(self, params=None):
122143
"""
123144
return self._get(self.base_url + 'me', params=params)
124145

146+
@token_required
125147
def get_message(self, message_id, params=None):
126148
"""Retrieve the properties and relationships of a message object.
127149
@@ -134,6 +156,7 @@ def get_message(self, message_id, params=None):
134156
"""
135157
return self._get(self.base_url + 'me/messages/' + message_id, params=params)
136158

159+
@token_required
137160
def create_subscription(self, change_type, notification_url, resource, expiration_datetime, client_state=None):
138161
"""Creating a subscription is the first step to start receiving notifications for a resource.
139162
@@ -156,6 +179,7 @@ def create_subscription(self, change_type, notification_url, resource, expiratio
156179
}
157180
return self._post('https://graph.microsoft.com/beta/' + 'subscriptions', json=data)
158181

182+
@token_required
159183
def renew_subscription(self, subscription_id, expiration_datetime):
160184
"""The client can renew a subscription with a specific expiration date of up to three days from the time
161185
of request. The expirationDateTime property is required.
@@ -172,6 +196,7 @@ def renew_subscription(self, subscription_id, expiration_datetime):
172196
}
173197
return self._patch('https://graph.microsoft.com/beta/' + 'subscriptions/{}'.format(subscription_id), json=data)
174198

199+
@token_required
175200
def delete_subscription(self, subscription_id):
176201
"""The client can stop receiving notifications by deleting the subscription using its ID.
177202
@@ -183,6 +208,7 @@ def delete_subscription(self, subscription_id):
183208
"""
184209
return self._delete('https://graph.microsoft.com/beta/' + 'subscriptions/{}'.format(subscription_id))
185210

211+
@token_required
186212
def list_notebooks(self):
187213
"""Retrieve a list of notebook objects.
188214
@@ -192,6 +218,7 @@ def list_notebooks(self):
192218
"""
193219
return self._get(self.base_url + 'me/onenote/notebooks')
194220

221+
@token_required
195222
def get_notebook(self, notebook_id):
196223
"""Retrieve the properties and relationships of a notebook object.
197224
@@ -204,6 +231,7 @@ def get_notebook(self, notebook_id):
204231
"""
205232
return self._get(self.base_url + 'me/onenote/notebooks/' + notebook_id)
206233

234+
@token_required
207235
def get_notebook_sections(self, notebook_id):
208236
"""Retrieve the properties and relationships of a notebook object.
209237
@@ -216,6 +244,7 @@ def get_notebook_sections(self, notebook_id):
216244
"""
217245
return self._get(self.base_url + 'me/onenote/notebooks/{}/sections'.format(notebook_id))
218246

247+
@token_required
219248
def create_page(self, section_id, files):
220249
"""Create a new page in the specified section.
221250
@@ -227,12 +256,9 @@ def create_page(self, section_id, files):
227256
A dict.
228257
229258
"""
230-
# headers = {
231-
# 'Content-Type': 'text/html'
232-
# }
233-
headers = None
234-
return self._post(self.base_url + '/me/onenote/sections/{}/pages'.format(section_id), headers=headers, files=files)
259+
return self._post(self.base_url + '/me/onenote/sections/{}/pages'.format(section_id), files=files)
235260

261+
@token_required
236262
def get_me_events(self):
237263
"""Get a list of event objects in the user's mailbox. The list contains single instance meetings and
238264
series masters.
@@ -245,6 +271,7 @@ def get_me_events(self):
245271
"""
246272
return self._get(self.base_url + 'me/events')
247273

274+
@token_required
248275
def create_calendar_event(
249276
self, subject, content,
250277
start_datetime, start_timezone, end_datetime,
@@ -322,6 +349,7 @@ def create_calendar_event(
322349
url = 'me/calendars/{}/events'.format(calendar) if calendar is not None else 'me/events'
323350
return self._post(self.base_url + url, json=body)
324351

352+
@token_required
325353
def create_calendar(self, name):
326354
"""Create an event in the user's default calendar or specified calendar.
327355
@@ -341,6 +369,7 @@ def create_calendar(self, name):
341369
}
342370
return self._post(self.base_url + 'me/calendars', json=body)
343371

372+
@token_required
344373
def get_me_calendar(self, calendar_id=None):
345374
"""Get the properties and relationships of a calendar object. The calendar can be one for a user,
346375
or the default calendar of an Office 365 group.
@@ -354,6 +383,7 @@ def get_me_calendar(self, calendar_id=None):
354383
url = 'me/calendar/{}'.format(calendar_id) if calendar_id is not None else 'me/calendar'
355384
return self._get(self.base_url + url)
356385

386+
@token_required
357387
def get_me_calendars(self):
358388
"""
359389
@@ -362,6 +392,7 @@ def get_me_calendars(self):
362392
"""
363393
return self._get(self.base_url + 'me/calendars')
364394

395+
@token_required
365396
def send_mail(self, subject=None, recipients=None, body='', content_type='HTML', attachments=None):
366397
"""Helper to send email from current user.
367398
@@ -404,26 +435,30 @@ def send_mail(self, subject=None, recipients=None, body='', content_type='HTML',
404435
# Do a POST to Graph's sendMail API and return the response.
405436
return self._post(self.base_url + 'me/microsoft.graph.sendMail', json=email_msg)
406437

407-
# Outlook Contacts Methods
438+
@token_required
408439
def outlook_get_me_contacts(self, data_id=None, params=None):
409440
if data_id is None:
410441
url = "{0}me/contacts".format(self.base_url)
411442
else:
412443
url = "{0}me/contacts/{1}".format(self.base_url, data_id)
413444
return self._get(url, params=params)
414445

446+
@token_required
415447
def outlook_create_me_contact(self, **kwargs):
416448
url = "{0}me/contacts".format(self.base_url)
417449
return self._post(url, **kwargs)
418450

451+
@token_required
419452
def outlook_create_contact_in_folder(self, folder_id, **kwargs):
420453
url = "{0}/me/contactFolders/{1}/contacts".format(self.base_url, folder_id)
421454
return self._post(url, **kwargs)
422455

456+
@token_required
423457
def outlook_get_contact_folders(self, params=None):
424458
url = "{0}me/contactFolders".format(self.base_url)
425459
return self._get(url, params=params)
426460

461+
@token_required
427462
def outlook_create_contact_folder(self, **kwargs):
428463
url = "{0}me/contactFolders".format(self.base_url)
429464
return self._post(url, **kwargs)
@@ -454,7 +489,6 @@ def _request(self, method, url, headers=None, **kwargs):
454489
return self._parse(requests.request(method, url, headers=_headers, **kwargs))
455490

456491
def _parse(self, response):
457-
print(response.text)
458492
status_code = response.status_code
459493
if 'application/json' in response.headers['Content-Type']:
460494
r = response.json()

microsoftgraph/decorators.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from microsoftgraph.exceptions import AccessTokenRequired
2+
from functools import wraps
3+
4+
5+
def token_required(func):
6+
@wraps(func)
7+
def helper(*args, **kwargs):
8+
client = args[0]
9+
if not client.token:
10+
raise AccessTokenRequired('You must set the Token.')
11+
return func(*args, **kwargs)
12+
13+
return helper

microsoftgraph/exceptions.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ class UnknownError(BaseError):
66
pass
77

88

9+
class AccessTokenRequired(BaseError):
10+
pass
11+
12+
913
class BadRequest(BaseError):
1014
pass
1115

0 commit comments

Comments
 (0)