airbyte-cdk 6.18.0.dev2__py3-none-any.whl → 6.18.1__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.
@@ -2204,15 +2232,15 @@ definitions:
2204
2232
  Pertains to the fields defined by the connector relating to the OAuth flow.
2205
2233
 
2206
2234
  Interpolation capabilities:
2207
- - The variables placeholders are declared as `{my_var}`.
2208
- - 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.
2209
2237
 
2210
2238
  - The allowed interpolation context is:
2211
- + base64Encoder - encode to `base64`, {base64Encoder:{my_var_a}:{my_var_b}}
2212
- + base64Decorer - decode from `base64` encoded string, {base64Decoder:{my_string_variable_or_string_value}}
2213
- + urlEncoder - encode the input string to URL-like format, {urlEncoder:https://test.host.com/endpoint}
2214
- + urlDecorer - decode the input url-encoded string into text format, {urlDecoder:https%3A%2F%2Fairbyte.io}
2215
- + codeChallengeS256 - get the `codeChallenge` encoded value to provide additional data-provider specific authorisation values, {codeChallengeS256:{state_value}}
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 }}
2216
2244
 
2217
2245
  Examples:
2218
2246
  - The TikTok Marketing DeclarativeOAuth spec:
@@ -2221,12 +2249,12 @@ definitions:
2221
2249
  "type": "object",
2222
2250
  "additionalProperties": false,
2223
2251
  "properties": {
2224
- "consent_url": "https://ads.tiktok.com/marketing_api/auth?{client_id_key}={{client_id_key}}&{redirect_uri_key}={urlEncoder:{{redirect_uri_key}}}&{state_key}={{state_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}}",
2225
2253
  "access_token_url": "https://business-api.tiktok.com/open_api/v1.3/oauth2/access_token/",
2226
2254
  "access_token_params": {
2227
- "{auth_code_key}": "{{auth_code_key}}",
2228
- "{client_id_key}": "{{client_id_key}}",
2229
- "{client_secret_key}": "{{client_secret_key}}"
2255
+ "{{ auth_code_key }}": "{{ auth_code_value }}",
2256
+ "{{ client_id_key }}": "{{ client_id_value }}",
2257
+ "{{ client_secret_key }}": "{{ client_secret_value }}"
2230
2258
  },
2231
2259
  "access_token_headers": {
2232
2260
  "Content-Type": "application/json",
@@ -2244,7 +2272,6 @@ definitions:
2244
2272
  required:
2245
2273
  - consent_url
2246
2274
  - access_token_url
2247
- - extract_output
2248
2275
  properties:
2249
2276
  consent_url:
2250
2277
  title: Consent URL
@@ -2253,8 +2280,8 @@ definitions:
2253
2280
  The DeclarativeOAuth Specific string URL string template to initiate the authentication.
2254
2281
  The placeholders are replaced during the processing to provide neccessary values.
2255
2282
  examples:
2256
- - https://domain.host.com/marketing_api/auth?{client_id_key}={{client_id_key}}&{redirect_uri_key}={urlEncoder:{{redirect_uri_key}}}&{state_key}={{state_key}}
2257
- - https://endpoint.host.com/oauth2/authorize?{client_id_key}={{client_id_key}}&{redirect_uri_key}={urlEncoder:{{redirect_uri_key}}}&{scope_key}={urlEncoder:{{scope_key}}}&{state_key}={{state_key}}&subdomain={subdomain}
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}}
2258
2285
  scope:
2259
2286
  title: Scopes
2260
2287
  type: string
@@ -2269,7 +2296,7 @@ definitions:
2269
2296
  The DeclarativeOAuth Specific URL templated string to obtain the `access_token`, `refresh_token` etc.
2270
2297
  The placeholders are replaced during the processing to provide neccessary values.
2271
2298
  examples:
2272
- - https://auth.host.com/oauth2/token?{client_id_key}={{client_id_key}}&{client_secret_key}={{client_secret_key}}&{auth_code_key}={{auth_code_key}}&{redirect_uri_key}={urlEncoder:{{redirect_uri_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}}
2273
2300
  access_token_headers:
2274
2301
  title: Access Token Headers
2275
2302
  type: object
@@ -2278,7 +2305,7 @@ definitions:
2278
2305
  The DeclarativeOAuth Specific optional headers to inject while exchanging the `auth_code` to `access_token` during `completeOAuthFlow` step.
