fivetran-connector-sdk 1.5.1__py3-none-any.whl → 1.6.0__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.
- fivetran_connector_sdk/__init__.py +4 -4
- fivetran_connector_sdk/connector_helper.py +16 -15
- fivetran_connector_sdk/helpers.py +6 -2
- fivetran_connector_sdk/operations.py +17 -2
- {fivetran_connector_sdk-1.5.1.dist-info → fivetran_connector_sdk-1.6.0.dist-info}/METADATA +1 -1
- {fivetran_connector_sdk-1.5.1.dist-info → fivetran_connector_sdk-1.6.0.dist-info}/RECORD +9 -9
- {fivetran_connector_sdk-1.5.1.dist-info → fivetran_connector_sdk-1.6.0.dist-info}/WHEEL +0 -0
- {fivetran_connector_sdk-1.5.1.dist-info → fivetran_connector_sdk-1.6.0.dist-info}/entry_points.txt +0 -0
- {fivetran_connector_sdk-1.5.1.dist-info → fivetran_connector_sdk-1.6.0.dist-info}/top_level.txt +0 -0
@@ -39,7 +39,7 @@ from fivetran_connector_sdk.connector_helper import (
|
|
39
39
|
|
40
40
|
# Version format: <major_version>.<minor_version>.<patch_version>
|
41
41
|
# (where Major Version = 1 for GA, Minor Version is incremental MM from Jan 25 onwards, Patch Version is incremental within a month)
|
42
|
-
__version__ = "1.
|
42
|
+
__version__ = "1.6.0"
|
43
43
|
TESTER_VERSION = TESTER_VER
|
44
44
|
MAX_MESSAGE_LENGTH = 32 * 1024 * 1024 # 32MB
|
45
45
|
|
@@ -104,7 +104,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
|
104
104
|
if service != 'connector_sdk':
|
105
105
|
print_library_log(
|
106
106
|
f"The connection '{connection}' already exists and does not use the 'Connector SDK' service. You cannot update this connection.", Logging.Level.SEVERE)
|
107
|
-
|
107
|
+
sys.exit(1)
|
108
108
|
else:
|
109
109
|
if args.force:
|
110
110
|
confirm = "y"
|
@@ -129,7 +129,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
|
129
129
|
f"Visit the Fivetran dashboard to manage the connection: https://fivetran.com/dashboard/connectors/{connection_id}/status")
|
130
130
|
else:
|
131
131
|
print_library_log("Update canceled. The process is now terminating.")
|
132
|
-
|
132
|
+
sys.exit(1)
|
133
133
|
else:
|
134
134
|
upload_project(args.project_path, deploy_key,
|
135
135
|
group_id, group_name, connection)
|
@@ -152,7 +152,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
|
152
152
|
f"Unable to create a new connection, failed with error: {response.json()['message']}", Logging.Level.SEVERE)
|
153
153
|
cleanup_uploaded_project(deploy_key,group_id, connection)
|
154
154
|
print_library_log("Please try again with the deploy command after resolving the issue!")
|
155
|
-
|
155
|
+
sys.exit(1)
|
156
156
|
|
157
157
|
# Call this method to run the connector in production
|
158
158
|
def run(self,
|
@@ -2,6 +2,7 @@ import os
|
|
2
2
|
import re
|
3
3
|
import ast
|
4
4
|
import json
|
5
|
+
import sys
|
5
6
|
import time
|
6
7
|
import socket
|
7
8
|
import platform
|
@@ -140,7 +141,7 @@ def check_dict(incoming: dict, string_only: bool = False) -> dict:
|
|
140
141
|
if not isinstance(v, str):
|
141
142
|
print_library_log(
|
142
143
|
"All values in the configuration must be STRING. Please check your configuration and ensure that every value is a STRING.", Logging.Level.SEVERE)
|
143
|
-
|
144
|
+
sys.exit(1)
|
144
145
|
|
145
146
|
return incoming
|
146
147
|
|
@@ -173,12 +174,12 @@ def validate_deploy_parameters(connection, deploy_key):
|
|
173
174
|
"\t(Optional):\n"
|
174
175
|
"\t\t--destination <DESTINATION_NAME> (Becomes required if there are multiple destinations)\n"
|
175
176
|
"\t\t--configuration <CONFIGURATION_FILE> (Completely replaces the existing configuration)", Logging.Level.SEVERE)
|
176
|
-
|
177
|
+
sys.exit(1)
|
177
178
|
elif not is_connection_name_valid(connection):
|
178
179
|
print_library_log(f"Connection name: {connection} is invalid!\n The connection name should start with an "
|
179
180
|
f"underscore or a lowercase letter (a-z), followed by any combination of underscores, lowercase "
|
180
181
|
f"letters, or digits (0-9). Uppercase characters are not allowed.", Logging.Level.SEVERE)
|
181
|
-
|
182
|
+
sys.exit(1)
|
182
183
|
|
183
184
|
|
184
185
|
def is_port_in_use(port: int):
|
@@ -346,7 +347,7 @@ def validate_requirements_file(project_path: str, is_deploy: bool, version: str
|
|
346
347
|
if 'fivetran_connector_sdk' in unused_deps or 'requests' in unused_deps:
|
347
348
|
print_library_log(
|
348
349
|
f"Please fix your {REQUIREMENTS_TXT} file by removing pre-installed dependencies [fivetran_connector_sdk, requests] to proceed with the deployment.")
|
349
|
-
|
350
|
+
sys.exit(1)
|
350
351
|
print_library_log(f"Changes identified for unused libraries have been ignored. These changes have NOT been made to {REQUIREMENTS_TXT}.")
|
351
352
|
elif confirm.lower() == "y":
|
352
353
|
update_unused_requirements = True
|
@@ -420,13 +421,13 @@ def upload_project(project_path: str, deploy_key: str, group_id: str, group_name
|
|
420
421
|
upload_file_path, deploy_key, group_id, connection)
|
421
422
|
delete_file_if_exists(upload_file_path)
|
422
423
|
if not upload_result:
|
423
|
-
|
424
|
+
sys.exit(1)
|
424
425
|
|
425
426
|
|
426
427
|
def cleanup_uploaded_project(deploy_key: str, group_id: str, connection: str):
|
427
428
|
cleanup_result = cleanup_uploaded_code(deploy_key, group_id, connection)
|
428
429
|
if not cleanup_result:
|
429
|
-
|
430
|
+
sys.exit(1)
|
430
431
|
|
431
432
|
def update_connection(args: dict, id: str, name: str, group: str, config: dict, deploy_key: str, hd_agent_id: str):
|
432
433
|
"""Updates the connection with the given ID, name, group, configuration, and deployment key.
|
@@ -467,7 +468,7 @@ def update_connection(args: dict, id: str, name: str, group: str, config: dict,
|
|
467
468
|
print_library_log(
|
468
469
|
f"Unable to update Connection '{name}' in destination '{group}', failed with error: '{response.json()['message']}'.",
|
469
470
|
Logging.Level.SEVERE)
|
470
|
-
|
471
|
+
sys.exit(1)
|
471
472
|
return response
|
472
473
|
|
473
474
|
def handle_failing_tests_message_and_exit(resp, log_message):
|
@@ -476,7 +477,7 @@ def handle_failing_tests_message_and_exit(resp, log_message):
|
|
476
477
|
connection_id = resp.json().get('data', {}).get('id')
|
477
478
|
print_library_log(f"Connection ID: {connection_id}")
|
478
479
|
print_library_log("Please try again with the deploy command after resolving the issue!")
|
479
|
-
|
480
|
+
sys.exit(1)
|
480
481
|
|
481
482
|
def are_setup_tests_failing(response) -> bool:
|
482
483
|
"""Checks for failed setup tests in the response and returns True if any test has failed, otherwise False."""
|
@@ -520,7 +521,7 @@ def get_connection_id(name: str, group: str, group_id: str, deploy_key: str) ->
|
|
520
521
|
if not resp.ok:
|
521
522
|
print_library_log(
|
522
523
|
f"Unable to fetch connection list in destination '{group}'", Logging.Level.SEVERE)
|
523
|
-
|
524
|
+
sys.exit(1)
|
524
525
|
|
525
526
|
if resp.json()['data']['items']:
|
526
527
|
return resp.json()['data']['items'][0]['id'], resp.json()['data']['items'][0]['service']
|
@@ -599,11 +600,11 @@ def zip_folder(project_path: str) -> str:
|
|
599
600
|
print_library_log(
|
600
601
|
"The 'connector.py' file is missing. Please ensure that 'connector.py' is present in your project directory, and that the file name is in lowercase letters. All custom connectors require this file because Fivetran calls it to start a sync.",
|
601
602
|
Logging.Level.SEVERE)
|
602
|
-
|
603
|
+
sys.exit(1)
|
603
604
|
|
604
605
|
if custom_drivers_exists and not custom_driver_installation_script_exists:
|
605
606
|
print_library_log(INSTALLATION_SCRIPT_MISSING_MESSAGE, Logging.Level.SEVERE)
|
606
|
-
|
607
|
+
sys.exit(1)
|
607
608
|
|
608
609
|
return upload_filepath
|
609
610
|
|
@@ -719,14 +720,14 @@ def get_group_info(group: str, deploy_key: str) -> tuple[str, str]:
|
|
719
720
|
print_library_log(
|
720
721
|
f"The request failed with status code: {resp.status_code}. Please ensure you're using a valid base64-encoded API key and try again.",
|
721
722
|
Logging.Level.SEVERE)
|
722
|
-
|
723
|
+
sys.exit(1)
|
723
724
|
|
724
725
|
data = resp.json().get("data", {})
|
725
726
|
groups = data.get("items")
|
726
727
|
|
727
728
|
if not groups:
|
728
729
|
print_library_log("No destinations defined in the account", Logging.Level.SEVERE)
|
729
|
-
|
730
|
+
sys.exit(1)
|
730
731
|
|
731
732
|
if not group:
|
732
733
|
if len(groups) == 1:
|
@@ -735,7 +736,7 @@ def get_group_info(group: str, deploy_key: str) -> tuple[str, str]:
|
|
735
736
|
print_library_log(
|
736
737
|
"Destination name is required when there are multiple destinations in the account",
|
737
738
|
Logging.Level.SEVERE)
|
738
|
-
|
739
|
+
sys.exit(1)
|
739
740
|
else:
|
740
741
|
while True:
|
741
742
|
for grp in groups:
|
@@ -753,7 +754,7 @@ def get_group_info(group: str, deploy_key: str) -> tuple[str, str]:
|
|
753
754
|
|
754
755
|
print_library_log(
|
755
756
|
f"The specified destination '{group}' was not found in your account.", Logging.Level.SEVERE)
|
756
|
-
|
757
|
+
sys.exit(1)
|
757
758
|
|
758
759
|
def java_exe_helper(location: str, os_arch_suffix: str) -> str:
|
759
760
|
"""Returns the path to the Java executable.
|
@@ -29,19 +29,23 @@ from fivetran_connector_sdk.constants import (
|
|
29
29
|
RENAMED_TABLE_NAMES = {}
|
30
30
|
RENAMED_COL_NAMES = {}
|
31
31
|
|
32
|
-
def print_library_log(message: str, level: Logging.Level = Logging.Level.INFO):
|
32
|
+
def print_library_log(message: str, level: Logging.Level = Logging.Level.INFO, dev_log: bool = False):
|
33
33
|
"""Logs a library message with the specified logging level.
|
34
34
|
|
35
35
|
Args:
|
36
36
|
level (Logging.Level): The logging level.
|
37
37
|
message (str): The message to log.
|
38
|
+
dev_log (bool): Boolean value to check if it is dev log and shouldn't be visible to customer
|
38
39
|
"""
|
39
40
|
if constants.DEBUGGING or constants.EXECUTED_VIA_CLI:
|
41
|
+
if dev_log:
|
42
|
+
return
|
40
43
|
current_time = datetime.now().strftime("%b %d, %Y %I:%M:%S %p")
|
41
44
|
print(f"{Logging.get_color(level)}{current_time} {level.name} {LOGGING_PREFIX}: {message} {Logging.reset_color()}")
|
42
45
|
else:
|
46
|
+
message_origin = "library_dev" if dev_log else "library"
|
43
47
|
escaped_message = json.dumps(LOGGING_PREFIX + LOGGING_DELIMITER + message)
|
44
|
-
log_message = f'{{"level":"{level.name}", "message": {escaped_message}, "message_origin": "
|
48
|
+
log_message = f'{{"level":"{level.name}", "message": {escaped_message}, "message_origin": "{message_origin}"}}'
|
45
49
|
print(log_message)
|
46
50
|
|
47
51
|
def is_special(c):
|
@@ -1,6 +1,6 @@
|
|
1
|
-
import os
|
2
1
|
import json
|
3
2
|
import inspect
|
3
|
+
import sys
|
4
4
|
|
5
5
|
from datetime import datetime
|
6
6
|
from google.protobuf import timestamp_pb2
|
@@ -18,6 +18,12 @@ from fivetran_connector_sdk.helpers import (
|
|
18
18
|
from fivetran_connector_sdk.logger import Logging
|
19
19
|
from fivetran_connector_sdk.protos import connector_sdk_pb2, common_pb2
|
20
20
|
|
21
|
+
_LOG_DATA_TYPE_INFERENCE = {
|
22
|
+
"boolean": True,
|
23
|
+
"binary": True,
|
24
|
+
"json": True
|
25
|
+
}
|
26
|
+
|
21
27
|
class Operations:
|
22
28
|
@staticmethod
|
23
29
|
def upsert(table: str, data: dict) -> list[connector_sdk_pb2.UpdateResponse]:
|
@@ -189,13 +195,22 @@ def map_inferred_data_type(k, mapped_data, v):
|
|
189
195
|
elif isinstance(v, float):
|
190
196
|
mapped_data[k] = common_pb2.ValueType(float=v)
|
191
197
|
elif isinstance(v, bool):
|
198
|
+
if _LOG_DATA_TYPE_INFERENCE["boolean"]:
|
199
|
+
print_library_log("Fivetran: Boolean Datatype has been inferred", Logging.Level.INFO, True)
|
200
|
+
_LOG_DATA_TYPE_INFERENCE["boolean"] = False
|
192
201
|
mapped_data[k] = common_pb2.ValueType(bool=v)
|
193
202
|
elif isinstance(v, bytes):
|
203
|
+
if _LOG_DATA_TYPE_INFERENCE["binary"]:
|
204
|
+
print_library_log("Fivetran: Binary Datatype has been inferred", Logging.Level.INFO, True)
|
205
|
+
_LOG_DATA_TYPE_INFERENCE["binary"] = False
|
194
206
|
mapped_data[k] = common_pb2.ValueType(binary=v)
|
195
207
|
elif isinstance(v, list):
|
196
208
|
raise ValueError(
|
197
209
|
"Values for the columns cannot be of type 'list'. Please ensure that all values are of a supported type. Reference: https://fivetran.com/docs/connectors/connector-sdk/technical-reference#supporteddatatypes")
|
198
210
|
elif isinstance(v, dict):
|
211
|
+
if _LOG_DATA_TYPE_INFERENCE["json"]:
|
212
|
+
print_library_log("Fivetran: JSON Datatype has been inferred", Logging.Level.INFO, True)
|
213
|
+
_LOG_DATA_TYPE_INFERENCE["json"] = False
|
199
214
|
mapped_data[k] = common_pb2.ValueType(json=json.dumps(v))
|
200
215
|
elif isinstance(v, str):
|
201
216
|
mapped_data[k] = common_pb2.ValueType(string=v)
|
@@ -270,7 +285,7 @@ def _yield_check(stack):
|
|
270
285
|
if 'yield' not in calling_code:
|
271
286
|
print_library_log(
|
272
287
|
f"Please add 'yield' to '{called_method}' operation on line {stack[1].lineno} in file '{stack[1].filename}'", Logging.Level.SEVERE)
|
273
|
-
|
288
|
+
sys.exit(1)
|
274
289
|
else:
|
275
290
|
# This should never happen
|
276
291
|
raise RuntimeError(
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: fivetran_connector_sdk
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.6.0
|
4
4
|
Summary: Build custom connectors on Fivetran platform
|
5
5
|
Author-email: Fivetran <developers@fivetran.com>
|
6
6
|
Project-URL: Homepage, https://fivetran.com/docs/connectors/connector-sdk
|
@@ -1,9 +1,9 @@
|
|
1
|
-
fivetran_connector_sdk/__init__.py,sha256=
|
2
|
-
fivetran_connector_sdk/connector_helper.py,sha256=
|
1
|
+
fivetran_connector_sdk/__init__.py,sha256=C-VEwdIOtKmvF0km7I4cjzjxUZ7ZabQ4dAorggzY04c,21015
|
2
|
+
fivetran_connector_sdk/connector_helper.py,sha256=aVZmy3_SuK62zEa91E1WOeCHyFek5j9zH3ct_epGdYc,38971
|
3
3
|
fivetran_connector_sdk/constants.py,sha256=tY8-fwB7O11orifFcDIELxfvYNz9-bNUhh6f5cXXws0,2287
|
4
|
-
fivetran_connector_sdk/helpers.py,sha256=
|
4
|
+
fivetran_connector_sdk/helpers.py,sha256=OV7P79Mhju90m75ynG0AVbD_RCIvWfZimiTwrlPjmi8,12623
|
5
5
|
fivetran_connector_sdk/logger.py,sha256=arI6eNUfm_AvE3EV_PmbSRzrpTVpZKJ8pZMtDn93TKU,3018
|
6
|
-
fivetran_connector_sdk/operations.py,sha256=
|
6
|
+
fivetran_connector_sdk/operations.py,sha256=Ji6S9bH4ULf0QcrZX8M6RMhTc-IgJ7xXHmov5fbYPI0,11763
|
7
7
|
fivetran_connector_sdk/protos/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
8
8
|
fivetran_connector_sdk/protos/common_pb2.py,sha256=kUwVcyZHgLigNR-KnHZn7dHrlxaMnUXqzprsRx6T72M,6831
|
9
9
|
fivetran_connector_sdk/protos/common_pb2.pyi,sha256=S0hdIzoXyyOKD5cjiGeDDLYpQ9J3LjAvu4rCj1JvJWE,9038
|
@@ -11,8 +11,8 @@ fivetran_connector_sdk/protos/common_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXH
|
|
11
11
|
fivetran_connector_sdk/protos/connector_sdk_pb2.py,sha256=9Ke_Ti1s0vAeXapfXT-EryrT2-TSGQb8mhs4gxTpUMk,7732
|
12
12
|
fivetran_connector_sdk/protos/connector_sdk_pb2.pyi,sha256=FWYxRgshEF3QDYAE0TM_mv4N2gGvkxCH_uPpxnMc4oA,8406
|
13
13
|
fivetran_connector_sdk/protos/connector_sdk_pb2_grpc.py,sha256=ZfJLp4DW7uP4pFOZ74s_wQ6tD3eIPi-08UfnLwe4tzo,7163
|
14
|
-
fivetran_connector_sdk-1.
|
15
|
-
fivetran_connector_sdk-1.
|
16
|
-
fivetran_connector_sdk-1.
|
17
|
-
fivetran_connector_sdk-1.
|
18
|
-
fivetran_connector_sdk-1.
|
14
|
+
fivetran_connector_sdk-1.6.0.dist-info/METADATA,sha256=pBq3Y_LbaMhjBEqZeTxUK_DomwexOpbsyKhtd7QvnXw,3150
|
15
|
+
fivetran_connector_sdk-1.6.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
16
|
+
fivetran_connector_sdk-1.6.0.dist-info/entry_points.txt,sha256=uQn0KPnFlQmXJfxlk0tifdNsSXWfVlnAFzNqjXZM_xM,57
|
17
|
+
fivetran_connector_sdk-1.6.0.dist-info/top_level.txt,sha256=-_xk2MFY4psIh7jw1lJePMzFb5-vask8_ZtX-UzYWUI,23
|
18
|
+
fivetran_connector_sdk-1.6.0.dist-info/RECORD,,
|
File without changes
|
{fivetran_connector_sdk-1.5.1.dist-info → fivetran_connector_sdk-1.6.0.dist-info}/entry_points.txt
RENAMED
File without changes
|
{fivetran_connector_sdk-1.5.1.dist-info → fivetran_connector_sdk-1.6.0.dist-info}/top_level.txt
RENAMED
File without changes
|