Skip to content

Commit 59862c4

Browse files
committed
linting
1 parent 2042938 commit 59862c4

File tree

4 files changed

+49
-52
lines changed

4 files changed

+49
-52
lines changed

src/posit/connect/external/databricks.py

Lines changed: 36 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@
88
import abc
99
from typing import Callable, Dict, Optional
1010

11+
import requests
12+
1113
from ..client import Client
1214
from ..oauth import Credentials
1315
from .external import is_local
1416

15-
import requests
16-
1717
POSIT_OAUTH_INTEGRATION_AUTH_TYPE = "posit-oauth-integration"
1818
POSIT_LOCAL_CLIENT_CREDENTIALS_AUTH_TYPE = "posit-local-client-credentials"
1919

@@ -81,14 +81,15 @@ def _get_auth_type(local_auth_type: str) -> str:
8181

8282
return POSIT_OAUTH_INTEGRATION_AUTH_TYPE
8383

84+
8485
class PositLocalContentCredentialsProvider:
8586
"""`CredentialsProvider` implementation which provides a fallback for local development using a client credentials flow.
8687
87-
There is an open issue against the Databricks CLI which prevents it from returning service principal access tokens.
88+
There is an open issue against the Databricks CLI which prevents it from returning service principal access tokens.
8889
https://github.com/databricks/cli/issues/1939
8990
9091
Until the CLI issue is resolved, this CredentialsProvider implements the approach described in the Databricks documentation
91-
for manually generating a workspace-level access token using OAuth M2M authentication. Once it has acquired an access token,
92+
for manually generating a workspace-level access token using OAuth M2M authentication. Once it has acquired an access token,
9293
it returns it as a Bearer authorization header like other `CredentialsProvider` implementations.
9394
9495
See Also
@@ -103,7 +104,7 @@ def __init__(self, token_endpoint_url: str, client_id: str, client_secret: str):
103104

104105
def __call__(self) -> Dict[str, str]:
105106
response = requests.post(
106-
self._token_endpoint_url,
107+
self._token_endpoint_url,
107108
auth=(self._client_id, self._client_secret),
108109
data={
109110
"grant_type": "client_credentials",
@@ -115,6 +116,7 @@ def __call__(self) -> Dict[str, str]:
115116
credentials = Credentials(**response.json())
116117
return _new_bearer_authorization_header(credentials)
117118

119+
118120
class PositContentCredentialsProvider:
119121
"""`CredentialsProvider` implementation which initiates a credential exchange using a content-session-token.
120122
@@ -136,10 +138,10 @@ def __call__(self) -> Dict[str, str]:
136138