2279
2306
  examples:
2280
2307
  - {
2281
- "Authorization": "Basic {base64Encoder:{client_id}:{client_secret}}",
2308
+ "Authorization": "Basic {{ {{ client_id_value }}:{{ client_secret_value }} | base64Encoder }}",
2282
2309
  }
2283
2310
  access_token_params:
2284
2311
  title: Access Token Query Params (Json Encoded)
@@ -2289,9 +2316,9 @@ definitions:
2289
2316
  When this property is provided, the query params will be encoded as `Json` and included in the outgoing API request.
2290
2317
  examples:
2291
2318
  - {
2292
- "{auth_code_key}": "{{auth_code_key}}",
2293
- "{client_id_key}": "{{client_id_key}}",
2294
- "{client_secret_key}": "{{client_secret_key}}",
2319
+ "{{ auth_code_key }}": "{{ auth_code_value }}",
2320
+ "{{ client_id_key }}": "{{ client_id_value }}",
2321
+ "{{ client_secret_key }}": "{{ client_secret_value }}",
2295
2322
  }
2296
2323
  extract_output:
2297
2324
  title: Extract Output
@@ -2859,7 +2886,6 @@ definitions:
2859
2886
  parser:
2860
2887
  anyOf:
2861
2888
  - "$ref": "#/definitions/GzipParser"
2862
- - "$ref": "#/definitions/JsonParser"
2863
2889
  - "$ref": "#/definitions/JsonLineParser"
2864
2890
  - "$ref": "#/definitions/CsvParser"
2865
2891
  # PARSERS
@@ -2876,21 +2902,6 @@ definitions:
2876
2902
  anyOf:
2877
2903
  - "$ref": "#/definitions/JsonLineParser"
2878
2904
  - "$ref": "#/definitions/CsvParser"
2879
- - "$ref": "#/definitions/JsonParser"
2880
- JsonParser:
2881
- title: JsonParser
2882
- description: Parser used for parsing str, bytes, or bytearray data and returning data in a dictionary format.
2883
- type: object
2884
- additionalProperties: true
2885
- required:
2886
- - type
2887
- properties:
2888
- type:
2889
- type: string
2890
- enum: [JsonParser]
2891
- encoding:
2892
- type: string
2893
- default: utf-8
2894
2905
  JsonLineParser:
2895
2906
  type: object
2896
2907
  required:
@@ -2993,6 +3004,11 @@ definitions:
2993
3004
  anyOf:
2994
3005
  - "$ref": "#/definitions/CustomRequester"
2995
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"
2996
3012
  download_requester:
2997
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.
2998
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,47 +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
- """
54
- Attempts to deserialize data using orjson library. As an extra layer of safety we fallback on the json library to deserialize the data.
55
- """
56
- raw_data = data.read()
57
-
58
- body_json = self._parse_orjson(raw_data) or self._parse_json(raw_data)
59
-
60
- if body_json is None:
61
- raise AirbyteTracedException(
62
- message="Response JSON data failed to be parsed. See logs for more information.",
63
- internal_message=f"Response JSON data failed to be parsed.",
64
- failure_type=FailureType.system_error,
65
- )
66
-
67
- if isinstance(body_json, list):
68
- yield from body_json
69
- else:
70
- yield from [body_json]
71
-
72
- def _parse_orjson(self, raw_data: bytes) -> Optional[Any]:
73
- try:
74
- return orjson.loads(raw_data.decode(self.encoding))
75
- except Exception as exc:
76
- logger.warning(
77
- f"Failed to parse JSON data using orjson library. Falling back to json library. {exc=}"
78
- )
79
- return None
80
-
81
- def _parse_json(self, raw_data: bytes) -> Optional[Any]:
82
- try:
83
- return json.loads(raw_data.decode(self.encoding))
84
- except Exception as exc:
85
- logger.error(f"Failed to parse JSON data using json library. {exc=}")
86
- return None
87
-
88
-
89
45
  @dataclass
90
46
  class JsonLineParser(Parser):
91
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.",
@@ -859,8 +883,8 @@ class OauthConnectorInputSpecification(BaseModel):
859
883
  ...,
860
884
  description="The DeclarativeOAuth Specific string URL string template to initiate the authentication.\nThe placeholders are replaced during the processing to provide neccessary values.",
