fivetran-connector-sdk 1.2.1__py3-none-any.whl → 1.3.1__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.
@@ -31,13 +31,13 @@ from fivetran_connector_sdk.protos import connector_sdk_pb2_grpc
31
31
 
32
32
  # Version format: <major_version>.<minor_version>.<patch_version>
33
33
  # (where Major Version = 1 for GA, Minor Version is incremental MM from Jan 25 onwards, Patch Version is incremental within a month)
34
- __version__ = "1.2.1"
34
+ __version__ = "1.3.1"
35
35
 
36
36
  MAC_OS = "mac"
37
37
  WIN_OS = "windows"
38
38
  LINUX_OS = "linux"
39
39
 
40
- TESTER_VERSION = "0.25.0225.001"
40
+ TESTER_VERSION = "0.25.0411.001"
41
41
  TESTER_FILENAME = "run_sdk_tester.jar"
42
42
  VERSION_FILENAME = "version.txt"
43
43
  UPLOAD_FILENAME = "code.zip"
@@ -49,6 +49,8 @@ REQUIREMENTS_TXT = "requirements.txt"
49
49
  PYPI_PACKAGE_DETAILS_URL = "https://pypi.org/pypi/fivetran_connector_sdk/json"
50
50
  ONE_DAY_IN_SEC = 24 * 60 * 60
51
51
  MAX_RETRIES = 3
52
+ LOGGING_PREFIX = "Fivetran-Connector-SDK"
53
+ LOGGING_DELIMITER = ": "
52
54
  VIRTUAL_ENV_CONFIG = "pyvenv.cfg"
53
55
 
54
56
  # Compile patterns used in the implementation
@@ -73,6 +75,8 @@ DEBUGGING = False
73
75
  EXECUTED_VIA_CLI = False
74
76
  PRODUCTION_BASE_URL = "https://api.fivetran.com"
75
77
  TABLES = {}
78
+ RENAMED_TABLE_NAMES = {}
79
+ RENAMED_COL_NAMES = {}
76
80
  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."
77
81
  INSTALLATION_SCRIPT = "installation.sh"
78
82
  DRIVERS = "drivers"
@@ -175,6 +179,8 @@ class Operations:
175
179
  field_name = get_renamed_column_name(field)
176
180
  columns[field_name] = common_pb2.Column(
177
181
  name=field_name, type=common_pb2.DataType.UNSPECIFIED, primary_key=False)
182
+ new_table = common_pb2.Table(name=table, columns=columns.values())
183
+ TABLES[table] = new_table
178
184
 
179
185
  mapped_data = _map_data_to_columns(data, columns)
