Skip to content

Commit 838c19d

Browse files
committed
estuary-cdk: move shared OAuth fields to base class
1 parent 67d6e36 commit 838c19d

File tree

2 files changed

+35
-180
lines changed

2 files changed

+35
-180
lines changed

estuary-cdk/estuary_cdk/flow.py

Lines changed: 34 additions & 179 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from dataclasses import dataclass
33
from datetime import datetime
44
from enum import StrEnum, auto
5-
from typing import Any, ClassVar, Generic, Literal, TypeVar, override
5+
from typing import Any, ClassVar, Generic, Literal, Self, TypeVar
66

77
from pydantic import BaseModel, ConfigDict, Field, NonNegativeInt, PositiveInt
88

@@ -143,8 +143,11 @@ class ValidationError(Exception):
143143
errors: list[str]
144144

145145

146-
class ResourceOwnerPasswordOAuth2Credentials(BaseModel):
147-
grant_type: ClassVar[str] = "password"
146+
class _BaseOAuth2CredentialsData(BaseModel):
147+
"""
148+
Abstract base class containing common OAuth2 credential fields.
149+
"""
150+
148151
client_credentials_placement: ClassVar[OAuth2ClientCredentialsPlacement] = (
149152
OAuth2ClientCredentialsPlacement.FORM
150153
)
@@ -161,241 +164,93 @@ class ResourceOwnerPasswordOAuth2Credentials(BaseModel):
161164
json_schema_extra={"secret": True},
162165
)
163166

164-
@classmethod
165-
def with_client_credentials_placement(
166-
cls,
167-
placement: OAuth2ClientCredentialsPlacement,
168-
) -> type["ResourceOwnerPasswordOAuth2Credentials"]:
169-
"""
170-
Returns a subclass with a custom client credentials placement.
171-
"""
172-
173-
return type(cls.__name__, (cls,), {"client_credentials_placement": placement})
174-
175-
176-
class ClientCredentialsOAuth2Credentials(BaseModel):
177-
grant_type: ClassVar[str] = "client_credentials"
178-
client_credentials_placement: ClassVar[OAuth2ClientCredentialsPlacement] = (
179-
OAuth2ClientCredentialsPlacement.HEADERS
180-
)
181-
182167
# This configuration provides a "title" annotation for the UI to display
183168
# instead of the class name.
184169
model_config = ConfigDict(
185170
title="OAuth",
186171
)
187172

188-
credentials_title: Literal["OAuth Credentials"] = Field(
189-
default="OAuth Credentials", json_schema_extra={"type": "string"}
190-
)
191-
client_id: str = Field(
192-
title="Client Id",
193-
json_schema_extra={"secret": True},
194-
)
195-
client_secret: str = Field(
196-
title="Client Secret",
197-
json_schema_extra={"secret": True},
198-
)
199-
200-
@classmethod
201-
def with_client_credentials_placement(
202-
cls,
203-
placement: OAuth2ClientCredentialsPlacement,
204-
) -> type["ClientCredentialsOAuth2Credentials"]:
205-
"""
206-
Returns a subclass with a custom client credentials placement.
207-
"""
208-
209-
return type(cls.__name__, (cls,), {"client_credentials_placement": placement})
210-
211-
212-
class AuthorizationCodeFlowOAuth2Credentials(BaseModel, metaclass=abc.ABCMeta):
213-
grant_type: ClassVar[str] = "authorization_code"
214-
client_credentials_placement: ClassVar[OAuth2ClientCredentialsPlacement] = (
215-
OAuth2ClientCredentialsPlacement.FORM
216-
)
217-
218-
credentials_title: Literal["OAuth Credentials"] = Field(
219-
default="OAuth Credentials", json_schema_extra={"type": "string"}
220-
)
221-
client_id: str = Field(
222-
title="Client Id",
223-
json_schema_extra={"secret": True},
224-
)
225-
client_secret: str = Field(
226-
title="Client Secret",
227-
json_schema_extra={"secret": True},
228-
)
229-
230-
@abc.abstractmethod
231-
def _you_must_build_oauth2_credentials_for_a_provider(self): ...
232-
233173
@classmethod
234174
def with_client_credentials_placement(
235175
cls,
236176
placement: OAuth2ClientCredentialsPlacement,
237-
) -> type["AuthorizationCodeFlowOAuth2Credentials"]:
177+
) -> type[Self]:
238178
"""
239179
Returns a subclass with a custom client credentials placement.
240180
"""
241181

