airbyte-cdk 6.18.0.dev1__py3-none-any.whl → 6.18.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- airbyte_cdk/sources/declarative/auth/oauth.py +26 -0
- airbyte_cdk/sources/declarative/declarative_component_schema.yaml +52 -34
- airbyte_cdk/sources/declarative/decoders/composite_raw_decoder.py +0 -28
- airbyte_cdk/sources/declarative/models/declarative_component_schema.py +42 -18
- airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py +25 -11
- airbyte_cdk/sources/declarative/requesters/README.md +57 -0
- airbyte_cdk/sources/declarative/requesters/http_job_repository.py +33 -4
- airbyte_cdk/sources/streams/http/requests_native_auth/abstract_oauth.py +20 -4
- airbyte_cdk/sources/streams/http/requests_native_auth/oauth.py +34 -4
- airbyte_cdk/sources/types.py +3 -0
- {airbyte_cdk-6.18.0.dev1.dist-info → airbyte_cdk-6.18.1.dist-info}/METADATA +1 -1
- {airbyte_cdk-6.18.0.dev1.dist-info → airbyte_cdk-6.18.1.dist-info}/RECORD +15 -14
- {airbyte_cdk-6.18.0.dev1.dist-info → airbyte_cdk-6.18.1.dist-info}/LICENSE.txt +0 -0
- {airbyte_cdk-6.18.0.dev1.dist-info → airbyte_cdk-6.18.1.dist-info}/WHEEL +0 -0
- {airbyte_cdk-6.18.0.dev1.dist-info → airbyte_cdk-6.18.1.dist-info}/entry_points.txt +0 -0
@@ -56,8 +56,12 @@ class DeclarativeOauth2Authenticator(AbstractOauth2Authenticator, DeclarativeAut
|
|
56
56
|
token_expiry_is_time_of_expiration: bool = False
|
57
57
|
access_token_name: Union[InterpolatedString, str] = "access_token"
|
58
58
|
access_token_value: Optional[Union[InterpolatedString, str]] = None
|
59
|
+
client_id_name: Union[InterpolatedString, str] = "client_id"
|
60
|
+
client_secret_name: Union[InterpolatedString, str] = "client_secret"
|
59
61
|
expires_in_name: Union[InterpolatedString, str] = "expires_in"
|
62
|
+
refresh_token_name: Union[InterpolatedString, str] = "refresh_token"
|
60
63
|
refresh_request_body: Optional[Mapping[str, Any]] = None
|
64
|
+
grant_type_name: Union[InterpolatedString, str] = "grant_type"
|
61
65
|
grant_type: Union[InterpolatedString, str] = "refresh_token"
|
62
66
|
message_repository: MessageRepository = NoopMessageRepository()
|
63
67
|
|
@@ -69,8 +73,15 @@ class DeclarativeOauth2Authenticator(AbstractOauth2Authenticator, DeclarativeAut
|
|
69
73
|
)
|
70
74
|
else:
|
71
75
|
self._token_refresh_endpoint = None
|
76
|
+
self._client_id_name = InterpolatedString.create(self.client_id_name, parameters=parameters)
|
72
77
|
self._client_id = InterpolatedString.create(self.client_id, parameters=parameters)
|
78
|
+
self._client_secret_name = InterpolatedString.create(
|
79
|
+
self.client_secret_name, parameters=parameters
|
80
|
+
)
|
73
81
|
self._client_secret = InterpolatedString.create(self.client_secret, parameters=parameters)
|
82
|
+
self._refresh_token_name = InterpolatedString.create(
|
83
|
+
self.refresh_token_name, parameters=parameters
|
84
|
+
)
|
74
85
|
if self.refresh_token is not None:
|
75
86
|
self._refresh_token: Optional[InterpolatedString] = InterpolatedString.create(
|
76
87
|
self.refresh_token, parameters=parameters
|
@@ -83,6 +94,9 @@ class DeclarativeOauth2Authenticator(AbstractOauth2Authenticator, DeclarativeAut
|
|
83
94
|
self.expires_in_name = InterpolatedString.create(
|
84
95
|
self.expires_in_name, parameters=parameters
|
85
96
|
)
|
97
|
+
self.grant_type_name = InterpolatedString.create(
|
98
|
+
self.grant_type_name, parameters=parameters
|
99
|
+
)
|
86
100
|
self.grant_type = InterpolatedString.create(self.grant_type, parameters=parameters)
|
87
101
|
self._refresh_request_body = InterpolatedMapping(
|
88
102
|
self.refresh_request_body or {}, parameters=parameters
|
@@ -122,18 +136,27 @@ class DeclarativeOauth2Authenticator(AbstractOauth2Authenticator, DeclarativeAut
|
|
122
136
|
return refresh_token_endpoint
|
123
137
|
return None
|
124
138
|
|
139
|
+
def get_client_id_name(self) -> str:
|
140
|
+
return self._client_id_name.eval(self.config) # type: ignore # eval returns a string in this context
|
141
|
+
|
125
142
|
def get_client_id(self) -> str:
|
126
143
|
client_id: str = self._client_id.eval(self.config)
|
127
144
|
if not client_id:
|
128
145
|
raise ValueError("OAuthAuthenticator was unable to evaluate client_id parameter")
|
129
146
|
return client_id
|
130
147
|
|
148
|
+
def get_client_secret_name(self) -> str:
|
149
|
+
return self._client_secret_name.eval(self.config) # type: ignore # eval returns a string in this context
|
150
|
+
|
131
151
|
def get_client_secret(self) -> str:
|
132
152
|
client_secret: str = self._client_secret.eval(self.config)
|
133
153
|
if not client_secret:
|
134
154
|
raise ValueError("OAuthAuthenticator was unable to evaluate client_secret parameter")
|
135
155
|
return client_secret
|
136
156
|
|
157
|
+
def get_refresh_token_name(self) -> str:
|
158
|
+
return self._refresh_token_name.eval(self.config) # type: ignore # eval returns a string in this context
|
159
|
+
|
137
160
|
def get_refresh_token(self) -> Optional[str]:
|
138
161
|
return None if self._refresh_token is None else str(self._refresh_token.eval(self.config))
|
139
162
|
|
@@ -146,6 +169,9 @@ class DeclarativeOauth2Authenticator(AbstractOauth2Authenticator, DeclarativeAut
|
|
146
169
|
def get_expires_in_name(self) -> str:
|
147
170
|
return self.expires_in_name.eval(self.config) # type: ignore # eval returns a string in this context
|
148
171
|
|
172
|
+
def get_grant_type_name(self) -> str:
|
173
|
+
return self.grant_type_name.eval(self.config) # type: ignore # eval returns a string in this context
|
174
|
+
|
149
175
|
def get_grant_type(self) -> str:
|
150
176
|
return self.grant_type.eval(self.config) # type: ignore # eval returns a string in this context
|
151
177
|
|
@@ -678,7 +678,7 @@ definitions:
|
|
678
678
|
properties:
|
679
679
|
type:
|
680
680
|
type: string
|
681
|
-
enum: [CustomSchemaNormalization]
|
681
|
+
enum: [ CustomSchemaNormalization ]
|
682
682
|
class_name:
|
683
683
|
title: Class Name
|
684
684
|
description: Fully-qualified name of the class that will be implementing the custom normalization. The format is `source_<name>.<package>.<class_name>`.
|
@@ -1047,6 +1047,13 @@ definitions:
|
|
1047
1047
|
type:
|
1048
1048
|
type: string
|
1049
1049
|
enum: [OAuthAuthenticator]
|
1050
|
+
client_id_name:
|
1051
|
+
title: Client ID Property Name
|
1052
|
+
description: The name of the property to use to refresh the `access_token`.
|
1053
|
+
type: string
|
1054
|
+
default: "client_id"
|
1055
|
+
examples:
|
1056
|
+
- custom_app_id
|
1050
1057
|
client_id:
|
1051
1058
|
title: Client ID
|
1052
1059
|
description: The OAuth client ID. Fill it in the user inputs.
|
@@ -1054,6 +1061,13 @@ definitions:
|
|
1054
1061
|
examples:
|
1055
1062
|
- "{{ config['client_id }}"
|
1056
1063
|
- "{{ config['credentials']['client_id }}"
|
1064
|
+
client_secret_name:
|
1065
|
+
title: Client Secret Property Name
|
1066
|
+
description: The name of the property to use to refresh the `access_token`.
|
1067
|
+
type: string
|
1068
|
+
default: "client_secret"
|
1069
|
+
examples:
|
1070
|
+
- custom_app_secret
|
1057
1071
|
client_secret:
|
1058
1072
|
title: Client Secret
|
1059
1073
|
description: The OAuth client secret. Fill it in the user inputs.
|
@@ -1061,6 +1075,13 @@ definitions:
|
|
1061
1075
|
examples:
|
1062
1076
|
- "{{ config['client_secret }}"
|
1063
1077
|
- "{{ config['credentials']['client_secret }}"
|
1078
|
+
refresh_token_name:
|
1079
|
+
title: Refresh Token Property Name
|
1080
|
+
description: The name of the property to use to refresh the `access_token`.
|
1081
|
+
type: string
|
1082
|
+
default: "refresh_token"
|
1083
|
+
examples:
|
1084
|
+
- custom_app_refresh_value
|
1064
1085
|
refresh_token:
|
1065
1086
|
title: Refresh Token
|
1066
1087
|
description: Credential artifact used to get a new access token.
|
@@ -1094,6 +1115,13 @@ definitions:
|
|
1094
1115
|
default: "expires_in"
|
1095
1116
|
examples:
|
1096
1117
|
- expires_in
|
1118
|
+
grant_type_name:
|
1119
|
+
title: Grant Type Property Name
|
1120
|
+
description: The name of the property to use to refresh the `access_token`.
|
1121
|
+
type: string
|
1122
|
+
default: "grant_type"
|
1123
|
+
examples:
|
1124
|
+
- custom_grant_type
|
1097
1125
|
grant_type:
|
1098
1126
|
title: Grant Type
|
1099
1127
|
description: Specifies the OAuth2 grant type. If set to refresh_token, the refresh_token needs to be provided as well. For client_credentials, only client id and secret are required. Other grant types are not officially supported.
|
@@ -2014,20 +2042,6 @@ definitions:
|
|
2014
2042
|
$parameters:
|
2015
2043
|
type: object
|
2016
2044
|
additionalProperties: true
|
2017
|
-
JsonParser:
|
2018
|
-
title: JsonParser
|
2019
|
-
description: Parser used for parsing str, bytes, or bytearray data and returning data in a dictionary format.
|
2020
|
-
type: object
|
2021
|
-
additionalProperties: true
|
2022
|
-
required:
|
2023
|
-
- type
|
2024
|
-
properties:
|
2025
|
-
type:
|
2026
|
-
type: string
|
2027
|
-
enum: [JsonParser]
|
2028
|
-
encoding:
|
2029
|
-
type: string
|
2030
|
-
default: utf-8
|
2031
2045
|
ListPartitionRouter:
|
2032
2046
|
title: List Partition Router
|
2033
2047
|
description: A Partition router that specifies a list of attributes where each attribute describes a portion of the complete data set for a stream. During a sync, each value is iterated over and can be used as input to outbound API requests.
|
@@ -2218,15 +2232,15 @@ definitions:
|
|
2218
2232
|
Pertains to the fields defined by the connector relating to the OAuth flow.
|
2219
2233
|
|
2220
2234
|
Interpolation capabilities:
|
2221
|
-
- The variables placeholders are declared as `{my_var}`.
|
2222
|
-
- The nested resolution variables like `{{my_nested_var}}` is allowed as well.
|
2235
|
+
- The variables placeholders are declared as `{{my_var}}`.
|
2236
|
+
- The nested resolution variables like `{{ {{my_nested_var}} }}` is allowed as well.
|
2223
2237
|
|
2224
2238
|
- The allowed interpolation context is:
|
2225
|
-
+ base64Encoder - encode to `base64`, {
|
2226
|
-
+ base64Decorer - decode from `base64` encoded string, {
|
2227
|
-
+ urlEncoder - encode the input string to URL-like format, {
|
2228
|
-
+ urlDecorer - decode the input url-encoded string into text format, {urlDecoder:https%3A%2F%2Fairbyte.io}
|
2229
|
-
+ codeChallengeS256 - get the `codeChallenge` encoded value to provide additional data-provider specific authorisation values, {
|
2239
|
+
+ base64Encoder - encode to `base64`, {{ {{my_var_a}}:{{my_var_b}} | base64Encoder }}
|
2240
|
+
+ base64Decorer - decode from `base64` encoded string, {{ {{my_string_variable_or_string_value}} | base64Decoder }}
|
2241
|
+
+ urlEncoder - encode the input string to URL-like format, {{ https://test.host.com/endpoint | urlEncoder}}
|
2242
|
+
+ urlDecorer - decode the input url-encoded string into text format, {{ urlDecoder:https%3A%2F%2Fairbyte.io | urlDecoder}}
|
2243
|
+
+ codeChallengeS256 - get the `codeChallenge` encoded value to provide additional data-provider specific authorisation values, {{ {{state_value}} | codeChallengeS256 }}
|
2230
2244
|
|
2231
2245
|
Examples:
|
2232
2246
|
- The TikTok Marketing DeclarativeOAuth spec:
|
@@ -2235,12 +2249,12 @@ definitions:
|
|
2235
2249
|
"type": "object",
|
2236
2250
|
"additionalProperties": false,
|
2237
2251
|
"properties": {
|
2238
|
-
"consent_url": "https://ads.tiktok.com/marketing_api/auth?{client_id_key}={{
|
2252
|
+
"consent_url": "https://ads.tiktok.com/marketing_api/auth?{{client_id_key}}={{client_id_value}}&{{redirect_uri_key}}={{ {{redirect_uri_value}} | urlEncoder}}&{{state_key}}={{state_value}}",
|
2239
2253
|
"access_token_url": "https://business-api.tiktok.com/open_api/v1.3/oauth2/access_token/",
|
2240
2254
|
"access_token_params": {
|
2241
|
-
"{auth_code_key}": "{{
|
2242
|
-
"{client_id_key}": "{{
|
2243
|
-
"{client_secret_key}": "{{
|
2255
|
+
"{{ auth_code_key }}": "{{ auth_code_value }}",
|
2256
|
+
"{{ client_id_key }}": "{{ client_id_value }}",
|
2257
|
+
"{{ client_secret_key }}": "{{ client_secret_value }}"
|
2244
2258
|
},
|
2245
2259
|
"access_token_headers": {
|
2246
2260
|
"Content-Type": "application/json",
|
@@ -2258,7 +2272,6 @@ definitions:
|
|
2258
2272
|
required:
|
2259
2273
|
- consent_url
|
2260
2274
|
- access_token_url
|
2261
|
-
- extract_output
|
2262
2275
|
properties:
|
2263
2276
|
consent_url:
|
2264
2277
|
title: Consent URL
|
@@ -2267,8 +2280,8 @@ definitions:
|
|
2267
2280
|
The DeclarativeOAuth Specific string URL string template to initiate the authentication.
|
2268
2281
|
The placeholders are replaced during the processing to provide neccessary values.
|
2269
2282
|
examples:
|
2270
|
-
- https://domain.host.com/marketing_api/auth?{client_id_key}={{
|
2271
|
-
- https://endpoint.host.com/oauth2/authorize?{client_id_key}={{
|
2283
|
+
- https://domain.host.com/marketing_api/auth?{{client_id_key}}={{client_id_value}}&{{redirect_uri_key}}={{{{redirect_uri_value}} | urlEncoder}}&{{state_key}}={{state_value}}
|
2284
|
+
- https://endpoint.host.com/oauth2/authorize?{{client_id_key}}={{client_id_value}}&{{redirect_uri_key}}={{{{redirect_uri_value}} | urlEncoder}}&{{scope_key}}={{{{scope_value}} | urlEncoder}}&{{state_key}}={{state_value}}&subdomain={{subdomain}}
|
2272
2285
|
scope:
|
2273
2286
|
title: Scopes
|
2274
2287
|
type: string
|
@@ -2283,7 +2296,7 @@ definitions:
|
|
2283
2296
|
The DeclarativeOAuth Specific URL templated string to obtain the `access_token`, `refresh_token` etc.
|
2284
2297
|
The placeholders are replaced during the processing to provide neccessary values.
|
2285
2298
|
examples:
|
2286
|
-
- https://auth.host.com/oauth2/token?{client_id_key}={{
|
2299
|
+
- https://auth.host.com/oauth2/token?{{client_id_key}}={{client_id_value}}&{{client_secret_key}}={{client_secret_value}}&{{auth_code_key}}={{auth_code_value}}&{{redirect_uri_key}}={{{{redirect_uri_value}} | urlEncoder}}
|
2287
2300
|
access_token_headers:
|
2288
2301
|
title: Access Token Headers
|
2289
2302
|
type: object
|
@@ -2292,7 +2305,7 @@ definitions:
|
|
2292
2305
|
The DeclarativeOAuth Specific optional headers to inject while exchanging the `auth_code` to `access_token` during `completeOAuthFlow` step.
|
2293
2306
|
examples:
|
2294
2307
|
- {
|
2295
|
-
"Authorization": "Basic {
|
2308
|
+
"Authorization": "Basic {{ {{ client_id_value }}:{{ client_secret_value }} | base64Encoder }}",
|
2296
2309
|
}
|
2297
2310
|
access_token_params:
|
2298
2311
|
title: Access Token Query Params (Json Encoded)
|
@@ -2303,9 +2316,9 @@ definitions:
|
|
2303
2316
|
When this property is provided, the query params will be encoded as `Json` and included in the outgoing API request.
|
2304
2317
|
examples:
|
2305
2318
|
- {
|
2306
|
-
"{auth_code_key}": "{{
|
2307
|
-
"{client_id_key}": "{{
|
2308
|
-
"{client_secret_key}": "{{
|
2319
|
+
"{{ auth_code_key }}": "{{ auth_code_value }}",
|
2320
|
+
"{{ client_id_key }}": "{{ client_id_value }}",
|
2321
|
+
"{{ client_secret_key }}": "{{ client_secret_value }}",
|
2309
2322
|
}
|
2310
2323
|
extract_output:
|
2311
2324
|
title: Extract Output
|
@@ -2991,6 +3004,11 @@ definitions:
|
|
2991
3004
|
anyOf:
|
2992
3005
|
- "$ref": "#/definitions/CustomRequester"
|
2993
3006
|
- "$ref": "#/definitions/HttpRequester"
|
3007
|
+
url_requester:
|
3008
|
+
description: Requester component that describes how to prepare HTTP requests to send to the source API to extract the url from polling response by the completed async job.
|
3009
|
+
anyOf:
|
3010
|
+
- "$ref": "#/definitions/CustomRequester"
|
3011
|
+
- "$ref": "#/definitions/HttpRequester"
|
2994
3012
|
download_requester:
|
2995
3013
|
description: Requester component that describes how to prepare HTTP requests to send to the source API to download the data provided by the completed async job.
|
2996
3014
|
anyOf:
|
@@ -7,12 +7,9 @@ from dataclasses import dataclass
|
|
7
7
|
from io import BufferedIOBase, TextIOWrapper
|
8
8
|
from typing import Any, Generator, MutableMapping, Optional
|
9
9
|
|
10
|
-
import orjson
|
11
10
|
import requests
|
12
11
|
|
13
|
-
from airbyte_cdk.models import FailureType
|
14
12
|
from airbyte_cdk.sources.declarative.decoders.decoder import Decoder
|
15
|
-
from airbyte_cdk.utils import AirbyteTracedException
|
16
13
|
|
17
14
|
logger = logging.getLogger("airbyte")
|
18
15
|
|
@@ -45,31 +42,6 @@ class GzipParser(Parser):
|
|
45
42
|
yield from self.inner_parser.parse(gzipobj)
|
46
43
|
|
47
44
|
|
48
|
-
@dataclass
|
49
|
-
class JsonParser(Parser):
|
50
|
-
encoding: str = "utf-8"
|
51
|
-
|
52
|
-
def parse(self, data: BufferedIOBase) -> Generator[MutableMapping[str, Any], None, None]:
|
53
|
-
raw_data = data.read()
|
54
|
-
try:
|
55
|
-
body_json = orjson.loads(raw_data.decode(self.encoding))
|
56
|
-
except Exception:
|
57
|
-
try:
|
58
|
-
body_json = json.loads(raw_data.decode(self.encoding))
|
59
|
-
except Exception as exc:
|
60
|
-
raise AirbyteTracedException(
|
61
|
-
message="Response JSON data failed to be parsed. See logs for more inforation.",
|
62
|
-
internal_message=f"Response JSON data faild to be parsed: {exc=}, {raw_data=}",
|
63
|
-
failure_type=FailureType.system_error,
|
64
|
-
exception=exc,
|
65
|
-
)
|
66
|
-
|
67
|
-
if isinstance(body_json, list):
|
68
|
-
yield from body_json
|
69
|
-
else:
|
70
|
-
yield from [body_json]
|
71
|
-
|
72
|
-
|
73
45
|
@dataclass
|
74
46
|
class JsonLineParser(Parser):
|
75
47
|
encoding: Optional[str] = "utf-8"
|
@@ -481,12 +481,24 @@ class RefreshTokenUpdater(BaseModel):
|
|
481
481
|
|
482
482
|
class OAuthAuthenticator(BaseModel):
|
483
483
|
type: Literal["OAuthAuthenticator"]
|
484
|
+
client_id_name: Optional[str] = Field(
|
485
|
+
"client_id",
|
486
|
+
description="The name of the property to use to refresh the `access_token`.",
|
487
|
+
examples=["custom_app_id"],
|
488
|
+
title="Client ID Property Name",
|
489
|
+
)
|
484
490
|
client_id: str = Field(
|
485
491
|
...,
|
486
492
|
description="The OAuth client ID. Fill it in the user inputs.",
|
487
493
|
examples=["{{ config['client_id }}", "{{ config['credentials']['client_id }}"],
|
488
494
|
title="Client ID",
|
489
495
|
)
|
496
|
+
client_secret_name: Optional[str] = Field(
|
497
|
+
"client_secret",
|
498
|
+
description="The name of the property to use to refresh the `access_token`.",
|
499
|
+
examples=["custom_app_secret"],
|
500
|
+
title="Client Secret Property Name",
|
501
|
+
)
|
490
502
|
client_secret: str = Field(
|
491
503
|
...,
|
492
504
|
description="The OAuth client secret. Fill it in the user inputs.",
|
@@ -496,6 +508,12 @@ class OAuthAuthenticator(BaseModel):
|
|
496
508
|
],
|
497
509
|
title="Client Secret",
|
498
510
|
)
|
511
|
+
refresh_token_name: Optional[str] = Field(
|
512
|
+
"refresh_token",
|
513
|
+
description="The name of the property to use to refresh the `access_token`.",
|
514
|
+
examples=["custom_app_refresh_value"],
|
515
|
+
title="Refresh Token Property Name",
|
516
|
+
)
|
499
517
|
refresh_token: Optional[str] = Field(
|
500
518
|
None,
|
501
519
|
description="Credential artifact used to get a new access token.",
|
@@ -529,6 +547,12 @@ class OAuthAuthenticator(BaseModel):
|
|
529
547
|
examples=["expires_in"],
|
530
548
|
title="Token Expiry Property Name",
|
531
549
|
)
|
550
|
+
grant_type_name: Optional[str] = Field(
|
551
|
+
"grant_type",
|
552
|
+
description="The name of the property to use to refresh the `access_token`.",
|
553
|
+
examples=["custom_grant_type"],
|
554
|
+
title="Grant Type Property Name",
|
555
|
+
)
|
532
556
|
grant_type: Optional[str] = Field(
|
533
557
|
"refresh_token",
|
534
558
|
description="Specifies the OAuth2 grant type. If set to refresh_token, the refresh_token needs to be provided as well. For client_credentials, only client id and secret are required. Other grant types are not officially supported.",
|
@@ -805,14 +829,6 @@ class GzipJsonDecoder(BaseModel):
|
|
805
829
|
parameters: Optional[Dict[str, Any]] = Field(None, alias="$parameters")
|
806
830
|
|
807
831
|
|
808
|
-
class JsonParser(BaseModel):
|
809
|
-
class Config:
|
810
|
-
extra = Extra.allow
|
811
|
-
|
812
|
-
type: Literal["JsonParser"]
|
813
|
-
encoding: Optional[str] = "utf-8"
|
814
|
-
|
815
|
-
|
816
832
|
class MinMaxDatetime(BaseModel):
|
817
833
|
type: Literal["MinMaxDatetime"]
|
818
834
|
datetime: str = Field(
|
@@ -867,8 +883,8 @@ class OauthConnectorInputSpecification(BaseModel):
|
|
867
883
|
...,
|
868
884
|
description="The DeclarativeOAuth Specific string URL string template to initiate the authentication.\nThe placeholders are replaced during the processing to provide neccessary values.",
|
869
885
|
examples=[
|
870
|
-
"https://domain.host.com/marketing_api/auth?{client_id_key}={{
|
871
|
-
"https://endpoint.host.com/oauth2/authorize?{client_id_key}={{
|
886
|
+
"https://domain.host.com/marketing_api/auth?{{client_id_key}}={{client_id_value}}&{{redirect_uri_key}}={{{{redirect_uri_value}} | urlEncoder}}&{{state_key}}={{state_value}}",
|
887
|
+
"https://endpoint.host.com/oauth2/authorize?{{client_id_key}}={{client_id_value}}&{{redirect_uri_key}}={{{{redirect_uri_value}} | urlEncoder}}&{{scope_key}}={{{{scope_value}} | urlEncoder}}&{{state_key}}={{state_value}}&subdomain={{subdomain}}",
|
872
888
|
],
|
873
889
|
title="Consent URL",
|
874
890
|
)
|
@@ -882,14 +898,18 @@ class OauthConnectorInputSpecification(BaseModel):
|
|
882
898
|
...,
|
883
899
|
description="The DeclarativeOAuth Specific URL templated string to obtain the `access_token`, `refresh_token` etc.\nThe placeholders are replaced during the processing to provide neccessary values.",
|
884
900
|
examples=[
|
885
|
-
"https://auth.host.com/oauth2/token?{client_id_key}={{
|
901
|
+
"https://auth.host.com/oauth2/token?{{client_id_key}}={{client_id_value}}&{{client_secret_key}}={{client_secret_value}}&{{auth_code_key}}={{auth_code_value}}&{{redirect_uri_key}}={{{{redirect_uri_value}} | urlEncoder}}"
|
886
902
|
],
|
887
903
|
title="Access Token URL",
|
888
904
|
)
|
889
905
|
access_token_headers: Optional[Dict[str, Any]] = Field(
|
890
906
|
None,
|
891
907
|
description="The DeclarativeOAuth Specific optional headers to inject while exchanging the `auth_code` to `access_token` during `completeOAuthFlow` step.",
|
892
|
-
examples=[
|
908
|
+
examples=[
|
909
|
+
{
|
910
|
+
"Authorization": "Basic {{ {{ client_id_value }}:{{ client_secret_value }} | base64Encoder }}"
|
911
|
+
}
|
912
|
+
],
|
893
913
|
title="Access Token Headers",
|
894
914
|
)
|
895
915
|
access_token_params: Optional[Dict[str, Any]] = Field(
|
@@ -897,15 +917,15 @@ class OauthConnectorInputSpecification(BaseModel):
|
|
897
917
|
description="The DeclarativeOAuth Specific optional query parameters to inject while exchanging the `auth_code` to `access_token` during `completeOAuthFlow` step.\nWhen this property is provided, the query params will be encoded as `Json` and included in the outgoing API request.",
|
898
918
|
examples=[
|
899
919
|
{
|
900
|
-
"{auth_code_key}": "{{
|
901
|
-
"{client_id_key}": "{{
|
902
|
-
"{client_secret_key}": "{{
|
920
|
+
"{{ auth_code_key }}": "{{ auth_code_value }}",
|
921
|
+
"{{ client_id_key }}": "{{ client_id_value }}",
|
922
|
+
"{{ client_secret_key }}": "{{ client_secret_value }}",
|
903
923
|
}
|
904
924
|
],
|
905
925
|
title="Access Token Query Params (Json Encoded)",
|
906
926
|
)
|
907
|
-
extract_output: List[str] = Field(
|
908
|
-
|
927
|
+
extract_output: Optional[List[str]] = Field(
|
928
|
+
None,
|
909
929
|
description="The DeclarativeOAuth Specific list of strings to indicate which keys should be extracted and returned back to the input config.",
|
910
930
|
examples=[["access_token", "refresh_token", "other_field"]],
|
911
931
|
title="Extract Output",
|
@@ -974,7 +994,7 @@ class OAuthConfigSpecification(BaseModel):
|
|
974
994
|
)
|
975
995
|
oauth_connector_input_specification: Optional[OauthConnectorInputSpecification] = Field(
|
976
996
|
None,
|
977
|
-
description='The DeclarativeOAuth specific blob.\nPertains to the fields defined by the connector relating to the OAuth flow.\n\nInterpolation capabilities:\n- The variables placeholders are declared as `{my_var}`.\n- The nested resolution variables like `{{my_nested_var}}` is allowed as well.\n\n- The allowed interpolation context is:\n + base64Encoder - encode to `base64`, {
|
997
|
+
description='The DeclarativeOAuth specific blob.\nPertains to the fields defined by the connector relating to the OAuth flow.\n\nInterpolation capabilities:\n- The variables placeholders are declared as `{{my_var}}`.\n- The nested resolution variables like `{{ {{my_nested_var}} }}` is allowed as well.\n\n- The allowed interpolation context is:\n + base64Encoder - encode to `base64`, {{ {{my_var_a}}:{{my_var_b}} | base64Encoder }}\n + base64Decorer - decode from `base64` encoded string, {{ {{my_string_variable_or_string_value}} | base64Decoder }}\n + urlEncoder - encode the input string to URL-like format, {{ https://test.host.com/endpoint | urlEncoder}}\n + urlDecorer - decode the input url-encoded string into text format, {{ urlDecoder:https%3A%2F%2Fairbyte.io | urlDecoder}}\n + codeChallengeS256 - get the `codeChallenge` encoded value to provide additional data-provider specific authorisation values, {{ {{state_value}} | codeChallengeS256 }}\n\nExamples:\n - The TikTok Marketing DeclarativeOAuth spec:\n {\n "oauth_connector_input_specification": {\n "type": "object",\n "additionalProperties": false,\n "properties": {\n "consent_url": "https://ads.tiktok.com/marketing_api/auth?{{client_id_key}}={{client_id_value}}&{{redirect_uri_key}}={{ {{redirect_uri_value}} | urlEncoder}}&{{state_key}}={{state_value}}",\n "access_token_url": "https://business-api.tiktok.com/open_api/v1.3/oauth2/access_token/",\n "access_token_params": {\n "{{ auth_code_key }}": "{{ auth_code_value }}",\n "{{ client_id_key }}": "{{ client_id_value }}",\n "{{ client_secret_key }}": "{{ client_secret_value }}"\n },\n "access_token_headers": {\n "Content-Type": "application/json",\n "Accept": "application/json"\n },\n "extract_output": ["data.access_token"],\n "client_id_key": "app_id",\n "client_secret_key": "secret",\n "auth_code_key": "auth_code"\n }\n }\n }',
|
978
998
|
title="DeclarativeOAuth Connector Specification",
|
979
999
|
)
|
980
1000
|
complete_oauth_output_specification: Optional[Dict[str, Any]] = Field(
|
@@ -2058,6 +2078,10 @@ class AsyncRetriever(BaseModel):
|
|
2058
2078
|
...,
|
2059
2079
|
description="Requester component that describes how to prepare HTTP requests to send to the source API to fetch the status of the running async job.",
|
2060
2080
|
)
|
2081
|
+
url_requester: Optional[Union[CustomRequester, HttpRequester]] = Field(
|
2082
|
+
None,
|
2083
|
+
description="Requester component that describes how to prepare HTTP requests to send to the source API to extract the url from polling response by the completed async job.",
|
2084
|
+
)
|
2061
2085
|
download_requester: Union[CustomRequester, HttpRequester] = Field(
|
2062
2086
|
...,
|
2063
2087
|
description="Requester component that describes how to prepare HTTP requests to send to the source API to download the data provided by the completed async job.",
|
@@ -72,7 +72,6 @@ from airbyte_cdk.sources.declarative.decoders.composite_raw_decoder import (
|
|
72
72
|
CsvParser,
|
73
73
|
GzipParser,
|
74
74
|
JsonLineParser,
|
75
|
-
JsonParser,
|
76
75
|
)
|
77
76
|
from airbyte_cdk.sources.declarative.extractors import (
|
78
77
|
DpathExtractor,
|
@@ -248,9 +247,6 @@ from airbyte_cdk.sources.declarative.models.declarative_component_schema import
|
|
248
247
|
from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
|
249
248
|
JsonLineParser as JsonLineParserModel,
|
250
249
|
)
|
251
|
-
from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
|
252
|
-
JsonParser as JsonParserModel,
|
253
|
-
)
|
254
250
|
from airbyte_cdk.sources.declarative.models.declarative_component_schema import (
|
255
251
|
JwtAuthenticator as JwtAuthenticatorModel,
|
256
252
|
)
|
@@ -526,7 +522,6 @@ class ModelToComponentFactory:
|
|
526
522
|
JsonDecoderModel: self.create_json_decoder,
|
527
523
|
JsonlDecoderModel: self.create_jsonl_decoder,
|
528
524
|
JsonLineParserModel: self.create_json_line_parser,
|
529
|
-
JsonParserModel: self.create_json_parser,
|
530
525
|
GzipJsonDecoderModel: self.create_gzipjson_decoder,
|
531
526
|
GzipParserModel: self.create_gzip_parser,
|
532
527
|
KeysToLowerModel: self.create_keys_to_lower_transformation,
|
@@ -1753,11 +1748,6 @@ class ModelToComponentFactory:
|
|
1753
1748
|
def create_json_decoder(model: JsonDecoderModel, config: Config, **kwargs: Any) -> JsonDecoder:
|
1754
1749
|
return JsonDecoder(parameters={})
|
1755
1750
|
|
1756
|
-
@staticmethod
|
1757
|
-
def create_json_parser(model: JsonParserModel, config: Config, **kwargs: Any) -> JsonParser:
|
1758
|
-
encoding = model.encoding or "utf-8"
|
1759
|
-
return JsonParser(encoding=encoding)
|
1760
|
-
|
1761
1751
|
@staticmethod
|
1762
1752
|
def create_jsonl_decoder(
|
1763
1753
|
model: JsonlDecoderModel, config: Config, **kwargs: Any
|
@@ -1895,15 +1885,24 @@ class ModelToComponentFactory:
|
|
1895
1885
|
expires_in_name=InterpolatedString.create(
|
1896
1886
|
model.expires_in_name or "expires_in", parameters=model.parameters or {}
|
1897
1887
|
).eval(config),
|
1888
|
+
client_id_name=InterpolatedString.create(
|
1889
|
+
model.client_id_name or "client_id", parameters=model.parameters or {}
|
1890
|
+
).eval(config),
|
1898
1891
|
client_id=InterpolatedString.create(
|
1899
1892
|
model.client_id, parameters=model.parameters or {}
|
1900
1893
|
).eval(config),
|
1894
|
+
client_secret_name=InterpolatedString.create(
|
1895
|
+
model.client_secret_name or "client_secret", parameters=model.parameters or {}
|
1896
|
+
).eval(config),
|
1901
1897
|
client_secret=InterpolatedString.create(
|
1902
1898
|
model.client_secret, parameters=model.parameters or {}
|
1903
1899
|
).eval(config),
|
1904
1900
|
access_token_config_path=model.refresh_token_updater.access_token_config_path,
|
1905
1901
|
refresh_token_config_path=model.refresh_token_updater.refresh_token_config_path,
|
1906
1902
|
token_expiry_date_config_path=model.refresh_token_updater.token_expiry_date_config_path,
|
1903
|
+
grant_type_name=InterpolatedString.create(
|
1904
|
+
model.grant_type_name or "grant_type", parameters=model.parameters or {}
|
1905
|
+
).eval(config),
|
1907
1906
|
grant_type=InterpolatedString.create(
|
1908
1907
|
model.grant_type or "refresh_token", parameters=model.parameters or {}
|
1909
1908
|
).eval(config),
|
@@ -1921,11 +1920,15 @@ class ModelToComponentFactory:
|
|
1921
1920
|
return DeclarativeOauth2Authenticator( # type: ignore
|
1922
1921
|
access_token_name=model.access_token_name or "access_token",
|
1923
1922
|
access_token_value=model.access_token_value,
|
1923
|
+
client_id_name=model.client_id_name or "client_id",
|
1924
1924
|
client_id=model.client_id,
|
1925
|
+
client_secret_name=model.client_secret_name or "client_secret",
|
1925
1926
|
client_secret=model.client_secret,
|
1926
1927
|
expires_in_name=model.expires_in_name or "expires_in",
|
1928
|
+
grant_type_name=model.grant_type_name or "grant_type",
|
1927
1929
|
grant_type=model.grant_type or "refresh_token",
|
1928
1930
|
refresh_request_body=model.refresh_request_body,
|
1931
|
+
refresh_token_name=model.refresh_token_name or "refresh_token",
|
1929
1932
|
refresh_token=model.refresh_token,
|
1930
1933
|
scopes=model.scopes,
|
1931
1934
|
token_expiry_date=model.token_expiry_date,
|
@@ -2297,7 +2300,7 @@ class ModelToComponentFactory:
|
|
2297
2300
|
extractor=download_extractor,
|
2298
2301
|
name=name,
|
2299
2302
|
record_filter=None,
|
2300
|
-
transformations=
|
2303
|
+
transformations=transformations,
|
2301
2304
|
schema_normalization=TypeTransformer(TransformConfig.NoTransform),
|
2302
2305
|
config=config,
|
2303
2306
|
parameters={},
|
@@ -2334,6 +2337,16 @@ class ModelToComponentFactory:
|
|
2334
2337
|
if model.delete_requester
|
2335
2338
|
else None
|
2336
2339
|
)
|
2340
|
+
url_requester = (
|
2341
|
+
self._create_component_from_model(
|
2342
|
+
model=model.url_requester,
|
2343
|
+
decoder=decoder,
|
2344
|
+
config=config,
|
2345
|
+
name=f"job extract_url - {name}",
|
2346
|
+
)
|
2347
|
+
if model.url_requester
|
2348
|
+
else None
|
2349
|
+
)
|
2337
2350
|
status_extractor = self._create_component_from_model(
|
2338
2351
|
model=model.status_extractor, decoder=decoder, config=config, name=name
|
2339
2352
|
)
|
@@ -2344,6 +2357,7 @@ class ModelToComponentFactory:
|
|
2344
2357
|
creation_requester=creation_requester,
|
2345
2358
|
polling_requester=polling_requester,
|
2346
2359
|
download_retriever=download_retriever,
|
2360
|
+
url_requester=url_requester,
|
2347
2361
|
abort_requester=abort_requester,
|
2348
2362
|
delete_requester=delete_requester,
|
2349
2363
|
status_extractor=status_extractor,
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# AsyncHttpJobRepository sequence diagram
|
2
|
+
|
3
|
+
- Components marked as optional are not required and can be ignored.
|
4
|
+
- if `url_requester` is not provided, `urls_extractor` will get urls from the `polling_job_response`
|
5
|
+
- interpolation_context, e.g. `create_job_response` or `polling_job_response` can be obtained from stream_slice
|
6
|
+
|
7
|
+
|
8
|
+
```mermaid
|
9
|
+
---
|
10
|
+
title: AsyncHttpJobRepository Sequence Diagram
|
11
|
+
---
|
12
|
+
sequenceDiagram
|
13
|
+
participant AsyncHttpJobRepository as AsyncOrchestrator
|
14
|
+
participant CreationRequester as creation_requester
|
15
|
+
participant PollingRequester as polling_requester
|
16
|
+
participant UrlRequester as url_requester (Optional)
|
17
|
+
participant DownloadRetriever as download_retriever
|
18
|
+
participant AbortRequester as abort_requester (Optional)
|
19
|
+
participant DeleteRequester as delete_requester (Optional)
|
20
|
+
participant Reporting Server as Async Reporting Server
|
21
|
+
|
22
|
+
AsyncHttpJobRepository ->> CreationRequester: Initiate job creation
|
23
|
+
CreationRequester ->> Reporting Server: Create job request
|
24
|
+
Reporting Server -->> CreationRequester: Job ID response
|
25
|
+
CreationRequester -->> AsyncHttpJobRepository: Job ID
|
26
|
+
|
27
|
+
loop Poll for job status
|
28
|
+
AsyncHttpJobRepository ->> PollingRequester: Check job status
|
29
|
+
PollingRequester ->> Reporting Server: Status request (interpolation_context: `create_job_response`)
|
30
|
+
Reporting Server -->> PollingRequester: Status response
|
31
|
+
PollingRequester -->> AsyncHttpJobRepository: Job status
|
32
|
+
end
|
33
|
+
|
34
|
+
alt Status: Ready
|
35
|
+
AsyncHttpJobRepository ->> UrlRequester: Request download URLs (if applicable)
|
36
|
+
UrlRequester ->> Reporting Server: URL request (interpolation_context: `polling_job_response`)
|
37
|
+
Reporting Server -->> UrlRequester: Download URLs
|
38
|
+
UrlRequester -->> AsyncHttpJobRepository: Download URLs
|
39
|
+
|
40
|
+
AsyncHttpJobRepository ->> DownloadRetriever: Download reports
|
41
|
+
DownloadRetriever ->> Reporting Server: Retrieve report data (interpolation_context: `url`)
|
42
|
+
Reporting Server -->> DownloadRetriever: Report data
|
43
|
+
DownloadRetriever -->> AsyncHttpJobRepository: Report data
|
44
|
+
else Status: Failed
|
45
|
+
AsyncHttpJobRepository ->> AbortRequester: Send abort request
|
46
|
+
AbortRequester ->> Reporting Server: Abort job
|
47
|
+
Reporting Server -->> AbortRequester: Abort confirmation
|
48
|
+
AbortRequester -->> AsyncHttpJobRepository: Confirmation
|
49
|
+
end
|
50
|
+
|
51
|
+
AsyncHttpJobRepository ->> DeleteRequester: Send delete job request
|
52
|
+
DeleteRequester ->> Reporting Server: Delete job
|
53
|
+
Reporting Server -->> DeleteRequester: Deletion confirmation
|
54
|
+
DeleteRequester -->> AsyncHttpJobRepository: Confirmation
|
55
|
+
|
56
|
+
|
57
|
+
```
|
@@ -31,6 +31,10 @@ LOGGER = logging.getLogger("airbyte")
|
|
31
31
|
|
32
32
|
@dataclass
|
33
33
|
class AsyncHttpJobRepository(AsyncJobRepository):
|
34
|
+
"""
|
35
|
+
See Readme file for more details about flow.
|
36
|
+
"""
|
37
|
+
|
34
38
|
creation_requester: Requester
|
35
39
|
polling_requester: Requester
|
36
40
|
download_retriever: SimpleRetriever
|
@@ -44,6 +48,9 @@ class AsyncHttpJobRepository(AsyncJobRepository):
|
|
44
48
|
record_extractor: RecordExtractor = field(
|
45
49
|
init=False, repr=False, default_factory=lambda: ResponseToFileExtractor({})
|
46
50
|
)
|
51
|
+
url_requester: Optional[Requester] = (
|
52
|
+
None # use it in case polling_requester provides some <id> and extra request is needed to obtain list of urls to download from
|
53
|
+
)
|
47
54
|
|
48
55
|
def __post_init__(self) -> None:
|
49
56
|
self._create_job_response_by_id: Dict[str, Response] = {}
|
@@ -186,10 +193,13 @@ class AsyncHttpJobRepository(AsyncJobRepository):
|
|
186
193
|
|
187
194
|
"""
|
188
195
|
|
189
|
-
for url in self.
|
190
|
-
|
191
|
-
|
192
|
-
|
196
|
+
for url in self._get_download_url(job):
|
197
|
+
job_slice = job.job_parameters()
|
198
|
+
stream_slice = StreamSlice(
|
199
|
+
partition=job_slice.partition,
|
200
|
+
cursor_slice=job_slice.cursor_slice,
|
201
|
+
extra_fields={**job_slice.extra_fields, "url": url},
|
202
|
+
)
|
193
203
|
for message in self.download_retriever.read_records({}, stream_slice):
|
194
204
|
if isinstance(message, Record):
|
195
205
|
yield message.data
|
@@ -226,3 +236,22 @@ class AsyncHttpJobRepository(AsyncJobRepository):
|
|
226
236
|
cursor_slice={},
|
227
237
|
)
|
228
238
|
return stream_slice
|
239
|
+
|
240
|
+
def _get_download_url(self, job: AsyncJob) -> Iterable[str]:
|
241
|
+
if not self.url_requester:
|
242
|
+
url_response = self._polling_job_response_by_id[job.api_job_id()]
|
243
|
+
else:
|
244
|
+
stream_slice: StreamSlice = StreamSlice(
|
245
|
+
partition={
|
246
|
+
"polling_job_response": self._polling_job_response_by_id[job.api_job_id()]
|
247
|
+
},
|
248
|
+
cursor_slice={},
|
249
|
+
)
|
250
|
+
url_response = self.url_requester.send_request(stream_slice=stream_slice) # type: ignore # we expect url_requester to always be presented, otherwise raise an exception as we cannot proceed with the report
|
251
|
+
if not url_response:
|
252
|
+
raise AirbyteTracedException(
|
253
|
+
internal_message="Always expect a response or an exception from url_requester",
|
254
|
+
failure_type=FailureType.system_error,
|
255
|
+
)
|
256
|
+
|
257
|
+
yield from self.urls_extractor.extract_records(url_response) # type: ignore # we expect urls_extractor to always return list of strings
|
@@ -81,10 +81,10 @@ class AbstractOauth2Authenticator(AuthBase):
|
|
81
81
|
Override to define additional parameters
|
82
82
|
"""
|
83
83
|
payload: MutableMapping[str, Any] = {
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
84
|
+
self.get_grant_type_name(): self.get_grant_type(),
|
85
|
+
self.get_client_id_name(): self.get_client_id(),
|
86
|
+
self.get_client_secret_name(): self.get_client_secret(),
|
87
|
+
self.get_refresh_token_name(): self.get_refresh_token(),
|
88
88
|
}
|
89
89
|
|
90
90
|
if self.get_scopes():
|
@@ -206,14 +206,26 @@ class AbstractOauth2Authenticator(AuthBase):
|
|
206
206
|
def get_token_refresh_endpoint(self) -> Optional[str]:
|
207
207
|
"""Returns the endpoint to refresh the access token"""
|
208
208
|
|
209
|
+
@abstractmethod
|
210
|
+
def get_client_id_name(self) -> str:
|
211
|
+
"""The client id name to authenticate"""
|
212
|
+
|
209
213
|
@abstractmethod
|
210
214
|
def get_client_id(self) -> str:
|
211
215
|
"""The client id to authenticate"""
|
212
216
|
|
217
|
+
@abstractmethod
|
218
|
+
def get_client_secret_name(self) -> str:
|
219
|
+
"""The client secret name to authenticate"""
|
220
|
+
|
213
221
|
@abstractmethod
|
214
222
|
def get_client_secret(self) -> str:
|
215
223
|
"""The client secret to authenticate"""
|
216
224
|
|
225
|
+
@abstractmethod
|
226
|
+
def get_refresh_token_name(self) -> str:
|
227
|
+
"""The refresh token name to authenticate"""
|
228
|
+
|
217
229
|
@abstractmethod
|
218
230
|
def get_refresh_token(self) -> Optional[str]:
|
219
231
|
"""The token used to refresh the access token when it expires"""
|
@@ -246,6 +258,10 @@ class AbstractOauth2Authenticator(AuthBase):
|
|
246
258
|
def get_grant_type(self) -> str:
|
247
259
|
"""Returns grant_type specified for requesting access_token"""
|
248
260
|
|
261
|
+
@abstractmethod
|
262
|
+
def get_grant_type_name(self) -> str:
|
263
|
+
"""Returns grant_type specified name for requesting access_token"""
|
264
|
+
|
249
265
|
@property
|
250
266
|
@abstractmethod
|
251
267
|
def access_token(self) -> str:
|
@@ -30,12 +30,16 @@ class Oauth2Authenticator(AbstractOauth2Authenticator):
|
|
30
30
|
client_id: str,
|
31
31
|
client_secret: str,
|
32
32
|
refresh_token: str,
|
33
|
+
client_id_name: str = "client_id",
|
34
|
+
client_secret_name: str = "client_secret",
|
35
|
+
refresh_token_name: str = "refresh_token",
|
33
36
|
scopes: List[str] | None = None,
|
34
37
|
token_expiry_date: pendulum.DateTime | None = None,
|
35
38
|
token_expiry_date_format: str | None = None,
|
36
39
|
access_token_name: str = "access_token",
|
37
40
|
expires_in_name: str = "expires_in",
|
38
41
|
refresh_request_body: Mapping[str, Any] | None = None,
|
42
|
+
grant_type_name: str = "grant_type",
|
39
43
|
grant_type: str = "refresh_token",
|
40
44
|
token_expiry_is_time_of_expiration: bool = False,
|
41
45
|
refresh_token_error_status_codes: Tuple[int, ...] = (),
|
@@ -43,13 +47,17 @@ class Oauth2Authenticator(AbstractOauth2Authenticator):
|
|
43
47
|
refresh_token_error_values: Tuple[str, ...] = (),
|
44
48
|
):
|
45
49
|
self._token_refresh_endpoint = token_refresh_endpoint
|
50
|
+
self._client_secret_name = client_secret_name
|
46
51
|
self._client_secret = client_secret
|
52
|
+
self._client_id_name = client_id_name
|
47
53
|
self._client_id = client_id
|
54
|
+
self._refresh_token_name = refresh_token_name
|
48
55
|
self._refresh_token = refresh_token
|
49
56
|
self._scopes = scopes
|
50
57
|
self._access_token_name = access_token_name
|
51
58
|
self._expires_in_name = expires_in_name
|
52
59
|
self._refresh_request_body = refresh_request_body
|
60
|
+
self._grant_type_name = grant_type_name
|
53
61
|
self._grant_type = grant_type
|
54
62
|
|
55
63
|
self._token_expiry_date = token_expiry_date or pendulum.now().subtract(days=1) # type: ignore [no-untyped-call]
|
@@ -63,12 +71,21 @@ class Oauth2Authenticator(AbstractOauth2Authenticator):
|
|
63
71
|
def get_token_refresh_endpoint(self) -> str:
|
64
72
|
return self._token_refresh_endpoint
|
65
73
|
|
74
|
+
def get_client_id_name(self) -> str:
|
75
|
+
return self._client_id_name
|
76
|
+
|
66
77
|
def get_client_id(self) -> str:
|
67
78
|
return self._client_id
|
68
79
|
|
80
|
+
def get_client_secret_name(self) -> str:
|
81
|
+
return self._client_secret_name
|
82
|
+
|
69
83
|
def get_client_secret(self) -> str:
|
70
84
|
return self._client_secret
|
71
85
|
|
86
|
+
def get_refresh_token_name(self) -> str:
|
87
|
+
return self._refresh_token_name
|
88
|
+
|
72
89
|
def get_refresh_token(self) -> str:
|
73
90
|
return self._refresh_token
|
74
91
|
|
@@ -84,6 +101,9 @@ class Oauth2Authenticator(AbstractOauth2Authenticator):
|
|
84
101
|
def get_refresh_request_body(self) -> Mapping[str, Any]:
|
85
102
|
return self._refresh_request_body # type: ignore [return-value]
|
86
103
|
|
104
|
+
def get_grant_type_name(self) -> str:
|
105
|
+
return self._grant_type_name
|
106
|
+
|
87
107
|
def get_grant_type(self) -> str:
|
88
108
|
return self._grant_type
|
89
109
|
|
@@ -129,8 +149,11 @@ class SingleUseRefreshTokenOauth2Authenticator(Oauth2Authenticator):
|
|
129
149
|
expires_in_name: str = "expires_in",
|
130
150
|
refresh_token_name: str = "refresh_token",
|
131
151
|
refresh_request_body: Mapping[str, Any] | None = None,
|
152
|
+
grant_type_name: str = "grant_type",
|
132
153
|
grant_type: str = "refresh_token",
|
154
|
+
client_id_name: str = "client_id",
|
133
155
|
client_id: Optional[str] = None,
|
156
|
+
client_secret_name: str = "client_secret",
|
134
157
|
client_secret: Optional[str] = None,
|
135
158
|
access_token_config_path: Sequence[str] = ("credentials", "access_token"),
|
136
159
|
refresh_token_config_path: Sequence[str] = ("credentials", "refresh_token"),
|
@@ -174,23 +197,30 @@ class SingleUseRefreshTokenOauth2Authenticator(Oauth2Authenticator):
|
|
174
197
|
("credentials", "client_secret"),
|
175
198
|
)
|
176
199
|
)
|
200
|
+
self._client_id_name = client_id_name
|
201
|
+
self._client_secret_name = client_secret_name
|
177
202
|
self._access_token_config_path = access_token_config_path
|
178
203
|
self._refresh_token_config_path = refresh_token_config_path
|
179
204
|
self._token_expiry_date_config_path = token_expiry_date_config_path
|
180
205
|
self._token_expiry_date_format = token_expiry_date_format
|
181
206
|
self._refresh_token_name = refresh_token_name
|
207
|
+
self._grant_type_name = grant_type_name
|
182
208
|
self._connector_config = connector_config
|
183
209
|
self.__message_repository = message_repository
|
184
210
|
super().__init__(
|
185
|
-
token_refresh_endpoint,
|
186
|
-
self.
|
187
|
-
self.
|
188
|
-
self.
|
211
|
+
token_refresh_endpoint=token_refresh_endpoint,
|
212
|
+
client_id_name=self._client_id_name,
|
213
|
+
client_id=self.get_client_id(),
|
214
|
+
client_secret_name=self._client_secret_name,
|
215
|
+
client_secret=self.get_client_secret(),
|
216
|
+
refresh_token=self.get_refresh_token(),
|
217
|
+
refresh_token_name=self._refresh_token_name,
|
189
218
|
scopes=scopes,
|
190
219
|
token_expiry_date=self.get_token_expiry_date(),
|
191
220
|
access_token_name=access_token_name,
|
192
221
|
expires_in_name=expires_in_name,
|
193
222
|
refresh_request_body=refresh_request_body,
|
223
|
+
grant_type_name=self._grant_type_name,
|
194
224
|
grant_type=grant_type,
|
195
225
|
token_expiry_date_format=token_expiry_date_format,
|
196
226
|
token_expiry_is_time_of_expiration=token_expiry_is_time_of_expiration,
|
airbyte_cdk/sources/types.py
CHANGED
@@ -53,7 +53,7 @@ airbyte_cdk/sources/declarative/async_job/timer.py,sha256=Fb8P72CQ7jIzJyzMSSNuBf
|
|
53
53
|
airbyte_cdk/sources/declarative/auth/__init__.py,sha256=e2CRrcBWGhz3sQu3Oh34d1riEIwXipGS8hrSB1pu0Oo,284
|
54
54
|
airbyte_cdk/sources/declarative/auth/declarative_authenticator.py,sha256=nf-OmRUHYG4ORBwyb5CANzuHEssE-oNmL-Lccn41Td8,1099
|
55
55
|
airbyte_cdk/sources/declarative/auth/jwt.py,sha256=7r5q1zOekjw8kEmEk1oUyovzVt3cbD6BuFnRILeLZi8,8250
|
56
|
-
airbyte_cdk/sources/declarative/auth/oauth.py,sha256=
|
56
|
+
airbyte_cdk/sources/declarative/auth/oauth.py,sha256=EDx-tY0ZhBXe6yVHkqjUAqMWJAl4rQXjQbr4rElt_Ds,10555
|
57
57
|
airbyte_cdk/sources/declarative/auth/selective_authenticator.py,sha256=qGwC6YsCldr1bIeKG6Qo-A9a5cTdHw-vcOn3OtQrS4c,1540
|
58
58
|
airbyte_cdk/sources/declarative/auth/token.py,sha256=r4u3WXyVa7WmiSZ9-eZXlrUI-pS0D4YWJnwjLzwV-Fk,11210
|
59
59
|
airbyte_cdk/sources/declarative/auth/token_provider.py,sha256=9oq3dcBPAPwXSfkISjhA05dMhIzxaDQTmwOydBrnsMk,3028
|
@@ -66,11 +66,11 @@ airbyte_cdk/sources/declarative/concurrent_declarative_source.py,sha256=tSTCSmyM
|
|
66
66
|
airbyte_cdk/sources/declarative/datetime/__init__.py,sha256=l9LG7Qm6e5r_qgqfVKnx3mXYtg1I9MmMjomVIPfU4XA,177
|
67
67
|
airbyte_cdk/sources/declarative/datetime/datetime_parser.py,sha256=SX9JjdesN1edN2WVUVMzU_ptqp2QB1OnsnjZ4mwcX7w,2579
|
68
68
|
airbyte_cdk/sources/declarative/datetime/min_max_datetime.py,sha256=0BHBtDNQZfvwM45-tY5pNlTcKAFSGGNxemoi0Jic-0E,5785
|
69
|
-
airbyte_cdk/sources/declarative/declarative_component_schema.yaml,sha256=
|
69
|
+
airbyte_cdk/sources/declarative/declarative_component_schema.yaml,sha256=00X3palFmBp9WqQugXgtzFVn7s17KYWKTrn83ObmBzc,134673
|
70
70
|
airbyte_cdk/sources/declarative/declarative_source.py,sha256=nF7wBqFd3AQmEKAm4CnIo29CJoQL562cJGSCeL8U8bA,1531
|
71
71
|
airbyte_cdk/sources/declarative/declarative_stream.py,sha256=JRyNeOIpsFu4ztVZsN6sncqUEIqIE-bUkD2TPgbMgk0,10375
|
72
72
|
airbyte_cdk/sources/declarative/decoders/__init__.py,sha256=edGj4fGxznBk4xzRQyCA1rGfbpqe7z-RE0K3kQQWbgA,858
|
73
|
-
airbyte_cdk/sources/declarative/decoders/composite_raw_decoder.py,sha256
|
73
|
+
airbyte_cdk/sources/declarative/decoders/composite_raw_decoder.py,sha256=-aO3ujXX9YTP2ZDvI2BP-x0VOKdAq2TlHo4zG8DCTlY,2748
|
74
74
|
airbyte_cdk/sources/declarative/decoders/decoder.py,sha256=sl-Gt8lXi7yD2Q-sD8je5QS2PbgrgsYjxRLWsay7DMc,826
|
75
75
|
airbyte_cdk/sources/declarative/decoders/json_decoder.py,sha256=qdbjeR6RffKaah_iWvMsOcDolYuxJY5DaI3b9AMTZXg,3327
|
76
76
|
airbyte_cdk/sources/declarative/decoders/noop_decoder.py,sha256=iZh0yKY_JzgBnJWiubEusf5c0o6Khd-8EWFWT-8EgFo,542
|
@@ -106,12 +106,12 @@ airbyte_cdk/sources/declarative/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW
|
|
106
106
|
airbyte_cdk/sources/declarative/migrations/legacy_to_per_partition_state_migration.py,sha256=iemy3fKLczcU0-Aor7tx5jcT6DRedKMqyK7kCOp01hg,3924
|
107
107
|
airbyte_cdk/sources/declarative/migrations/state_migration.py,sha256=KWPjealMLKSMtajXgkdGgKg7EmTLR-CqqD7UIh0-eDU,794
|
108
108
|
airbyte_cdk/sources/declarative/models/__init__.py,sha256=nUFxNCiKeYRVXuZEKA7GD-lTHxsiKcQ8FitZjKhPIvE,100
|
109
|
-
airbyte_cdk/sources/declarative/models/declarative_component_schema.py,sha256=
|
109
|
+
airbyte_cdk/sources/declarative/models/declarative_component_schema.py,sha256=1wrAW9XeEq2xdUAAkmHcelka-LOwyYb-izRcACkNPKM,94915
|
110
110
|
airbyte_cdk/sources/declarative/parsers/__init__.py,sha256=ZnqYNxHsKCgO38IwB34RQyRMXTs4GTvlRi3ImKnIioo,61
|
111
111
|
airbyte_cdk/sources/declarative/parsers/custom_exceptions.py,sha256=Rir9_z3Kcd5Es0-LChrzk-0qubAsiK_RSEnLmK2OXm8,553
|
112
112
|
airbyte_cdk/sources/declarative/parsers/manifest_component_transformer.py,sha256=CXwTfD3wSQq3okcqwigpprbHhSURUokh4GK2OmOyKC8,9132
|
113
113
|
airbyte_cdk/sources/declarative/parsers/manifest_reference_resolver.py,sha256=IWUOdF03o-aQn0Occo1BJCxU0Pz-QILk5L67nzw2thw,6803
|
114
|
-
airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py,sha256=
|
114
|
+
airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py,sha256=NElLb7eLDVmxDgtTX9fQ-ZPrpfH3d7RpMDaQiLtvuuQ,110550
|
115
115
|
airbyte_cdk/sources/declarative/partition_routers/__init__.py,sha256=HJ-Syp3p7RpyR_OK0X_a2kSyISfu3W-PKrRI16iY0a8,957
|
116
116
|
airbyte_cdk/sources/declarative/partition_routers/async_job_partition_router.py,sha256=n82J15S8bjeMZ5uROu--P3hnbQoxkY5v7RPHYx7g7ro,2929
|
117
117
|
airbyte_cdk/sources/declarative/partition_routers/cartesian_product_stream_slicer.py,sha256=c5cuVFM6NFkuQqG8Z5IwkBuwDrvXZN1CunUOM_L0ezg,6892
|
@@ -119,6 +119,7 @@ airbyte_cdk/sources/declarative/partition_routers/list_partition_router.py,sha25
|
|
119
119
|
airbyte_cdk/sources/declarative/partition_routers/partition_router.py,sha256=YyEIzdmLd1FjbVP3QbQ2VFCLW_P-OGbVh6VpZShp54k,2218
|
120
120
|
airbyte_cdk/sources/declarative/partition_routers/single_partition_router.py,sha256=SKzKjSyfccq4dxGIh-J6ejrgkCHzaiTIazmbmeQiRD4,1942
|
121
121
|
airbyte_cdk/sources/declarative/partition_routers/substream_partition_router.py,sha256=5bgXoJfBg_6i53krQMptAGb50XB5XoVfqQxKQhlLtBA,15383
|
122
|
+
airbyte_cdk/sources/declarative/requesters/README.md,sha256=WabtHlwHg_J34aL1Kwm8vboYqBaSgsFjq10qR-P2sx8,2658
|
122
123
|
airbyte_cdk/sources/declarative/requesters/__init__.py,sha256=d7a3OoHbqaJDyyPli3nqqJ2yAW_SLX6XDaBAKOwvpxw,364
|
123
124
|
airbyte_cdk/sources/declarative/requesters/error_handlers/__init__.py,sha256=SkEDcJxlT1683rNx93K9whoS0OyUukkuOfToGtgpF58,776
|
124
125
|
airbyte_cdk/sources/declarative/requesters/error_handlers/backoff_strategies/__init__.py,sha256=1WZdpFmWL6W_Dko0qjflTaKIWeqt8jHT-D6HcujIp3s,884
|
@@ -133,7 +134,7 @@ airbyte_cdk/sources/declarative/requesters/error_handlers/default_error_handler.
|
|
133
134
|
airbyte_cdk/sources/declarative/requesters/error_handlers/default_http_response_filter.py,sha256=q0YkeYUUWO6iErUy0vjqiOkhg8_9d5YcCmtlpXAJJ9E,1314
|
134
135
|
airbyte_cdk/sources/declarative/requesters/error_handlers/error_handler.py,sha256=Tan66odx8VHzfdyyXMQkXz2pJYksllGqvxmpoajgcK4,669
|
135
136
|
airbyte_cdk/sources/declarative/requesters/error_handlers/http_response_filter.py,sha256=vhWsEKNTYEzZ4gerhHqnDNKu4wGIP485NAzpSQ5DRZg,7941
|
136
|
-
airbyte_cdk/sources/declarative/requesters/http_job_repository.py,sha256=
|
137
|
+
airbyte_cdk/sources/declarative/requesters/http_job_repository.py,sha256=3GtOefPH08evlSUxaILkiKLTHbIspFY4qd5B3ZqNE60,10063
|
137
138
|
airbyte_cdk/sources/declarative/requesters/http_requester.py,sha256=RqYPkgJFAWfcZBTc-JBcGHPm4JL1ZQOhs9GKU4MP2eE,14723
|
138
139
|
airbyte_cdk/sources/declarative/requesters/paginators/__init__.py,sha256=uArbKs9JKNCt7t9tZoeWwjDpyI1HoPp29FNW0JzvaEM,644
|
139
140
|
airbyte_cdk/sources/declarative/requesters/paginators/default_paginator.py,sha256=FnSl3qPvv5wD6ieAI2Ic5c4dqBk-3fRe4tCaWzq3YwM,11840
|
@@ -287,12 +288,12 @@ airbyte_cdk/sources/streams/http/http.py,sha256=JAMpiTdS9HFNOlwayWNvQdxoqs2rpW9w
|
|
287
288
|
airbyte_cdk/sources/streams/http/http_client.py,sha256=tDE0ROtxjGMVphvsw8INvGMtZ97hIF-v47pZ3jIyiwc,23011
|
288
289
|
airbyte_cdk/sources/streams/http/rate_limiting.py,sha256=IwdjrHKUnU97XO4qONgYRv4YYW51xQ8SJm4WLafXDB8,6351
|
289
290
|
airbyte_cdk/sources/streams/http/requests_native_auth/__init__.py,sha256=RN0D3nOX1xLgwEwKWu6pkGy3XqBFzKSNZ8Lf6umU2eY,413
|
290
|
-
airbyte_cdk/sources/streams/http/requests_native_auth/abstract_oauth.py,sha256=
|
291
|
+
airbyte_cdk/sources/streams/http/requests_native_auth/abstract_oauth.py,sha256=xX-qKbMVN-OTwNda-B6cVQqnQrNDGBnvavAcSvXK2wU,11179
|
291
292
|
airbyte_cdk/sources/streams/http/requests_native_auth/abstract_token.py,sha256=Y3n7J-sk5yGjv_OxtY6Z6k0PEsFZmtIRi-x0KCbaHdA,1010
|
292
|
-
airbyte_cdk/sources/streams/http/requests_native_auth/oauth.py,sha256=
|
293
|
+
airbyte_cdk/sources/streams/http/requests_native_auth/oauth.py,sha256=wjsp4Xf8u3AnY7ZmTTGfLnfj6ztDBogR5biURkqcwCA,16156
|
293
294
|
airbyte_cdk/sources/streams/http/requests_native_auth/token.py,sha256=h5PTzcdH-RQLeCg7xZ45w_484OPUDSwNWl_iMJQmZoI,2526
|
294
295
|
airbyte_cdk/sources/streams/utils/__init__.py,sha256=4Hw-PX1-VgESLF16cDdvuYCzGJtHntThLF4qIiULWeo,61
|
295
|
-
airbyte_cdk/sources/types.py,sha256=
|
296
|
+
airbyte_cdk/sources/types.py,sha256=nLPkTpyfGV4E6e99qcBWX4r8C3fE4I8Fvgx2EjvT9ic,5005
|
296
297
|
airbyte_cdk/sources/utils/__init__.py,sha256=TTN6VUxVy6Is8BhYQZR5pxJGQh8yH4duXh4O1TiMiEY,118
|
297
298
|
airbyte_cdk/sources/utils/casing.py,sha256=QC-gV1O4e8DR4-bhdXieUPKm_JamzslVyfABLYYRSXA,256
|
298
299
|
airbyte_cdk/sources/utils/record_helper.py,sha256=jeB0mucudzna7Zvj-pCBbwFrbLJ36SlAWZTh5O4Fb9Y,2168
|
@@ -342,8 +343,8 @@ airbyte_cdk/utils/slice_hasher.py,sha256=-pHexlNYoWYPnXNH-M7HEbjmeJe9Zk7SJijdQ7d
|
|
342
343
|
airbyte_cdk/utils/spec_schema_transformations.py,sha256=-5HTuNsnDBAhj-oLeQXwpTGA0HdcjFOf2zTEMUTTg_Y,816
|
343
344
|
airbyte_cdk/utils/stream_status_utils.py,sha256=ZmBoiy5HVbUEHAMrUONxZvxnvfV9CesmQJLDTAIWnWw,1171
|
344
345
|
airbyte_cdk/utils/traced_exception.py,sha256=C8uIBuCL_E4WnBAOPSxBicD06JAldoN9fGsQDp463OY,6292
|
345
|
-
airbyte_cdk-6.18.
|
346
|
-
airbyte_cdk-6.18.
|
347
|
-
airbyte_cdk-6.18.
|
348
|
-
airbyte_cdk-6.18.
|
349
|
-
airbyte_cdk-6.18.
|
346
|
+
airbyte_cdk-6.18.1.dist-info/LICENSE.txt,sha256=Wfe61S4BaGPj404v8lrAbvhjYR68SHlkzeYrg3_bbuM,1051
|
347
|
+
airbyte_cdk-6.18.1.dist-info/METADATA,sha256=OMpca59Gc1MJOlwEgvDJX0uwp7skSel83qkbtcan6hE,6000
|
348
|
+
airbyte_cdk-6.18.1.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
|
349
|
+
airbyte_cdk-6.18.1.dist-info/entry_points.txt,sha256=fj-e3PAQvsxsQzyyq8UkG1k8spunWnD4BAH2AwlR6NM,95
|
350
|
+
airbyte_cdk-6.18.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|