180
186
  record = connector_sdk_pb2.Record(
@@ -324,7 +330,6 @@ def _get_columns(table: str) -> dict:
324
330
  columns = {}
325
331
  if table in TABLES:
326
332
  for column in TABLES[table].columns:
327
- column.name = get_renamed_column_name(column.name)
328
333
  columns[column.name] = column
329
334
 
330
335
  return columns
@@ -406,10 +411,7 @@ def map_defined_data_type(columns, k, mapped_data, v):
406
411
  mapped_data[k] = common_pb2.ValueType(naive_datetime=timestamp)
407
412
  elif columns[k].type == common_pb2.DataType.UTC_DATETIME:
408
413
  timestamp = timestamp_pb2.Timestamp()
409
- if '.' in v:
410
- dt = datetime.strptime(v, "%Y-%m-%dT%H:%M:%S.%f%z")
411
- else:
412
- dt = datetime.strptime(v, "%Y-%m-%dT%H:%M:%S%z")
414
+ dt = v if isinstance(v, datetime) else _parse_datetime_str(v)
413
415
  timestamp.FromDatetime(dt)
414
416
  mapped_data[k] = common_pb2.ValueType(utc_datetime=timestamp)
415
417
  elif columns[k].type == common_pb2.DataType.BINARY:
@@ -425,6 +427,10 @@ def map_defined_data_type(columns, k, mapped_data, v):
425
427
  raise ValueError(f"Unsupported data type encountered: {columns[k].type}. Please use valid data types.")
426
428
 
427
429
 
430
+ def _parse_datetime_str(dt):
431
+ return datetime.strptime(dt, "%Y-%m-%dT%H:%M:%S.%f%z" if '.' in dt else "%Y-%m-%dT%H:%M:%S%z")
432
+
433
+
428
434
  def _yield_check(stack):
429
435
  """Checks for the presence of 'yield' in the calling code.
430
436
  Args:
@@ -456,7 +462,7 @@ def _check_dict(incoming: dict, string_only: bool = False) -> dict:
456
462
  Args:
457
463
  incoming (dict): The dictionary to validate.
458
464
  string_only (bool): Whether to allow only string values.
459
-
465
+
460
466
  Returns:
461
467
  dict: The validated dictionary.
462
468
  """
@@ -527,9 +533,9 @@ def print_library_log(message: str, level: Logging.Level = Logging.Level.INFO):
527
533
  if DEBUGGING or EXECUTED_VIA_CLI:
528
534
  current_time = datetime.now().strftime("%b %d, %Y %I:%M:%S %p")
529
535
  escaped_message = json.dumps(message).strip('"')
530
- print(f"{current_time} {level.name}: {escaped_message}")
536
+ print(f"{current_time} {level.name} {LOGGING_PREFIX}: {escaped_message}")
531
537
  else:
532
- escaped_message = json.dumps(message)
538
+ escaped_message = json.dumps(LOGGING_PREFIX + LOGGING_DELIMITER + message)
533
539
  log_message = f'{{"level":"{level.name}", "message": {escaped_message}, "message_origin": "library"}}'
534
540
  print(log_message)
535
541
 
@@ -658,14 +664,20 @@ def get_renamed_table_name(source_table):
658
664
  """
659
665
  Process a source table name to ensure it conforms to naming rules.
660
666
  """
661
- return safe_drop_underscores(source_table)
667
+ if source_table not in RENAMED_TABLE_NAMES:
668
+ RENAMED_TABLE_NAMES[source_table] = safe_drop_underscores(source_table)
669
+
670
+ return RENAMED_TABLE_NAMES[source_table]
662
671
 
663
672
 
664
673
  def get_renamed_column_name(source_column):
665
674
  """
666
675
  Process a source column name to ensure it conforms to naming rules.
667
676
  """
668
- return redshift_safe(source_column)
677
+ if source_column not in RENAMED_COL_NAMES:
678
+ RENAMED_COL_NAMES[source_column] = redshift_safe(source_column)
679
+
680
+ return RENAMED_COL_NAMES[source_column]
669
681
 
670
682
  class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
671
683
  def __init__(self, update, schema=None):
@@ -928,7 +940,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
928
940
  self.__update_connection(
929
941
  args, connection_id, connection, group_name, connection_config, deploy_key, hd_agent_id)
930
942
  print("✓")
931
- print_library_log(f"Connector ID: {connection_id}")
943
+ print_library_log(f"Connection ID: {connection_id}")
932
944
  print_library_log(
933
945
  f"Visit the Fivetran dashboard to manage the connection: https://fivetran.com/dashboard/connectors/{connection_id}/status")
934
946
  else:
@@ -948,7 +960,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
948
960
  connection_id = response.json()['data']['id']
949
961
  # Python Version is not passed into connection_config as default value will be picked up from ConnectorSdkCredentials.java class.
950
962
  print_library_log(f"Python Version: {args.python_version if args.python_version else DEFAULT_PYTHON_VERSION}", Logging.Level.INFO)
951
- print_library_log(f"Connector ID: {connection_id}")
963
+ print_library_log(f"Connection ID: {connection_id}")
952
964
  print_library_log(
953
965
  f"Visit the Fivetran dashboard to start the initial sync: https://fivetran.com/dashboard/connectors/{connection_id}/status")
954
966
  else:
@@ -989,13 +1001,19 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
989
1001
  if not args.configuration:
990
1002
  del config["secrets_list"]
991
1003
 
1004
+ json_payload = {
1005
+ "config": config,
1006
+ "run_setup_tests": True
1007
+ }
1008
+
1009
+ # hybrid_deployment_agent_id is optional when redeploying your connection.
1010
+ # Customer can use it to change existing hybrid_deployment_agent_id.
1011
+ if hd_agent_id:
1012
+ json_payload["hybrid_deployment_agent_id"] = hd_agent_id
1013
+
992
1014
  response = rq.patch(f"{PRODUCTION_BASE_URL}/v1/connectors/{id}",
993
1015
  headers={"Authorization": f"Basic {deploy_key}"},
994
- json={
995
- "config": config,
996
- "hybrid_deployment_agent_id": hd_agent_id,
997
- "run_setup_tests": True
998
- })
1016
+ json=json_payload)
999
1017
 
