tinybird-cli 5.9.1.dev3__tar.gz → 5.10.1.dev0__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 (48) hide show
  1. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/PKG-INFO +5 -8
  2. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/tinybird/__cli__.py +2 -2
  3. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/tinybird/client.py +5 -3
  4. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/tinybird/datafile.py +25 -16
  5. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/tinybird/feedback_manager.py +2 -0
  6. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/tinybird/sql.py +1 -1
  7. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/tinybird/sql_template.py +4 -4
  8. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/tinybird/syncasync.py +4 -4
  9. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/tinybird/tb_cli_modules/cli.py +4 -4
  10. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/tinybird/tb_cli_modules/common.py +29 -6
  11. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/tinybird/tb_cli_modules/connection.py +12 -1
  12. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/tinybird/tb_cli_modules/datasource.py +1 -1
  13. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/tinybird/tornado_template.py +1 -1
  14. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/tinybird_cli.egg-info/PKG-INFO +5 -8
  15. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/setup.cfg +0 -0
  16. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/tinybird/ch_utils/constants.py +0 -0
  17. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/tinybird/ch_utils/engine.py +0 -0
  18. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/tinybird/check_pypi.py +0 -0
  19. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/tinybird/config.py +0 -0
  20. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/tinybird/connectors.py +0 -0
  21. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/tinybird/context.py +0 -0
  22. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/tinybird/datatypes.py +0 -0
  23. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/tinybird/git_settings.py +0 -0
  24. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/tinybird/sql_template_fmt.py +0 -0
  25. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/tinybird/sql_toolset.py +0 -0
  26. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/tinybird/tb_cli.py +0 -0
  27. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/tinybird/tb_cli_modules/auth.py +0 -0
  28. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/tinybird/tb_cli_modules/branch.py +0 -0
  29. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/tinybird/tb_cli_modules/cicd.py +0 -0
  30. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/tinybird/tb_cli_modules/config.py +0 -0
  31. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/tinybird/tb_cli_modules/exceptions.py +0 -0
  32. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/tinybird/tb_cli_modules/fmt.py +0 -0
  33. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/tinybird/tb_cli_modules/job.py +0 -0
  34. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/tinybird/tb_cli_modules/pipe.py +0 -0
  35. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/tinybird/tb_cli_modules/regions.py +0 -0
  36. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/tinybird/tb_cli_modules/tag.py +0 -0
  37. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/tinybird/tb_cli_modules/telemetry.py +0 -0
  38. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/tinybird/tb_cli_modules/test.py +0 -0
  39. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/tinybird/tb_cli_modules/tinyunit/tinyunit.py +0 -0
  40. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/tinybird/tb_cli_modules/tinyunit/tinyunit_lib.py +0 -0
  41. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/tinybird/tb_cli_modules/token.py +0 -0
  42. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/tinybird/tb_cli_modules/workspace.py +0 -0
  43. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/tinybird/tb_cli_modules/workspace_members.py +0 -0
  44. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/tinybird_cli.egg-info/SOURCES.txt +0 -0
  45. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/tinybird_cli.egg-info/dependency_links.txt +0 -0
  46. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/tinybird_cli.egg-info/entry_points.txt +0 -0
  47. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/tinybird_cli.egg-info/requires.txt +0 -0
  48. {tinybird-cli-5.9.1.dev3 → tinybird-cli-5.10.1.dev0}/tinybird_cli.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tinybird-cli
3
- Version: 5.9.1.dev3
3
+ Version: 5.10.1.dev0
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/cli/introduction.html
6
6
  Author: Tinybird
@@ -18,16 +18,13 @@ The Tinybird command-line tool allows you to use all the Tinybird functionality
18
18
  Changelog
19
19
  ----------
20
20
 
21
- 5.9.1.dev3
21
+ 5.10.0
22
22
  ***********
23
23
 
24
- - `Fixed` Correctly parse lambda expressions in indexes
25
-
26
- 5.9.1.dev2
27
- ***********
28
-
29
- - `Changed` Upgrade clickhouse-toolset to 0.32.dev0
30
24
  - `Added` new "File not found" error to `tb check` when including files from missing paths.
