fivetran-connector-sdk 0.13.17.1__tar.gz → 0.13.30.1__tar.gz
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-0.13.17.1 → fivetran_connector_sdk-0.13.30.1}/PKG-INFO +2 -2
- {fivetran_connector_sdk-0.13.17.1 → fivetran_connector_sdk-0.13.30.1}/pyproject.toml +1 -1
- {fivetran_connector_sdk-0.13.17.1 → fivetran_connector_sdk-0.13.30.1}/src/fivetran_connector_sdk/__init__.py +105 -64
- {fivetran_connector_sdk-0.13.17.1 → fivetran_connector_sdk-0.13.30.1}/src/fivetran_connector_sdk.egg-info/PKG-INFO +2 -2
- {fivetran_connector_sdk-0.13.17.1 → fivetran_connector_sdk-0.13.30.1}/src/fivetran_connector_sdk.egg-info/requires.txt +1 -1
- {fivetran_connector_sdk-0.13.17.1 → fivetran_connector_sdk-0.13.30.1}/README.md +0 -0
- {fivetran_connector_sdk-0.13.17.1 → fivetran_connector_sdk-0.13.30.1}/setup.cfg +0 -0
- {fivetran_connector_sdk-0.13.17.1 → fivetran_connector_sdk-0.13.30.1}/src/fivetran_connector_sdk/protos/__init__.py +0 -0
- {fivetran_connector_sdk-0.13.17.1 → fivetran_connector_sdk-0.13.30.1}/src/fivetran_connector_sdk/protos/common_pb2.py +0 -0
- {fivetran_connector_sdk-0.13.17.1 → fivetran_connector_sdk-0.13.30.1}/src/fivetran_connector_sdk/protos/common_pb2.pyi +0 -0
- {fivetran_connector_sdk-0.13.17.1 → fivetran_connector_sdk-0.13.30.1}/src/fivetran_connector_sdk/protos/common_pb2_grpc.py +0 -0
- {fivetran_connector_sdk-0.13.17.1 → fivetran_connector_sdk-0.13.30.1}/src/fivetran_connector_sdk/protos/connector_sdk_pb2.py +0 -0
- {fivetran_connector_sdk-0.13.17.1 → fivetran_connector_sdk-0.13.30.1}/src/fivetran_connector_sdk/protos/connector_sdk_pb2.pyi +0 -0
- {fivetran_connector_sdk-0.13.17.1 → fivetran_connector_sdk-0.13.30.1}/src/fivetran_connector_sdk/protos/connector_sdk_pb2_grpc.py +0 -0
- {fivetran_connector_sdk-0.13.17.1 → fivetran_connector_sdk-0.13.30.1}/src/fivetran_connector_sdk.egg-info/SOURCES.txt +0 -0
- {fivetran_connector_sdk-0.13.17.1 → fivetran_connector_sdk-0.13.30.1}/src/fivetran_connector_sdk.egg-info/dependency_links.txt +0 -0
- {fivetran_connector_sdk-0.13.17.1 → fivetran_connector_sdk-0.13.30.1}/src/fivetran_connector_sdk.egg-info/entry_points.txt +0 -0
- {fivetran_connector_sdk-0.13.17.1 → fivetran_connector_sdk-0.13.30.1}/src/fivetran_connector_sdk.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: fivetran_connector_sdk
|
3
|
-
Version: 0.13.
|
3
|
+
Version: 0.13.30.1
|
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
|
@@ -15,7 +15,7 @@ Requires-Dist: grpcio==1.60.1
|
|
15
15
|
Requires-Dist: grpcio-tools==1.60.1
|
16
16
|
Requires-Dist: requests==2.32.3
|
17
17
|
Requires-Dist: pipreqs==0.5.0
|
18
|
-
Requires-Dist:
|
18
|
+
Requires-Dist: unidecode==1.3.8
|
19
19
|
|
20
20
|
# **fivetran-connector-sdk**
|
21
21
|
[](https://pepy.tech/project/fivetran-connector-sdk)
|
@@ -6,7 +6,8 @@ import importlib.util
|
|
6
6
|
import inspect
|
7
7
|
import json
|
8
8
|
import os
|
9
|
-
|
9
|
+
import unicodedata
|
10
|
+
from unidecode import unidecode
|
10
11
|
import platform
|
11
12
|
import requests as rq
|
12
13
|
import shutil
|
@@ -28,13 +29,13 @@ from fivetran_connector_sdk.protos import connector_sdk_pb2
|
|
28
29
|
from fivetran_connector_sdk.protos import connector_sdk_pb2_grpc
|
29
30
|
|
30
31
|
# Version format: <major_version>.<MM>.<DD>.<iteration> (where MM is incremental value from Jan 2024)
|
31
|
-
__version__ = "0.13.
|
32
|
+
__version__ = "0.13.30.1"
|
32
33
|
|
33
34
|
MAC_OS = "mac"
|
34
35
|
WIN_OS = "windows"
|
35
36
|
LINUX_OS = "linux"
|
36
37
|
|
37
|
-
TESTER_VERSION = "0.25.
|
38
|
+
TESTER_VERSION = "0.25.0130.001"
|
38
39
|
TESTER_FILENAME = "run_sdk_tester.jar"
|
39
40
|
VERSION_FILENAME = "version.txt"
|
40
41
|
UPLOAD_FILENAME = "code.zip"
|
@@ -54,11 +55,6 @@ WORD_OR_DOLLAR_PATTERN = re.compile(r'[\w$]')
|
|
54
55
|
DROP_LEADING_UNDERSCORE = re.compile(r'_+([a-zA-Z]\w*)')
|
55
56
|
WORD_PATTERN = re.compile(r'\w')
|
56
57
|
|
57
|
-
# Transliteration rules
|
58
|
-
TO_ASCII_RULES = (
|
59
|
-
"Han-Latin; Katakana-Latin; Arabic-Latin; NFD; [:Nonspacing Mark:] Remove; NFC"
|
60
|
-
)
|
61
|
-
|
62
58
|
EXCLUDED_DIRS = ["__pycache__", "lib", "include", OUTPUT_FILES_DIR]
|
63
59
|
EXCLUDED_PIPREQS_DIRS = ["bin,etc,include,lib,Lib,lib64,Scripts,share"]
|
64
60
|
VALID_COMMANDS = ["debug", "deploy", "reset", "version"]
|
@@ -79,6 +75,7 @@ INSTALLATION_SCRIPT = "installation.sh"
|
|
79
75
|
DRIVERS = "drivers"
|
80
76
|
JAVA_LONG_MAX_VALUE = 9223372036854775807
|
81
77
|
MAX_CONFIG_FIELDS = 100
|
78
|
+
SUPPORTED_PYTHON_VERSIONS = {"3.12.8", "3.11.11", "3.10.16", "3.9.21"}
|
82
79
|
|
83
80
|
|
84
81
|
class Logging:
|
@@ -100,7 +97,7 @@ class Logging:
|
|
100
97
|
"""
|
101
98
|
if DEBUGGING:
|
102
99
|
current_time = datetime.now().strftime("%b %d, %Y %I:%M:%S %p")
|
103
|
-
escaped_message = json.dumps(message)
|
100
|
+
escaped_message = json.dumps(message).strip('"')
|
104
101
|
print(f"{current_time} {level.name}: {escaped_message}")
|
105
102
|
else:
|
106
103
|
escaped_message = json.dumps(message)
|
@@ -164,13 +161,14 @@ class Operations:
|
|
164
161
|
|
165
162
|
responses = []
|
166
163
|
|
167
|
-
table =
|
164
|
+
table = get_renamed_table_name(table)
|
168
165
|
columns = _get_columns(table)
|
169
166
|
if not columns:
|
170
167
|
global TABLES
|
171
168
|
for field in data.keys():
|
172
|
-
|
173
|
-
|
169
|
+
field_name = get_renamed_column_name(field)
|
170
|
+
columns[field_name] = common_pb2.Column(
|
171
|
+
name=field_name, type=common_pb2.DataType.UNSPECIFIED, primary_key=False)
|
174
172
|
|
175
173
|
mapped_data = _map_data_to_columns(data, columns)
|
176
174
|
record = connector_sdk_pb2.Record(
|
@@ -199,7 +197,7 @@ class Operations:
|
|
199
197
|
"""
|
200
198
|
_yield_check(inspect.stack())
|
201
199
|
|
202
|
-
table =
|
200
|
+
table = get_renamed_table_name(table)
|
203
201
|
columns = _get_columns(table)
|
204
202
|
mapped_data = _map_data_to_columns(modified, columns)
|
205
203
|
record = connector_sdk_pb2.Record(
|
@@ -225,7 +223,7 @@ class Operations:
|
|
225
223
|
"""
|
226
224
|
_yield_check(inspect.stack())
|
227
225
|
|
228
|
-
table =
|
226
|
+
table = get_renamed_table_name(table)
|
229
227
|
columns = _get_columns(table)
|
230
228
|
mapped_data = _map_data_to_columns(keys, columns)
|
231
229
|
record = connector_sdk_pb2.Record(
|
@@ -316,6 +314,7 @@ def _get_columns(table: str) -> dict:
|
|
316
314
|
columns = {}
|
317
315
|
if table in TABLES:
|
318
316
|
for column in TABLES[table].columns:
|
317
|
+
column.name = get_renamed_column_name(column.name)
|
319
318
|
columns[column.name] = column
|
320
319
|
|
321
320
|
return columns
|
@@ -333,12 +332,13 @@ def _map_data_to_columns(data: dict, columns: dict) -> dict:
|
|
333
332
|
"""
|
334
333
|
mapped_data = {}
|
335
334
|
for k, v in data.items():
|
335
|
+
key = get_renamed_column_name(k)
|
336
336
|
if v is None:
|
337
|
-
mapped_data[
|
338
|
-
elif (
|
339
|
-
map_defined_data_type(columns,
|
337
|
+
mapped_data[key] = common_pb2.ValueType(null=True)
|
338
|
+
elif (key in columns) and columns[key].type != common_pb2.DataType.UNSPECIFIED:
|
339
|
+
map_defined_data_type(columns, key, mapped_data, v)
|
340
340
|
else:
|
341
|
-
map_inferred_data_type(
|
341
|
+
map_inferred_data_type(key, mapped_data, v)
|
342
342
|
|
343
343
|
return mapped_data
|
344
344
|
|
@@ -489,7 +489,7 @@ def log_unused_deps_error(package_name: str, version: str):
|
|
489
489
|
f" Current version: {version}", Logging.Level.SEVERE)
|
490
490
|
|
491
491
|
|
492
|
-
def validate_deploy_parameters(connection, deploy_key):
|
492
|
+
def validate_deploy_parameters(connection, deploy_key, python_version):
|
493
493
|
if not deploy_key or not connection:
|
494
494
|
print_library_log("The deploy command needs the following parameters:"
|
495
495
|
"\n\tRequired:\n"
|
@@ -504,6 +504,10 @@ def validate_deploy_parameters(connection, deploy_key):
|
|
504
504
|
f"underscore or a lowercase letter (a-z), followed by any combination of underscores, lowercase "
|
505
505
|
f"letters, or digits (0-9). Uppercase characters are not allowed.", Logging.Level.SEVERE)
|
506
506
|
os._exit(1)
|
507
|
+
if python_version and python_version not in SUPPORTED_PYTHON_VERSIONS:
|
508
|
+
print_library_log(f"This Python version is not supported: {python_version}. "
|
509
|
+
f"We only support the following python versions: {SUPPORTED_PYTHON_VERSIONS} ", Logging.Level.SEVERE)
|
510
|
+
os._exit(1)
|
507
511
|
|
508
512
|
def print_library_log(message: str, level: Logging.Level = Logging.Level.INFO):
|
509
513
|
"""Logs a library message with the specified logging level.
|
@@ -514,9 +518,12 @@ def print_library_log(message: str, level: Logging.Level = Logging.Level.INFO):
|
|
514
518
|
"""
|
515
519
|
if DEBUGGING or EXECUTED_VIA_CLI:
|
516
520
|
current_time = datetime.now().strftime("%b %d, %Y %I:%M:%S %p")
|
517
|
-
|
521
|
+
escaped_message = json.dumps(message).strip('"')
|
522
|
+
print(f"{current_time} {level.name}: {escaped_message}")
|
518
523
|
else:
|
519
|
-
|
524
|
+
escaped_message = json.dumps(message)
|
525
|
+
log_message = f'{{"level":"{level.name}", "message": {escaped_message}, "message_origin": "library"}}'
|
526
|
+
print(log_message)
|
520
527
|
|
521
528
|
|
522
529
|
def is_port_in_use(port: int):
|
@@ -605,7 +612,16 @@ def transliterate(name):
|
|
605
612
|
"""
|
606
613
|
if contains_only_word_dash_dot(name):
|
607
614
|
return name
|
608
|
-
|
615
|
+
# Step 1: Normalize the name to NFD form (decomposed form)
|
616
|
+
normalized_name = unicodedata.normalize('NFD', name)
|
617
|
+
# Step 2: Remove combining characters (diacritics, accents, etc.)
|
618
|
+
normalized_name = ''.join(char for char in normalized_name if not unicodedata.combining(char))
|
619
|
+
# Step 3: Normalize back to NFC form (composed form)
|
620
|
+
normalized_name = unicodedata.normalize('NFC', normalized_name)
|
621
|
+
# Step 4: Convert the string to ASCII using `unidecode` (removes any remaining non-ASCII characters)
|
622
|
+
normalized_name = unidecode(normalized_name)
|
623
|
+
# Step 5: Return the normalized name
|
624
|
+
return normalized_name
|
609
625
|
|
610
626
|
|
611
627
|
def redshift_safe(name):
|
@@ -630,13 +646,19 @@ def safe_drop_underscores(name):
|
|
630
646
|
return safe_name
|
631
647
|
|
632
648
|
|
633
|
-
def
|
649
|
+
def get_renamed_table_name(source_table):
|
634
650
|
"""
|
635
651
|
Process a source table name to ensure it conforms to naming rules.
|
636
652
|
"""
|
637
653
|
return safe_drop_underscores(source_table)
|
638
654
|
|
639
655
|
|
656
|
+
def get_renamed_column_name(source_column):
|
657
|
+
"""
|
658
|
+
Process a source column name to ensure it conforms to naming rules.
|
659
|
+
"""
|
660
|
+
return redshift_safe(source_column)
|
661
|
+
|
640
662
|
class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
641
663
|
def __init__(self, update, schema=None):
|
642
664
|
"""Initializes the Connector instance.
|
@@ -703,20 +725,31 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
|
703
725
|
force (bool): Force update an existing connection.
|
704
726
|
|
705
727
|
"""
|
706
|
-
# Run the pipreqs command and capture stderr
|
707
|
-
result = subprocess.run(
|
708
|
-
["pipreqs", "--savepath", "tmp_requirements.txt", "--ignore"] + EXCLUDED_PIPREQS_DIRS,
|
709
|
-
stderr=subprocess.PIPE,
|
710
|
-
text=True # Ensures output is in string format
|
711
|
-
)
|
712
|
-
|
713
|
-
if result.returncode != 0:
|
714
|
-
print_library_log("pipreqs failed with:", Logging.Level.SEVERE)
|
715
|
-
print(result.stderr)
|
716
|
-
sys.exit(1)
|
717
|
-
|
718
728
|
# tmp_requirements is only generated when pipreqs command is successful
|
719
729
|
tmp_requirements_file_path = os.path.join(project_path, 'tmp_requirements.txt')
|
730
|
+
# Run the pipreqs command and capture stderr
|
731
|
+
attempt = 0
|
732
|
+
while attempt < MAX_RETRIES:
|
733
|
+
attempt += 1
|
734
|
+
result = subprocess.run(
|
735
|
+
["pipreqs", project_path, "--savepath", tmp_requirements_file_path, "--ignore"] + EXCLUDED_PIPREQS_DIRS,
|
736
|
+
stderr=subprocess.PIPE,
|
737
|
+
text=True # Ensures output is in string format
|
738
|
+
)
|
739
|
+
|
740
|
+
if result.returncode == 0:
|
741
|
+
break
|
742
|
+
|
743
|
+
print_library_log(f"Attempt {attempt}: pipreqs check failed.", Logging.Level.WARNING)
|
744
|
+
|
745
|
+
if attempt < MAX_RETRIES:
|
746
|
+
retry_after = 3 ** attempt
|
747
|
+
print_library_log(f"Retrying in {retry_after} seconds...", Logging.Level.SEVERE)
|
748
|
+
time.sleep(retry_after)
|
749
|
+
else:
|
750
|
+
print_library_log(f"pipreqs failed after {MAX_RETRIES} attempts with:", Logging.Level.SEVERE)
|
751
|
+
print(result.stderr)
|
752
|
+
sys.exit(1)
|
720
753
|
|
721
754
|
tmp_requirements = self.fetch_requirements_as_dict(self, tmp_requirements_file_path)
|
722
755
|
tmp_requirements.pop("fivetran_connector_sdk")
|
@@ -798,12 +831,11 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
|
798
831
|
return requirements
|
799
832
|
|
800
833
|
# Call this method to deploy the connector to Fivetran platform
|
801
|
-
def deploy(self,
|
834
|
+
def deploy(self, args: dict, deploy_key: str, group: str, connection: str, configuration: dict = None):
|
802
835
|
"""Deploys the connector to the Fivetran platform.
|
803
836
|
|
804
837
|
Args:
|
805
|
-
|
806
|
-
force (bool): Force update an existing connection.
|
838
|
+
args (dict): The command arguments.
|
807
839
|
deploy_key (str): The deployment key.
|
808
840
|
group (str): The group name.
|
809
841
|
connection (str): The connection name.
|
@@ -814,7 +846,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
|
814
846
|
|
815
847
|
print_library_log("We support only `.py` files and a `requirements.txt` file as part of the code upload. *No other code files* are supported or uploaded during the deployment process. Ensure that your code is structured accordingly and all dependencies are listed in `requirements.txt`")
|
816
848
|
|
817
|
-
validate_deploy_parameters(connection, deploy_key)
|
849
|
+
validate_deploy_parameters(connection, deploy_key, args.python_version)
|
818
850
|
|
819
851
|
_check_dict(configuration, True)
|
820
852
|
|
@@ -827,10 +859,15 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
|
827
859
|
"schema": connection,
|
828
860
|
"secrets_list": secrets_list,
|
829
861
|
"sync_method": "DIRECT",
|
830
|
-
"custom_payloads": []
|
862
|
+
"custom_payloads": []
|
831
863
|
}
|
832
864
|
|
833
|
-
|
865
|
+
# args.python_version is already validated in validate_deploy_parameters - so its safe to add in connection_config
|
866
|
+
if args.python_version:
|
867
|
+
connection_config["python_version"] = args.python_version
|
868
|
+
print_library_log(f"Python version {args.python_version} to be used at runtime.", Logging.Level.INFO)
|
869
|
+
|
870
|
+
self.validate_requirements_file(args.project_path, True, args.force)
|
834
871
|
|
835
872
|
group_id, group_name = self.__get_group_info(group, deploy_key)
|
836
873
|
connection_id, service = self.__get_connection_id(connection, group, group_id, deploy_key) or (None, None)
|
@@ -841,17 +878,19 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
|
841
878
|
f"The connection '{connection}' already exists and does not use the 'Connector SDK' service. You cannot update this connection.", Logging.Level.SEVERE)
|
842
879
|
os._exit(1)
|
843
880
|
else:
|
844
|
-
if force:
|
881
|
+
if args.force:
|
845
882
|
confirm = "y"
|
846
883
|
else:
|
847
884
|
confirm = input(
|
848
|
-
f"The connection '{connection}' already exists in the destination '{group}'. Updating it will overwrite the existing code
|
849
|
-
|
885
|
+
f"The connection '{connection}' already exists in the destination '{group}'. Updating it will overwrite the existing code. Do you want to proceed with the update? (Y/N): ")
|
886
|
+
if confirm.lower() == "y" and args.configuration:
|
887
|
+
confirm_config = input(f"Your deploy will overwrite the configuration using the values provided in '{args.configuration}': key-value pairs not present in the new configuration will be removed; existing keys' values set in the cofiguration file or in the dashboard will be overwritten with new (empty or non-empty) values; new key-value pairs will be added. Do you want to proceed with the update? (Y/N): ")
|
888
|
+
if confirm.lower() == "y" and (not connection_config["secrets_list"] or (confirm_config.lower() == "y")):
|
850
889
|
print_library_log("Updating the connection...\n")
|
851
890
|
self.__upload_project(
|
852
|
-
project_path, deploy_key, group_id, group_name, connection)
|
891
|
+
args.project_path, deploy_key, group_id, group_name, connection)
|
853
892
|
self.__update_connection(
|
854
|
-
connection_id, connection, group_name, connection_config, deploy_key)
|
893
|
+
args, connection_id, connection, group_name, connection_config, deploy_key)
|
855
894
|
print("✓")
|
856
895
|
print_library_log(f"Connector ID: {connection_id}")
|
857
896
|
print_library_log(
|
@@ -860,7 +899,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
|
860
899
|
print_library_log("Update canceled. The process is now terminating.")
|
861
900
|
os._exit(1)
|
862
901
|
else:
|
863
|
-
self.__upload_project(project_path, deploy_key,
|
902
|
+
self.__upload_project(args.project_path, deploy_key,
|
864
903
|
group_id, group_name, connection)
|
865
904
|
response = self.__create_connection(
|
866
905
|
deploy_key, group_id, connection_config)
|
@@ -894,17 +933,18 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
|
894
933
|
os._exit(1)
|
895
934
|
|
896
935
|
@staticmethod
|
897
|
-
def __update_connection(id: str, name: str, group: str, config: dict, deploy_key: str):
|
936
|
+
def __update_connection(args: dict, id: str, name: str, group: str, config: dict, deploy_key: str):
|
898
937
|
"""Updates the connection with the given ID, name, group, configuration, and deployment key.
|
899
938
|
|
900
939
|
Args:
|
940
|
+
args (dict): The command arguments.
|
901
941
|
id (str): The connection ID.
|
902
942
|
name (str): The connection name.
|
903
943
|
group (str): The group name.
|
904
944
|
config (dict): The configuration dictionary.
|
905
945
|
deploy_key (str): The deployment key.
|
906
946
|
"""
|
907
|
-
if not
|
947
|
+
if not args.configuration:
|
908
948
|
del config["secrets_list"]
|
909
949
|
|
910
950
|
resp = rq.patch(f"{PRODUCTION_BASE_URL}/v1/connectors/{id}",
|
@@ -1130,7 +1170,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
|
1130
1170
|
|
1131
1171
|
if not resp.ok:
|
1132
1172
|
print_library_log(
|
1133
|
-
f"
|
1173
|
+
f"The request failed with status code: {resp.status_code}. Please ensure you're using a valid base64-encoded API key and try again.", Logging.Level.SEVERE)
|
1134
1174
|
os._exit(1)
|
1135
1175
|
|
1136
1176
|
data = resp.json().get("data", {})
|
@@ -1203,7 +1243,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
|
1203
1243
|
project_path: str = None,
|
1204
1244
|
configuration: dict = None,
|
1205
1245
|
state: dict = None,
|
1206
|
-
log_level: Logging.Level = Logging.Level.FINE)
|
1246
|
+
log_level: Logging.Level = Logging.Level.FINE):
|
1207
1247
|
"""Tests the connector code by running it with the connector tester.\n
|
1208
1248
|
state.json docs: https://fivetran.com/docs/connectors/connector-sdk/detailed-guide#workingwithstatejsonfile\n
|
1209
1249
|
configuration.json docs: https://fivetran.com/docs/connectors/connector-sdk/detailed-guide#workingwithconfigurationjsonfile
|
@@ -1213,9 +1253,6 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
|
1213
1253
|
configuration (dict): The configuration dictionary, same as configuration.json if present.
|
1214
1254
|
state (dict): The state dictionary, same as state.json if present.
|
1215
1255
|
log_level (Logging.Level): The logging level.
|
1216
|
-
|
1217
|
-
Returns:
|
1218
|
-
bool: True if there was an error, False otherwise.
|
1219
1256
|
"""
|
1220
1257
|
global DEBUGGING
|
1221
1258
|
DEBUGGING = True
|
@@ -1412,7 +1449,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
|
1412
1449
|
else:
|
1413
1450
|
try:
|
1414
1451
|
configuration = self.configuration if self.configuration else request.configuration
|
1415
|
-
print_library_log("Initiating the 'schema' method call...", Logging.Level.
|
1452
|
+
print_library_log("Initiating the 'schema' method call...", Logging.Level.INFO)
|
1416
1453
|
response = self.schema_method(configuration)
|
1417
1454
|
self.process_tables(response)
|
1418
1455
|
return connector_sdk_pb2.SchemaResponse(without_schema=common_pb2.TableList(tables=TABLES.values()))
|
@@ -1428,7 +1465,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
|
1428
1465
|
if 'table' not in entry:
|
1429
1466
|
raise ValueError("Entry missing table name: " + entry)
|
1430
1467
|
|
1431
|
-
table_name =
|
1468
|
+
table_name = get_renamed_table_name(entry['table'])
|
1432
1469
|
|
1433
1470
|
if table_name in TABLES:
|
1434
1471
|
raise ValueError("Table already defined: " + table_name)
|
@@ -1447,13 +1484,15 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
|
1447
1484
|
|
1448
1485
|
def process_primary_keys(self, columns, entry):
|
1449
1486
|
for pkey_name in entry["primary_key"]:
|
1450
|
-
|
1487
|
+
column_name = get_renamed_column_name(pkey_name)
|
1488
|
+
column = columns[column_name] if column_name in columns else common_pb2.Column(name=column_name)
|
1451
1489
|
column.primary_key = True
|
1452
|
-
columns[
|
1490
|
+
columns[column_name] = column
|
1453
1491
|
|
1454
1492
|
def process_columns(self, columns, entry):
|
1455
1493
|
for name, type in entry["columns"].items():
|
1456
|
-
|
1494
|
+
column_name = get_renamed_column_name(name)
|
1495
|
+
column = columns[column_name] if column_name in columns else common_pb2.Column(name=column_name)
|
1457
1496
|
|
1458
1497
|
if isinstance(type, str):
|
1459
1498
|
self.process_data_type(column, type)
|
@@ -1471,7 +1510,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
|
1471
1510
|
if "primary_key" in entry and name in entry["primary_key"]:
|
1472
1511
|
column.primary_key = True
|
1473
1512
|
|
1474
|
-
columns[
|
1513
|
+
columns[column_name] = column
|
1475
1514
|
|
1476
1515
|
def process_data_type(self, column, type):
|
1477
1516
|
if type.upper() == "BOOLEAN":
|
@@ -1519,7 +1558,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
|
1519
1558
|
state = self.state if self.state else json.loads(request.state_json)
|
1520
1559
|
|
1521
1560
|
try:
|
1522
|
-
print_library_log("Initiating the 'update' method call...", Logging.Level.
|
1561
|
+
print_library_log("Initiating the 'update' method call...", Logging.Level.INFO)
|
1523
1562
|
for resp in self.update_method(configuration=configuration, state=state):
|
1524
1563
|
if isinstance(resp, list):
|
1525
1564
|
for r in resp:
|
@@ -1636,7 +1675,8 @@ def main():
|
|
1636
1675
|
global EXECUTED_VIA_CLI
|
1637
1676
|
EXECUTED_VIA_CLI = True
|
1638
1677
|
|
1639
|
-
parser = argparse.ArgumentParser(allow_abbrev=False)
|
1678
|
+
parser = argparse.ArgumentParser(allow_abbrev=False, add_help=True)
|
1679
|
+
parser._option_string_actions["-h"].help = "Show this help message and exit"
|
1640
1680
|
|
1641
1681
|
# Positional
|
1642
1682
|
parser.add_argument("command", help="|".join(VALID_COMMANDS))
|
@@ -1645,10 +1685,11 @@ def main():
|
|
1645
1685
|
# Optional (Not all of these are valid with every mutually exclusive option below)
|
1646
1686
|
parser.add_argument("--state", type=str, default=None, help="Provide state as JSON string or file")
|
1647
1687
|
parser.add_argument("--configuration", type=str, default=None, help="Provide secrets as JSON file")
|
1648
|
-
parser.add_argument("--api-key", type=str, default=None, help="Provide
|
1688
|
+
parser.add_argument("--api-key", type=str, default=None, help="Provide your base64-encoded API key for deployment")
|
1649
1689
|
parser.add_argument("--destination", type=str, default=None, help="Destination name (aka 'group name')")
|
1650
1690
|
parser.add_argument("--connection", type=str, default=None, help="Connection name (aka 'destination schema')")
|
1651
1691
|
parser.add_argument("-f", "--force", action="store_true", help="Force update an existing connection")
|
1692
|
+
parser.add_argument("--python-version", "--python", type=str, help=f"Supported Python versions you can use: {SUPPORTED_PYTHON_VERSIONS}. Defaults to 3.12.8")
|
1652
1693
|
|
1653
1694
|
args = parser.parse_args()
|
1654
1695
|
|
@@ -1677,7 +1718,7 @@ def main():
|
|
1677
1718
|
if args.command.lower() == "deploy":
|
1678
1719
|
if args.state:
|
1679
1720
|
print_library_log("'state' parameter is not used for 'deploy' command", Logging.Level.WARNING)
|
1680
|
-
connector_object.deploy(args
|
1721
|
+
connector_object.deploy(args, ft_deploy_key, ft_group, ft_connection, configuration)
|
1681
1722
|
|
1682
1723
|
elif args.command.lower() == "debug":
|
1683
1724
|
connector_object.debug(args.project_path, configuration, state)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: fivetran_connector_sdk
|
3
|
-
Version: 0.13.
|
3
|
+
Version: 0.13.30.1
|
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
|
@@ -15,7 +15,7 @@ Requires-Dist: grpcio==1.60.1
|
|
15
15
|
Requires-Dist: grpcio-tools==1.60.1
|
16
16
|
Requires-Dist: requests==2.32.3
|
17
17
|
Requires-Dist: pipreqs==0.5.0
|
18
|
-
Requires-Dist:
|
18
|
+
Requires-Dist: unidecode==1.3.8
|
19
19
|
|
20
20
|
# **fivetran-connector-sdk**
|
21
21
|
[](https://pepy.tech/project/fivetran-connector-sdk)
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|