Skip to content

Commit d49ea04

Browse files
authored
Merge pull request #45 from microsoftgraph/task/refactor-graph-session
Task/refactor graph session
2 parents f00e4b8 + a199bda commit d49ea04

File tree

12 files changed

+74
-68
lines changed

12 files changed

+74
-68
lines changed

README.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Import modules
1414

1515
```python
1616
from azure.identity import UsernamePasswordCredential, DeviceCodeCredential
17-
from msgraphcore import GraphSession, AuthorizationHandler, AuthMiddlewareOptions, TokenCredentialAuthProvider
17+
from msgraphcore import GraphSession
1818
```
1919

2020
Configure Credential Object
@@ -31,14 +31,13 @@ device_credential = DeviceCodeCredential(
3131

3232
```
3333

34-
Create an authorization provider object and a list of scopes
34+
Pass the credential object and scopes to the GraphSession constructor.
3535
```python
3636
scopes = ['mail.send', 'user.read']
37-
auth_provider = TokenCredentialAuthProvider(scopes, device_credential)
37+
graph_session = GraphSession(device_credential, scopes)
3838
```
3939

4040
```python
41-
graph_session = GraphSession(auth_provider)
4241
result = graph_session.get('/me')
4342
print(result.json())
4443
```

msgraphcore/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
"""msgraph-core"""
22

33
from msgraphcore.graph_session import GraphSession
4-
from .middleware.authorization import AuthProviderBase, TokenCredentialAuthProvider
54
from .constants import SDK_VERSION
65

76
__version__ = SDK_VERSION

msgraphcore/constants.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
REQUEST_TIMEOUT = 100
55
CONNECTION_TIMEOUT = 30
66
BASE_URL = 'https://graph.microsoft.com/v1.0'
7-
SDK_VERSION = '0.0.1-0'
7+
SDK_VERSION = '0.0.3'
88

99
# Used as the key for AuthMiddlewareOption in MiddlewareControl
1010
AUTH_MIDDLEWARE_OPTIONS = 'AUTH_MIDDLEWARE_OPTIONS'

msgraphcore/graph_session.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
"""
22
Graph Session
33
"""
4-
54
from requests import Session
65

76
from msgraphcore.constants import BASE_URL, SDK_VERSION
8-
from msgraphcore.middleware._middleware import MiddlewarePipeline, BaseMiddleware
9-
from msgraphcore.middleware._base_auth import AuthProviderBase
7+
from msgraphcore.middleware.middleware import MiddlewarePipeline, BaseMiddleware
8+
from msgraphcore.middleware.abc_token_credential import TokenCredential
109
from msgraphcore.middleware.authorization import AuthorizationHandler
1110
from msgraphcore.middleware.options.middleware_control import middleware_control
1211

@@ -15,15 +14,17 @@ class GraphSession(Session):
1514
"""Extends Session with Graph functionality
1615
1716
Extends Session by adding support for middleware options and middleware pipeline
18-
19-
2017
"""
21-
def __init__(self, auth_provider: AuthProviderBase, middleware: list = []):
18+
def __init__(self,
19+
credential: TokenCredential,
20+
scopes: [str] = ['.default'],
21+
middleware: list = []
22+
):
2223
super().__init__()
2324
self._append_sdk_version()
2425
self._base_url = BASE_URL
2526

26-
auth_handler = AuthorizationHandler(auth_provider)
27+
auth_handler = AuthorizationHandler(credential, scopes)
2728

2829
# The authorization handler should be the first middleware in the pipeline.
2930
middleware.insert(0, auth_handler)

msgraphcore/middleware/_base_auth.py renamed to msgraphcore/middleware/abc_token_credential.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,3 @@ class TokenCredential(ABC):
66
def get_token(self, *scopes, **kwargs):
77
pass
88

9-
10-
class AuthProviderBase(ABC):
11-
@abstractmethod
12-
def get_access_token(self):
13-
pass
Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,34 @@
1-
from ._base_auth import AuthProviderBase, TokenCredential
1+
from .abc_token_credential import TokenCredential
22
from ..constants import AUTH_MIDDLEWARE_OPTIONS
3-
from ._middleware import BaseMiddleware
3+
from .middleware import BaseMiddleware
44
from .options.middleware_control import middleware_control
55

66

77
class AuthorizationHandler(BaseMiddleware):
8-
def __init__(self, auth_provider: AuthProviderBase):
8+
def __init__(self, credential: TokenCredential, scopes: [str]):
99
super().__init__()
10-
self.auth_provider = auth_provider
10+
self.credential = credential
11+
self.scopes = scopes
1112
self.retry_count = 0
1213

1314
def send(self, request, **kwargs):
14-
# Checks if there are any options for this middleware
15-
options = self._get_middleware_options()
16-
# If there is, get the scopes from the options
17-
if options:
18-
self.auth_provider.scopes = options.scopes
19-
20-
token = self.auth_provider.get_access_token()
21-
request.headers.update({'Authorization': 'Bearer {}'.format(token)})
15+
request.headers.update({'Authorization': 'Bearer {}'.format(self._get_access_token())})
2216
response = super().send(request, **kwargs)
2317

24-
# Token might have expired just before transmission, retry the request
18+
# Token might have expired just before transmission, retry the request one more time
2519
if response.status_code == 401 and self.retry_count < 2:
2620
self.retry_count += 1
2721
return self.send(request, **kwargs)
28-
2922
return response
3023

31-
def _get_middleware_options(self):
32-
return middleware_control.get(AUTH_MIDDLEWARE_OPTIONS)
24+
def _get_access_token(self):
25+
return self.credential.get_token(*self.get_scopes())[0]
3326

34-
35-
class TokenCredentialAuthProvider(AuthProviderBase):
36-
def __init__(self, credential: TokenCredential, scopes: [str] = ['.default']):
37-
self.credential = credential
38-
self.scopes = scopes
39-
40-
def get_access_token(self):
41-
return self.credential.get_token(*self.scopes)[0]
27+
def get_scopes(self):
28+
# Checks if there are any options for this middleware
29+
auth_options_present = middleware_control.get(AUTH_MIDDLEWARE_OPTIONS)
30+
# If there is, get the scopes from the options
31+
if auth_options_present:
32+
return auth_options_present.scopes
33+
else:
34+
return self.scopes

msgraphcore/middleware/_middleware.py renamed to msgraphcore/middleware/middleware.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import ssl
2+
from urllib3 import PoolManager
23

34
from requests.adapters import HTTPAdapter
4-
from urllib3 import PoolManager
55

66

77
class MiddlewarePipeline(HTTPAdapter):

samples/samples.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
11
import json
22
from pprint import pprint
3-
43
from azure.identity import InteractiveBrowserCredential
5-
from msgraphcore.middleware.authorization import TokenCredentialAuthProvider
6-
74
from msgraphcore import GraphSession
85

6+
scopes = ['user.read']
97
browser_credential = InteractiveBrowserCredential(client_id='ENTER_YOUR_CLIENT_ID')
10-
auth_provider = TokenCredentialAuthProvider(browser_credential)
11-
graph_session = GraphSession(auth_provider)
8+
graph_session = GraphSession(browser_credential, scopes)
129

1310

1411
def post_sample():

tests/integration/test_middleware_pipeline.py

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
from unittest import TestCase
33

44
from msgraphcore.graph_session import GraphSession
5-
from msgraphcore.middleware.authorization import AuthProviderBase
65

76

87
class MiddlewarePipelineTest(TestCase):
@@ -12,19 +11,16 @@ def setUp(self):
1211
def test_middleware_pipeline(self):
1312
url = 'https://proxy.apisandbox.msdn.microsoft.com/svc?url=https://graph.microsoft.com/v1.0/me'
1413
scopes = ['user.read']
15-
auth_provider = _CustomAuthProvider(scopes)
16-
graph_session = GraphSession(auth_provider)
14+
credential = _CustomTokenCredential()
15+
graph_session = GraphSession(credential, scopes)
1716
result = graph_session.get(url)
1817

1918
self.assertEqual(result.status_code, 200)
2019

2120

22-
class _CustomAuthProvider(AuthProviderBase):
23-
def __init__(self, scopes):
24-
pass
25-
26-
def get_access_token(self):
27-
return '{token:https://graph.microsoft.com/}'
21+
class _CustomTokenCredential:
22+
def get_token(self, scopes):
23+
return ['{token:https://graph.microsoft.com/}']
2824

2925

3026

tests/unit/test_auth_handler.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import unittest
2+
3+
from msgraphcore.constants import AUTH_MIDDLEWARE_OPTIONS
4+
from msgraphcore.middleware.authorization import AuthorizationHandler
5+
from msgraphcore.middleware.options.middleware_control import middleware_control
6+
from msgraphcore.middleware.options.auth_middleware_options import AuthMiddlewareOptions
7+
8+
9+
class TestAuthorizationHandler(unittest.TestCase):
10+
def test_auth_options_override_default_scopes(self):
11+
auth_option = ['email.read']
12+
default_scopes = ['.default']
13+
14+
middleware_control.set(AUTH_MIDDLEWARE_OPTIONS, AuthMiddlewareOptions(auth_option))
15+
auth_handler = AuthorizationHandler(None, default_scopes)
16+
17+
auth_handler_scopes = auth_handler.get_scopes()
18+
self.assertEqual(auth_option, auth_handler_scopes)
19+
20+
def test_auth_handler_get_scopes_does_not_overwrite_default_scopes(self):
21+
auth_option = ['email.read']
22+
default_scopes = ['.default']
23+
24+
middleware_control.set(AUTH_MIDDLEWARE_OPTIONS, AuthMiddlewareOptions(auth_option))
25+
auth_handler = AuthorizationHandler(None, default_scopes)
26+
auth_handler.get_scopes()
27+
28+
self.assertEqual(auth_handler.scopes, default_scopes)
29+
30+

tests/unit/test_graph_session.py

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,12 @@
66

77
from msgraphcore.graph_session import GraphSession
88
from msgraphcore.constants import BASE_URL, SDK_VERSION
9-
from msgraphcore.middleware._base_auth import AuthProviderBase
109

1110

1211
class GraphSessionTest(TestCase):
1312
def setUp(self) -> None:
14-
self.auth_provider = _CustomAuthProvider(['user.read'])
15-
self.requests = GraphSession(self.auth_provider)
13+
self.credential = _CustomTokenCredential()
14+
self.requests = GraphSession(self.credential, ['user.read'])
1615

1716
def tearDown(self) -> None:
1817
self.requests = None
@@ -31,7 +30,7 @@ def test_updated_sdk_version(self):
3130
.startswith('graph-python-'+SDK_VERSION))
3231

3332
def test_initialized_with_middlewares(self):
34-
graph_session = GraphSession(self.auth_provider)
33+
graph_session = GraphSession(self.credential)
3534
mocked_middleware = graph_session.get_adapter('https://')
3635

3736
self.assertIsInstance(mocked_middleware, HTTPAdapter)
@@ -57,9 +56,6 @@ def test_does_not_build_graph_urls_for_full_urls(self):
5756
self.assertEqual(other_url, request_url)
5857

5958

60-
class _CustomAuthProvider(AuthProviderBase):
61-
def __init__(self, scopes):
62-
pass
63-
64-
def get_access_token(self):
65-
return '{token:https://graph.microsoft.com/}'
59+
class _CustomTokenCredential:
60+
def get_token(self, scopes):
61+
return ['{token:https://graph.microsoft.com/}']

tests/unit/test_middleware_pipeline.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from collections import OrderedDict
22
from unittest import TestCase
33

4-
from msgraphcore.middleware._middleware import MiddlewarePipeline, BaseMiddleware
4+
from msgraphcore.middleware.middleware import MiddlewarePipeline, BaseMiddleware
55

66

77
class MiddlewarePipelineTest(TestCase):

0 commit comments

Comments
 (0)