fivetran-connector-sdk 0.13.22.1__py3-none-any.whl → 1.0.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.
@@ -28,14 +28,15 @@ from fivetran_connector_sdk.protos import common_pb2
28
28
  from fivetran_connector_sdk.protos import connector_sdk_pb2
29
29
  from fivetran_connector_sdk.protos import connector_sdk_pb2_grpc
30
30
 
31
- # Version format: <major_version>.<MM>.<DD>.<iteration> (where MM is incremental value from Jan 2024)
32
- __version__ = "0.13.22.1"
31
+ # Version format: <major_version>.<minor_version>.<patch_version>
32
+ # (where Major Version = 1 for GA, Minor Version is incremental MM from Jan 25 onwards, Patch Version is incremental within a month)
33
+ __version__ = "1.0.0"
33
34
 
34
35
  MAC_OS = "mac"
35
36
  WIN_OS = "windows"
36
37
  LINUX_OS = "linux"
37
38
 
38
- TESTER_VERSION = "0.25.0117.001"
39
+ TESTER_VERSION = "0.25.0130.001"
39
40
  TESTER_FILENAME = "run_sdk_tester.jar"
40
41
  VERSION_FILENAME = "version.txt"
41
42
  UPLOAD_FILENAME = "code.zip"
@@ -76,6 +77,7 @@ DRIVERS = "drivers"
76
77
  JAVA_LONG_MAX_VALUE = 9223372036854775807
77
78
  MAX_CONFIG_FIELDS = 100
78
79
  SUPPORTED_PYTHON_VERSIONS = {"3.12.8", "3.11.11", "3.10.16", "3.9.21"}
80
+ DEFAULT_PYTHON_VERSION = "3.12.8"
79
81
 
80
82
 
81
83
  class Logging:
@@ -161,13 +163,14 @@ class Operations:
161
163
 
162
164
  responses = []
163
165
 
164
- table = for_table(table)
166
+ table = get_renamed_table_name(table)
165
167
  columns = _get_columns(table)
166
168
  if not columns:
167
169
  global TABLES
168
170
  for field in data.keys():
169
- columns[field] = common_pb2.Column(
170
- name=field, type=common_pb2.DataType.UNSPECIFIED, primary_key=False)
171
+ field_name = get_renamed_column_name(field)
172
+ columns[field_name] = common_pb2.Column(
173
+ name=field_name, type=common_pb2.DataType.UNSPECIFIED, primary_key=False)
171
174
 
172
175
  mapped_data = _map_data_to_columns(data, columns)
