fivetran-connector-sdk 0.12.17.1__tar.gz → 0.13.16.2__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.
Files changed (18) hide show
  1. {fivetran_connector_sdk-0.12.17.1 → fivetran_connector_sdk-0.13.16.2}/PKG-INFO +3 -2
  2. {fivetran_connector_sdk-0.12.17.1 → fivetran_connector_sdk-0.13.16.2}/pyproject.toml +2 -1
  3. {fivetran_connector_sdk-0.12.17.1 → fivetran_connector_sdk-0.13.16.2}/src/fivetran_connector_sdk/__init__.py +199 -27
  4. {fivetran_connector_sdk-0.12.17.1 → fivetran_connector_sdk-0.13.16.2}/src/fivetran_connector_sdk.egg-info/PKG-INFO +3 -2
  5. {fivetran_connector_sdk-0.12.17.1 → fivetran_connector_sdk-0.13.16.2}/src/fivetran_connector_sdk.egg-info/requires.txt +1 -0
  6. {fivetran_connector_sdk-0.12.17.1 → fivetran_connector_sdk-0.13.16.2}/README.md +0 -0
  7. {fivetran_connector_sdk-0.12.17.1 → fivetran_connector_sdk-0.13.16.2}/setup.cfg +0 -0
  8. {fivetran_connector_sdk-0.12.17.1 → fivetran_connector_sdk-0.13.16.2}/src/fivetran_connector_sdk/protos/__init__.py +0 -0
  9. {fivetran_connector_sdk-0.12.17.1 → fivetran_connector_sdk-0.13.16.2}/src/fivetran_connector_sdk/protos/common_pb2.py +0 -0
  10. {fivetran_connector_sdk-0.12.17.1 → fivetran_connector_sdk-0.13.16.2}/src/fivetran_connector_sdk/protos/common_pb2.pyi +0 -0
  11. {fivetran_connector_sdk-0.12.17.1 → fivetran_connector_sdk-0.13.16.2}/src/fivetran_connector_sdk/protos/common_pb2_grpc.py +0 -0
  12. {fivetran_connector_sdk-0.12.17.1 → fivetran_connector_sdk-0.13.16.2}/src/fivetran_connector_sdk/protos/connector_sdk_pb2.py +0 -0
  13. {fivetran_connector_sdk-0.12.17.1 → fivetran_connector_sdk-0.13.16.2}/src/fivetran_connector_sdk/protos/connector_sdk_pb2.pyi +0 -0
  14. {fivetran_connector_sdk-0.12.17.1 → fivetran_connector_sdk-0.13.16.2}/src/fivetran_connector_sdk/protos/connector_sdk_pb2_grpc.py +0 -0
  15. {fivetran_connector_sdk-0.12.17.1 → fivetran_connector_sdk-0.13.16.2}/src/fivetran_connector_sdk.egg-info/SOURCES.txt +0 -0
  16. {fivetran_connector_sdk-0.12.17.1 → fivetran_connector_sdk-0.13.16.2}/src/fivetran_connector_sdk.egg-info/dependency_links.txt +0 -0
  17. {fivetran_connector_sdk-0.12.17.1 → fivetran_connector_sdk-0.13.16.2}/src/fivetran_connector_sdk.egg-info/entry_points.txt +0 -0
  18. {fivetran_connector_sdk-0.12.17.1 → fivetran_connector_sdk-0.13.16.2}/src/fivetran_connector_sdk.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: fivetran_connector_sdk
3
- Version: 0.12.17.1
3
+ Version: 0.13.16.2
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,6 +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: transliterate==1.10.2
18
19
 
19
20
  # **fivetran-connector-sdk**
