20
20
from ms_graph .personal_contacts import PersonalContacts
21
21
from ms_graph .mail import Mail
22
22
23
+ from ms_graph .workbooks_and_charts .workbook import Workbooks
23
24
24
- class MicrosoftGraphClient ():
25
+
26
+ class MicrosoftGraphClient :
25
27
26
28
"""
27
29
### Overview:
@@ -30,25 +32,25 @@ class MicrosoftGraphClient():
30
32
API Service.
31
33
"""
32
34
33
- RESOURCE = ' https://graph.microsoft.com/'
35
+ RESOURCE = " https://graph.microsoft.com/"
34
36
35
- AUTHORITY_URL = ' https://login.microsoftonline.com/'
36
- AUTH_ENDPOINT = ' /oauth2/v2.0/authorize?'
37
- TOKEN_ENDPOINT = ' /oauth2/v2.0/token'
37
+ AUTHORITY_URL = " https://login.microsoftonline.com/"
38
+ AUTH_ENDPOINT = " /oauth2/v2.0/authorize?"
39
+ TOKEN_ENDPOINT = " /oauth2/v2.0/token"
38
40
39
- OFFICE365_AUTHORITY_URL = ' https://login.live.com'
40
- OFFICE365_AUTH_ENDPOINT = ' /oauth20_authorize.srf?'
41
- OFFICE365_TOKEN_ENDPOINT = ' /oauth20_token.srf'
41
+ OFFICE365_AUTHORITY_URL = " https://login.live.com"
42
+ OFFICE365_AUTH_ENDPOINT = " /oauth20_authorize.srf?"
43
+ OFFICE365_TOKEN_ENDPOINT = " /oauth20_token.srf"
42
44
43
45
def __init__ (
44
46
self ,
45
47
client_id : str ,
46
48
client_secret : str ,
47
49
redirect_uri : str ,
48
50
scope : List [str ],
49
- account_type : str = ' consumers' ,
51
+ account_type : str = " consumers" ,
50
52
office365 : bool = False ,
51
- credentials : str = None
53
+ credentials : str = None ,
52
54
):
53
55
"""Initializes the Graph Client.
54
56
@@ -71,7 +73,7 @@ def __init__(
71
73
to have access to.
72
74
73
75
account_type : str, optional
74
- [description], by default ' common'
76
+ [description], by default " common"
75
77
76
78
office365 : bool, optional
77
79
[description], by default False
@@ -85,19 +87,19 @@ def __init__(
85
87
86
88
self .client_id = client_id
87
89
self .client_secret = client_secret
88
- self .api_version = ' v1.0'
90
+ self .api_version = " v1.0"
89
91
self .account_type = account_type
90
92
self .redirect_uri = redirect_uri
91
93
92
94
self .scope = scope
93
- self .state = '' .join (random .choice (letters ) for i in range (10 ))
95
+ self .state = "" .join (random .choice (letters ) for i in range (10 ))
94
96
95
97
self .access_token = None
96
98
self .refresh_token = None
97
99
self .graph_session = None
98
100
self .id_token = None
99
101
100
- self .base_url = self .RESOURCE + self .api_version + '/'
102
+ self .base_url = self .RESOURCE + self .api_version + "/"
101
103
self .office_url = self .OFFICE365_AUTHORITY_URL + self .OFFICE365_AUTH_ENDPOINT
102
104
self .graph_url = self .AUTHORITY_URL + self .account_type + self .AUTH_ENDPOINT
103
105
self .office365 = office365
@@ -107,7 +109,7 @@ def __init__(
107
109
self .client_app = msal .ConfidentialClientApplication (
108
110
client_id = self .client_id ,
109
111
authority = self .AUTHORITY_URL + self .account_type ,
110
- client_credential = self .client_secret
112
+ client_credential = self .client_secret ,
111
113
)
112
114
113
115
def _state (self , action : str , token_dict : dict = None ) -> bool :
@@ -134,18 +136,18 @@ def _state(self, action: str, token_dict: dict = None) -> bool:
134
136
does_exists = pathlib .Path (self .credentials ).exists ()
135
137
136
138
# If it exists and we are loading it then proceed.
137
- if does_exists and action == ' load' :
139
+ if does_exists and action == " load" :
138
140
139
141
# Load the file.
140
- with open (file = self .credentials , mode = 'r' , encoding = ' utf-8' ) as state_file :
142
+ with open (file = self .credentials , mode = "r" , encoding = " utf-8" ) as state_file :
141
143
credentials = json .load (fp = state_file )
142
144
143
145
# Grab the Token if it exists.
144
- if ' refresh_token' in credentials :
146
+ if " refresh_token" in credentials :
145
147
146
- self .refresh_token = credentials [' refresh_token' ]
147
- self .access_token = credentials [' access_token' ]
148
- self .id_token = credentials [' id_token' ]
148
+ self .refresh_token = credentials [" refresh_token" ]
149
+ self .access_token = credentials [" access_token" ]
150
+ self .id_token = credentials [" id_token" ]
149
151
self .token_dict = credentials
150
152
151
153
return True
@@ -154,22 +156,22 @@ def _state(self, action: str, token_dict: dict = None) -> bool:
154
156
return False
155
157
156
158
# If we are saving the state then open the file and dump the dictionary.
157
- elif action == ' save' :
159
+ elif action == " save" :
158
160
159
- token_dict [' expires_in' ] = time .time (
160
- ) + int (token_dict [ 'expires_in' ])
161
- token_dict [' ext_expires_in' ] = time . time (
162
- ) + int ( token_dict [ 'ext_expires_in' ])
161
+ token_dict [" expires_in" ] = time .time () + int ( token_dict [ "expires_in" ])
162
+ token_dict [ "ext_expires_in" ] = time . time ( ) + int (
163
+ token_dict [" ext_expires_in" ]
164
+ )
163
165
164
- self .refresh_token = token_dict [' refresh_token' ]
165
- self .access_token = token_dict [' access_token' ]
166
- self .id_token = token_dict [' id_token' ]
166
+ self .refresh_token = token_dict [" refresh_token" ]
167
+ self .access_token = token_dict [" access_token" ]
168
+ self .id_token = token_dict [" id_token" ]
167
169
self .token_dict = token_dict
168
170
169
- with open (file = self .credentials , mode = 'w+' , encoding = ' utf-8' ) as state_file :
171
+ with open (file = self .credentials , mode = "w+" , encoding = " utf-8" ) as state_file :
170
172
json .dump (obj = token_dict , fp = state_file , indent = 2 )
171
173
172
- def _token_seconds (self , token_type : str = ' access_token' ) -> int :
174
+ def _token_seconds (self , token_type : str = " access_token" ) -> int :
173
175
"""Determines time till expiration for a token.
174
176
175
177
Return the number of seconds until the current access token or refresh token
@@ -179,34 +181,36 @@ def _token_seconds(self, token_type: str = 'access_token') -> int:
179
181
### Arguments:
180
182
----
181
183
token_type {str} -- The type of token you would like to determine lifespan for.
182
- Possible values are [' access_token', ' refresh_token' ] (default: {access_token})
184
+ Possible values are [" access_token", " refresh_token" ] (default: {access_token})
183
185
184
186
### Returns:
185
187
----
186
188
{int} -- The number of seconds till expiration.
187
189
"""
188
190
189
191
# if needed check the access token.
190
- if token_type == ' access_token' :
192
+ if token_type == " access_token" :
191
193
192
194
# if the time to expiration is less than or equal to 0, return 0.
193
- if not self .access_token or (time .time () + 60 >= self .token_dict ['expires_in' ]):
195
+ if not self .access_token or (
196
+ time .time () + 60 >= self .token_dict ["expires_in" ]
197
+ ):
194
198
return 0
195
199
196
200
# else return the number of seconds until expiration.
197
- token_exp = int (self .token_dict [' expires_in' ] - time .time () - 60 )
201
+ token_exp = int (self .token_dict [" expires_in" ] - time .time () - 60 )
198
202
199
203
# if needed check the refresh token.
200
- elif token_type == ' refresh_token' :
204
+ elif token_type == " refresh_token" :
201
205
202
206
# if the time to expiration is less than or equal to 0, return 0.
203
- if not self .refresh_token or (time .time () + 60 >= self .token_dict ['ext_expires_in' ]):
207
+ if not self .refresh_token or (
208
+ time .time () + 60 >= self .token_dict ["ext_expires_in" ]
209
+ ):
204
210
return 0
205
211
206
212
# else return the number of seconds until expiration.
207
- token_exp = int (
208
- self .token_dict ['ext_expires_in' ] - time .time () - 60
209
- )
213
+ token_exp = int (self .token_dict ["ext_expires_in" ] - time .time () - 60 )
210
214
211
215
return token_exp
212
216
@@ -223,7 +227,7 @@ def _token_validation(self, nseconds: int = 60):
223
227
valid for before attempting to get a refresh token. (default: {5})
224
228
"""
225
229
226
- if self ._token_seconds (token_type = ' access_token' ) < nseconds :
230
+ if self ._token_seconds (token_type = " access_token" ) < nseconds :
227
231
self .grab_refresh_token ()
228
232
229
233
def _silent_sso (self ) -> bool :
@@ -236,21 +240,21 @@ def _silent_sso(self) -> bool:
236
240
"""
237
241
238
242
# if the current access token is not expired then we are still authenticated.
239
- if self ._token_seconds (token_type = ' access_token' ) > 0 :
243
+ if self ._token_seconds (token_type = " access_token" ) > 0 :
240
244
return True
241
245
242
246
# if the current access token is expired then try and refresh access token.
243
247
elif self .refresh_token and self .grab_refresh_token ():
244
248
return True
245
249
246
- # More than likely a first time login, so can' t do silent authenticaiton.
250
+ # More than likely a first time login, so can" t do silent authenticaiton.
247
251
return False
248
252
249
253
def login (self ) -> None :
250
254
"""Logs the user into the session."""
251
255
252
256
# Load the State.
253
- self ._state (action = ' load' )
257
+ self ._state (action = " load" )
254
258
255
259
# Try a Silent SSO First.
256
260
if self ._silent_sso ():
@@ -267,11 +271,11 @@ def login(self) -> None:
267
271
268
272
# aks the user to go to the URL provided, they will be prompted
269
273
# to authenticate themsevles.
270
- print (f' Please go to URL provided authorize your account: { url } ' )
274
+ print (f" Please go to URL provided authorize your account: { url } " )
271
275
272
276
# ask the user to take the final URL after authentication and
273
277
# paste here so we can parse.
274
- my_response = input (' Paste the full URL redirect here: ' )
278
+ my_response = input (" Paste the full URL redirect here: " )
275
279
276
280
# store the redirect URL
277
281
self ._redirect_code = my_response
@@ -292,9 +296,7 @@ def authorization_url(self):
292
296
293
297
# Build the Auth URL.
294
298
auth_url = self .client_app .get_authorization_request_url (
295
- scopes = self .scope ,
296
- state = self .state ,
297
- redirect_uri = self .redirect_uri
299
+ scopes = self .scope , state = self .state , redirect_uri = self .redirect_uri
298
300
)
299
301
300
302
return auth_url
@@ -315,16 +317,11 @@ def grab_access_token(self) -> Dict:
315
317
316
318
# Grab the Token.
317
319
token_dict = self .client_app .acquire_token_by_authorization_code (
318
- code = code ,
319
- scopes = self .scope ,
320
- redirect_uri = self .redirect_uri
320
+ code = code , scopes = self .scope , redirect_uri = self .redirect_uri
321
321
)
322
322
323
323
# Save the token dict.
324
- self ._state (
325
- action = 'save' ,
326
- token_dict = token_dict
327
- )
324
+ self ._state (action = "save" , token_dict = token_dict )
328
325
329
326
return token_dict
330
327
@@ -339,19 +336,17 @@ def grab_refresh_token(self) -> Dict:
339
336
340
337
# Grab a new token using our refresh token.
341
338
token_dict = self .client_app .acquire_token_by_refresh_token (
342
- refresh_token = self .refresh_token ,
343
- scopes = self .scope
339
+ refresh_token = self .refresh_token , scopes = self .scope
344
340
)
345
341
346
- if ' error' in token_dict :
342
+ if " error" in token_dict :
347
343
print (token_dict )
348
- raise PermissionError ("Permissions not authorized, delete json file and run again." )
344
+ raise PermissionError (
345
+ "Permissions not authorized, delete json file and run again."
346
+ )
349
347
350
348
# Save the Token.
351
- self ._state (
352
- action = 'save' ,
353
- token_dict = token_dict
354
- )
349
+ self ._state (action = "save" , token_dict = token_dict )
355
350
356
351
return token_dict
357
352
@@ -449,7 +444,9 @@ def personal_contacts(self) -> PersonalContacts:
449
444
"""
450
445
451
446
# Grab the `PersonalContacts` Object for the session.
452
- personal_contacts_object : PersonalContacts = PersonalContacts (session = self .graph_session )
447
+ personal_contacts_object : PersonalContacts = PersonalContacts (
448
+ session = self .graph_session
449
+ )
453
450
454
451
return personal_contacts_object
455
452
@@ -466,3 +463,17 @@ def mail(self) -> Mail:
466
463
mail_service : Mail = Mail (session = self .graph_session )
467
464
468
465
return mail_service
466
+
467
+ def workbooks (self ) -> Workbooks :
468
+ """Used to access the Workbooks Services and metadata.
469
+
470
+ ### Returns
471
+ ---
472
+ Workbooks:
473
+ The `Workbooks` services Object.
474
+ """
475
+
476
+ # Grab the `Workbooks` Object for the session.
477
+ workbook_service : Workbooks = Workbooks (session = self .graph_session )
478
+
479
+ return workbook_service
0 commit comments