173
176
  record = connector_sdk_pb2.Record(
@@ -196,7 +199,7 @@ class Operations:
196
199
  """
197
200
  _yield_check(inspect.stack())
198
201
 
199
- table = for_table(table)
202
+ table = get_renamed_table_name(table)
200
203
  columns = _get_columns(table)
201
204
  mapped_data = _map_data_to_columns(modified, columns)
202
205
  record = connector_sdk_pb2.Record(
@@ -222,7 +225,7 @@ class Operations:
222
225
  """
223
226
  _yield_check(inspect.stack())
224
227
 
225
- table = for_table(table)
228
+ table = get_renamed_table_name(table)
226
229
  columns = _get_columns(table)
227
230
  mapped_data = _map_data_to_columns(keys, columns)
228
231
  record = connector_sdk_pb2.Record(
@@ -313,6 +316,7 @@ def _get_columns(table: str) -> dict:
313
316
  columns = {}
314
317
  if table in TABLES:
315
318
  for column in TABLES[table].columns:
319
+ column.name = get_renamed_column_name(column.name)
316
320
  columns[column.name] = column
317
321
 
318
322
  return columns
@@ -330,12 +334,13 @@ def _map_data_to_columns(data: dict, columns: dict) -> dict:
330
334
  """
331
335
  mapped_data = {}
332
336
  for k, v in data.items():
337
+ key = get_renamed_column_name(k)
333
338
  if v is None:
334
- mapped_data[k] = common_pb2.ValueType(null=True)
335
- elif (k in columns) and columns[k].type != common_pb2.DataType.UNSPECIFIED:
336
- map_defined_data_type(columns, k, mapped_data, v)
339
+ mapped_data[key] = common_pb2.ValueType(null=True)
340
+ elif (key in columns) and columns[key].type != common_pb2.DataType.UNSPECIFIED:
341
+ map_defined_data_type(columns, key, mapped_data, v)
337
342
  else:
338
- map_inferred_data_type(k, mapped_data, v)
343
+ map_inferred_data_type(key, mapped_data, v)
339
344
 
340
345
  return mapped_data
341
346
 
@@ -643,13 +648,19 @@ def safe_drop_underscores(name):
643
648
  return safe_name
644
649
 
645
650
 
646
- def for_table(source_table):
651
+ def get_renamed_table_name(source_table):
647
652
  """
648
653
  Process a source table name to ensure it conforms to naming rules.
649
654
  """
650
655
  return safe_drop_underscores(source_table)
651
656
 
652
657
 
658
+ def get_renamed_column_name(source_column):
659
+ """
660
+ Process a source column name to ensure it conforms to naming rules.
661
+ """
662
+ return redshift_safe(source_column)
663
+
653
664
  class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
654
665
  def __init__(self, update, schema=None):
655
666
  """Initializes the Connector instance.
@@ -716,14 +727,16 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
716
727
  force (bool): Force update an existing connection.
717
728
 
718
729
  """
730
+ # tmp_requirements is only generated when pipreqs command is successful
731
+ tmp_requirements_file_path = os.path.join(project_path, 'tmp_requirements.txt')
719
732
  # Run the pipreqs command and capture stderr
720
733
  attempt = 0
721
734
  while attempt < MAX_RETRIES:
722
735
  attempt += 1
723
736
  result = subprocess.run(
724
- ["pipreqs", "--savepath", "tmp_requirements.txt", "--ignore"] + EXCLUDED_PIPREQS_DIRS,
737
+ ["pipreqs", project_path, "--savepath", tmp_requirements_file_path, "--ignore"] + EXCLUDED_PIPREQS_DIRS,
725
738
  stderr=subprocess.PIPE,
726
- text=True # Ensures output is in string format
739
+ text=True # Ensures output is in string format
727
740
  )
728
741
 
729
742
  if result.returncode == 0:
@@ -740,9 +753,6 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
740
753
  print(result.stderr)
741
754
  sys.exit(1)
742
755
 
743
- # tmp_requirements is only generated when pipreqs command is successful
744
- tmp_requirements_file_path = os.path.join(project_path, 'tmp_requirements.txt')
745
-
746
756
  tmp_requirements = self.fetch_requirements_as_dict(self, tmp_requirements_file_path)
747
757
  tmp_requirements.pop("fivetran_connector_sdk")
748
758
  if tmp_requirements.get('requests') is not None:
@@ -872,17 +882,19 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
872
882
  else:
873
883
  if args.force:
874
884
  confirm = "y"
885
+ if args.configuration:
886
+ confirm_config = "y"
875
887
  else:
876
888
  confirm = input(
877
889
  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): ")
878
- if confirm.lower() == "y" and connection_config["secrets_list"]:
890
+ if confirm.lower() == "y" and args.configuration:
879
891
  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): ")
880
892
  if confirm.lower() == "y" and (not connection_config["secrets_list"] or (confirm_config.lower() == "y")):
881
893
  print_library_log("Updating the connection...\n")
882
894
  self.__upload_project(
883
895
  args.project_path, deploy_key, group_id, group_name, connection)
884
896
  self.__update_connection(
885
- connection_id, connection, group_name, connection_config, deploy_key)
897
+ args, connection_id, connection, group_name, connection_config, deploy_key)
886
898
  print("✓")
887
899
  print_library_log(f"Connector ID: {connection_id}")
888
900
  print_library_log(
@@ -899,6 +911,8 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
899
911
  print_library_log(
900
912
  f"The connection '{connection}' has been created successfully.\n")
901
913
  connection_id = response.json()['data']['id']
914
+ # Python Version is not passed into connection_config as default value will be picked up from ConnectorSdkCredentials.java class.
915
+ print_library_log(f"Python Version: {args.python_version if args.python_version else DEFAULT_PYTHON_VERSION}", Logging.Level.INFO)
902
916
  print_library_log(f"Connector ID: {connection_id}")
903
917
  print_library_log(
904
918
  f"Visit the Fivetran dashboard to start the initial sync: https://fivetran.com/dashboard/connectors/{connection_id}/status")
@@ -925,17 +939,18 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
925
939
  os._exit(1)
926
940
 
927
941
  @staticmethod
928
- def __update_connection(id: str, name: str, group: str, config: dict, deploy_key: str):
942
+ def __update_connection(args: dict, id: str, name: str, group: str, config: dict, deploy_key: str):
929
943
  """Updates the connection with the given ID, name, group, configuration, and deployment key.
930
944
 
931
945
  Args:
946
+ args (dict): The command arguments.
932
947
  id (str): The connection ID.
933
948
  name (str): The connection name.
934
949
  group (str): The group name.
935
950
  config (dict): The configuration dictionary.
936
951
  deploy_key (str): The deployment key.
937
952
  """
938
- if not config["secrets_list"]:
953
+ if not args.configuration:
939
954
  del config["secrets_list"]
940
955
 
941
956
  resp = rq.patch(f"{PRODUCTION_BASE_URL}/v1/connectors/{id}",
@@ -1161,7 +1176,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
1161
1176
 
1162
1177
  if not resp.ok:
1163
1178
  print_library_log(
1164
- f"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.", Logging.Level.SEVERE)
1179
+ 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)
1165
1180
  os._exit(1)
1166
1181
 
1167
1182
  data = resp.json().get("data", {})
@@ -1234,7 +1249,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
1234
1249
  project_path: str = None,
1235
1250
  configuration: dict = None,
1236
1251
  state: dict = None,
1237
- log_level: Logging.Level = Logging.Level.FINE) -> bool:
1252
+ log_level: Logging.Level = Logging.Level.FINE):
1238
1253
  """Tests the connector code by running it with the connector tester.\n
1239
1254
  state.json docs: https://fivetran.com/docs/connectors/connector-sdk/detailed-guide#workingwithstatejsonfile\n
1240
1255
  configuration.json docs: https://fivetran.com/docs/connectors/connector-sdk/detailed-guide#workingwithconfigurationjsonfile
@@ -1244,9 +1259,6 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
1244
1259
  configuration (dict): The configuration dictionary, same as configuration.json if present.
1245
1260
  state (dict): The state dictionary, same as state.json if present.
1246
1261
  log_level (Logging.Level): The logging level.
1247
-
1248
- Returns:
1249
- bool: True if there was an error, False otherwise.
1250
1262
  """
1251
1263
  global DEBUGGING
1252
1264
  DEBUGGING = True
@@ -1443,7 +1455,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
1443
1455
  else:
1444
1456
  try:
1445
1457
  configuration = self.configuration if self.configuration else request.configuration
1446
- print_library_log("Initiating the 'schema' method call...", Logging.Level.WARNING)
1458
+ print_library_log("Initiating the 'schema' method call...", Logging.Level.INFO)
1447
1459
  response = self.schema_method(configuration)
1448
1460
  self.process_tables(response)
1449
1461
  return connector_sdk_pb2.SchemaResponse(without_schema=common_pb2.TableList(tables=TABLES.values()))
@@ -1459,7 +1471,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
1459
1471
  if 'table' not in entry:
1460
1472
  raise ValueError("Entry missing table name: " + entry)
1461
1473
 
1462
- table_name = for_table(entry['table'])
1474
+ table_name = get_renamed_table_name(entry['table'])
1463
1475
 
1464
1476
  if table_name in TABLES:
1465
1477
  raise ValueError("Table already defined: " + table_name)
@@ -1478,13 +1490,15 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
1478
1490
 
1479
1491
  def process_primary_keys(self, columns, entry):
1480
1492
  for pkey_name in entry["primary_key"]:
1481
- column = columns[pkey_name] if pkey_name in columns else common_pb2.Column(name=pkey_name)
1493
+ column_name = get_renamed_column_name(pkey_name)
1494
+ column = columns[column_name] if column_name in columns else common_pb2.Column(name=column_name)
1482
1495
  column.primary_key = True
1483
- columns[pkey_name] = column
1496
+ columns[column_name] = column
1484
1497
 
1485
1498
  def process_columns(self, columns, entry):
1486
1499
  for name, type in entry["columns"].items():
1487
- column = columns[name] if name in columns else common_pb2.Column(name=name)
1500
+ column_name = get_renamed_column_name(name)
1501
+ column = columns[column_name] if column_name in columns else common_pb2.Column(name=column_name)
1488
1502
 
1489
1503
  if isinstance(type, str):
1490
1504
  self.process_data_type(column, type)
@@ -1502,7 +1516,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
1502
1516
  if "primary_key" in entry and name in entry["primary_key"]:
1503
1517
  column.primary_key = True
1504
1518
 
1505
- columns[name] = column
1519
+ columns[column_name] = column
1506
1520
 
1507
1521
  def process_data_type(self, column, type):
1508
1522
  if type.upper() == "BOOLEAN":
@@ -1550,7 +1564,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
1550
1564
  state = self.state if self.state else json.loads(request.state_json)
1551
1565
 
1552
1566
  try:
1553
- print_library_log("Initiating the 'update' method call...", Logging.Level.WARNING)
1567
+ print_library_log("Initiating the 'update' method call...", Logging.Level.INFO)
1554
1568
  for resp in self.update_method(configuration=configuration, state=state):
1555
1569
  if isinstance(resp, list):
1556
1570
  for r in resp:
@@ -1667,7 +1681,8 @@ def main():
1667
1681
  global EXECUTED_VIA_CLI
1668
1682
  EXECUTED_VIA_CLI = True
1669
1683
 
1670
- parser = argparse.ArgumentParser(allow_abbrev=False)
1684
+ parser = argparse.ArgumentParser(allow_abbrev=False, add_help=True)
1685
+ parser._option_string_actions["-h"].help = "Show this help message and exit"
1671
1686
 
1672
1687
  # Positional
1673
1688
  parser.add_argument("command", help="|".join(VALID_COMMANDS))
@@ -1676,7 +1691,7 @@ def main():
1676
1691
  # Optional (Not all of these are valid with every mutually exclusive option below)
1677
1692
  parser.add_argument("--state", type=str, default=None, help="Provide state as JSON string or file")
1678
1693
  parser.add_argument("--configuration", type=str, default=None, help="Provide secrets as JSON file")
1679
- parser.add_argument("--api-key", type=str, default=None, help="Provide api key for deployment to production")
1694
+ parser.add_argument("--api-key", type=str, default=None, help="Provide your base64-encoded API key for deployment")
1680
1695
  parser.add_argument("--destination", type=str, default=None, help="Destination name (aka 'group name')")
1681
1696
  parser.add_argument("--connection", type=str, default=None, help="Connection name (aka 'destination schema')")
1682
1697
  parser.add_argument("-f", "--force", action="store_true", help="Force update an existing connection")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: fivetran_connector_sdk
3
- Version: 0.13.22.1
3
+ Version: 1.0.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,4 +1,4 @@
1
- fivetran_connector_sdk/__init__.py,sha256=dUvOznhj7qZvJCMTnHMUiwTJBzmEGH10-IaG6ZD6gVo,73066
1
+ fivetran_connector_sdk/__init__.py,sha256=Nx4fuu2Q901W2ecDr7f9-UgkRjsM4Oz0sYX0XVrChkA,74181
2
2
  fivetran_connector_sdk/protos/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  fivetran_connector_sdk/protos/common_pb2.py,sha256=kUwVcyZHgLigNR-KnHZn7dHrlxaMnUXqzprsRx6T72M,6831
4
4
  fivetran_connector_sdk/protos/common_pb2.pyi,sha256=S0hdIzoXyyOKD5cjiGeDDLYpQ9J3LjAvu4rCj1JvJWE,9038
@@ -6,8 +6,8 @@ fivetran_connector_sdk/protos/common_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXH
6
6
  fivetran_connector_sdk/protos/connector_sdk_pb2.py,sha256=9Ke_Ti1s0vAeXapfXT-EryrT2-TSGQb8mhs4gxTpUMk,7732
7
7
  fivetran_connector_sdk/protos/connector_sdk_pb2.pyi,sha256=FWYxRgshEF3QDYAE0TM_mv4N2gGvkxCH_uPpxnMc4oA,8406
8
8
  fivetran_connector_sdk/protos/connector_sdk_pb2_grpc.py,sha256=ZfJLp4DW7uP4pFOZ74s_wQ6tD3eIPi-08UfnLwe4tzo,7163
9
- fivetran_connector_sdk-0.13.22.1.dist-info/METADATA,sha256=Oh5malMU3O9KjmJmsoLobGVq1Eq0_lC81Dd-QlIO8eM,2971
10
- fivetran_connector_sdk-0.13.22.1.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
11
- fivetran_connector_sdk-0.13.22.1.dist-info/entry_points.txt,sha256=uQn0KPnFlQmXJfxlk0tifdNsSXWfVlnAFzNqjXZM_xM,57
12
- fivetran_connector_sdk-0.13.22.1.dist-info/top_level.txt,sha256=-_xk2MFY4psIh7jw1lJePMzFb5-vask8_ZtX-UzYWUI,23
13
- fivetran_connector_sdk-0.13.22.1.dist-info/RECORD,,
9
+ fivetran_connector_sdk-1.0.0.dist-info/METADATA,sha256=vcXCf52OxYYGX-uqK0gG52I382Iu_UNASzRAruUtshw,2967
10
+ fivetran_connector_sdk-1.0.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
11
+ fivetran_connector_sdk-1.0.0.dist-info/entry_points.txt,sha256=uQn0KPnFlQmXJfxlk0tifdNsSXWfVlnAFzNqjXZM_xM,57
12
+ fivetran_connector_sdk-1.0.0.dist-info/top_level.txt,sha256=-_xk2MFY4psIh7jw1lJePMzFb5-vask8_ZtX-UzYWUI,23
13
+ fivetran_connector_sdk-1.0.0.dist-info/RECORD,,