20
21
  [![Downloads](https://static.pepy.tech/badge/fivetran-connector-sdk)](https://pepy.tech/project/fivetran-connector-sdk)
@@ -16,7 +16,8 @@ dependencies = [
16
16
  "grpcio==1.60.1",
17
17
  "grpcio-tools==1.60.1",
18
18
  "requests==2.32.3",
19
- "pipreqs==0.5.0"
19
+ "pipreqs==0.5.0",
20
+ "transliterate==1.10.2"
20
21
  ]
21
22
 
22
23
  [project.scripts]
@@ -6,6 +6,7 @@ import importlib.util
6
6
  import inspect
7
7
  import json
8
8
  import os
9
+ from transliterate import translit
9
10
  import platform
10
11
  import requests as rq
11
12
  import shutil
@@ -26,13 +27,14 @@ from fivetran_connector_sdk.protos import common_pb2
26
27
  from fivetran_connector_sdk.protos import connector_sdk_pb2
27
28
  from fivetran_connector_sdk.protos import connector_sdk_pb2_grpc
28
29
 
29
- __version__ = "0.12.17.1"
30
+ # Version format: <major_version>.<MM>.<DD>.<iteration> (where MM is incremental value from Jan 2024)
31
+ __version__ = "0.13.16.2"
30
32
 
31
33
  MAC_OS = "mac"
32
34
  WIN_OS = "windows"
33
35
  LINUX_OS = "linux"
34
36
 
35
- TESTER_VERSION = "0.24.1209.001"
37
+ TESTER_VERSION = "0.25.0116.001"
36
38
  TESTER_FILENAME = "run_sdk_tester.jar"
37
39
  VERSION_FILENAME = "version.txt"
38
40
  UPLOAD_FILENAME = "code.zip"
@@ -45,6 +47,18 @@ PYPI_PACKAGE_DETAILS_URL = "https://pypi.org/pypi/fivetran_connector_sdk/json"
45
47
  ONE_DAY_IN_SEC = 24 * 60 * 60
46
48
  MAX_RETRIES = 3
47
49
 
50
+ # Compile patterns used in the implementation
51
+ WORD_DASH_DOT_PATTERN = re.compile(r'^[\w.-]*$')
52
+ NON_WORD_PATTERN = re.compile(r'\W')
53
+ WORD_OR_DOLLAR_PATTERN = re.compile(r'[\w$]')
54
+ DROP_LEADING_UNDERSCORE = re.compile(r'_+([a-zA-Z]\w*)')
55
+ WORD_PATTERN = re.compile(r'\w')
56
+
57
+ # Transliteration rules
58
+ TO_ASCII_RULES = (
59
+ "Han-Latin; Katakana-Latin; Arabic-Latin; NFD; [:Nonspacing Mark:] Remove; NFC"
60
+ )
61
+
48
62
  EXCLUDED_DIRS = ["__pycache__", "lib", "include", OUTPUT_FILES_DIR]
49
63
  EXCLUDED_PIPREQS_DIRS = ["bin,etc,include,lib,Lib,lib64,Scripts,share"]
50
64
  VALID_COMMANDS = ["debug", "deploy", "reset", "version"]
@@ -60,7 +74,9 @@ DEBUGGING = False
60
74
  EXECUTED_VIA_CLI = False
61
75
  PRODUCTION_BASE_URL = "https://api.fivetran.com"
62
76
  TABLES = {}
63
-
77
+ 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."
78
+ INSTALLATION_SCRIPT = "installation.sh"
79
+ DRIVERS = "drivers"
64
80
  JAVA_LONG_MAX_VALUE = 9223372036854775807
65
81
  MAX_CONFIG_FIELDS = 100
66
82
 
@@ -84,9 +100,12 @@ class Logging:
84
100
  """
85
101
  if DEBUGGING:
86
102
  current_time = datetime.now().strftime("%b %d, %Y %I:%M:%S %p")
87
- print(f"{current_time} {level.name}: {message}")
103
+ escaped_message = json.dumps(message) [1:-1]
104
+ print(f"{current_time} {level.name}: {escaped_message}")
88
105
  else:
89
- print(f'{{"level":"{level.name}", "message": "{message}", "message_origin": "connector_sdk"}}')
106
+ escaped_message = json.dumps(message)
107
+ log_message = f'{{"level":"{level.name}", "message": {escaped_message}, "message_origin": "connector_sdk"}}'
108
+ print(log_message)
90
109
 
91
110
  @staticmethod
92
111
  def fine(message: str):
@@ -145,6 +164,7 @@ class Operations:
145
164
 
146
165
  responses = []
147
166
 
167
+ table = for_table(table)
148
168
  columns = _get_columns(table)
149
169
  if not columns:
150
170
  global TABLES
@@ -179,6 +199,7 @@ class Operations:
179
199
  """
180
200
  _yield_check(inspect.stack())
181
201
 
202
+ table = for_table(table)
182
203
  columns = _get_columns(table)
183
204
  mapped_data = _map_data_to_columns(modified, columns)
184
205
  record = connector_sdk_pb2.Record(
@@ -204,6 +225,7 @@ class Operations:
204
225
  """
205
226
  _yield_check(inspect.stack())
206
227
 
228
+ table = for_table(table)
207
229
  columns = _get_columns(table)
208
230
  mapped_data = _map_data_to_columns(keys, columns)
209
231
  record = connector_sdk_pb2.Record(
@@ -465,7 +487,6 @@ def log_unused_deps_error(package_name: str, version: str):
465
487
  print_library_log(f"Please remove `{package_name}` from requirements.txt."
466
488
  f" The latest version of `{package_name}` is always available when executing your code."
467
489
  f" Current version: {version}", Logging.Level.SEVERE)
468
- os._exit(1)
469
490
 
470
491
 
471
492
  def validate_deploy_parameters(connection, deploy_key):
@@ -522,6 +543,100 @@ def update_base_url_if_required():
522
543
  print_library_log(f"Updating PRODUCTION_BASE_URL to: {base_url}")
523
544
 
524
545
 
546
+ def is_special(c):
547
+ """Check if the character is a special character."""
548
+ return not WORD_OR_DOLLAR_PATTERN.fullmatch(c)
549
+
550
+
551
+ def starts_word(previous, current):
552
+ """
553
+ Check if the current character starts a new word based on the previous character.
554
+ """
555
+ return (previous and previous.islower() and current.isupper()) or (
556
+ previous and previous.isdigit() != current.isdigit()
557
+ )
558
+
559
+
560
+ def underscore_invalid_leading_character(name, valid_leading_regex):
561
+ """
562
+ Ensure the name starts with a valid leading character.
563
+ """
564
+ if name and not valid_leading_regex.match(name[0]):
565
+ name = f'_{name}'
566
+ return name
567
+
568
+
569
+ def single_underscore_case(name):
570
+ """
571
+ Convert the input name to single underscore case, replacing special characters and spaces.
572
+ """
573
+ acc = []
574
+ previous = None
575
+
576
+ for char_index, c in enumerate(name):
577
+ if char_index == 0 and c == '$':
578
+ acc.append('_')
579
+ elif is_special(c):
580
+ acc.append('_')
581
+ elif c == ' ':
582
+ acc.append('_')
583
+ elif starts_word(previous, c):
584
+ acc.append('_')
585
+ acc.append(c.lower())
586
+ else:
587
+ acc.append(c.lower())
588
+
589
+ previous = c
590
+
591
+ name = ''.join(acc)
592
+ return re.sub(r'_+', '_', name)
593
+
594
+
595
+ def contains_only_word_dash_dot(name):
596
+ """
597
+ Check if the name contains only word characters, dashes, and dots.
598
+ """
599
+ return bool(WORD_DASH_DOT_PATTERN.fullmatch(name))
600
+
601
+
602
+ def transliterate(name):
603
+ """
604
+ Transliterate the input name if it contains non-word, dash, or dot characters.
605
+ """
606
+ if contains_only_word_dash_dot(name):
607
+ return name
608
+ return translit(name, 'en', reversed=True)
609
+
610
+
611
+ def redshift_safe(name):
612
+ """
613
+ Make the name safe for use in Redshift.
614
+ """
615
+ name = transliterate(name)
616
+ name = NON_WORD_PATTERN.sub('_', name)
617
+ name = single_underscore_case(name)
618
+ name = underscore_invalid_leading_character(name, WORD_PATTERN)
619
+ return name
620
+
621
+
622
+ def safe_drop_underscores(name):
623
+ """
624
+ Drop leading underscores if the name starts with valid characters after sanitization.
625
+ """
626
+ safe_name = redshift_safe(name)
627
+ match = DROP_LEADING_UNDERSCORE.match(safe_name)
628
+ if match:
629
+ return match.group(1)
630
+ return safe_name
631
+
632
+
633
+ def for_table(source_table):
634
+ """
635
+ Process a source table name to ensure it conforms to naming rules.
636
+ """
637
+ return safe_drop_underscores(source_table)
638
+
639
+
525
640
  class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
526
641
  def __init__(self, update, schema=None):
527
642
  """Initializes the Connector instance.
@@ -574,7 +689,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
574
689
  print_library_log(f"Invalid requirement format: '{requirement}'", Logging.Level.SEVERE)
575
690
  return requirements_dict
576
691
 
577
- def validate_requirements_file(self, project_path: str, is_deploy: bool):
692
+ def validate_requirements_file(self, project_path: str, is_deploy: bool, force: bool = False):
578
693
  """Validates the `requirements.txt` file against the project's actual dependencies.
579
694
 
580
695
  This method generates a temporary requirements file using `pipreqs`, compares
@@ -585,10 +700,22 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
585
700
  Args:
586
701
  project_path (str): The path to the project directory containing the `requirements.txt`.
587
702
  is_deploy (bool): If `True`, the method will exit the process on critical errors.
703
+ force (bool): Force update an existing connection.
588
704
 
589
705
  """
590
- subprocess.check_call(["pipreqs", "--savepath", "tmp_requirements.txt", "--ignore"] + EXCLUDED_PIPREQS_DIRS,
591
- stderr=subprocess.PIPE)
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
+ # tmp_requirements is only generated when pipreqs command is successful
592
719
  tmp_requirements_file_path = os.path.join(project_path, 'tmp_requirements.txt')
593
720
 
594
721
  tmp_requirements = self.fetch_requirements_as_dict(self, tmp_requirements_file_path)
@@ -597,6 +724,11 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
597
724
  tmp_requirements.pop("requests")
598
725
  os.remove(tmp_requirements_file_path)
599
726
 
727
+ # remove corrupt requirements listed by pipreqs
728
+ corrupt_requirements = [key for key in tmp_requirements if key.startswith("~")]
729
+ for requirement in corrupt_requirements:
730
+ del tmp_requirements[requirement]
731
+
600
732
  if len(tmp_requirements) > 0:
601
733
  requirements = self.load_or_add_requirements_file(project_path)
602
734
 
@@ -609,11 +741,26 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
609
741
 
610
742
  missing_deps = {key: tmp_requirements[key] for key in (tmp_requirements.keys() - requirements.keys())}
611
743
  if missing_deps:
612
- self.handle_missing_deps(is_deploy, missing_deps)
744
+ self.handle_missing_deps(missing_deps)
613
745
 
614
746
  unused_deps = list(requirements.keys() - tmp_requirements.keys())
615
747
  if unused_deps:
616
748
  self.handle_unused_deps(unused_deps)
749
+
750
+ if is_deploy and (version_mismatch_deps or missing_deps or unused_deps):
751
+ if force:
752
+ confirm = "y"
753
+ else:
754
+ confirm = input("We detected issues in your requirements.txt. "
755
+ "Would you like us to update it to reflect the necessary changes? (Y/N):")
756
+ if confirm.lower() == "y":
757
+ with open(REQUIREMENTS_TXT, "w") as file:
758
+ file.write("\n".join(tmp_requirements.values()))
759
+ print_library_log(f"`{REQUIREMENTS_TXT}` has been updated successfully")
760
+ else:
761
+ if missing_deps or 'fivetran_connector_sdk' in unused_deps or 'requests' in unused_deps:
762
+ print_library_log(f"Please fix your {REQUIREMENTS_TXT} file to proceed with deployment.")
763
+ os._exit(1)
617
764
  else:
618
765
  if os.path.exists(REQUIREMENTS_TXT):
619
766
  print_library_log("`requirements.txt` is not required as no additional "
@@ -632,15 +779,13 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
632
779
  "they are not used or already installed. Please remove them from requirements.txt:")
633
780
  print(*unused_deps)
634
781
 
635
- def handle_missing_deps(self, is_deploy, missing_deps):
782
+ def handle_missing_deps(self, missing_deps):
636
783
  print_library_log("Please include the following dependency libraries in requirements.txt, to be used by "
637
784
  "Fivetran production. "
638
785
  "For more information, please visit: "
639
786
  "https://fivetran.com/docs/connectors/connector-sdk/detailed-guide"
640
787
  "#workingwithrequirementstxtfile", Logging.Level.SEVERE)
641
788
  print(*list(missing_deps.values()))
642
- if is_deploy:
643
- os._exit(1)
644
789
 
645
790
  def load_or_add_requirements_file(self, project_path):
646
791
  if os.path.exists(REQUIREMENTS_TXT):
@@ -685,7 +830,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
685
830
  "custom_payloads": [],
686
831
  }
687
832
 
688
- self.validate_requirements_file(project_path, True)
833
+ self.validate_requirements_file(project_path, True, force)
689
834
 
690
835
  group_id, group_name = self.__get_group_info(group, deploy_key)
691
836
  connection_id, service = self.__get_connection_id(connection, group, group_id, deploy_key) or (None, None)
@@ -849,9 +994,15 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
849
994
  """
850
995
  upload_filepath = os.path.join(project_path, UPLOAD_FILENAME)
851
996
  connector_file_exists = False
997
+ custom_drivers_exists = False
998
+ custom_driver_installation_script_exists = False
852
999
 
853
1000
  with ZipFile(upload_filepath, 'w', ZIP_DEFLATED) as zipf:
854
1001
  for root, files in self.__dir_walker(project_path):
1002
+ if os.path.basename(root) == DRIVERS:
1003
+ custom_drivers_exists = True
1004
+ if INSTALLATION_SCRIPT in files:
1005
+ custom_driver_installation_script_exists = True
855
1006
  for file in files:
856
1007
  if file == "connector.py":
857
1008
  connector_file_exists = True
@@ -863,6 +1014,11 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
863
1014
  print_library_log(
864
1015
  "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.", Logging.Level.SEVERE)
865
1016
  os._exit(1)
1017
+
1018
+ if custom_drivers_exists and not custom_driver_installation_script_exists:
1019
+ print_library_log(INSTALLATION_SCRIPT_MISSING_MESSAGE, Logging.Level.SEVERE)
1020
+ os._exit(1)
1021
+
866
1022
  return upload_filepath
867
1023
 
868
1024
  def __dir_walker(self, top):
@@ -881,6 +1037,9 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
881
1037
  if (name not in EXCLUDED_DIRS) and (not name.startswith(".")):
882
1038
  dirs.append(name)
883
1039
  else:
1040
+ # Include all files if in `drivers` folder
1041
+ if os.path.basename(top) == DRIVERS:
1042
+ files.append(name)
884
1043
  if name.endswith(".py") or name == "requirements.txt":
885
1044
  files.append(name)
886
1045
 
@@ -1253,6 +1412,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
1253
1412
  else:
1254
1413
  try:
1255
1414
  configuration = self.configuration if self.configuration else request.configuration
1415
+ print_library_log("Initiating the 'schema' method call...", Logging.Level.WARNING)
1256
1416
  response = self.schema_method(configuration)
1257
1417
  self.process_tables(response)
1258
1418
  return connector_sdk_pb2.SchemaResponse(without_schema=common_pb2.TableList(tables=TABLES.values()))
@@ -1260,6 +1420,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
1260
1420
  except Exception as e:
1261
1421
  tb = traceback.format_exc()
1262
1422
  error_message = f"Error: {str(e)}\n{tb}"
1423
+ print_library_log(error_message, Logging.Level.SEVERE)
1263
1424
  raise RuntimeError(error_message) from e
1264
1425
 
1265
1426
  def process_tables(self, response):
@@ -1267,7 +1428,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
1267
1428
  if 'table' not in entry:
1268
1429
  raise ValueError("Entry missing table name: " + entry)
1269
1430
 
1270
- table_name = entry['table']
1431
+ table_name = for_table(entry['table'])
1271
1432
 
1272
1433
  if table_name in TABLES:
1273
1434
  raise ValueError("Table already defined: " + table_name)
@@ -1358,6 +1519,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
1358
1519
  state = self.state if self.state else json.loads(request.state_json)
1359
1520
 
1360
1521
  try:
1522
+ print_library_log("Initiating the 'update' method call...", Logging.Level.WARNING)
1361
1523
  for resp in self.update_method(configuration=configuration, state=state):
1362
1524
  if isinstance(resp, list):
1363
1525
  for r in resp:
@@ -1372,6 +1534,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
1372
1534
  except Exception as e:
1373
1535
  tb = traceback.format_exc()
1374
1536
  error_message = f"Error: {str(e)}\n{tb}"
1537
+ print_library_log(error_message, Logging.Level.SEVERE)
1375
1538
  raise RuntimeError(error_message) from e
1376
1539
 
1377
1540
 
@@ -1387,19 +1550,24 @@ def find_connector_object(project_path) -> Connector:
1387
1550
  sys.path.append(project_path) # Allows python interpreter to search for modules in this path
1388
1551
  module_name = "connector_connector_code"
1389
1552
  connector_py = os.path.join(project_path, "connector.py")
1390
- spec = importlib.util.spec_from_file_location(module_name, connector_py)
1391
- module = importlib.util.module_from_spec(spec)
1392
- sys.modules[module_name] = module
1393
- spec.loader.exec_module(module)
1394
- for obj in dir(module):
1395
- if not obj.startswith('__'): # Exclude built-in attributes
1396
- obj_attr = getattr(module, obj)
1397
- if '<fivetran_connector_sdk.Connector object at' in str(obj_attr):
1398
- return obj_attr
1553
+ try:
1554
+ spec = importlib.util.spec_from_file_location(module_name, connector_py)
1555
+ module = importlib.util.module_from_spec(spec)
1556
+ sys.modules[module_name] = module
1557
+ spec.loader.exec_module(module)
1558
+ for obj in dir(module):
1559
+ if not obj.startswith('__'): # Exclude built-in attributes
1560
+ obj_attr = getattr(module, obj)
1561
+ if '<fivetran_connector_sdk.Connector object at' in str(obj_attr):
1562
+ return obj_attr
1563
+ except FileNotFoundError:
1564
+ print_library_log(
1565
+ "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)
1566
+ return
1399
1567
 
1400
1568
  print_library_log(
1401
1569
  "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)
1402
- sys.exit(1)
1570
+ return
1403
1571
 
1404
1572
 
1405
1573
  def suggest_correct_command(input_command: str) -> bool:
@@ -1487,9 +1655,15 @@ def main():
1487
1655
  if args.command.lower() == "version":
1488
1656
  print_library_log("fivetran_connector_sdk " + __version__)
1489
1657
  return
1658
+ elif args.command.lower() == "reset":
1659
+ reset_local_file_directory(args)
1660
+ return
1490
1661
 
1491
1662
  connector_object = find_connector_object(args.project_path)
1492
1663
 
1664
+ if not connector_object:
1665
+ sys.exit(1)
1666
+
1493
1667
  # Process optional args
1494
1668
  ft_group = args.destination if args.destination else os.getenv('FIVETRAN_DESTINATION_NAME', None)
1495
1669
  ft_connection = args.connection if args.connection else os.getenv('FIVETRAN_CONNECTION_NAME', None)
@@ -1508,8 +1682,6 @@ def main():
1508
1682
  elif args.command.lower() == "debug":
1509
1683
  connector_object.debug(args.project_path, configuration, state)
1510
1684
 
1511
- elif args.command.lower() == "reset":
1512
- reset_local_file_directory(args)
1513
1685
  else:
1514
1686
  if not suggest_correct_command(args.command):
1515
1687
  raise NotImplementedError(f"Invalid command: {args.command}, see `fivetran --help`")
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: fivetran_connector_sdk
3
- Version: 0.12.17.1
3
+ Version: 0.13.16.2
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,6 +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: transliterate==1.10.2
18
19
 
19
20
  # **fivetran-connector-sdk**
20
21
  [![Downloads](https://static.pepy.tech/badge/fivetran-connector-sdk)](https://pepy.tech/project/fivetran-connector-sdk)
@@ -2,3 +2,4 @@ grpcio==1.60.1
2
2
  grpcio-tools==1.60.1
3
3
  requests==2.32.3
4
4
  pipreqs==0.5.0
5
+ transliterate==1.10.2