fivetran-connector-sdk 0.8.21.1__tar.gz → 0.8.26.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.8.21.1 → fivetran_connector_sdk-0.8.26.1}/PKG-INFO +2 -2
- {fivetran_connector_sdk-0.8.21.1 → fivetran_connector_sdk-0.8.26.1}/README.md +1 -1
- {fivetran_connector_sdk-0.8.21.1 → fivetran_connector_sdk-0.8.26.1}/src/fivetran_connector_sdk/__init__.py +172 -35
- {fivetran_connector_sdk-0.8.21.1 → fivetran_connector_sdk-0.8.26.1}/src/fivetran_connector_sdk.egg-info/PKG-INFO +2 -2
- {fivetran_connector_sdk-0.8.21.1 → fivetran_connector_sdk-0.8.26.1}/pyproject.toml +0 -0
- {fivetran_connector_sdk-0.8.21.1 → fivetran_connector_sdk-0.8.26.1}/setup.cfg +0 -0
- {fivetran_connector_sdk-0.8.21.1 → fivetran_connector_sdk-0.8.26.1}/src/fivetran_connector_sdk/protos/__init__.py +0 -0
- {fivetran_connector_sdk-0.8.21.1 → fivetran_connector_sdk-0.8.26.1}/src/fivetran_connector_sdk/protos/common_pb2.py +0 -0
- {fivetran_connector_sdk-0.8.21.1 → fivetran_connector_sdk-0.8.26.1}/src/fivetran_connector_sdk/protos/common_pb2.pyi +0 -0
- {fivetran_connector_sdk-0.8.21.1 → fivetran_connector_sdk-0.8.26.1}/src/fivetran_connector_sdk/protos/common_pb2_grpc.py +0 -0
- {fivetran_connector_sdk-0.8.21.1 → fivetran_connector_sdk-0.8.26.1}/src/fivetran_connector_sdk/protos/connector_sdk_pb2.py +0 -0
- {fivetran_connector_sdk-0.8.21.1 → fivetran_connector_sdk-0.8.26.1}/src/fivetran_connector_sdk/protos/connector_sdk_pb2.pyi +0 -0
- {fivetran_connector_sdk-0.8.21.1 → fivetran_connector_sdk-0.8.26.1}/src/fivetran_connector_sdk/protos/connector_sdk_pb2_grpc.py +0 -0
- {fivetran_connector_sdk-0.8.21.1 → fivetran_connector_sdk-0.8.26.1}/src/fivetran_connector_sdk.egg-info/SOURCES.txt +0 -0
- {fivetran_connector_sdk-0.8.21.1 → fivetran_connector_sdk-0.8.26.1}/src/fivetran_connector_sdk.egg-info/dependency_links.txt +0 -0
- {fivetran_connector_sdk-0.8.21.1 → fivetran_connector_sdk-0.8.26.1}/src/fivetran_connector_sdk.egg-info/entry_points.txt +0 -0
- {fivetran_connector_sdk-0.8.21.1 → fivetran_connector_sdk-0.8.26.1}/src/fivetran_connector_sdk.egg-info/requires.txt +0 -0
- {fivetran_connector_sdk-0.8.21.1 → fivetran_connector_sdk-0.8.26.1}/src/fivetran_connector_sdk.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: fivetran_connector_sdk
|
3
|
-
Version: 0.8.
|
3
|
+
Version: 0.8.26.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
|
@@ -17,7 +17,7 @@ Requires-Dist: get_pypi_latest_version==0.0.12
|
|
17
17
|
Requires-Dist: pipreqs==0.5.0
|
18
18
|
|
19
19
|
# **fivetran-connector-sdk**
|
20
|
-
The *fivetran-connector-sdk*
|
20
|
+
The *fivetran-connector-sdk* SDK allows users to execute custom, self-written Python code within [Fivetran's](https://www.fivetran.com/) secure cloud environment. Fivetran automatically manages running the connectors on your scheduled frequency and manages the required compute resources.
|
21
21
|
|
22
22
|
The Connector SDK service is the best fit for the following use cases:
|
23
23
|
- Fivetran doesn't have a connector for your source and is unlikely to support it soon.
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# **fivetran-connector-sdk**
|
2
|
-
The *fivetran-connector-sdk*
|
2
|
+
The *fivetran-connector-sdk* SDK allows users to execute custom, self-written Python code within [Fivetran's](https://www.fivetran.com/) secure cloud environment. Fivetran automatically manages running the connectors on your scheduled frequency and manages the required compute resources.
|
3
3
|
|
4
4
|
The Connector SDK service is the best fit for the following use cases:
|
5
5
|
- Fivetran doesn't have a connector for your source and is unlikely to support it soon.
|
@@ -23,13 +23,13 @@ from fivetran_connector_sdk.protos import common_pb2
|
|
23
23
|
from fivetran_connector_sdk.protos import connector_sdk_pb2
|
24
24
|
from fivetran_connector_sdk.protos import connector_sdk_pb2_grpc
|
25
25
|
|
26
|
-
__version__ = "0.8.
|
26
|
+
__version__ = "0.8.26.1"
|
27
27
|
|
28
28
|
MAC_OS = "mac"
|
29
29
|
WIN_OS = "windows"
|
30
30
|
LINUX_OS = "linux"
|
31
31
|
|
32
|
-
TESTER_VERSION = "0.24.
|
32
|
+
TESTER_VERSION = "0.24.0826.001"
|
33
33
|
TESTER_FILENAME = "run_sdk_tester.jar"
|
34
34
|
VERSION_FILENAME = "version.txt"
|
35
35
|
UPLOAD_FILENAME = "code.zip"
|
@@ -39,8 +39,16 @@ OUTPUT_FILES_DIR = "files"
|
|
39
39
|
ONE_DAY_IN_SEC = 24 * 60 * 60
|
40
40
|
|
41
41
|
EXCLUDED_DIRS = ["__pycache__", "lib", "include", OUTPUT_FILES_DIR]
|
42
|
-
EXCLUDED_PIPREQS_DIRS = ["bin,etc,include,lib,Lib,lib64,Scripts"]
|
43
|
-
|
42
|
+
EXCLUDED_PIPREQS_DIRS = ["bin,etc,include,lib,Lib,lib64,Scripts,share"]
|
43
|
+
VALID_COMMANDS = ["debug", "deploy", "reset", "version"]
|
44
|
+
MAX_ALLOWED_EDIT_DISTANCE_FROM_VALID_COMMAND = 3
|
45
|
+
COMMANDS_AND_SYNONYMS = {
|
46
|
+
"debug": {"test", "verify", "diagnose", "check"},
|
47
|
+
"deploy": {"upload", "ship", "launch", "release"},
|
48
|
+
"reset": {"reinitialize", "reinitialise", "re-initialize", "re-initialise", "restart", "restore"},
|
49
|
+
}
|
50
|
+
|
51
|
+
CONNECTION_SCHEMA_NAME_PATTERN = r'^[_a-z][_a-z0-9]*$'
|
44
52
|
DEBUGGING = False
|
45
53
|
TABLES = {}
|
46
54
|
|
@@ -130,12 +138,6 @@ class Operations:
|
|
130
138
|
for field in data.keys():
|
131
139
|
columns[field] = common_pb2.Column(
|
132
140
|
name=field, type=common_pb2.DataType.UNSPECIFIED, primary_key=False)
|
133
|
-
new_table = common_pb2.Table(name=table, columns=columns.values())
|
134
|
-
|
135
|
-
responses.append(connector_sdk_pb2.UpdateResponse(
|
136
|
-
operation=connector_sdk_pb2.Operation(
|
137
|
-
schema_change=connector_sdk_pb2.SchemaChange(
|
138
|
-
without_schema=common_pb2.TableList(tables=[new_table])))))
|
139
141
|
|
140
142
|
mapped_data = _map_data_to_columns(data, columns)
|
141
143
|
record = connector_sdk_pb2.Record(
|
@@ -401,6 +403,19 @@ def _check_dict(incoming: dict, string_only: bool = False) -> dict:
|
|
401
403
|
return incoming
|
402
404
|
|
403
405
|
|
406
|
+
def is_connection_name_valid(connection: str):
|
407
|
+
"""Validates if the incoming connection schema name is valid or not.
|
408
|
+
Args:
|
409
|
+
connection (str): The connection schema name being validated.
|
410
|
+
|
411
|
+
Returns:
|
412
|
+
bool: True if connection name is valid.
|
413
|
+
"""
|
414
|
+
|
415
|
+
pattern = re.compile(CONNECTION_SCHEMA_NAME_PATTERN)
|
416
|
+
return pattern.match(connection)
|
417
|
+
|
418
|
+
|
404
419
|
class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
405
420
|
def __init__(self, update, schema=None):
|
406
421
|
"""Initializes the Connector instance.
|
@@ -455,8 +470,17 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
|
455
470
|
dict: A dictionary where keys are package names (lowercased) and
|
456
471
|
values are the full dependency strings.
|
457
472
|
"""
|
458
|
-
|
459
|
-
|
473
|
+
requirements_dict = {}
|
474
|
+
for requirement in self.fetch_requirements_from_file(file_path):
|
475
|
+
requirement = requirement.strip()
|
476
|
+
if not requirement or requirement.startswith("#"): # Skip empty lines and comments
|
477
|
+
continue
|
478
|
+
try:
|
479
|
+
key, _ = re.split(r"==|>=|<=|>|<", requirement)
|
480
|
+
requirements_dict[key.lower()] = requirement.lower()
|
481
|
+
except ValueError:
|
482
|
+
print(f"Error: Invalid requirement format: '{requirement}'")
|
483
|
+
return requirements_dict
|
460
484
|
|
461
485
|
def validate_requirements_file(self, project_path: str, is_deploy: bool):
|
462
486
|
"""Validates the `requirements.txt` file against the project's actual dependencies.
|
@@ -471,7 +495,8 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
|
471
495
|
is_deploy (bool): If `True`, the method will exit the process on critical errors.
|
472
496
|
|
473
497
|
"""
|
474
|
-
subprocess.
|
498
|
+
subprocess.check_call(["pipreqs", "--savepath", "tmp_requirements.txt", "--ignore"] + EXCLUDED_PIPREQS_DIRS,
|
499
|
+
stderr=subprocess.PIPE)
|
475
500
|
tmp_requirements_file_path = os.path.join(project_path, 'tmp_requirements.txt')
|
476
501
|
|
477
502
|
tmp_requirements = self.fetch_requirements_as_dict(self, tmp_requirements_file_path)
|
@@ -509,6 +534,10 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
|
509
534
|
|
510
535
|
unused_deps = list(requirements.keys() - tmp_requirements.keys())
|
511
536
|
if unused_deps:
|
537
|
+
if 'fivetran_connector_sdk' in unused_deps:
|
538
|
+
print("ERROR: Please remove fivetran_connector_sdk from requirements.txt. "
|
539
|
+
"We always use the latest version of fivetran_connector_sdk when executing your code.")
|
540
|
+
os._exit(1)
|
512
541
|
print("INFO: The following dependencies are not needed, "
|
513
542
|
"they are not used or already installed. Please remove them from requirements.txt:")
|
514
543
|
print(*unused_deps)
|
@@ -530,8 +559,22 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
|
530
559
|
connection (str): The connection name.
|
531
560
|
configuration (dict): The configuration dictionary.
|
532
561
|
"""
|
533
|
-
if not deploy_key
|
534
|
-
|
562
|
+
if not deploy_key or not connection:
|
563
|
+
print("SEVERE: The deploy command needs the following parameters:"
|
564
|
+
"\n\tRequired:\n"
|
565
|
+
"\t\t--api-key <BASE64-ENCODED-FIVETRAN-API-KEY-FOR-DEPLOYMENT>\n"
|
566
|
+
"\t\t--connection <VALID-CONNECTOR-SCHEMA_NAME>\n"
|
567
|
+
"\t(Optional):\n"
|
568
|
+
"\t\t--destination <DESTINATION_NAME> (Becomes required if there are multiple destinations)\n"
|
569
|
+
"\t\t--configuration <CONFIGURATION_FILE> (Completely replaces the existing configuration)")
|
570
|
+
os._exit(1)
|
571
|
+
|
572
|
+
if not is_connection_name_valid(connection):
|
573
|
+
print(f"SEVERE: Connection name: {connection} is invalid!\n The connection name should start with an "
|
574
|
+
f"underscore or a lowercase letter (a-z), followed by any combination of underscores, lowercase "
|
575
|
+
f"letters, or digits (0-9). Uppercase characters are not allowed.")
|
576
|
+
os._exit(1)
|
577
|
+
|
535
578
|
_check_dict(configuration, True)
|
536
579
|
|
537
580
|
secrets_list = []
|
@@ -549,25 +592,54 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
|
549
592
|
self.validate_requirements_file(project_path, True)
|
550
593
|
|
551
594
|
group_id, group_name = self.__get_group_info(group, deploy_key)
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
os.remove(upload_file_path)
|
556
|
-
if not upload_result:
|
557
|
-
os._exit(1)
|
558
|
-
connection_id = self.__get_connection_id(connection, group, group_id, deploy_key)
|
595
|
+
connection_id, service = self.__get_connection_id(
|
596
|
+
connection, group, group_id, deploy_key)
|
597
|
+
|
559
598
|
if connection_id:
|
560
|
-
|
561
|
-
|
562
|
-
|
599
|
+
if service != 'connector_sdk':
|
600
|
+
print(
|
601
|
+
f"SEVERE: The connection '{connection}' already exists and does not use the 'Connector SDK' service. You cannot update this connection.")
|
602
|
+
os._exit(1)
|
603
|
+
confirm = input(
|
604
|
+
f"The connection '{connection}' already exists in the destination '{group}'. Updating it will overwrite the existing code and configuration. Do you want to proceed with the update? (Y/N): ")
|
605
|
+
if confirm.lower() == "y":
|
606
|
+
print("INFO: Updating the connection...\n")
|
607
|
+
self.__upload_project(
|
608
|
+
project_path, deploy_key, group_id, group_name, connection)
|
609
|
+
self.__update_connection(
|
610
|
+
connection_id, connection, group_name, connection_config, deploy_key)
|
611
|
+
print("✓")
|
612
|
+
print(
|
613
|
+
f"INFO: Visit the Fivetran dashboard to manage the connection: https://fivetran.com/dashboard/connectors/{connection_id}/status")
|
614
|
+
else:
|
615
|
+
print("INFO: Update canceled. The process is now terminating.")
|
616
|
+
os._exit(1)
|
563
617
|
else:
|
564
|
-
|
618
|
+
self.__upload_project(project_path, deploy_key,
|
619
|
+
group_id, group_name, connection)
|
620
|
+
response = self.__create_connection(
|
621
|
+
deploy_key, group_id, connection_config)
|
565
622
|
if response.ok:
|
566
|
-
print(
|
623
|
+
print(
|
624
|
+
f"INFO: The connection '{connection}' has been created successfully.\n")
|
625
|
+
connection_id = response.json()['data']['id']
|
626
|
+
print(
|
627
|
+
f"INFO: Visit the Fivetran dashboard to start the initial sync: https://fivetran.com/dashboard/connectors/{connection_id}/status")
|
567
628
|
else:
|
568
|
-
print(
|
629
|
+
print(
|
630
|
+
f"SEVERE: Unable to create a new connection, failed with error: {response.json()['message']}")
|
569
631
|
os._exit(1)
|
570
632
|
|
633
|
+
def __upload_project(self, project_path: str, deploy_key: str, group_id: str, group_name: str, connection: str):
|
634
|
+
print(
|
635
|
+
f"INFO: Deploying '{project_path}' to connection '{connection}' in destination '{group_name}'.\n")
|
636
|
+
upload_file_path = self.__create_upload_file(project_path)
|
637
|
+
upload_result = self.__upload(
|
638
|
+
upload_file_path, deploy_key, group_id, connection)
|
639
|
+
os.remove(upload_file_path)
|
640
|
+
if not upload_result:
|
641
|
+
os._exit(1)
|
642
|
+
|
571
643
|
@staticmethod
|
572
644
|
def __force_sync(id: str, deploy_key: str) -> bool:
|
573
645
|
"""Forces a sync operation on the connection with the given ID and deployment key.
|
@@ -606,7 +678,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
|
606
678
|
})
|
607
679
|
|
608
680
|
if not resp.ok:
|
609
|
-
print(f"SEVERE: Unable to update Connection '{name}' in destination '{group}', failed with error: '{
|
681
|
+
print(f"SEVERE: Unable to update Connection '{name}' in destination '{group}', failed with error: '{resp.json()['message']}'.")
|
610
682
|
os._exit(1)
|
611
683
|
|
612
684
|
@staticmethod
|
@@ -626,13 +698,14 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
|
626
698
|
headers={"Authorization": f"Basic {deploy_key}"},
|
627
699
|
params={"schema": name})
|
628
700
|
if not resp.ok:
|
629
|
-
print(
|
701
|
+
print(
|
702
|
+
f"SEVERE: Unable to fetch connection list in destination '{group}'")
|
630
703
|
os._exit(1)
|
631
704
|
|
632
705
|
if resp.json()['data']['items']:
|
633
|
-
return resp.json()['data']['items'][0]['id']
|
706
|
+
return resp.json()['data']['items'][0]['id'], resp.json()['data']['items'][0]['service']
|
634
707
|
|
635
|
-
return None
|
708
|
+
return None, None
|
636
709
|
|
637
710
|
@staticmethod
|
638
711
|
def __create_connection(deploy_key: str, group_id: str, config: dict) -> rq.Response:
|
@@ -782,7 +855,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
|
782
855
|
|
783
856
|
if not resp.ok:
|
784
857
|
print(
|
785
|
-
f"SEVERE: Unable to
|
858
|
+
f"SEVERE: Unable to retrieve destination details. The request failed with status code: {resp.status_code}. Please ensure you're using a valid base64-encoded API key and try again.")
|
786
859
|
os._exit(1)
|
787
860
|
|
788
861
|
data = resp.json().get("data", {})
|
@@ -839,6 +912,9 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
|
|
839
912
|
self.state = _check_dict(state)
|
840
913
|
Logging.LOG_LEVEL = log_level
|
841
914
|
|
915
|
+
if not DEBUGGING:
|
916
|
+
print(f"Running on fivetran_connector_sdk: {__version__}")
|
917
|
+
|
842
918
|
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
|
843
919
|
connector_sdk_pb2_grpc.add_ConnectorServicer_to_server(self, server)
|
844
920
|
server.add_insecure_port("[::]:" + str(port))
|
@@ -1186,6 +1262,63 @@ def find_connector_object(project_path) -> Connector:
|
|
1186
1262
|
sys.exit(1)
|
1187
1263
|
|
1188
1264
|
|
1265
|
+
def suggest_correct_command(input_command: str) -> bool:
|
1266
|
+
# for typos
|
1267
|
+
# calculate the edit distance of the input command (lowercased) with each of the valid commands
|
1268
|
+
edit_distances_of_commands = sorted([(command, edit_distance(command, input_command.lower())) for command in VALID_COMMANDS], key=lambda x: x[1])
|
1269
|
+
|
1270
|
+
if edit_distances_of_commands[0][1] <= MAX_ALLOWED_EDIT_DISTANCE_FROM_VALID_COMMAND:
|
1271
|
+
# if the closest command is within the max allowed edit distance, we suggest that command
|
1272
|
+
# threshold is kept to prevent suggesting a valid command for an obvious wrong command like `fivetran iknowthisisntacommandbuttryanyway`
|
1273
|
+
print_suggested_command_message(edit_distances_of_commands[0][0], input_command)
|
1274
|
+
return True
|
1275
|
+
|
1276
|
+
# for synonyms
|
1277
|
+
for (command, synonyms) in COMMANDS_AND_SYNONYMS.items():
|
1278
|
+
# check if the input command (lowercased) is a recognised synonym of any of the valid commands, if yes, suggest that command
|
1279
|
+
if input_command.lower() in synonyms:
|
1280
|
+
print_suggested_command_message(command, input_command)
|
1281
|
+
return True
|
1282
|
+
|
1283
|
+
return False
|
1284
|
+
|
1285
|
+
|
1286
|
+
def print_suggested_command_message(valid_command: str, input_command: str) -> None:
|
1287
|
+
print(f"`fivetran {input_command}` is not a valid command.")
|
1288
|
+
print(f"Did you mean `fivetran {valid_command}`?")
|
1289
|
+
print("Use `fivetran --help` for more details.")
|
1290
|
+
|
1291
|
+
|
1292
|
+
def edit_distance(first_string: str, second_string: str) -> int:
|
1293
|
+
first_string_length: int = len(first_string)
|
1294
|
+
second_string_length: int = len(second_string)
|
1295
|
+
|
1296
|
+
# Initialize the previous row of distances (for the base case of an empty first string)
|
1297
|
+
# 'previous_row[j]' holds the edit distance between an empty prefix of 'first_string' and the first 'j' characters of 'second_string'.
|
1298
|
+
# The first row is filled with values [0, 1, 2, ..., second_string_length]
|
1299
|
+
previous_row: list[int] = list(range(second_string_length + 1))
|
1300
|
+
|
1301
|
+
# Rest of the rows
|
1302
|
+
for first_string_index in range(1, first_string_length + 1):
|
1303
|
+
# Start the current row with the distance for an empty second string
|
1304
|
+
current_row: list[int] = [first_string_index] # j = 0
|
1305
|
+
|
1306
|
+
# Iterate over each character in the second string
|
1307
|
+
for second_string_index in range(1, second_string_length + 1):
|
1308
|
+
if first_string[first_string_index - 1] == second_string[second_string_index - 1]:
|
1309
|
+
# If characters match, no additional cost
|
1310
|
+
current_row.append(previous_row[second_string_index - 1])
|
1311
|
+
else:
|
1312
|
+
# Minimum cost of insertion, deletion, or substitution
|
1313
|
+
current_row.append(1 + min(current_row[-1], previous_row[second_string_index], previous_row[second_string_index - 1]))
|
1314
|
+
|
1315
|
+
# Move to the next row
|
1316
|
+
previous_row = current_row
|
1317
|
+
|
1318
|
+
# The last value in the last row is the edit distance
|
1319
|
+
return previous_row[second_string_length]
|
1320
|
+
|
1321
|
+
|
1189
1322
|
def main():
|
1190
1323
|
"""The main entry point for the script.
|
1191
1324
|
Parses command line arguments and passes them to connector object methods
|
@@ -1194,7 +1327,7 @@ def main():
|
|
1194
1327
|
parser = argparse.ArgumentParser(allow_abbrev=False)
|
1195
1328
|
|
1196
1329
|
# Positional
|
1197
|
-
parser.add_argument("command", help="
|
1330
|
+
parser.add_argument("command", help="|".join(VALID_COMMANDS))
|
1198
1331
|
parser.add_argument("project_path", nargs='?', default=os.getcwd(), help="Path to connector project directory")
|
1199
1332
|
|
1200
1333
|
# Optional (Not all of these are valid with every mutually exclusive option below)
|
@@ -1261,8 +1394,12 @@ def main():
|
|
1261
1394
|
print("ERROR: Reset Failed")
|
1262
1395
|
raise e
|
1263
1396
|
|
1397
|
+
elif args.command.lower() == "version":
|
1398
|
+
print("fivetran_connector_sdk " + __version__)
|
1399
|
+
|
1264
1400
|
else:
|
1265
|
-
|
1401
|
+
if not suggest_correct_command(args.command):
|
1402
|
+
raise NotImplementedError(f"Invalid command: {args.command}, see `fivetran --help`")
|
1266
1403
|
|
1267
1404
|
|
1268
1405
|
if __name__ == "__main__":
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: fivetran_connector_sdk
|
3
|
-
Version: 0.8.
|
3
|
+
Version: 0.8.26.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
|
@@ -17,7 +17,7 @@ Requires-Dist: get_pypi_latest_version==0.0.12
|
|
17
17
|
Requires-Dist: pipreqs==0.5.0
|
18
18
|
|
19
19
|
# **fivetran-connector-sdk**
|
20
|
-
The *fivetran-connector-sdk*
|
20
|
+
The *fivetran-connector-sdk* SDK allows users to execute custom, self-written Python code within [Fivetran's](https://www.fivetran.com/) secure cloud environment. Fivetran automatically manages running the connectors on your scheduled frequency and manages the required compute resources.
|
21
21
|
|
22
22
|
The Connector SDK service is the best fit for the following use cases:
|
23
23
|
- Fivetran doesn't have a connector for your source and is unlikely to support it soon.
|
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
|