fivetran-connector-sdk 0.12.12.1__tar.gz → 0.13.10.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.12.12.1 → fivetran_connector_sdk-0.13.10.1}/PKG-INFO +2 -2
- {fivetran_connector_sdk-0.12.12.1 → fivetran_connector_sdk-0.13.10.1}/src/fivetran_connector_sdk/__init__.py +86 -29
- {fivetran_connector_sdk-0.12.12.1 → fivetran_connector_sdk-0.13.10.1}/src/fivetran_connector_sdk.egg-info/PKG-INFO +2 -2
- {fivetran_connector_sdk-0.12.12.1 → fivetran_connector_sdk-0.13.10.1}/README.md +0 -0
- {fivetran_connector_sdk-0.12.12.1 → fivetran_connector_sdk-0.13.10.1}/pyproject.toml +0 -0
- {fivetran_connector_sdk-0.12.12.1 → fivetran_connector_sdk-0.13.10.1}/setup.cfg +0 -0
- {fivetran_connector_sdk-0.12.12.1 → fivetran_connector_sdk-0.13.10.1}/src/fivetran_connector_sdk/protos/__init__.py +0 -0
- {fivetran_connector_sdk-0.12.12.1 → fivetran_connector_sdk-0.13.10.1}/src/fivetran_connector_sdk/protos/common_pb2.py +0 -0
- {fivetran_connector_sdk-0.12.12.1 → fivetran_connector_sdk-0.13.10.1}/src/fivetran_connector_sdk/protos/common_pb2.pyi +0 -0
- {fivetran_connector_sdk-0.12.12.1 → fivetran_connector_sdk-0.13.10.1}/src/fivetran_connector_sdk/protos/common_pb2_grpc.py +0 -0
- {fivetran_connector_sdk-0.12.12.1 → fivetran_connector_sdk-0.13.10.1}/src/fivetran_connector_sdk/protos/connector_sdk_pb2.py +0 -0
- {fivetran_connector_sdk-0.12.12.1 → fivetran_connector_sdk-0.13.10.1}/src/fivetran_connector_sdk/protos/connector_sdk_pb2.pyi +0 -0
- {fivetran_connector_sdk-0.12.12.1 → fivetran_connector_sdk-0.13.10.1}/src/fivetran_connector_sdk/protos/connector_sdk_pb2_grpc.py +0 -0
- {fivetran_connector_sdk-0.12.12.1 → fivetran_connector_sdk-0.13.10.1}/src/fivetran_connector_sdk.egg-info/SOURCES.txt +0 -0
- {fivetran_connector_sdk-0.12.12.1 → fivetran_connector_sdk-0.13.10.1}/src/fivetran_connector_sdk.egg-info/dependency_links.txt +0 -0
- {fivetran_connector_sdk-0.12.12.1 → fivetran_connector_sdk-0.13.10.1}/src/fivetran_connector_sdk.egg-info/entry_points.txt +0 -0
- {fivetran_connector_sdk-0.12.12.1 → fivetran_connector_sdk-0.13.10.1}/src/fivetran_connector_sdk.egg-info/requires.txt +0 -0
- {fivetran_connector_sdk-0.12.12.1 → fivetran_connector_sdk-0.13.10.1}/src/fivetran_connector_sdk.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.2
|
2
2
|
Name: fivetran_connector_sdk
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.13.10.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
|
@@ -26,7 +26,8 @@ from fivetran_connector_sdk.protos import common_pb2
|
|
26
26
|
from fivetran_connector_sdk.protos import connector_sdk_pb2
|
27
27
|
from fivetran_connector_sdk.protos import connector_sdk_pb2_grpc
|
28
28
|
|
29
|
-
|
29
|
+
# Version format: <major_version>.<MM>.<DD>.<iteration> (where MM is incremental value from Jan 2024)
|
30
|
+
__version__ = "0.13.10.1"
|
30
31
|
|
31
32
|
MAC_OS = "mac"
|
32
33
|
WIN_OS = "windows"
|
@@ -38,6 +39,7 @@ VERSION_FILENAME = "version.txt"
|
|
38
39
|
UPLOAD_FILENAME = "code.zip"
|
39
40
|
LAST_VERSION_CHECK_FILE = "_last_version_check"
|
40
41
|
ROOT_LOCATION = ".ft_sdk_connector_tester"
|
42
|
+
CONFIG_FILE = "_config.json"
|
41
43
|
OUTPUT_FILES_DIR = "files"
|
42
44
|
REQUIREMENTS_TXT = "requirements.txt"
|
43
45
|
PYPI_PACKAGE_DETAILS_URL = "https://pypi.org/pypi/fivetran_connector_sdk/json"
|
@@ -57,6 +59,7 @@ COMMANDS_AND_SYNONYMS = {
|
|
57
59
|
CONNECTION_SCHEMA_NAME_PATTERN = r'^[_a-z][_a-z0-9]*$'
|
58
60
|
DEBUGGING = False
|
59
61
|
EXECUTED_VIA_CLI = False
|
62
|
+
PRODUCTION_BASE_URL = "https://api.fivetran.com"
|
60
63
|
TABLES = {}
|
61
64
|
|
62
65
|
JAVA_LONG_MAX_VALUE = 9223372036854775807
|
@@ -463,7 +466,6 @@ def log_unused_deps_error(package_name: str, version: str):
|
|
463
466
|
print_library_log(f"Please remove `{package_name}` from requirements.txt."
|
464
467
|
f" The latest version of `{package_name}` is always available when executing your code."
|
465
468
|
f" Current version: {version}", Logging.Level.SEVERE)
|
466
|
-
os._exit(1)
|
467
469
|
|
468
470
|
|
469
471
|
def validate_deploy_parameters(connection, deploy_key):
|
@@ -508,6 +510,18 @@ def get_available_port():
|
|
508
510
|
return None
|
509
511
|
|
510
512
|
|
513
|
+
def update_base_url_if_required():
|
514
|
+
config_file_path = os.path.join(_tester_root_dir(), CONFIG_FILE)
|
515
|
+
if os.path.isfile(config_file_path):
|
516
|
+
with open(config_file_path, 'r') as f:
|
517
|
+
data = json.load(f)
|
518
|
+
base_url = data.get('production_base_url')
|
519
|
+
if base_url is not None:
|
520
|
+
global PRODUCTION_BASE_URL
|
521
|
+
PRODUCTION_BASE_URL = base_url
|
522
|
+
print_library_log(f"Updating PRODUCTION_BASE_URL to: {base_url}")
|
523
|
+
|
524
|
+
|
511
525
|
class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
512
526
|
def __init__(self, update, schema=None):
|
513
527
|
"""Initializes the Connector instance.
|
@@ -522,6 +536,8 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
|
522
536
|
self.configuration = None
|
523
537
|
self.state = None
|
524
538
|
|
539
|
+
update_base_url_if_required()
|
540
|
+
|
525
541
|
@staticmethod
|
526
542
|
def fetch_requirements_from_file(file_path: str) -> list[str]:
|
527
543
|
"""Reads a requirements file and returns a list of dependencies.
|
@@ -558,7 +574,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
|
558
574
|
print_library_log(f"Invalid requirement format: '{requirement}'", Logging.Level.SEVERE)
|
559
575
|
return requirements_dict
|
560
576
|
|
561
|
-
def validate_requirements_file(self, project_path: str, is_deploy: bool):
|
577
|
+
def validate_requirements_file(self, project_path: str, is_deploy: bool, force: bool = False):
|
562
578
|
"""Validates the `requirements.txt` file against the project's actual dependencies.
|
563
579
|
|
564
580
|
This method generates a temporary requirements file using `pipreqs`, compares
|
@@ -569,10 +585,22 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
|
569
585
|
Args:
|
570
586
|
project_path (str): The path to the project directory containing the `requirements.txt`.
|
571
587
|
is_deploy (bool): If `True`, the method will exit the process on critical errors.
|
588
|
+
force (bool): Force update an existing connection.
|
572
589
|
|
573
590
|
"""
|
574
|
-
|
575
|
-
|
591
|
+
# Run the pipreqs command and capture stderr
|
592
|
+
result = subprocess.run(
|
593
|
+
["pipreqs", "--savepath", "tmp_requirements.txt", "--ignore"] + EXCLUDED_PIPREQS_DIRS,
|
594
|
+
stderr=subprocess.PIPE,
|
595
|
+
text=True # Ensures output is in string format
|
596
|
+
)
|
597
|
+
|
598
|
+
if result.returncode != 0:
|
599
|
+
print_library_log("pipreqs failed with:", Logging.Level.SEVERE)
|
600
|
+
print(result.stderr)
|
601
|
+
sys.exit(1)
|
602
|
+
|
603
|
+
# tmp_requirements is only generated when pipreqs command is successful
|
576
604
|
tmp_requirements_file_path = os.path.join(project_path, 'tmp_requirements.txt')
|
577
605
|
|
578
606
|
tmp_requirements = self.fetch_requirements_as_dict(self, tmp_requirements_file_path)
|
@@ -581,6 +609,11 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
|
581
609
|
tmp_requirements.pop("requests")
|
582
610
|
os.remove(tmp_requirements_file_path)
|
583
611
|
|
612
|
+
# remove corrupt requirements listed by pipreqs
|
613
|
+
corrupt_requirements = [key for key in tmp_requirements if key.startswith("~")]
|
614
|
+
for requirement in corrupt_requirements:
|
615
|
+
del tmp_requirements[requirement]
|
616
|
+
|
584
617
|
if len(tmp_requirements) > 0:
|
585
618
|
requirements = self.load_or_add_requirements_file(project_path)
|
586
619
|
|
@@ -593,11 +626,26 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
|
593
626
|
|
594
627
|
missing_deps = {key: tmp_requirements[key] for key in (tmp_requirements.keys() - requirements.keys())}
|
595
628
|
if missing_deps:
|
596
|
-
self.handle_missing_deps(
|
629
|
+
self.handle_missing_deps(missing_deps)
|
597
630
|
|
598
631
|
unused_deps = list(requirements.keys() - tmp_requirements.keys())
|
599
632
|
if unused_deps:
|
600
633
|
self.handle_unused_deps(unused_deps)
|
634
|
+
|
635
|
+
if is_deploy and (version_mismatch_deps or missing_deps or unused_deps):
|
636
|
+
if force:
|
637
|
+
confirm = "y"
|
638
|
+
else:
|
639
|
+
confirm = input("We detected issues in your requirements.txt. "
|
640
|
+
"Would you like us to update it to reflect the necessary changes? (Y/N):")
|
641
|
+
if confirm.lower() == "y":
|
642
|
+
with open(REQUIREMENTS_TXT, "w") as file:
|
643
|
+
file.write("\n".join(tmp_requirements.values()))
|
644
|
+
print_library_log(f"`{REQUIREMENTS_TXT}` has been updated successfully")
|
645
|
+
else:
|
646
|
+
if missing_deps or 'fivetran_connector_sdk' in unused_deps or 'requests' in unused_deps:
|
647
|
+
print_library_log(f"Please fix your {REQUIREMENTS_TXT} file to proceed with deployment.")
|
648
|
+
os._exit(1)
|
601
649
|
else:
|
602
650
|
if os.path.exists(REQUIREMENTS_TXT):
|
603
651
|
print_library_log("`requirements.txt` is not required as no additional "
|
@@ -616,15 +664,13 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
|
616
664
|
"they are not used or already installed. Please remove them from requirements.txt:")
|
617
665
|
print(*unused_deps)
|
618
666
|
|
619
|
-
def handle_missing_deps(self,
|
667
|
+
def handle_missing_deps(self, missing_deps):
|
620
668
|
print_library_log("Please include the following dependency libraries in requirements.txt, to be used by "
|
621
669
|
"Fivetran production. "
|
622
670
|
"For more information, please visit: "
|
623
671
|
"https://fivetran.com/docs/connectors/connector-sdk/detailed-guide"
|
624
672
|
"#workingwithrequirementstxtfile", Logging.Level.SEVERE)
|
625
673
|
print(*list(missing_deps.values()))
|
626
|
-
if is_deploy:
|
627
|
-
os._exit(1)
|
628
674
|
|
629
675
|
def load_or_add_requirements_file(self, project_path):
|
630
676
|
if os.path.exists(REQUIREMENTS_TXT):
|
@@ -669,7 +715,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
|
669
715
|
"custom_payloads": [],
|
670
716
|
}
|
671
717
|
|
672
|
-
self.validate_requirements_file(project_path, True)
|
718
|
+
self.validate_requirements_file(project_path, True, force)
|
673
719
|
|
674
720
|
group_id, group_name = self.__get_group_info(group, deploy_key)
|
675
721
|
connection_id, service = self.__get_connection_id(connection, group, group_id, deploy_key) or (None, None)
|
@@ -746,7 +792,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
|
746
792
|
if not config["secrets_list"]:
|
747
793
|
del config["secrets_list"]
|
748
794
|
|
749
|
-
resp = rq.patch(f"
|
795
|
+
resp = rq.patch(f"{PRODUCTION_BASE_URL}/v1/connectors/{id}",
|
750
796
|
headers={"Authorization": f"Basic {deploy_key}"},
|
751
797
|
json={
|
752
798
|
"config": config,
|
@@ -771,7 +817,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
|
771
817
|
Returns:
|
772
818
|
str: The connection ID, or None
|
773
819
|
"""
|
774
|
-
resp = rq.get(f"
|
820
|
+
resp = rq.get(f"{PRODUCTION_BASE_URL}/v1/groups/{group_id}/connectors",
|
775
821
|
headers={"Authorization": f"Basic {deploy_key}"},
|
776
822
|
params={"schema": name})
|
777
823
|
if not resp.ok:
|
@@ -796,7 +842,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
|
796
842
|
Returns:
|
797
843
|
rq.Response: The response object.
|
798
844
|
"""
|
799
|
-
response = rq.post(f"
|
845
|
+
response = rq.post(f"{PRODUCTION_BASE_URL}/v1/connectors",
|
800
846
|
headers={"Authorization": f"Basic {deploy_key}"},
|
801
847
|
json={
|
802
848
|
"group_id": group_id,
|
@@ -888,7 +934,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
|
888
934
|
bool: True if the upload was successful, False otherwise.
|
889
935
|
"""
|
890
936
|
print_library_log("Uploading your project...")
|
891
|
-
response = rq.post(f"
|
937
|
+
response = rq.post(f"{PRODUCTION_BASE_URL}/v1/deploy/{group_id}/{connection}",
|
892
938
|
files={'file': open(local_path, 'rb')},
|
893
939
|
headers={"Authorization": f"Basic {deploy_key}"})
|
894
940
|
if response.ok:
|
@@ -911,7 +957,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
|
911
957
|
bool: True if the cleanup was successful, False otherwise.
|
912
958
|
"""
|
913
959
|
print_library_log("INFO: Cleaning up your uploaded project ")
|
914
|
-
response = rq.post(f"
|
960
|
+
response = rq.post(f"{PRODUCTION_BASE_URL}/v1/cleanup_code/{group_id}/{connection}",
|
915
961
|
headers={"Authorization": f"Basic {deploy_key}"})
|
916
962
|
if response.ok:
|
917
963
|
print("✓")
|
@@ -947,7 +993,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
|
947
993
|
Returns:
|
948
994
|
tuple[str, str]: A tuple containing the group ID and group name.
|
949
995
|
"""
|
950
|
-
groups_url = "
|
996
|
+
groups_url = f"{PRODUCTION_BASE_URL}/v1/groups"
|
951
997
|
|
952
998
|
params = {"limit": 500}
|
953
999
|
headers = {"Authorization": f"Basic {deploy_key}"}
|
@@ -1096,8 +1142,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
|
1096
1142
|
print("✓")
|
1097
1143
|
except:
|
1098
1144
|
shutil.rmtree(tester_root_dir)
|
1099
|
-
raise RuntimeError(f"\nSEVERE: Failed to install the connector tester. Error details: "
|
1100
|
-
traceback.format_exc())
|
1145
|
+
raise RuntimeError(f"\nSEVERE: Failed to install the connector tester. Error details: {traceback.format_exc()}")
|
1101
1146
|
|
1102
1147
|
project_path = os.getcwd() if project_path is None else project_path
|
1103
1148
|
self.validate_requirements_file(project_path, False)
|
@@ -1114,7 +1159,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
|
1114
1159
|
# server.wait_for_termination()
|
1115
1160
|
|
1116
1161
|
try:
|
1117
|
-
print_library_log(
|
1162
|
+
print_library_log("Running connector tester...")
|
1118
1163
|
for log_msg in self.__run_tester(java_exe, tester_root_dir, project_path, available_port):
|
1119
1164
|
print(log_msg, end="")
|
1120
1165
|
except:
|
@@ -1238,6 +1283,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
|
1238
1283
|
else:
|
1239
1284
|
try:
|
1240
1285
|
configuration = self.configuration if self.configuration else request.configuration
|
1286
|
+
print_library_log("Initiating the 'schema' method call...", Logging.Level.WARNING)
|
1241
1287
|
response = self.schema_method(configuration)
|
1242
1288
|
self.process_tables(response)
|
1243
1289
|
return connector_sdk_pb2.SchemaResponse(without_schema=common_pb2.TableList(tables=TABLES.values()))
|
@@ -1245,6 +1291,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
|
1245
1291
|
except Exception as e:
|
1246
1292
|
tb = traceback.format_exc()
|
1247
1293
|
error_message = f"Error: {str(e)}\n{tb}"
|
1294
|
+
print_library_log(error_message, Logging.Level.SEVERE)
|
1248
1295
|
raise RuntimeError(error_message) from e
|
1249
1296
|
|
1250
1297
|
def process_tables(self, response):
|
@@ -1343,6 +1390,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
|
1343
1390
|
state = self.state if self.state else json.loads(request.state_json)
|
1344
1391
|
|
1345
1392
|
try:
|
1393
|
+
print_library_log("Initiating the 'update' method call...", Logging.Level.WARNING)
|
1346
1394
|
for resp in self.update_method(configuration=configuration, state=state):
|
1347
1395
|
if isinstance(resp, list):
|
1348
1396
|
for r in resp:
|
@@ -1357,6 +1405,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
|
1357
1405
|
except Exception as e:
|
1358
1406
|
tb = traceback.format_exc()
|
1359
1407
|
error_message = f"Error: {str(e)}\n{tb}"
|
1408
|
+
print_library_log(error_message, Logging.Level.SEVERE)
|
1360
1409
|
raise RuntimeError(error_message) from e
|
1361
1410
|
|
1362
1411
|
|
@@ -1372,19 +1421,24 @@ def find_connector_object(project_path) -> Connector:
|
|
1372
1421
|
sys.path.append(project_path) # Allows python interpreter to search for modules in this path
|
1373
1422
|
module_name = "connector_connector_code"
|
1374
1423
|
connector_py = os.path.join(project_path, "connector.py")
|
1375
|
-
|
1376
|
-
|
1377
|
-
|
1378
|
-
|
1379
|
-
|
1380
|
-
|
1381
|
-
|
1382
|
-
|
1383
|
-
|
1424
|
+
try:
|
1425
|
+
spec = importlib.util.spec_from_file_location(module_name, connector_py)
|
1426
|
+
module = importlib.util.module_from_spec(spec)
|
1427
|
+
sys.modules[module_name] = module
|
1428
|
+
spec.loader.exec_module(module)
|
1429
|
+
for obj in dir(module):
|
1430
|
+
if not obj.startswith('__'): # Exclude built-in attributes
|
1431
|
+
obj_attr = getattr(module, obj)
|
1432
|
+
if '<fivetran_connector_sdk.Connector object at' in str(obj_attr):
|
1433
|
+
return obj_attr
|
1434
|
+
except FileNotFoundError:
|
1435
|
+
print_library_log(
|
1436
|
+
"The connector object is missing in the current directory. Please ensure that you are running the command from correct directory or that you have defined a connector object using the correct syntax in your `connector.py` file. Reference: https://fivetran.com/docs/connectors/connector-sdk/technical-reference#technicaldetailsrequiredobjectconnector", Logging.Level.SEVERE)
|
1437
|
+
return
|
1384
1438
|
|
1385
1439
|
print_library_log(
|
1386
1440
|
"The connector object is missing. Please ensure that you have defined a connector object using the correct syntax in your `connector.py` file. Reference: https://fivetran.com/docs/connectors/connector-sdk/technical-reference#technicaldetailsrequiredobjectconnector", Logging.Level.SEVERE)
|
1387
|
-
|
1441
|
+
return
|
1388
1442
|
|
1389
1443
|
|
1390
1444
|
def suggest_correct_command(input_command: str) -> bool:
|
@@ -1475,6 +1529,9 @@ def main():
|
|
1475
1529
|
|
1476
1530
|
connector_object = find_connector_object(args.project_path)
|
1477
1531
|
|
1532
|
+
if not connector_object:
|
1533
|
+
sys.exit(1)
|
1534
|
+
|
1478
1535
|
# Process optional args
|
1479
1536
|
ft_group = args.destination if args.destination else os.getenv('FIVETRAN_DESTINATION_NAME', None)
|
1480
1537
|
ft_connection = args.connection if args.connection else os.getenv('FIVETRAN_CONNECTION_NAME', None)
|
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.2
|
2
2
|
Name: fivetran_connector_sdk
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.13.10.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
|
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
|
File without changes
|
File without changes
|