2020from ms_graph .personal_contacts import PersonalContacts
2121from ms_graph .mail import Mail
2222
23+ from ms_graph .workbooks_and_charts .workbook import Workbooks
2324
24- class MicrosoftGraphClient ():
25+
26+ class MicrosoftGraphClient :
2527
2628 """
2729 ### Overview:
@@ -30,25 +32,25 @@ class MicrosoftGraphClient():
3032 API Service.
3133 """
3234
33- RESOURCE = ' https://graph.microsoft.com/'
35+ RESOURCE = " https://graph.microsoft.com/"
3436
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"
3840
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"
4244
4345 def __init__ (
4446 self ,
4547 client_id : str ,
4648 client_secret : str ,
4749 redirect_uri : str ,
4850 scope : List [str ],
49- account_type : str = ' consumers' ,
51+ account_type : str = " consumers" ,
5052 office365 : bool = False ,
51- credentials : str = None
53+ credentials : str = None ,
5254 ):
5355 """Initializes the Graph Client.
5456
@@ -71,7 +73,7 @@ def __init__(
7173 to have access to.
7274
7375 account_type : str, optional
74- [description], by default ' common'
76+ [description], by default " common"
7577
7678 office365 : bool, optional
7779 [description], by default False
@@ -85,19 +87,19 @@ def __init__(
8587
8688 self .client_id = client_id
8789 self .client_secret = client_secret
88- self .api_version = ' v1.0'
90+ self .api_version = " v1.0"
8991 self .account_type = account_type
9092 self .redirect_uri = redirect_uri
9193
9294 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 ))
9496
9597 self .access_token = None
9698 self .refresh_token = None
9799 self .graph_session = None
98100 self .id_token = None
99101
100- self .base_url = self .RESOURCE + self .api_version + '/'
102+ self .base_url = self .RESOURCE + self .api_version + "/"
101103 self .office_url = self .OFFICE365_AUTHORITY_URL + self .OFFICE365_AUTH_ENDPOINT
102104 self .graph_url = self .AUTHORITY_URL + self .account_type + self .AUTH_ENDPOINT
103105 self .office365 = office365
@@ -107,7 +109,7 @@ def __init__(
107109 self .client_app = msal .ConfidentialClientApplication (
108110 client_id = self .client_id ,
109111 authority = self .AUTHORITY_URL + self .account_type ,
110- client_credential = self .client_secret
112+ client_credential = self .client_secret ,
111113 )
112114
113115 def _state (self , action : str , token_dict : dict = None ) -> bool :
@@ -134,18 +136,18 @@ def _state(self, action: str, token_dict: dict = None) -> bool:
134136 does_exists = pathlib .Path (self .credentials ).exists ()
135137
136138 # If it exists and we are loading it then proceed.
137- if does_exists and action == ' load' :
139+ if does_exists and action == " load" :
138140
139141 # 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 :
141143 credentials = json .load (fp = state_file )
142144
143145 # Grab the Token if it exists.
144- if ' refresh_token' in credentials :
146+ if " refresh_token" in credentials :
145147
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" ]
149151 self .token_dict = credentials
150152
151153 return True
@@ -154,22 +156,22 @@ def _state(self, action: str, token_dict: dict = None) -> bool:
154156 return False
155157
156158 # If we are saving the state then open the file and dump the dictionary.
157- elif action == ' save' :
159+ elif action == " save" :
158160
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+ )
163165
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" ]
167169 self .token_dict = token_dict
168170
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 :
170172 json .dump (obj = token_dict , fp = state_file , indent = 2 )
171173
172- def _token_seconds (self , token_type : str = ' access_token' ) -> int :
174+ def _token_seconds (self , token_type : str = " access_token" ) -> int :
173175 """Determines time till expiration for a token.
174176
175177 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:
179181 ### Arguments:
180182 ----
181183 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})
183185
184186 ### Returns:
185187 ----
186188 {int} -- The number of seconds till expiration.
187189 """
188190
189191 # if needed check the access token.
190- if token_type == ' access_token' :
192+ if token_type == " access_token" :
191193
192194 # 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+ ):
194198 return 0
195199
196200 # 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 )
198202
199203 # if needed check the refresh token.
200- elif token_type == ' refresh_token' :
204+ elif token_type == " refresh_token" :
201205
202206 # 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+ ):
204210 return 0
205211
206212 # 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 )
210214
211215 return token_exp
212216
@@ -223,7 +227,7 @@ def _token_validation(self, nseconds: int = 60):
223227 valid for before attempting to get a refresh token. (default: {5})
224228 """
225229
226- if self ._token_seconds (token_type = ' access_token' ) < nseconds :
230+ if self ._token_seconds (token_type = " access_token" ) < nseconds :
227231 self .grab_refresh_token ()
228232
229233 def _silent_sso (self ) -> bool :
@@ -236,21 +240,21 @@ def _silent_sso(self) -> bool:
236240 """
237241
238242 # 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 :
240244 return True
241245
242246 # if the current access token is expired then try and refresh access token.
243247 elif self .refresh_token and self .grab_refresh_token ():
244248 return True
245249
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.
247251 return False
248252
249253 def login (self ) -> None :
250254 """Logs the user into the session."""
251255
252256 # Load the State.
253- self ._state (action = ' load' )
257+ self ._state (action = " load" )
254258
255259 # Try a Silent SSO First.
256260 if self ._silent_sso ():
@@ -267,11 +271,11 @@ def login(self) -> None:
267271
268272 # aks the user to go to the URL provided, they will be prompted
269273 # 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 } " )
271275
272276 # ask the user to take the final URL after authentication and
273277 # 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: " )
275279
276280 # store the redirect URL
277281 self ._redirect_code = my_response
@@ -292,9 +296,7 @@ def authorization_url(self):
292296
293297 # Build the Auth URL.
294298 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
298300 )
299301
300302 return auth_url
@@ -315,16 +317,11 @@ def grab_access_token(self) -> Dict:
315317
316318 # Grab the Token.
317319 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
321321 )
322322
323323 # 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 )
328325
329326 return token_dict
330327
@@ -339,19 +336,17 @@ def grab_refresh_token(self) -> Dict:
339336
340337 # Grab a new token using our refresh token.
341338 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
344340 )
345341
346- if ' error' in token_dict :
342+ if " error" in token_dict :
347343 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+ )
349347
350348 # Save the Token.
351- self ._state (
352- action = 'save' ,
353- token_dict = token_dict
354- )
349+ self ._state (action = "save" , token_dict = token_dict )
355350
356351 return token_dict
357352
@@ -449,7 +444,9 @@ def personal_contacts(self) -> PersonalContacts:
449444 """
450445
451446 # 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+ )
453450
454451 return personal_contacts_object
455452
@@ -466,3 +463,17 @@ def mail(self) -> Mail:
466463 mail_service : Mail = Mail (session = self .graph_session )
467464
468465 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