25
+ - `Added` support for Kafka Data Sources with CA certificate.
26
+ - `Changed` Upgrade clickhouse-toolset to 0.32.dev0
27
+ - `Fixed` Correctly parse lambda expressions in indexes
31
28
 
32
29
  5.9.0
33
30
  ***********
@@ -4,5 +4,5 @@ __description__ = 'Tinybird Command Line Tool'
4
4
  __url__ = 'https://www.tinybird.co/docs/cli/introduction.html'
5
5
  __author__ = 'Tinybird'
6
6
  __author_email__ = 'support@tinybird.co'
7
- __version__ = '5.9.1.dev3'
8
- __revision__ = '6e69bc3'
7
+ __version__ = '5.10.1.dev0'
8
+ __revision__ = 'f8aa90b'
@@ -111,7 +111,7 @@ class TinyB(object):
111
111
  retries: int = LIMIT_RETRIES,
112
112
  use_token: Optional[str] = None,
113
113
  **kwargs,
114
- ): # noqa: C901
114
+ ):
115
115
  url = f"{self.host.strip('/')}/{endpoint.strip('/')}"
116
116
 
117
117
  token_to_use = use_token if use_token else self.token
@@ -275,7 +275,7 @@ class TinyB(object):
275
275
  bigquery_connection = (
276
276
  await self.bigquery_connection() if connector == "bigquery" or connector is None else None
277
277
  )
278
- connectors = connectors + [bigquery_connection] if bigquery_connection else connectors
278
+ connectors = [*connectors, bigquery_connection] if bigquery_connection else connectors
279
279
  if connector:
280
280
  return [
281
281
  {
@@ -865,6 +865,7 @@ class TinyB(object):
865
865
  kafka_auto_offset_reset=None,
866
866
  kafka_schema_registry_url=None,
867
867
  kafka_sasl_mechanism="PLAIN",
868
+ kafka_ssl_ca_pem=None,
868
869
  ):
869
870
  params = {
870
871
  "service": "kafka",
@@ -880,7 +881,8 @@ class TinyB(object):
880
881
  params["kafka_schema_registry_url"] = kafka_schema_registry_url
881
882
  if kafka_auto_offset_reset:
882
883
  params["kafka_auto_offset_reset"] = kafka_auto_offset_reset
883
-
884
+ if kafka_ssl_ca_pem:
885
+ params["kafka_ssl_ca_pem"] = kafka_ssl_ca_pem
884
886
  connection_params = {key: value for key, value in params.items() if value is not None}
885
887
 
886
888
  return await self._req(
@@ -60,7 +60,13 @@ from toposort import toposort
60
60
  from tinybird.config import PROJECT_PATHS
61
61
  from tinybird.sql_template_fmt import DEFAULT_FMT_LINE_LENGTH, format_sql_template
62
62
  from tinybird.syncasync import sync_to_async
63
- from tinybird.tb_cli_modules.common import _get_tb_client, get_current_main_workspace, getenv_bool, wait_job
63
+ from tinybird.tb_cli_modules.common import (
64
+ _get_tb_client,
65
+ get_ca_pem_content,
66
+ get_current_main_workspace,
67
+ getenv_bool,
68
+ wait_job,
69
+ )
64
70
  from tinybird.tb_cli_modules.config import CLIConfig
65
71
  from tinybird.tb_cli_modules.exceptions import CLIGitReleaseException, CLIPipeException
66
72
 
@@ -72,7 +78,7 @@ from .sql_template import get_template_and_variables, get_used_tables_in_templat
72
78
  from .tornado_template import UnClosedIfError
73
79
 
74
80
  os.environ["GIT_PYTHON_REFRESH"] = "quiet"
75
- from git import HEAD, Diff, GitCommandError, InvalidGitRepositoryError, Repo # noqa: E402
81
+ from git import HEAD, Diff, GitCommandError, InvalidGitRepositoryError, Repo
76
82
 
77
83
  INTERNAL_TABLES: Tuple[str, ...] = (
78
84
  "datasources_ops_log",
@@ -960,7 +966,7 @@ def parse(
960
966
  basepath: str = ".",
961
967
  replace_includes: bool = True,
962
968
  skip_eval: bool = False,
963
- ) -> Datafile: # noqa: C901
969
+ ) -> Datafile:
964
970
  """
965
971
  Parses `s` string into a document
966
972
  >>> d = parse("FROM SCRATCH\\nSOURCE 'https://example.com'\\n#this is a comment\\nMAINTAINER 'rambo' #this is me\\nNODE \\"test_01\\"\\n DESCRIPTION this is a node that does whatever\\nSQL >\\n\\n SELECT * from test_00\\n\\n\\nNODE \\"test_02\\"\\n DESCRIPTION this is a node that does whatever\\nSQL >\\n\\n SELECT * from test_01\\n WHERE a > 1\\n GROUP by a\\n")
@@ -1110,9 +1116,10 @@ def parse(
1110
1116
  pass
1111
1117
  finally:
1112
1118
  file.seek(0)
1113
- lines[lineno : lineno + 1] = [""] + list(
1114
- StringIO(Template(file.read()).safe_substitute(attrs), newline=None)
1115
- )
1119
+ lines[lineno : lineno + 1] = [
1120
+ "",
1121
+ *list(StringIO(Template(file.read()).safe_substitute(attrs), newline=None)),
1122
+ ]
1116
1123
  except FileNotFoundError:
1117
1124
  raise IncludeFileNotFoundException(f, lineno)
1118
1125
 
@@ -1211,6 +1218,7 @@ def parse(
1211
1218
  "kafka_store_headers": assign_var("kafka_store_headers"),
1212
1219
  "kafka_store_binary_headers": assign_var("kafka_store_binary_headers"),
1213
1220
  "kafka_key_avro_deserialization": assign_var("kafka_key_avro_deserialization"),
1221
+ "kafka_ssl_ca_pem": assign_var("kafka_ssl_ca_pem"),
1214
1222
  "import_service": assign_var("import_service"),
1215
1223
  "import_connection_name": assign_var("import_connection_name"),
1216
1224
  "import_schedule": assign_var("import_schedule"),
@@ -1318,7 +1326,7 @@ async def process_file(
1318
1326
  workspace_map: Optional[Dict] = None,
1319
1327
  workspace_lib_paths: Optional[List[Tuple[str, str]]] = None,
1320
1328
  current_ws: Optional[Dict[str, Any]] = None,
1321
- ): # noqa: C901 B006
1329
+ ):
1322
1330
  if workspace_map is None:
1323
1331
  workspace_map = {}
1324
1332
 
@@ -1349,6 +1357,7 @@ async def process_file(
1349
1357
  "kafka_connection_name": params.get("kafka_connection_name", None),
1350
1358
  "kafka_auto_offset_reset": params.get("kafka_auto_offset_reset", None),
1351
1359
  "kafka_schema_registry_url": params.get("kafka_schema_registry_url", None),
1360
+ "kafka_ssl_ca_pem": get_ca_pem_content(params.get("kafka_ssl_ca_pem", None), filename),
1352
1361
  }
1353
1362
 
1354
1363
  connector = await tb_client.get_connection(**connector_params)
@@ -2868,7 +2877,7 @@ async def new_pipe(
2868
2877
  config: Any = None,
2869
2878
  fork_downstream: Optional[bool] = False,
2870
2879
  fork: Optional[bool] = False,
2871
- ): # noqa: C901
2880
+ ):
2872
2881
  # TODO use tb_client instead of calling the urls directly.
2873
2882
  host = tb_client.host
2874
2883
  token = tb_client.token
@@ -3270,10 +3279,10 @@ async def new_ds(
3270
3279
  if job_url:
3271
3280
  click.echo(FeedbackManager.success_dynamodb_initial_load(job_url=job_url))
3272
3281
 
3273
- if "tokens" in ds and ds["tokens"]:
3282
+ if ds.get("tokens"):
3274
3283
  await manage_tokens()
3275
3284
 
3276
- if "shared_with" in ds and ds["shared_with"]:
3285
+ if ds.get("shared_with"):
3277
3286
  if not user_token:
3278
3287
  click.echo(FeedbackManager.info_skipping_shared_with_entry())
3279
3288
  else:
@@ -4189,7 +4198,7 @@ async def folder_push(
4189
4198
  use_main: bool = False,
4190
4199
  check_outdated: bool = True,
4191
4200
  hide_folders: bool = False,
4192
- ): # noqa: C901
4201
+ ):
4193
4202
  workspaces: List[Dict[str, Any]] = (await tb_client.user_workspaces_and_branches()).get("workspaces", [])
4194
4203
  current_ws: Dict[str, Any] = next(
4195
4204
  (workspace for workspace in workspaces if config and workspace.get("id", ".") == config.get("id", "..")), {}
@@ -4754,7 +4763,7 @@ DATAFILE_INDENT = " " * 4
4754
4763
 
4755
4764
 
4756
4765
  def format_schema(file_parts: List[str], node: Dict[str, Any]) -> List[str]:
4757
- if "schema" in node and node["schema"]:
4766
+ if node.get("schema"):
4758
4767
  file_parts.append("SCHEMA >")
4759
4768
  file_parts.append(DATAFILE_NEW_LINE)
4760
4769
  columns = schema_to_sql_columns(node["columns"])
@@ -4766,7 +4775,7 @@ def format_schema(file_parts: List[str], node: Dict[str, Any]) -> List[str]:
4766
4775
 
4767
4776
 
4768
4777
  def format_indices(file_parts: List[str], node: Dict[str, Any]) -> List[str]:
4769
- if "indexes" in node and node["indexes"]:
4778
+ if node.get("indexes"):
4770
4779
  indexes = node["indexes"]
4771
4780
  file_parts.append("INDEXES >")
4772
4781
  file_parts.append(DATAFILE_NEW_LINE)
@@ -4995,7 +5004,7 @@ async def format_node_type(file_parts: List[str], node: Dict[str, Any]) -> List[
4995
5004
  file_parts.append(DATAFILE_NEW_LINE)
4996
5005
  file_parts.append(f'COPY_MODE {node.get("mode")}')
4997
5006
 
4998
- if CopyParameters.COPY_SCHEDULE in node and node[CopyParameters.COPY_SCHEDULE]:
5007
+ if node.get(CopyParameters.COPY_SCHEDULE):
4999
5008
  is_ondemand = node[CopyParameters.COPY_SCHEDULE].lower() == ON_DEMAND
5000
5009
  file_parts.append(DATAFILE_NEW_LINE)
5001
5010
  file_parts.append(
@@ -5154,7 +5163,7 @@ async def folder_pull(
5154
5163
  verbose: bool = True,
5155
5164
  progress_bar: bool = False,
5156
5165
  fmt: bool = False,
5157
- ): # noqa: C901
5166
+ ):
5158
5167
  pattern = re.compile(match) if match else None
5159
5168
 
5160
5169
  def _get_latest_versions(resources: List[str]):
@@ -5321,7 +5330,7 @@ async def diff_command(
5321
5330
 
5322
5331
  if filenames:
5323
5332
  if len(filenames) == 1:
5324
- filenames = [filenames[0]] + get_project_filenames(filenames[0])
5333
+ filenames = [filenames[0], *get_project_filenames(filenames[0])]
5325
5334
  await folder_pull(client, target_dir, False, None, True, verbose=False)
5326
5335
  else:
5327
5336
  filenames = get_project_filenames(".")
@@ -238,6 +238,8 @@ class FeedbackManager:
238
238
  error_connection_does_not_exists = error_message("Connection {connection_id} does not exist")
239
239
  error_connection_create = error_message("Connection {connection_name} could not be created: {error}")
240
240
  error_connection_integration_not_available = error_message("Connection could not be created: {error}")
241
+ error_connection_invalid_ca_pem = error_message("Invalid CA certificate in PEM format")
242
+ error_connection_ca_pem_not_found = error_message("CA certificate in PEM format not found at {ca_pem}")
241
243
  error_workspace = error_message("Workspace {workspace} not found. use 'tb workspace ls' to list your workspaces")
242
244
  error_deleted_include = error_message(
243
245
  "Related include file {include_file} was deleted and it's used in {filename}. Delete or remove dependency from {filename}."
@@ -526,7 +526,7 @@ REGEX_WHITESPACE = re.compile(r"\s*")
526
526
  REGEX_COMMENT = re.compile(r"\-\-[^\n\r]*[\n\r]")
527
527
 
528
528
 
529
- def _parse_table_structure(schema: str) -> List[Dict[str, Any]]: # noqa: C901
529
+ def _parse_table_structure(schema: str) -> List[Dict[str, Any]]:
530
530
  # CH syntax from https://clickhouse.com/docs/en/sql-reference/statements/create/table/
531
531
  # name1 [type1] [NULL|NOT NULL] [DEFAULT|MATERIALIZED|ALIAS expr1] [compression_codec] [TTL expr1]
532
532
  try:
@@ -359,7 +359,7 @@ def defined(x=None):
359
359
  return True
360
360
 
361
361
 
362
- def array_type(types): # noqa: C901
362
+ def array_type(types):
363
363
  def _f(
364
364
  x, _type=None, default=None, defined=True, required=None, description=None, enum=None, example=None, format=None
365
365
  ):
@@ -1370,7 +1370,7 @@ _namespace = {
1370
1370
  }
1371
1371
 
1372
1372
 
1373
- reserved_vars = set(["_tt_tmp", "_tt_append", "isinstance", "str", "error", "custom_error"] + list(vars(builtins)))
1373
+ reserved_vars = set(["_tt_tmp", "_tt_append", "isinstance", "str", "error", "custom_error", *list(vars(builtins))])
1374
1374
  for p in DEFAULT_PARAM_NAMES: # we handle these in an specific manner
1375
1375
  reserved_vars.discard(p) # `format` is part of builtins
1376
1376
  error_vars = ["error", "custom_error"]
@@ -1552,7 +1552,7 @@ def get_var_names(t):
1552
1552
  raise SQLTemplateException(e)
1553
1553
 
1554
1554
 
1555
- def get_var_data(content, node_id=None): # noqa: C901
1555
+ def get_var_data(content, node_id=None):
1556
1556
  def node_to_value(x):
1557
1557
  if type(x) in (ast.Bytes, ast.Str):
1558
1558
  return x.s
@@ -1703,7 +1703,7 @@ def get_var_data(content, node_id=None): # noqa: C901
1703
1703
  return [dict(name=k, **v) for k, v in vars.items()]
1704
1704
 
1705
1705
 
1706
- def get_var_names_and_types(t, node_id=None): # noqa: C901
1706
+ def get_var_names_and_types(t, node_id=None):
1707
1707
  """
1708
1708
  >>> get_var_names_and_types(Template("SELECT * FROM filter_value WHERE description = {{Float32(with_value, 0.0)}}"))
1709
1709
  [{'name': 'with_value', 'type': 'Float32', 'default': 0.0}]
@@ -177,7 +177,7 @@ class _WorkItem:
177
177
  return
178
178
  try:
179
179
  result = self.fn(*self.args, **self.kwargs)
180
- except BaseException as exc: # noqa: B036
180
+ except BaseException as exc:
181
181
  self.future.set_exception(exc)
182
182
  # Break a reference cycle with the exception 'exc'
183
183
  self = None
@@ -473,11 +473,11 @@ class AsyncToSync:
473
473
  if exc_info[1]:
474
474
  try:
475
475
  raise exc_info[1]
476
- except BaseException: # noqa: B036
476
+ except BaseException:
477
477
  result = await self.awaitable(*args, **kwargs)
478
478
  else:
479
479
  result = await self.awaitable(*args, **kwargs)
480
- except BaseException as e: # noqa: B036
480
+ except BaseException as e:
481
481
  call_result.set_exception(e)
482
482
  else:
483
483
  call_result.set_result(result)
@@ -639,7 +639,7 @@ class SyncToAsync:
639
639
  if exc_info[1]:
640
640
  try:
641
641
  raise exc_info[1]
642
- except BaseException: # noqa: B036
642
+ except BaseException:
643
643
  return func(*args, **kwargs)
644
644
  else:
645
645
  return func(*args, **kwargs)
@@ -76,7 +76,7 @@ DEFAULT_PATTERNS: List[Tuple[str, Union[str, Callable[[str], str]]]] = [
76
76
  ]
77
77
 
78
78
 
79
- @click.group(cls=CatchAuthExceptions, context_settings={"help_option_names": ["-h", "--help"]}) # noqa: C901
79
+ @click.group(cls=CatchAuthExceptions, context_settings={"help_option_names": ["-h", "--help"]})
80
80
  @click.option(
81
81
  "--debug/--no-debug",
82
82
  default=False,
@@ -212,7 +212,7 @@ async def cli(
212
212
  if ctx.invoked_subcommand == "auth":
213
213
  return
214
214
 
215
- from tinybird.connectors import create_connector # noqa
215
+ from tinybird.connectors import create_connector
216
216
 
217
217
  if gcp_project_id and gcs_bucket and google_application_credentials and not sf_account:
218
218
  bq_config = {
@@ -679,7 +679,7 @@ async def push(
679
679
  return
680
680
 
681
681
 
682
- @cli.command() # noqa: C901
682
+ @cli.command()
683
683
  @click.option(
684
684
  "--folder", default=None, type=click.Path(exists=True, file_okay=False), help="Folder where files will be placed"
685
685
  )
@@ -1251,7 +1251,7 @@ def __unpatch_click_output():
1251
1251
  click.secho = __old_click_echo
1252
1252
 
1253
1253
 
1254
- @cli.command(short_help="Learn how to include info about the CLI in your shell PROMPT") # noqa: C901
1254
+ @cli.command(short_help="Learn how to include info about the CLI in your shell PROMPT")
1255
1255
  @click.pass_context
1256
1256
  @coro
1257
1257
  async def prompt(_ctx: Context) -> None:
@@ -1133,17 +1133,15 @@ async def push_data(
1133
1133
  appended_rows = 0
1134
1134
  parser = None
1135
1135
 
1136
- if "error" in res and res["error"]:
1136
+ if res.get("error"):
1137
1137
  raise CLIException(FeedbackManager.error_exception(error=res["error"]))
1138
- if "errors" in res and res["errors"]:
1138
+ if res.get("errors"):
1139
1139
  raise CLIException(FeedbackManager.error_exception(error=res["errors"]))
1140
- if "blocks" in res and res["blocks"]:
1140
+ if res.get("blocks"):
1141
1141
  for block in res["blocks"]:
1142
1142
  if "process_return" in block and block["process_return"] is not None:
1143
1143
  process_return = block["process_return"][0]
1144
- parser = (
1145
- process_return["parser"] if "parser" in process_return and process_return["parser"] else parser
1146
- )
1144
+ parser = process_return["parser"] if process_return.get("parser") else parser
1147
1145
  if parser and parser != "clickhouse":
1148
1146
  parser = process_return["parser"]
1149
1147
  appended_rows += process_return["lines"]
@@ -2056,3 +2054,28 @@ async def create_aws_iamrole_connection(client: TinyB, service: str, connection_
2056
2054
  """
2057
2055
  )
2058
2056
  click.echo(FeedbackManager.success_connection_file_created(name=conn_file_name))
2057
+
2058
+
2059
+ def get_ca_pem_content(ca_pem: Optional[str], filename: Optional[str] = None) -> Optional[str]:
2060
+ if not ca_pem:
2061
+ return None
2062
+
2063
+ def is_valid_content(text_content: str) -> bool:
2064
+ return text_content.startswith("-----BEGIN CERTIFICATE-----")
2065
+
2066
+ ca_pem_content = ca_pem
2067
+ base_path = Path(getcwd(), filename).parent if filename else Path(getcwd())
2068
+ ca_pem_path = Path(base_path, ca_pem)
2069
+ path_exists = os.path.exists(ca_pem_path)
2070
+
2071
+ if not path_exists:
2072
+ raise CLIConnectionException(FeedbackManager.error_connection_ca_pem_not_found(ca_pem=ca_pem))
2073
+
2074
+ if ca_pem.endswith(".pem") and path_exists:
2075
+ with open(ca_pem_path, "r") as f:
2076
+ ca_pem_content = f.read()
2077
+
2078
+ if not is_valid_content(ca_pem_content):
2079
+ raise CLIConnectionException(FeedbackManager.error_connection_invalid_ca_pem())
2080
+
2081
+ return ca_pem_content
@@ -21,6 +21,7 @@ from tinybird.tb_cli_modules.common import (
21
21
  coro,
22
22
  create_aws_iamrole_connection,
23
23
  echo_safe_humanfriendly_tables_format_smart_table,
24
+ get_ca_pem_content,
24
25
  validate_aws_iamrole_connection_name,
25
26
  validate_aws_iamrole_integration,
26
27
  validate_connection_name,
@@ -44,6 +45,7 @@ DATA_CONNECTOR_SETTINGS: Dict[DataConnectorType, List[str]] = {
44
45
  "kafka_security_protocol",
45
46
  "kafka_sasl_mechanism",
46
47
  "kafka_schema_registry_url",
48
+ "kafka_ssl_ca_pem",
47
49
  ],
48
50
  DataConnectorType.GCLOUD_SCHEDULER: ["gcscheduler_region"],
49
51
  DataConnectorType.SNOWFLAKE: [
@@ -131,6 +133,7 @@ def connection_create(ctx: Context) -> None:
131
133
  default="PLAIN",
132
134
  help="Authentication method for connection-based protocols. Defaults to 'PLAIN'",
133
135
  )
136
+ @click.option("--ssl-ca-pem", default=None, help="Path or content of the CA Certificate file in PEM format")
134
137
  @click.pass_context
135
138
  @coro
136
139
  async def connection_create_kafka(
@@ -142,6 +145,7 @@ async def connection_create_kafka(
142
145
  auto_offset_reset: Optional[str],
143
146
  schema_registry_url: Optional[str],
144
147
  sasl_mechanism: Optional[str],
148
+ ssl_ca_pem: Optional[str],
145
149
  ) -> None:
146
150
  """
147
151
  Add a Kafka connection
@@ -174,7 +178,14 @@ async def connection_create_kafka(
174
178
  client: TinyB = obj["client"]
175
179
 
176
180
  result = await client.connection_create_kafka(
177
- bootstrap_servers, key, secret, connection_name, auto_offset_reset, schema_registry_url, sasl_mechanism
181
+ bootstrap_servers,
182
+ key,
183
+ secret,
184
+ connection_name,
185
+ auto_offset_reset,
186
+ schema_registry_url,
187
+ sasl_mechanism,
188
+ get_ca_pem_content(ssl_ca_pem),
178
189
  )
179
190
 
180
191
  id = result["id"]
@@ -229,7 +229,7 @@ async def datasource_replace(
229
229
 
230
230
  - Replace from local file `tb datasource replace [datasource_name] /path/to/local/file --sql-condition "country='ES'"`
231
231
 
232
- - Replace from connector `tb datasource replace [datasource_name] --connector [connector_name] --sql [the_sql_to_extract_from] --sql-condition "country='ES'"`
232
+ - Replace from connector `tb datasource replace [datasource_name] --connector [connector_name] --sql [the_sql_to_extract_from] --sql-condition "country='ES'"`
233
233
  """
234
234
 
235
235
  if not url and not connector:
@@ -848,7 +848,7 @@ def _parse(reader: _TemplateReader, template, in_block=None, in_loop=None):
848
848
  # When there are more than 2 curlies in a row, use the
849
849
  # innermost ones. This is useful when generating languages
850
850
  # like latex where curlies are also meaningful
851
- if curly + 2 < reader.remaining() and reader[curly + 1] == "{" and reader[curly + 2] == "{": # noqa: W504
851
+ if curly + 2 < reader.remaining() and reader[curly + 1] == "{" and reader[curly + 2] == "{":
852
852
  curly += 1
853
853
  continue
854
854
  break
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tinybird-cli
3
- Version: 5.9.1.dev3
3
+ Version: 5.10.1.dev0
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/cli/introduction.html
6
6
  Author: Tinybird
@@ -18,16 +18,13 @@ The Tinybird command-line tool allows you to use all the Tinybird functionality
18
18
  Changelog
19
19
  ----------
20
20
 
21
- 5.9.1.dev3
21
+ 5.10.0
22
22
  ***********
23
23
 
24
- - `Fixed` Correctly parse lambda expressions in indexes
25
-
26
- 5.9.1.dev2
27
- ***********
28
-
29
- - `Changed` Upgrade clickhouse-toolset to 0.32.dev0
30
24
  - `Added` new "File not found" error to `tb check` when including files from missing paths.
25
+ - `Added` support for Kafka Data Sources with CA certificate.
26
+ - `Changed` Upgrade clickhouse-toolset to 0.32.dev0
27
+ - `Fixed` Correctly parse lambda expressions in indexes
31
28
 
32
29
  5.9.0
33
30
  ***********