fivetran-connector-sdk 2.2.0__py3-none-any.whl → 2.3.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 +17 -2
- fivetran_connector_sdk/connector_helper.py +184 -27
- fivetran_connector_sdk/constants.py +5 -2
- fivetran_connector_sdk/operations.py +10 -6
- {fivetran_connector_sdk-2.2.0.dist-info → fivetran_connector_sdk-2.3.0.dist-info}/METADATA +1 -1
- {fivetran_connector_sdk-2.2.0.dist-info → fivetran_connector_sdk-2.3.0.dist-info}/RECORD +9 -9
- {fivetran_connector_sdk-2.2.0.dist-info → fivetran_connector_sdk-2.3.0.dist-info}/WHEEL +0 -0
- {fivetran_connector_sdk-2.2.0.dist-info → fivetran_connector_sdk-2.3.0.dist-info}/entry_points.txt +0 -0
- {fivetran_connector_sdk-2.2.0.dist-info → fivetran_connector_sdk-2.3.0.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,6 @@
|
|
1
1
|
import os
|
2
2
|
import sys
|
3
|
+
import uuid
|
3
4
|
import grpc
|
4
5
|
import json
|
5
6
|
import shutil
|
@@ -38,12 +39,12 @@ from fivetran_connector_sdk.connector_helper import (
|
|
38
39
|
get_available_port, tester_root_dir_helper,
|
39
40
|
check_dict, check_newer_version, cleanup_uploaded_project,
|
40
41
|
get_destination_group, get_connection_name, get_api_key, get_state,
|
41
|
-
get_python_version, get_hd_agent_id, get_configuration
|
42
|
+
get_python_version, get_hd_agent_id, get_configuration, evaluate_project
|
42
43
|
)
|
43
44
|
|
44
45
|
# Version format: <major_version>.<minor_version>.<patch_version>
|
45
46
|
# (where Major Version = 2, Minor Version is incremental MM from Aug 25 onwards, Patch Version is incremental within a month)
|
46
|
-
__version__ = "2.
|
47
|
+
__version__ = "2.3.0"
|
47
48
|
TESTER_VERSION = TESTER_VER
|
48
49
|
MAX_MESSAGE_LENGTH = 32 * 1024 * 1024 # 32MB
|
49
50
|
|
@@ -65,6 +66,9 @@ class Connector(connector_sdk_pb2_grpc.SourceConnectorServicer):
|
|
65
66
|
|
66
67
|
update_base_url_if_required()
|
67
68
|
|
69
|
+
def evaluate(self, project_path: str, deploy_key: str):
|
70
|
+
evaluate_project(project_path, deploy_key)
|
71
|
+
|
68
72
|
# Call this method to deploy the connector to Fivetran platform
|
69
73
|
def deploy(self, project_path: str, deploy_key: str, group: str, connection: str, hd_agent_id: str,
|
70
74
|
configuration: dict = None, config_path = None, python_version: str = None, force: bool = False):
|
@@ -476,6 +480,12 @@ def main():
|
|
476
480
|
if not connector_object:
|
477
481
|
sys.exit(1)
|
478
482
|
|
483
|
+
if args.command.lower() == "evaluate":
|
484
|
+
print_library_log("Evaluating the connector code...")
|
485
|
+
ft_deploy_key = get_api_key(args)
|
486
|
+
connector_object.evaluate(args.project_path, ft_deploy_key)
|
487
|
+
sys.exit(0)
|
488
|
+
|
479
489
|
if args.command.lower() == "deploy":
|
480
490
|
ft_group = get_destination_group(args)
|
481
491
|
ft_connection = get_connection_name(args)
|
@@ -492,6 +502,8 @@ def main():
|
|
492
502
|
configuration, config_path = get_configuration(args)
|
493
503
|
state = get_state(args)
|
494
504
|
try:
|
505
|
+
os.environ["FIVETRAN_CONNECTION_ID"] = "test_connection_id"
|
506
|
+
os.environ["FIVETRAN_DEPLOYMENT_MODEL"] = "local_debug"
|
495
507
|
connector_object.debug(args.project_path, configuration, state)
|
496
508
|
except subprocess.CalledProcessError as e:
|
497
509
|
print_library_log(f"Connector tester failed with exit code: {e.returncode}", Logging.Level.SEVERE)
|
@@ -499,6 +511,9 @@ def main():
|
|
499
511
|
except Exception as e:
|
500
512
|
print_library_log(f"Debug command failed: {str(e)}", Logging.Level.SEVERE)
|
501
513
|
sys.exit(1)
|
514
|
+
finally:
|
515
|
+
del os.environ["FIVETRAN_CONNECTION_ID"]
|
516
|
+
del os.environ["FIVETRAN_DEPLOYMENT_MODEL"]
|
502
517
|
else:
|
503
518
|
if not suggest_correct_command(args.command):
|
504
519
|
raise NotImplementedError(f"Invalid command: {args.command}, see `fivetran --help`")
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import io
|
1
2
|
import os
|
2
3
|
import re
|
3
4
|
import ast
|
@@ -35,6 +36,7 @@ from fivetran_connector_sdk.constants import (
|
|
35
36
|
CONFIG_FILE,
|
36
37
|
OUTPUT_FILES_DIR,
|
37
38
|
REQUIREMENTS_TXT,
|
39
|
+
CONFIGURATION_JSON,
|
38
40
|
PYPI_PACKAGE_DETAILS_URL,
|
39
41
|
ONE_DAY_IN_SEC,
|
40
42
|
MAX_RETRIES,
|
@@ -48,6 +50,7 @@ from fivetran_connector_sdk.constants import (
|
|
48
50
|
UTF_8,
|
49
51
|
CONNECTION_SCHEMA_NAME_PATTERN,
|
50
52
|
TABLES,
|
53
|
+
EVALUATION_MARKDOWN,
|
51
54
|
)
|
52
55
|
|
53
56
|
def get_destination_group(args):
|
@@ -113,21 +116,7 @@ def get_configuration(args, retrying = 0):
|
|
113
116
|
env_configuration = os.getenv('FIVETRAN_CONFIGURATION', None)
|
114
117
|
try:
|
115
118
|
if not configuration and not args.force and args.command.lower() == "deploy":
|
116
|
-
|
117
|
-
if not retrying:
|
118
|
-
json_filepath = os.path.join(args.project_path, "configuration.json")
|
119
|
-
if os.path.exists(json_filepath):
|
120
|
-
print_library_log("configuration.json file detected in the project, "
|
121
|
-
"but no configuration input provided via the command line", Logging.Level.WARNING)
|
122
|
-
env_configuration = env_configuration if env_configuration else "configuration.json"
|
123
|
-
confirm = input(f"Does this debug run/deploy need configuration (y/N):")
|
124
|
-
if confirm.lower()=='y':
|
125
|
-
configuration = get_input_from_cli("Provide the configuration file path", env_configuration)
|
126
|
-
config_values = validate_and_load_configuration(args.project_path, configuration)
|
127
|
-
return config_values, configuration
|
128
|
-
else:
|
129
|
-
print_library_log("No input required for configuration. Continuing without configuration.", Logging.Level.INFO)
|
130
|
-
return {}, None
|
119
|
+
return _deploy_config_flow(args, env_configuration, retrying)
|
131
120
|
config_values = validate_and_load_configuration(args.project_path, configuration)
|
132
121
|
return config_values, configuration
|
133
122
|
except ValueError as e:
|
@@ -179,11 +168,43 @@ def tester_root_dir_helper() -> str:
|
|
179
168
|
"""Returns the root directory for the tester."""
|
180
169
|
return os.path.join(os.path.expanduser("~"), ROOT_LOCATION)
|
181
170
|
|
171
|
+
|
172
|
+
def _deploy_config_flow(args, env_configuration, retrying):
|
173
|
+
"""Handles the configuration flow for the deploy command."""
|
174
|
+
confirm = 'y'
|
175
|
+
if not retrying:
|
176
|
+
json_filepath = os.path.join(args.project_path, CONFIGURATION_JSON)
|
177
|
+
if os.path.exists(json_filepath):
|
178
|
+
print_library_log("configuration.json file detected in the project, "
|
179
|
+
"but no configuration input provided via the command line", Logging.Level.WARNING)
|
180
|
+
env_configuration = env_configuration if env_configuration else CONFIGURATION_JSON
|
181
|
+
confirm = input("Does this debug run/deploy need configuration (y/N):")
|
182
|
+
if confirm.lower() == 'y':
|
183
|
+
configuration = get_input_from_cli("Provide the configuration file path", env_configuration)
|
184
|
+
config_values = validate_and_load_configuration(args.project_path, configuration)
|
185
|
+
return config_values, configuration
|
186
|
+
else:
|
187
|
+
print_library_log("No input required for configuration. Continuing without configuration.", Logging.Level.INFO)
|
188
|
+
return {}, None
|
189
|
+
|
190
|
+
|
182
191
|
def _warn_exit_usage(filename, line_no, func):
|
183
192
|
print_library_log(f"Avoid using {func} to exit from the Python code as this can cause the connector to become stuck. Throw an error if required " +
|
184
193
|
f"at: {filename}:{line_no}. See the Technical Reference for details: https://fivetran.com/docs/connector-sdk/technical-reference#handlingexceptions",
|
185
194
|
Logging.Level.WARNING)
|
186
195
|
|
196
|
+
|
197
|
+
def _check_and_warn_attribute_exit(node):
|
198
|
+
"""Checks for the presence of 'exit()' in the AST node and warns if found."""
|
199
|
+
if isinstance(node.func, ast.Name) and node.func.id == "exit":
|
200
|
+
_warn_exit_usage(ROOT_FILENAME, node.lineno, "exit()")
|
201
|
+
elif isinstance(node.func, ast.Attribute) and isinstance(node.func.value, ast.Name):
|
202
|
+
if node.func.attr == "_exit" and node.func.value.id == "os":
|
203
|
+
_warn_exit_usage(ROOT_FILENAME, node.lineno, "os._exit()")
|
204
|
+
if node.func.attr == "exit" and node.func.value.id == "sys":
|
205
|
+
_warn_exit_usage(ROOT_FILENAME, node.lineno, "sys.exit()")
|
206
|
+
|
207
|
+
|
187
208
|
def exit_check(project_path):
|
188
209
|
"""Checks for the presence of 'exit()' in the calling code.
|
189
210
|
Args:
|
@@ -199,13 +220,7 @@ def exit_check(project_path):
|
|
199
220
|
tree = ast.parse(f.read())
|
200
221
|
for node in ast.walk(tree):
|
201
222
|
if isinstance(node, ast.Call):
|
202
|
-
|
203
|
-
_warn_exit_usage(ROOT_FILENAME, node.lineno, "exit()")
|
204
|
-
elif isinstance(node.func, ast.Attribute) and isinstance(node.func.value, ast.Name):
|
205
|
-
if node.func.attr == "_exit" and node.func.value.id == "os":
|
206
|
-
_warn_exit_usage(ROOT_FILENAME, node.lineno, "os._exit()")
|
207
|
-
if node.func.attr == "exit" and node.func.value.id == "sys":
|
208
|
-
_warn_exit_usage(ROOT_FILENAME, node.lineno, "sys.exit()")
|
223
|
+
_check_and_warn_attribute_exit(node)
|
209
224
|
except SyntaxError as e:
|
210
225
|
print_library_log(f"SyntaxError in {ROOT_FILENAME}: {e}", Logging.Level.SEVERE)
|
211
226
|
|
@@ -458,6 +473,7 @@ def load_or_add_requirements_file(requirements_file_path):
|
|
458
473
|
requirements = fetch_requirements_as_dict(requirements_file_path)
|
459
474
|
else:
|
460
475
|
with open(requirements_file_path, 'w', encoding=UTF_8):
|
476
|
+
# Intentional empty block: Creating an empty requirements.txt file
|
461
477
|
pass
|
462
478
|
requirements = {}
|
463
479
|
print_library_log("`requirements.txt` file not found in your project folder.", Logging.Level.WARNING)
|
@@ -476,6 +492,19 @@ def remove_unwanted_packages(requirements: dict):
|
|
476
492
|
if requirements.get('requests') is not None:
|
477
493
|
requirements.pop("requests")
|
478
494
|
|
495
|
+
def evaluate_project(project_path: str, deploy_key: str):
|
496
|
+
print_library_log(f"Evaluating '{project_path}'...")
|
497
|
+
upload_file_path = create_upload_file(project_path)
|
498
|
+
try:
|
499
|
+
evaluation_report = evaluate(upload_file_path, project_path, deploy_key)
|
500
|
+
if not evaluation_report:
|
501
|
+
print_library_log(
|
502
|
+
"Project evaluation failed. No evaluation report was generated. Please check your project for errors and try again.",
|
503
|
+
Logging.Level.SEVERE
|
504
|
+
)
|
505
|
+
sys.exit(1)
|
506
|
+
finally:
|
507
|
+
delete_file_if_exists(upload_file_path)
|
479
508
|
|
480
509
|
def upload_project(project_path: str, deploy_key: str, group_id: str, group_name: str, connection: str):
|
481
510
|
print_library_log(
|
@@ -672,6 +701,15 @@ def zip_folder(project_path: str) -> str:
|
|
672
701
|
|
673
702
|
return upload_filepath
|
674
703
|
|
704
|
+
def _should_descend_into_dir(name, path):
|
705
|
+
"""Determines whether to traverse into a subdirectory."""
|
706
|
+
if not os.path.isdir(path):
|
707
|
+
return False
|
708
|
+
if name in EXCLUDED_DIRS or name.startswith("."):
|
709
|
+
return False
|
710
|
+
if VIRTUAL_ENV_CONFIG in os.listdir(path): # Check for virtual env indicator
|
711
|
+
return False
|
712
|
+
return True
|
675
713
|
|
676
714
|
def dir_walker(top):
|
677
715
|
"""Walks the directory tree starting at the given top directory.
|
@@ -685,10 +723,8 @@ def dir_walker(top):
|
|
685
723
|
dirs, files = [], []
|
686
724
|
for name in os.listdir(top):
|
687
725
|
path = os.path.join(top, name)
|
688
|
-
if
|
689
|
-
|
690
|
-
if VIRTUAL_ENV_CONFIG not in os.listdir(path): # Check for virtual env indicator
|
691
|
-
dirs.append(name)
|
726
|
+
if _should_descend_into_dir(name, path):
|
727
|
+
dirs.append(name)
|
692
728
|
else:
|
693
729
|
# Include all files if in `drivers` folder
|
694
730
|
if os.path.basename(top) == DRIVERS:
|
@@ -702,6 +738,126 @@ def dir_walker(top):
|
|
702
738
|
for x in dir_walker(new_path):
|
703
739
|
yield x
|
704
740
|
|
741
|
+
|
742
|
+
def render_section(lines, title, items):
|
743
|
+
"""
|
744
|
+
Renders a section of the markdown report.
|
745
|
+
Args:
|
746
|
+
lines (list): The list of lines to append to.
|
747
|
+
title (str): The title of the section.
|
748
|
+
items (list): The list of items in the section.
|
749
|
+
"""
|
750
|
+
lines.extend([f"## {title}", ""])
|
751
|
+
if not items:
|
752
|
+
lines.extend(["_No recommendations._", ""])
|
753
|
+
return
|
754
|
+
for index, item in enumerate(items, 1):
|
755
|
+
issue = item.get("issue", "(missing issue)")
|
756
|
+
lines.append(f"### {index}. {issue}")
|
757
|
+
if recommendation := item.get("recommendation"):
|
758
|
+
lines.append(f"- **Recommendation:** {recommendation}")
|
759
|
+
if current_code := item.get("current_code"):
|
760
|
+
fenced_code = f"```python\n{current_code.rstrip()}\n```"
|
761
|
+
lines += ["\n**Current Code:**", fenced_code]
|
762
|
+
if fix := item.get("code_fix"):
|
763
|
+
fenced_code = f"```python\n{fix.rstrip()}\n```"
|
764
|
+
lines += ["\n**Code Fix:**", fenced_code]
|
765
|
+
lines.append("")
|
766
|
+
|
767
|
+
|
768
|
+
def render_markdown(report):
|
769
|
+
"""
|
770
|
+
Renders the evaluation report as markdown.
|
771
|
+
Args:
|
772
|
+
report (dict): The evaluation report.
|
773
|
+
"""
|
774
|
+
lines = []
|
775
|
+
lines += ["# Evaluation Report", ""]
|
776
|
+
overall_score = report.get("score")
|
777
|
+
if overall_score:
|
778
|
+
lines += [f"**Overall Score:** {overall_score}", ""]
|
779
|
+
|
780
|
+
subscore = report.get("subscores") or {}
|
781
|
+
if subscore:
|
782
|
+
lines += ["## Subscores", "", "| Metric | Score |", "|---|---:|"]
|
783
|
+
lines += [f"| {key} | {value} |" for key, value in subscore.items()]
|
784
|
+
lines.append("")
|
785
|
+
|
786
|
+
render_section(lines, "Required", report.get("required"))
|
787
|
+
render_section(lines, "Good to Have", report.get("good_to_have"))
|
788
|
+
|
789
|
+
return "\n".join(lines)
|
790
|
+
|
791
|
+
|
792
|
+
def save_evaluation_report(evaluation_report, project_path: str):
|
793
|
+
"""Saves the evaluation report to a file in the project path.
|
794
|
+
Args:
|
795
|
+
evaluation_report: The evaluation data.
|
796
|
+
project_path: The path to the project.
|
797
|
+
"""
|
798
|
+
# Save evaluation report
|
799
|
+
working_dir = os.path.join(project_path, OUTPUT_FILES_DIR)
|
800
|
+
os.makedirs(working_dir, exist_ok=True)
|
801
|
+
report_path = os.path.join(working_dir, EVALUATION_MARKDOWN)
|
802
|
+
markdown_content = render_markdown(evaluation_report)
|
803
|
+
try:
|
804
|
+
with open(report_path, 'w', encoding=UTF_8) as report_file:
|
805
|
+
report_file.write(markdown_content)
|
806
|
+
print_library_log(f"Evaluation report saved to {report_path}")
|
807
|
+
except Exception as e:
|
808
|
+
print_library_log(f"Failed to save evaluation report: {e}", Logging.Level.SEVERE)
|
809
|
+
|
810
|
+
|
811
|
+
def evaluate(local_path: str, project_path:str, deploy_key: str):
|
812
|
+
"""Uploads the local code file for evaluation and returns the evaluation report.
|
813
|
+
|
814
|
+
The server responds with a file containing JSON. This function streams the
|
815
|
+
file safely, parses the JSON, and prints it.
|
816
|
+
|
817
|
+
Args:
|
818
|
+
local_path (str): The local file path.
|
819
|
+
project_path (str): The path to the project.
|
820
|
+
deploy_key (str): The deployment key.
|
821
|
+
|
822
|
+
Returns:
|
823
|
+
dict | None: Parsed evaluation report if successful, None otherwise.
|
824
|
+
"""
|
825
|
+
print_library_log("Uploading your project for evaluation...")
|
826
|
+
|
827
|
+
with open(local_path, 'rb') as f:
|
828
|
+
response = rq.post(
|
829
|
+
f"{constants.PRODUCTION_BASE_URL}{constants.EVALUATE_ENDPOINT}",
|
830
|
+
files={'file': f},
|
831
|
+
headers={"Authorization": f"Basic {deploy_key}"},
|
832
|
+
stream=True # stream to handle file responses safely
|
833
|
+
)
|
834
|
+
|
835
|
+
if response.ok:
|
836
|
+
try:
|
837
|
+
buffer = io.BytesIO()
|
838
|
+
for chunk in response.iter_content(chunk_size=8192):
|
839
|
+
buffer.write(chunk)
|
840
|
+
buffer.seek(0)
|
841
|
+
|
842
|
+
evaluation_data = json.load(buffer)
|
843
|
+
|
844
|
+
print_library_log("✓ Evaluation Report:")
|
845
|
+
print(json.dumps(evaluation_data, indent=4))
|
846
|
+
save_evaluation_report(evaluation_data, project_path)
|
847
|
+
|
848
|
+
return evaluation_data
|
849
|
+
|
850
|
+
except json.JSONDecodeError as e:
|
851
|
+
print_library_log(f"Failed to parse evaluation report: {e}", Logging.Level.SEVERE)
|
852
|
+
return None
|
853
|
+
|
854
|
+
print_library_log(
|
855
|
+
f"Unable to upload the project for evaluation, failed with error: {response.text}",
|
856
|
+
Logging.Level.SEVERE
|
857
|
+
)
|
858
|
+
return None
|
859
|
+
|
860
|
+
|
705
861
|
def upload(local_path: str, deploy_key: str, group_id: str, connection: str) -> bool:
|
706
862
|
"""Uploads the local code file for the specified group and connection.
|
707
863
|
|
@@ -722,7 +878,8 @@ def upload(local_path: str, deploy_key: str, group_id: str, connection: str) ->
|
|
722
878
|
print("✓")
|
723
879
|
return True
|
724
880
|
|
725
|
-
|
881
|
+
error_message = response.reason + ": " + json.loads(response.text).get("message", "")
|
882
|
+
print_library_log(f"Unable to upload the project, failed with error. {error_message}", Logging.Level.SEVERE)
|
726
883
|
return False
|
727
884
|
|
728
885
|
def cleanup_uploaded_code(deploy_key: str, group_id: str, connection: str) -> bool:
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import os
|
2
2
|
import re
|
3
3
|
|
4
|
-
TESTER_VER = "2.25.
|
4
|
+
TESTER_VER = "2.25.0918.001"
|
5
5
|
|
6
6
|
WIN_OS = "windows"
|
7
7
|
ARM_64 = "arm64"
|
@@ -33,9 +33,11 @@ ROOT_LOCATION = ".ft_sdk_connector_tester"
|
|
33
33
|
CONFIG_FILE = "_config.json"
|
34
34
|
OUTPUT_FILES_DIR = "files"
|
35
35
|
REQUIREMENTS_TXT = "requirements.txt"
|
36
|
+
EVALUATION_MARKDOWN = "evaluation_report.md"
|
37
|
+
CONFIGURATION_JSON = "configuration.json"
|
36
38
|
PYPI_PACKAGE_DETAILS_URL = "https://pypi.org/pypi/fivetran_connector_sdk/json"
|
37
39
|
ONE_DAY_IN_SEC = 24 * 60 * 60
|
38
|
-
CHECKPOINT_OP_TIMEOUT_IN_SEC =
|
40
|
+
CHECKPOINT_OP_TIMEOUT_IN_SEC = 120 # seconds
|
39
41
|
MAX_RETRIES = 3
|
40
42
|
LOGGING_PREFIX = "Fivetran-Connector-SDK"
|
41
43
|
LOGGING_DELIMITER = ": "
|
@@ -64,6 +66,7 @@ COMMANDS_AND_SYNONYMS = {
|
|
64
66
|
|
65
67
|
CONNECTION_SCHEMA_NAME_PATTERN = r'^[_a-z][_a-z0-9]*$'
|
66
68
|
PRODUCTION_BASE_URL = "https://api.fivetran.com"
|
69
|
+
EVALUATE_ENDPOINT = "/v1/evaluate"
|
67
70
|
INSTALLATION_SCRIPT_MISSING_MESSAGE = "The 'installation.sh' file is missing in the 'drivers' directory. Please ensure that 'installation.sh' is present to properly configure drivers."
|
68
71
|
INSTALLATION_SCRIPT = "installation.sh"
|
69
72
|
DRIVERS = "drivers"
|
@@ -150,7 +150,6 @@ def _get_table_pk(table: str) -> bool:
|
|
150
150
|
Returns:
|
151
151
|
dict: The columns for the table.
|
152
152
|
"""
|
153
|
-
columns = {}
|
154
153
|
if table in TABLES:
|
155
154
|
for column in TABLES[table].columns:
|
156
155
|
if column.primary_key:
|
@@ -179,15 +178,20 @@ def _map_data_to_columns(data: dict, columns: dict, table: str = "") -> dict:
|
|
179
178
|
map_inferred_data_type(key, mapped_data, v, table)
|
180
179
|
return mapped_data
|
181
180
|
|
181
|
+
def _log_boolean_inference_once(table):
|
182
|
+
"""Log boolean inference once per table and mark it as logged."""
|
183
|
+
key = f"boolean_{table}"
|
184
|
+
if _LOG_DATA_TYPE_INFERENCE.get(key, True):
|
185
|
+
print_library_log("Fivetran: Boolean Datatype has been inferred for " + table, Logging.Level.INFO, True)
|
186
|
+
if not _get_table_pk(table):
|
187
|
+
print_library_log("Fivetran: Boolean Datatype inference issue for " + table, Logging.Level.INFO, True)
|
188
|
+
_LOG_DATA_TYPE_INFERENCE[key] = False
|
189
|
+
|
182
190
|
def map_inferred_data_type(k, mapped_data, v, table=""):
|
183
191
|
# We can infer type from the value
|
184
192
|
if isinstance(v, bool):
|
185
193
|
mapped_data[k] = common_pb2.ValueType(bool=v)
|
186
|
-
|
187
|
-
print_library_log("Fivetran: Boolean Datatype has been inferred for " + table, Logging.Level.INFO, True)
|
188
|
-
if not _get_table_pk(table):
|
189
|
-
print_library_log("Fivetran: Boolean Datatype inference issue for " + table, Logging.Level.INFO, True)
|
190
|
-
_LOG_DATA_TYPE_INFERENCE["boolean_" + table] = False
|
194
|
+
_log_boolean_inference_once(table)
|
191
195
|
elif isinstance(v, int):
|
192
196
|
if abs(v) > JAVA_LONG_MAX_VALUE:
|
193
197
|
mapped_data[k] = common_pb2.ValueType(float=v)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: fivetran_connector_sdk
|
3
|
-
Version: 2.
|
3
|
+
Version: 2.3.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,10 +1,10 @@
|
|
1
|
-
fivetran_connector_sdk/__init__.py,sha256=
|
2
|
-
fivetran_connector_sdk/connector_helper.py,sha256=
|
3
|
-
fivetran_connector_sdk/constants.py,sha256=
|
1
|
+
fivetran_connector_sdk/__init__.py,sha256=zp6iAJh22DP3s-1v7EF3K0SAnrWjVx1Zz_-FxcHZKHs,23809
|
2
|
+
fivetran_connector_sdk/connector_helper.py,sha256=UMI743HxfVKJMkQhDD2QNFfqOqicqJ6BqyqL6UjcDuo,48462
|
3
|
+
fivetran_connector_sdk/constants.py,sha256=4fVdH9BaJAPIHpEe1crYn3Cz1TFJ0GJIyfgHG8tw9UA,2564
|
4
4
|
fivetran_connector_sdk/helpers.py,sha256=7YVB1JQ9T0hg90Z0pjJxFp0pQzeBfefrfvS4SYtrlv4,15254
|
5
5
|
fivetran_connector_sdk/logger.py,sha256=ud8v8-mKx65OAPaZvxBqt2-CU0vjgBeiYwuiqsYh_hA,3063
|
6
6
|
fivetran_connector_sdk/operation_stream.py,sha256=DXLDv961xZ_GVSEPUFLtZy0IEf_ayQSEXFpEJp-CAu4,6194
|
7
|
-
fivetran_connector_sdk/operations.py,sha256=
|
7
|
+
fivetran_connector_sdk/operations.py,sha256=ce43mOBYE2IYxuc8oaG-PXSxjY-iGKMhYWN3bBHUq18,12416
|
8
8
|
fivetran_connector_sdk/protos/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
9
9
|
fivetran_connector_sdk/protos/common_pb2.py,sha256=zkzs6Rd-lvsev6Nsq37xc4HLJZ_uNXPkotCLY7Y7i5U,8770
|
10
10
|
fivetran_connector_sdk/protos/common_pb2.pyi,sha256=FdqlPKRqiXdUDT3e7adP5X42_Qzv_ItydUNJFKnJJIE,11478
|
@@ -12,8 +12,8 @@ fivetran_connector_sdk/protos/common_pb2_grpc.py,sha256=qni6h6BoA1nwJXr2bNtznfTk
|
|
12
12
|
fivetran_connector_sdk/protos/connector_sdk_pb2.py,sha256=Inv87MlK5Q56GNvMNFQHyqIePDMKnkW9y_BrT9DgPck,7835
|
13
13
|
fivetran_connector_sdk/protos/connector_sdk_pb2.pyi,sha256=3AC-bK6ZM-Bmr_RETOB3y_0u4ATWlwcbHzqVanDuOB0,8115
|
14
14
|
fivetran_connector_sdk/protos/connector_sdk_pb2_grpc.py,sha256=bGlvc_vGwA9-FTqrj-BYlVcA-7jS8A9MSZ-XpZFytvY,8795
|
15
|
-
fivetran_connector_sdk-2.
|
16
|
-
fivetran_connector_sdk-2.
|
17
|
-
fivetran_connector_sdk-2.
|
18
|
-
fivetran_connector_sdk-2.
|
19
|
-
fivetran_connector_sdk-2.
|
15
|
+
fivetran_connector_sdk-2.3.0.dist-info/METADATA,sha256=UuKyXswGsJPKY7FOLdALqiLKFn6wHLgUm8zyi7FjljA,3197
|
16
|
+
fivetran_connector_sdk-2.3.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
17
|
+
fivetran_connector_sdk-2.3.0.dist-info/entry_points.txt,sha256=uQn0KPnFlQmXJfxlk0tifdNsSXWfVlnAFzNqjXZM_xM,57
|
18
|
+
fivetran_connector_sdk-2.3.0.dist-info/top_level.txt,sha256=-_xk2MFY4psIh7jw1lJePMzFb5-vask8_ZtX-UzYWUI,23
|
19
|
+
fivetran_connector_sdk-2.3.0.dist-info/RECORD,,
|
File without changes
|
{fivetran_connector_sdk-2.2.0.dist-info → fivetran_connector_sdk-2.3.0.dist-info}/entry_points.txt
RENAMED
File without changes
|
{fivetran_connector_sdk-2.2.0.dist-info → fivetran_connector_sdk-2.3.0.dist-info}/top_level.txt
RENAMED
File without changes
|