861
885
  examples=[
862
- "https://domain.host.com/marketing_api/auth?{client_id_key}={{client_id_key}}&{redirect_uri_key}={urlEncoder:{{redirect_uri_key}}}&{state_key}={{state_key}}",
863
- "https://endpoint.host.com/oauth2/authorize?{client_id_key}={{client_id_key}}&{redirect_uri_key}={urlEncoder:{{redirect_uri_key}}}&{scope_key}={urlEncoder:{{scope_key}}}&{state_key}={{state_key}}&subdomain={subdomain}",
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}}",
864
888
  ],
865
889
  title="Consent URL",
866
890
  )
@@ -874,14 +898,18 @@ class OauthConnectorInputSpecification(BaseModel):
874
898
  ...,
875
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.",
876
900
  examples=[
877
- "https://auth.host.com/oauth2/token?{client_id_key}={{client_id_key}}&{client_secret_key}={{client_secret_key}}&{auth_code_key}={{auth_code_key}}&{redirect_uri_key}={urlEncoder:{{redirect_uri_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}}"
878
902
  ],
879
903
  title="Access Token URL",
880
904
  )
881
905
  access_token_headers: Optional[Dict[str, Any]] = Field(
882
906
  None,
883
907
  description="The DeclarativeOAuth Specific optional headers to inject while exchanging the `auth_code` to `access_token` during `completeOAuthFlow` step.",
884
- examples=[{"Authorization": "Basic {base64Encoder:{client_id}:{client_secret}}"}],
908
+ examples=[
909
+ {
910
+ "Authorization": "Basic {{ {{ client_id_value }}:{{ client_secret_value }} | base64Encoder }}"
911
+ }
912
+ ],
885
913
  title="Access Token Headers",
886
914
  )
887
915
  access_token_params: Optional[Dict[str, Any]] = Field(
@@ -889,15 +917,15 @@ class OauthConnectorInputSpecification(BaseModel):
889
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.",
890
918
  examples=[
891
919
  {
892
- "{auth_code_key}": "{{auth_code_key}}",
893
- "{client_id_key}": "{{client_id_key}}",
894
- "{client_secret_key}": "{{client_secret_key}}",
920
+ "{{ auth_code_key }}": "{{ auth_code_value }}",
921
+ "{{ client_id_key }}": "{{ client_id_value }}",
922
+ "{{ client_secret_key }}": "{{ client_secret_value }}",
895
923
  }
896
924
  ],
897
925
  title="Access Token Query Params (Json Encoded)",
898
926
  )