242-
return type(cls.__name__, (cls,), {"client_credentials_placement": placement})
182+
return type(
183+
str(cls.__name__), (cls,), {"client_credentials_placement": placement}
184+
) # pyright: ignore[reportReturnType]
243185

244186
@classmethod
245-
def for_provider(
246-
cls,
247-
provider: str,
248-
) -> type["AuthorizationCodeFlowOAuth2Credentials"]:
187+
def for_provider(cls, provider: str) -> type[Self]:
249188
"""
250189
Builds an OAuth2Credentials model for the given OAuth2 `provider`.
251190
This routine is only available in Pydantic V2 environments.
252191
"""
253192
from pydantic import ConfigDict
254193

255-
return type(
194+
return type( # pyright: ignore[reportReturnType]
256195
cls.__name__,
257196
(cls,),
258197
{
259198
"model_config": ConfigDict(
260199
json_schema_extra={"x-oauth2-provider": provider},
261200
title="OAuth",
262201
),
263-
"_you_must_build_oauth2_credentials_for_a_provider": lambda _: None,
202+
"_you_must_build_oauth2_credentials_for_a_provider": lambda _: None, # pyright: ignore[reportUnknownLambdaType]
264203
},
265204
)
266205

267206

268-
class LongLivedClientCredentialsOAuth2Credentials(BaseModel, metaclass=abc.ABCMeta):
269-
# This configuration provides a "title" annotation for the UI to display
270-
# instead of the class name.
271-
model_config = ConfigDict(
272-
title="OAuth",
273-
)
207+
class ResourceOwnerPasswordOAuth2Credentials(_BaseOAuth2CredentialsData):
208+
grant_type: ClassVar[str] = "password"
274209

275-
credentials_title: Literal["OAuth Credentials"] = Field(
276-
default="OAuth Credentials", json_schema_extra={"type": "string"}
277-
)
278-
client_id: str = Field(
279-
title="Client Id",
280-
json_schema_extra={"secret": True},
281-
)
282-
client_secret: str = Field(
283-
title="Client Secret",
284-
json_schema_extra={"secret": True},
210+
211+
class ClientCredentialsOAuth2Credentials(_BaseOAuth2CredentialsData):
212+
grant_type: ClassVar[str] = "client_credentials"
213+
client_credentials_placement: ClassVar[OAuth2ClientCredentialsPlacement] = (
214+
OAuth2ClientCredentialsPlacement.HEADERS
285215
)
286-
access_token: str = Field(title="Access Token", json_schema_extra={"secret": True})
216+
217+
218+
class AuthorizationCodeFlowOAuth2Credentials(
219+
_BaseOAuth2CredentialsData, metaclass=abc.ABCMeta
220+
):
221+
grant_type: ClassVar[str] = "authorization_code"
287222

288223
@abc.abstractmethod
289224
def _you_must_build_oauth2_credentials_for_a_provider(self): ...
290225

291-
@classmethod
292-
def for_provider(
293-
cls,
294-
provider: str,
295-
) -> type["LongLivedClientCredentialsOAuth2Credentials"]:
296-
"""
297-
Builds an OAuth2Credentials model for the given OAuth2 `provider`.
298-
This routine is only available in Pydantic V2 environments.
299-
"""
300-
from pydantic import ConfigDict
301226

302-
return type(
303-
cls.__name__,
304-
(cls,),
305-
{
306-
"model_config": ConfigDict(
307-
json_schema_extra={"x-oauth2-provider": provider},
308-
title="OAuth",
309-
),
310-
"_you_must_build_oauth2_credentials_for_a_provider": lambda _: None,
311-
},
312-
)
227+
class LongLivedClientCredentialsOAuth2Credentials(
228+
_BaseOAuth2CredentialsData, metaclass=abc.ABCMeta
229+
):
230+
access_token: str = Field(title="Access Token", json_schema_extra={"secret": True})
231+
232+
@abc.abstractmethod
233+
def _you_must_build_oauth2_credentials_for_a_provider(self): ...
313234

314235

315-
class BaseOAuth2Credentials(BaseModel, metaclass=abc.ABCMeta):
236+
class BaseOAuth2Credentials(_BaseOAuth2CredentialsData, metaclass=abc.ABCMeta):
316237
grant_type: ClassVar[str] = "refresh_token"
317-
client_credentials_placement: ClassVar[OAuth2ClientCredentialsPlacement] = (
318-
OAuth2ClientCredentialsPlacement.FORM
319-
)
320238

321-
credentials_title: Literal["OAuth Credentials"] = Field(
322-
default="OAuth Credentials", json_schema_extra={"type": "string"}
323-
)
324-
client_id: str = Field(
325-
title="Client Id",
326-
json_schema_extra={"secret": True},
327-
)
328-
client_secret: str = Field(
329-
title="Client Secret",
330-
json_schema_extra={"secret": True},
331-
)
332239
refresh_token: str = Field(
333240
title="Refresh Token",
334241
json_schema_extra={"secret": True},
335242
)
336243

337-
@classmethod
338-
def with_client_credentials_placement(
339-
cls,
340-
placement: OAuth2ClientCredentialsPlacement,
341-
) -> type["BaseOAuth2Credentials"]:
342-
"""
343-
Returns a subclass with a custom client credentials placement.
344-
"""
345-
346-
return type(cls.__name__, (cls,), {"client_credentials_placement": placement})
347-
348244
@abc.abstractmethod
349245
def _you_must_build_oauth2_credentials_for_a_provider(self): ...
350246

351-
@classmethod
352-
def for_provider(cls, provider: str) -> type["BaseOAuth2Credentials"]:
353-
"""
354-
Builds an OAuth2Credentials model for the given OAuth2 `provider`.
355-
This routine is only available in Pydantic V2 environments.
356-
"""
357-
from pydantic import ConfigDict
358-
359-
return type(
360-
cls.__name__,
361-
(cls,),
362-
{
363-
"model_config": ConfigDict(
364-
json_schema_extra={"x-oauth2-provider": provider},
365-
title="OAuth",
366-
),
367-
"_you_must_build_oauth2_credentials_for_a_provider": lambda _: None,
368-
},
369-
)
370-
371247

372248
class RotatingOAuth2Credentials(BaseOAuth2Credentials, metaclass=abc.ABCMeta):
373249
access_token: str = Field(title="Access Token", json_schema_extra={"secret": True})
374250
access_token_expires_at: datetime = Field(
375251
title="Access token expiration time.",
376252
)
377253

378-
@classmethod
379-
@override
380-
def for_provider(cls, provider: str) -> type["RotatingOAuth2Credentials"]:
381-
"""
382-
Builds an OAuth2Credentials model for the given OAuth2 `provider`.
383-
This routine is only available in Pydantic V2 environments.
384-
"""
385-
from pydantic import ConfigDict
386-
387-
return type(
388-
cls.__name__,
389-
(cls,),
390-
{
391-
"model_config": ConfigDict(
392-
json_schema_extra={"x-oauth2-provider": provider},
393-
title="OAuth",
394-
),
395-
"_you_must_build_oauth2_credentials_for_a_provider": lambda _: None,
396-
},
397-
)
398-
399254

400255
class GoogleServiceAccountSpec(BaseModel):
401256
scopes: list[str]

source-looker/tests/snapshots/snapshots__spec__capture.stdout.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
"client_id",
2727
"client_secret"
2828
],
29-
"title": "OAuth2",
29+
"title": "OAuth",
3030
"type": "object"
3131
}
3232
},

0 commit comments

Comments
 (0)