1000
1018
  if response.ok and response.status_code == HTTPStatus.OK:
1001
1019
  if Connector.__are_setup_tests_failing(response):
@@ -1013,7 +1031,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
1013
1031
  print_library_log(log_message, Logging.Level.SEVERE)
1014
1032
  Connector.__print_failing_setup_tests(resp)
1015
1033
  connection_id = resp.json().get('data', {}).get('id')
1016
- print_library_log(f"Connector ID: {connection_id}")
1034
+ print_library_log(f"Connection ID: {connection_id}")
1017
1035
  print_library_log("Please try again with the deploy command after resolving the issue!")
1018
1036
  os._exit(1)
1019
1037
 
@@ -1414,7 +1432,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
1414
1432
 
1415
1433
  try:
1416
1434
  print_library_log("Running connector tester...")
1417
- for log_msg in self.__run_tester(java_exe, tester_root_dir, project_path, available_port):
1435
+ for log_msg in self.__run_tester(java_exe, tester_root_dir, project_path, available_port, json.dumps(self.state), json.dumps(self.configuration)):
1418
1436
  print(log_msg, end="")
1419
1437
  except:
1420
1438
  print(traceback.format_exc())
@@ -1455,7 +1473,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
1455
1473
  yield line
1456
1474
 
1457
1475
  @staticmethod
1458
- def __run_tester(java_exe: str, root_dir: str, project_path: str, port: int):
1476
+ def __run_tester(java_exe: str, root_dir: str, project_path: str, port: int, state_json: str, configuration_json: str):
1459
1477
  """Runs the connector tester.
1460
1478
 
1461
1479
  Args:
@@ -1478,7 +1496,9 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
1478
1496
  "--connector-sdk=true",
1479
1497
  f"--port={port}",
1480
1498
  f"--working-dir={working_dir}",
1481
- "--tester-type=source"]
1499
+ "--tester-type=source",
1500
+ f"--state={state_json}",
1501
+ f"--configuration={configuration_json}"]
1482
1502
 
1483
1503
  popen = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
1484
1504
  for line in Connector.process_stream(popen.stderr):
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: fivetran_connector_sdk
3
- Version: 1.2.1
3
+ Version: 1.3.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
@@ -1,4 +1,4 @@
1
- fivetran_connector_sdk/__init__.py,sha256=3Kp8MaT8ZtvrOo_dNGFGt5mdumojUSPhYwNL7x6CBvE,80242
1
+ fivetran_connector_sdk/__init__.py,sha256=4ovEZ7G46t5JFEAUmyGNksa0RVs9x5eKsYfzmyBDuNs,81121
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-1.2.1.dist-info/METADATA,sha256=3h_7qvx3Wv4GvLUTsNnTx2CLWCZpHtVTi-H-vScF4wA,2967
10
- fivetran_connector_sdk-1.2.1.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
11
- fivetran_connector_sdk-1.2.1.dist-info/entry_points.txt,sha256=uQn0KPnFlQmXJfxlk0tifdNsSXWfVlnAFzNqjXZM_xM,57
12
- fivetran_connector_sdk-1.2.1.dist-info/top_level.txt,sha256=-_xk2MFY4psIh7jw1lJePMzFb5-vask8_ZtX-UzYWUI,23
13
- fivetran_connector_sdk-1.2.1.dist-info/RECORD,,
9
+ fivetran_connector_sdk-1.3.1.dist-info/METADATA,sha256=qEnx2X-ac7iQhdJ7bx0p9yKhliRUAz_cxhRRgX3P-Jk,2967
10
+ fivetran_connector_sdk-1.3.1.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
11
+ fivetran_connector_sdk-1.3.1.dist-info/entry_points.txt,sha256=uQn0KPnFlQmXJfxlk0tifdNsSXWfVlnAFzNqjXZM_xM,57
12
+ fivetran_connector_sdk-1.3.1.dist-info/top_level.txt,sha256=-_xk2MFY4psIh7jw1lJePMzFb5-vask8_ZtX-UzYWUI,23
13
+ fivetran_connector_sdk-1.3.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (76.0.0)
2
+ Generator: setuptools (78.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5