137139
class PositCredentialsProvider:
138140
"""`CredentialsProvider` implementation which initiates a credential exchange using a user-session-token.
139-
140-
The user-session-token is provided by Connect through the HTTP session header
141+
142+
The user-session-token is provided by Connect through the HTTP session header
141143
`Posit-Connect-User-Session-Token`.
142-
144+
143145
See Also
144146
--------
145147
* https://github.com/posit-dev/posit-sdk-py/blob/main/src/posit/connect/oauth/oauth.py
@@ -154,47 +156,51 @@ def __call__(self) -> Dict[str, str]:
154156
credentials = self._client.oauth.get_credentials(self._user_session_token)
155157
return _new_bearer_authorization_header(credentials)
156158

159+
157160
class PositLocalContentCredentialsStrategy(CredentialsStrategy):
158161
"""`CredentialsStrategy` implementation which supports local development using OAuth M2M authentication against Databricks.
159162
160-
There is an open issue against the Databricks CLI which prevents it from returning service principal access tokens.
163+
There is an open issue against the Databricks CLI which prevents it from returning service principal access tokens.
161164
https://github.com/databricks/cli/issues/1939
162165
163166
Until the CLI issue is resolved, this CredentialsStrategy provides a drop-in replacement as a local_strategy that can be used
164167
to develop applications which target Service Account OAuth integrations on Connect.
165168
166169
Examples
167170
--------
168-
In the example below, the `PositContentCredentialsStrategy` can be initialized anywhere that
171+
In the example below, the `PositContentCredentialsStrategy` can be initialized anywhere that
169172
the Python process can read environment variables.
170173
171174
CLIENT_ID and CLIENT_SECRET are credentials associated with the Databricks service principal.
172175
173176
```python
174-
from posit.connect.external.databricks import PositContentCredentialsStrategy, PositLocalContentCredentialsStrategy
177+
from posit.connect.external.databricks import (
178+
PositContentCredentialsStrategy,
179+
PositLocalContentCredentialsStrategy,
180+
)
175181
176182
import pandas as pd
177183
from databricks import sql
178-
from databricks.sdk.core import ApiClient, Config
184+
from databricks.sdk.core import ApiClient, Config
179185
from databricks.sdk.service.iam import CurrentUserAPI
180186
181187
DATABRICKS_HOST = "<REDACTED>"
182188
DATABRICKS_HOST_URL = f"https://{DATABRICKS_HOST}"
183189
SQL_HTTP_PATH = "<REDACTED>"
184190
TOKEN_ENDPOINT_URL = f"https://{DATABRICKS_HOST}/oidc/v1/token"
185191
186-
CLIENT_ID = "<REDACTED>"
187-
CLIENT_SECRET = "<REDACTED>"
192+
CLIENT_ID = "<REDACTED>"
193+
CLIENT_SECRET = "<REDACTED>"
188194
189-
# Rather than relying on the Databricks CLI as a local strategy, we use
195+
# Rather than relying on the Databricks CLI as a local strategy, we use
190196
# PositLocalContentCredentialsStrategy as a drop-in replacement.
191-
# Can be replaced with the Databricks CLI implementation when
197+
# Can be replaced with the Databricks CLI implementation when
192198
# https://github.com/databricks/cli/issues/1939 is resolved.
193199
local_strategy = PositLocalContentCredentialsStrategy(
194200
token_endpoint_url=TOKEN_ENDPOINT_URL,
195201
client_id=CLIENT_ID,
196202
client_secret=CLIENT_SECRET,
197-
)
203+
)
198204
199205
posit_strategy = PositContentCredentialsStrategy(local_strategy=local_strategy)
200206
@@ -214,27 +220,27 @@ class PositLocalContentCredentialsStrategy(CredentialsStrategy):
214220
rows = cursor.fetchall()
215221
print(pd.DataFrame([row.asDict() for row in rows]))
216222
```
217-
223+
218224
See Also
219225
--------
220226
* https://docs.databricks.com/en/dev-tools/auth/oauth-m2m.html#manually-generate-a-workspace-level-access-token
221227
"""
222228

223229
def __init__(self, token_endpoint_url: str, client_id: str, client_secret: str):
224-
self._token_endpoint_url = token_endpoint_url
230+
self._token_endpoint_url = token_endpoint_url
225231
self._client_id = client_id
226232
self._client_secret = client_secret
227233

228-
def sql_credentials_provider(self, *args, **kwargs):
234+
def sql_credentials_provider(self, *args, **kwargs):
229235
return lambda: self.__call__(*args, **kwargs)
230236

231237
def auth_type(self) -> str:
232-
return POSIT_LOCAL_CLIENT_CREDENTIALS_AUTH_TYPE
238+
return POSIT_LOCAL_CLIENT_CREDENTIALS_AUTH_TYPE
233239

234-
def __call__(self, *args, **kwargs) -> CredentialsProvider:
240+
def __call__(self, *args, **kwargs) -> CredentialsProvider: # noqa: ARG002
235241
return PositLocalContentCredentialsProvider(
236-
self._token_endpoint_url,
237-
self._client_id,
242+
self._token_endpoint_url,
243+
self._client_id,
238244
self._client_secret,
239245
)
240246

@@ -247,7 +253,7 @@ class PositContentCredentialsStrategy(CredentialsStrategy):
247253
248254
Examples
249255
--------
250-
NOTE: in the example below, the `PositContentCredentialsStrategy` can be initialized anywhere that
256+
NOTE: in the example below, the `PositContentCredentialsStrategy` can be initialized anywhere that
251257
the Python process can read environment variables.
252258
253259
```python
@@ -265,9 +271,9 @@ class PositContentCredentialsStrategy(CredentialsStrategy):
265271
# NOTE: currently the databricks_cli local strategy only supports auth code OAuth flows.
266272
# https://github.com/databricks/cli/issues/1939
267273
#
268-
# This means that the databricks_cli supports local development using the developer's
269-
# databricks credentials, but not the credentials for a service principal.
270-
# To fallback to service principal credentials in local development, use
274+
# This means that the databricks_cli supports local development using the developer's
275+
# databricks credentials, but not the credentials for a service principal.
276+
# To fallback to service principal credentials in local development, use
271277
# `PositLocalContentCredentialsStrategy` as a drop-in replacement.
272278
posit_strategy = PositContentCredentialsStrategy(local_strategy=databricks_cli)
273279
@@ -331,7 +337,7 @@ class PositCredentialsStrategy(CredentialsStrategy):
331337
332338
Examples
333339
--------
334-
NOTE: In the example below, the PositCredentialsProvider *must* be initialized within the context of the
340+
NOTE: In the example below, the PositCredentialsProvider *must* be initialized within the context of the
335341
shiny `server` function, which provides access to the HTTP session headers.
336342
337343
```python
@@ -343,7 +349,7 @@ class PositCredentialsStrategy(CredentialsStrategy):
343349
from databricks.sdk.service.iam import CurrentUserAPI
344350
from posit.connect.external.databricks import PositCredentialsStrategy
345351
from shiny import App, Inputs, Outputs, Session, render, ui
346-
352+
347353
DATABRICKS_HOST = "<REDACTED>"
348354
DATABRICKS_HOST_URL = f"https://{DATABRICKS_HOST}"
349355
SQL_HTTP_PATH = "<REDACTED>"
@@ -352,7 +358,6 @@ class PositCredentialsStrategy(CredentialsStrategy):
352358
353359
354360
def server(i: Inputs, o: Outputs, session: Session):
355-
356361
# HTTP session headers are available in this context.
357362
session_token = session.http_conn.headers.get("Posit-Connect-User-Session-Token")
358363
posit_strategy = PositCredentialsStrategy(

src/posit/connect/oauth/oauth.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
USER_SESSION_TOKEN_TYPE = "urn:posit:connect:user-session-token"
1414
CONTENT_SESSION_TOKEN_TYPE = "urn:posit:connect:content-session-token"
1515

16+
1617
def _get_content_session_token() -> str:
1718
"""Return the content session token.
1819
@@ -28,15 +29,18 @@ def _get_content_session_token() -> str:
2829
"""
2930
value = os.environ.get("CONNECT_CONTENT_SESSION_TOKEN")
3031
if not value:
31-
raise ValueError("Invalid value for 'CONNECT_CONTENT_SESSION_TOKEN': Must be a non-empty string.")
32+
raise ValueError(
33+
"Invalid value for 'CONNECT_CONTENT_SESSION_TOKEN': Must be a non-empty string."
34+
)
3235
return value
3336

37+
3438
class OAuth(Resources):
3539
def __init__(self, params: ResourceParameters, api_key: str) -> None:
3640
super().__init__(params)
3741
self.api_key = api_key
3842

39-
def _get_credentials_url(self) -> str:
43+
def _get_credentials_url(self) -> str:
4044
return self.params.url + "v1/oauth/integrations/credentials"
4145

4246
@property
@@ -65,11 +69,12 @@ def get_content_credentials(self, content_session_token: Optional[str] = None) -
6569
data = {}
6670
data["grant_type"] = GRANT_TYPE
6771
data["subject_token_type"] = CONTENT_SESSION_TOKEN_TYPE
68-
data["subject_token"] = content_session_token or _get_content_session_token()
72+
data["subject_token"] = content_session_token or _get_content_session_token()
6973

7074
response = self.params.session.post(self._get_credentials_url(), data=data)
7175
return Credentials(**response.json())
7276

77+
7378
class Credentials(TypedDict, total=False):
7479
access_token: str
7580
issued_token_type: str

tests/posit/connect/external/test_databricks.py

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import base64
2-
32
from typing import Dict
43
from unittest.mock import patch
54

@@ -72,10 +71,7 @@ def register_mocks():
7271
)
7372

7473

75-
76-
7774
class TestPositCredentialsHelpers:
78-
7975
def test_new_bearer_authorization_header(self):
8076
credential = Credentials()
8177
credential["token_type"] = "token_type"
@@ -91,19 +87,17 @@ def test_new_bearer_authorization_header(self):
9187
def test_get_auth_type_local(self):
9288
assert _get_auth_type("local-auth") == "local-auth"
9389

94-
9590
@patch.dict("os.environ", {"RSTUDIO_PRODUCT": "CONNECT"})
9691
def test_get_auth_type_connect(self):
9792
assert _get_auth_type("local-auth") == POSIT_OAUTH_INTEGRATION_AUTH_TYPE
9893

9994
@responses.activate
10095
def test_local_content_credentials_provider(self):
101-
10296
token_url = "https://my-token/url"
10397
client_id = "client_id"
10498
client_secret = "client_secret_123"
10599
basic_auth = f"{client_id}:{client_secret}"
106-
b64_basic_auth = base64.b64encode(basic_auth.encode('utf-8')).decode('utf-8')
100+
b64_basic_auth = base64.b64encode(basic_auth.encode("utf-8")).decode("utf-8")
107101

108102
responses.post(
109103
token_url,
@@ -114,7 +108,7 @@ def test_local_content_credentials_provider(self):
114108
"scope": "all-apis",
115109
},
116110
),
117-
responses.matchers.header_matcher({"Authorization": f"Basic {b64_basic_auth}"})
111+
responses.matchers.header_matcher({"Authorization": f"Basic {b64_basic_auth}"}),
118112
],
119113
json={
120114
"access_token": "oauth2-m2m-access-token",
@@ -145,16 +139,13 @@ def test_posit_credentials_provider(self):
145139
cp = PositCredentialsProvider(client=client, user_session_token="cit")
146140
assert cp() == {"Authorization": "Bearer dynamic-viewer-access-token"}
147141

148-
149142
@responses.activate
150143
def test_local_content_credentials_strategy(self):
151-
152144
token_url = "https://my-token/url"
153145
client_id = "client_id"
154146
client_secret = "client_secret_123"
155147
basic_auth = f"{client_id}:{client_secret}"
156-
b64_basic_auth = base64.b64encode(basic_auth.encode('utf-8')).decode('utf-8')
157-
148+
b64_basic_auth = base64.b64encode(basic_auth.encode("utf-8")).decode("utf-8")
158149

159150
responses.post(
160151
token_url,
@@ -165,7 +156,7 @@ def test_local_content_credentials_strategy(self):
165156
"scope": "all-apis",
166157
},
167158
),
168-
responses.matchers.header_matcher({"Authorization": f"Basic {b64_basic_auth}"})
159+
responses.matchers.header_matcher({"Authorization": f"Basic {b64_basic_auth}"}),
169160
],
170161
json={
171162
"access_token": "oauth2-m2m-access-token",
@@ -183,8 +174,6 @@ def test_local_content_credentials_strategy(self):
183174
assert cs.auth_type() == "posit-local-client-credentials"
184175
assert cp() == {"Authorization": "Bearer oauth2-m2m-access-token"}
185176

186-
187-
188177
@patch.dict("os.environ", {"CONNECT_CONTENT_SESSION_TOKEN": "cit"})
189178
@responses.activate
190179
@patch.dict("os.environ", {"RSTUDIO_PRODUCT": "CONNECT"})
@@ -201,7 +190,6 @@ def test_posit_content_credentials_strategy(self):
201190
assert cs.auth_type() == "posit-oauth-integration"
202191
assert cp() == {"Authorization": "Bearer content-access-token"}
203192

204-
205193
@responses.activate
206194
@patch.dict("os.environ", {"RSTUDIO_PRODUCT": "CONNECT"})
207195
def test_posit_credentials_strategy(self):

tests/posit/connect/oauth/test_oauth.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,8 @@
88

99

1010
class TestOAuthIntegrations:
11-
1211
@patch.dict("os.environ", {"CONNECT_CONTENT_SESSION_TOKEN": "cit"})
13-
def test_get_content_session_token_success(self):
12+
def test_get_content_session_token_success(self):
1413
assert _get_content_session_token() == "cit"
1514

1615
def test_get_content_session_token_failure(self):

0 commit comments

Comments
 (0)