899
- extract_output: List[str] = Field(
900
- ...,
927
+ extract_output: Optional[List[str]] = Field(
928
+ None,
901
929
  description="The DeclarativeOAuth Specific list of strings to indicate which keys should be extracted and returned back to the input config.",
902
930
  examples=[["access_token", "refresh_token", "other_field"]],
903
931
  title="Extract Output",
@@ -966,7 +994,7 @@ class OAuthConfigSpecification(BaseModel):
966
994
  )
967
995
  oauth_connector_input_specification: Optional[OauthConnectorInputSpecification] = Field(
968
996
  None,
969
- 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`, {base64Encoder:{my_var_a}:{my_var_b}}\n + base64Decorer - decode from `base64` encoded string, {base64Decoder:{my_string_variable_or_string_value}}\n + urlEncoder - encode the input string to URL-like format, {urlEncoder:https://test.host.com/endpoint}\n + urlDecorer - decode the input url-encoded string into text format, {urlDecoder:https%3A%2F%2Fairbyte.io}\n + codeChallengeS256 - get the `codeChallenge` encoded value to provide additional data-provider specific authorisation values, {codeChallengeS256:{state_value}}\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_key}}&{redirect_uri_key}={urlEncoder:{{redirect_uri_key}}}&{state_key}={{state_key}}",\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_key}}",\n "{client_id_key}": "{{client_id_key}}",\n "{client_secret_key}": "{{client_secret_key}}"\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 }',
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 }',
970
998
  title="DeclarativeOAuth Connector Specification",
971
999
  )
972
1000
  complete_oauth_output_specification: Optional[Dict[str, Any]] = Field(
@@ -1173,14 +1201,6 @@ class LegacySessionTokenAuthenticator(BaseModel):
1173
1201
  parameters: Optional[Dict[str, Any]] = Field(None, alias="$parameters")
1174
1202
 
1175
1203
 
1176
- class JsonParser(BaseModel):
1177
- class Config:
1178
- extra = Extra.allow
1179
-
1180
- type: Literal["JsonParser"]
1181
- encoding: Optional[str] = "utf-8"
1182
-
1183
-
1184
1204
  class JsonLineParser(BaseModel):
1185
1205
  type: Literal["JsonLineParser"]
1186
1206
  encoding: Optional[str] = "utf-8"
@@ -1579,7 +1599,7 @@ class RecordSelector(BaseModel):
1579
1599
 
1580
1600
  class GzipParser(BaseModel):
1581
1601
  type: Literal["GzipParser"]
1582
- inner_parser: Union[JsonLineParser, CsvParser, JsonParser]
1602
+ inner_parser: Union[JsonLineParser, CsvParser]
1583
1603
 
1584
1604
 
1585
1605
  class Spec(BaseModel):
@@ -1614,7 +1634,7 @@ class CompositeErrorHandler(BaseModel):
1614
1634
 
1615
1635
  class CompositeRawDecoder(BaseModel):
1616
1636
  type: Literal["CompositeRawDecoder"]
1617
- parser: Union[GzipParser, JsonParser, JsonLineParser, CsvParser]
1637
+ parser: Union[GzipParser, JsonLineParser, CsvParser]
1618
1638
 
1619
1639
 
1620
1640
  class DeclarativeSource1(BaseModel):
@@ -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.urls_extractor.extract_records(
190
- self._polling_job_response_by_id[job.api_job_id()]
191
- ):
192
- stream_slice: StreamSlice = StreamSlice(partition={"url": url}, cursor_slice={})
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
- "grant_type": self.get_grant_type(),
85
- "client_id": self.get_client_id(),
86
- "client_secret": self.get_client_secret(),
87
- "refresh_token": self.get_refresh_token(),
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.get_client_id(),
187
- self.get_client_secret(),
188
- self.get_refresh_token(),
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,
@@ -152,3 +152,6 @@ class StreamSlice(Mapping[str, Any]):
152
152
 
153
153
  def __hash__(self) -> int:
154
154
  return hash(orjson.dumps(self._stream_slice, option=orjson.OPT_SORT_KEYS))
155
+
156
+ def __bool__(self) -> bool:
157
+ return bool(self._stream_slice) or bool(self._extra_fields)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: airbyte-cdk
3
- Version: 6.18.0.dev2
3
+ Version: 6.18.1
4
4
  Summary: A framework for writing Airbyte Connectors.
5
5
  License: MIT
6
6
  Keywords: airbyte,connector-development-kit,cdk
@@ -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=Yr0ljFjln9FIWudQohXARyKEo6-4ACG840pZoi6JVrE,9165
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=fCvq1sFwE4h7sP9sjZHoggsw8K3pvOEMvnf_es3fizA,133625
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=VpKcCbRDHhU7yRvwCmc5yQO1KSSUYEOocngDLin6o2s,4359
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=_aQM6pF-a4Zk7kQ-_GiVaRi30bpyTQDg4G7KZD83yuI,93560
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=lmSh2Yp-lgRTbbSw3m6UH8L2nTRjt0w3aiISWHRG6IM,109739
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=o0520AmHMb7SAoeokVNwoOzuZzIAT6ryx9uFYGSOrs0,8664
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=sxmrQKAvN8piamssL3xh8KXevTwdaXuLs2O0hNEA5aQ,10635
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=ka-bBRWvIv09LmZNYl49p2lK9nd_Tvi2g0lIp3OkU40,14872
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=WWVigI7ZSoQU2TBCzDsHJtoX4Ima9v--lcLyYwUG_cE,4904
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.0.dev2.dist-info/LICENSE.txt,sha256=Wfe61S4BaGPj404v8lrAbvhjYR68SHlkzeYrg3_bbuM,1051
346
- airbyte_cdk-6.18.0.dev2.dist-info/METADATA,sha256=0EfivelRXCgquhtK-6kt045d65qTAYxlGhu5j2Vg_FY,6005
347
- airbyte_cdk-6.18.0.dev2.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
348
- airbyte_cdk-6.18.0.dev2.dist-info/entry_points.txt,sha256=fj-e3PAQvsxsQzyyq8UkG1k8spunWnD4BAH2AwlR6NM,95
349
- airbyte_cdk-6.18.0.dev2.dist-info/RECORD,,
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,,