tinybird-cli 3.8.1.dev1__tar.gz → 3.8.2.dev2__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 (46) hide show
  1. {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/PKG-INFO +18 -2
  2. {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/__cli__.py +2 -2
  3. {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/client.py +5 -2
  4. {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/datafile.py +28 -14
  5. {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/feedback_manager.py +1 -1
  6. {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/sql_template.py +71 -1
  7. {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/sql_toolset.py +1 -0
  8. {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/tb_cli_modules/cli.py +2 -2
  9. {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/tb_cli_modules/connection.py +27 -6
  10. {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird_cli.egg-info/PKG-INFO +18 -2
  11. {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/setup.cfg +0 -0
  12. {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/ch_utils/constants.py +0 -0
  13. {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/ch_utils/engine.py +0 -0
  14. {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/check_pypi.py +0 -0
  15. {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/config.py +0 -0
  16. {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/connectors.py +0 -0
  17. {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/context.py +0 -0
  18. {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/datatypes.py +0 -0
  19. {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/git_settings.py +0 -0
  20. {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/sql.py +0 -0
  21. {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/sql_template_fmt.py +0 -0
  22. {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/syncasync.py +0 -0
  23. {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/tb_cli.py +0 -0
  24. {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/tb_cli_modules/auth.py +0 -0
  25. {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/tb_cli_modules/branch.py +0 -0
  26. {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/tb_cli_modules/cicd.py +0 -0
  27. {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/tb_cli_modules/common.py +0 -0
  28. {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/tb_cli_modules/config.py +0 -0
  29. {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/tb_cli_modules/datasource.py +0 -0
  30. {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/tb_cli_modules/exceptions.py +0 -0
  31. {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/tb_cli_modules/job.py +0 -0
  32. {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/tb_cli_modules/pipe.py +0 -0
  33. {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/tb_cli_modules/regions.py +0 -0
  34. {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/tb_cli_modules/telemetry.py +0 -0
  35. {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/tb_cli_modules/test.py +0 -0
  36. {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/tb_cli_modules/tinyunit/tinyunit.py +0 -0
  37. {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/tb_cli_modules/tinyunit/tinyunit_lib.py +0 -0
  38. {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/tb_cli_modules/token.py +0 -0
  39. {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/tb_cli_modules/workspace.py +0 -0
  40. {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/tb_cli_modules/workspace_members.py +0 -0
  41. {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/tornado_template.py +0 -0
  42. {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird_cli.egg-info/SOURCES.txt +0 -0
  43. {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird_cli.egg-info/dependency_links.txt +0 -0
  44. {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird_cli.egg-info/entry_points.txt +0 -0
  45. {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird_cli.egg-info/requires.txt +0 -0
  46. {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/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: 3.8.1.dev1
3
+ Version: 3.8.2.dev2
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/cli/introduction.html
6
6
  Author: Tinybird
@@ -16,9 +16,25 @@ Tinybird CLI
16
16
  The Tinybird command-line tool allows you to use all the Tinybird functionality directly from the command line. Additionally, it includes several functions to create and manage data projects easily.
17
17
 
18
18
  Changelog
19
-
20
19
  ---------
21
20
 
21
+ 3.8.2.dev2
22
+ ************
23
+
24
+ - `Added` New `--policy` option for `create s3_iamrole` command that will generate different hints depending on the case
25
+
26
+ 3.8.2.dev1
27
+ ************
28
+
29
+ - `Added` Support for connection names when doing `tb connection rm`
30
+
31
+ 3.8.1
32
+ ************
33
+
34
+ - `Fixed` Avoid system vars evaluation when doing `tb fmt`
35
+ - `Fixed` environment variables substitution for Data Source engine parameters.
36
+
37
+
22
38
  3.8.0
23
39
  ************
24
40
 
@@ -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__ = '3.8.1.dev1'
8
- __revision__ = 'ee21c5f'
7
+ __version__ = '3.8.2.dev2'
8
+ __revision__ = 'b88d054'
@@ -997,8 +997,11 @@ class TinyB(object):
997
997
  async def get_s3_trust_policy(self) -> Dict[str, Any]:
998
998
  return await self._req("/v0/integrations/s3/policies/trust-policy")
999
999
 
1000
- async def get_s3_access_policy(self) -> Dict[str, Any]:
1001
- return await self._req("/v0/integrations/s3/policies/access-policy")
1000
+ async def get_s3_access_write_policy(self) -> Dict[str, Any]:
1001
+ return await self._req("/v0/integrations/s3/policies/write-access-policy")
1002
+
1003
+ async def get_s3_access_read_policy(self) -> Dict[str, Any]:
1004
+ return await self._req("/v0/integrations/s3/policies/read-access-policy")
1002
1005
 
1003
1006
  async def sql_get_format(self, sql: str, with_clickhouse_format: bool = False) -> str:
1004
1007
  try:
@@ -727,7 +727,9 @@ class Datafile:
727
727
  return True
728
728
 
729
729
 
730
- def parse_datasource(filename: str, replace_includes: bool = True, content: Optional[str] = None) -> Datafile:
730
+ def parse_datasource(
731
+ filename: str, replace_includes: bool = True, content: Optional[str] = None, skip_eval: bool = False
732
+ ) -> Datafile:
731
733
  basepath = ""
732
734
  if not content:
733
735
  with open(filename) as file:
@@ -737,7 +739,7 @@ def parse_datasource(filename: str, replace_includes: bool = True, content: Opti
737
739
  s = content
738
740
 
739
741
  try:
740
- doc = parse(s, "default", basepath, replace_includes=replace_includes)
742
+ doc = parse(s, "default", basepath, replace_includes=replace_includes, skip_eval=skip_eval)
741
743
  except ParseException as e:
742
744
  raise click.ClickException(
743
745
  FeedbackManager.error_parsing_file(filename=filename, lineno=e.lineno, error=e)
@@ -749,7 +751,9 @@ def parse_datasource(filename: str, replace_includes: bool = True, content: Opti
749
751
  return doc
750
752
 
751
753
 
752
- def parse_pipe(filename: str, replace_includes: bool = True, content: Optional[str] = None) -> Datafile:
754
+ def parse_pipe(
755
+ filename: str, replace_includes: bool = True, content: Optional[str] = None, skip_eval: bool = False
756
+ ) -> Datafile:
753
757
  basepath = ""
754
758
  if not content:
755
759
  with open(filename) as file:
@@ -760,7 +764,7 @@ def parse_pipe(filename: str, replace_includes: bool = True, content: Optional[s
760
764
 
761
765
  try:
762
766
  sql = ""
763
- doc = parse(s, basepath=basepath, replace_includes=replace_includes)
767
+ doc = parse(s, basepath=basepath, replace_includes=replace_includes, skip_eval=skip_eval)
764
768
  for node in doc.nodes:
765
769
  sql = node.get("sql", "")
766
770
  if sql.strip()[0] == "%":
@@ -795,7 +799,9 @@ def parse_pipe(filename: str, replace_includes: bool = True, content: Optional[s
795
799
  return doc
796
800
 
797
801
 
798
- def parse_token(filename: str, replace_includes: bool = True, content: Optional[str] = None) -> Datafile:
802
+ def parse_token(
803
+ filename: str, replace_includes: bool = True, content: Optional[str] = None, skip_eval: bool = False
804
+ ) -> Datafile:
799
805
  if not content:
800
806
  with open(filename) as file:
801
807
  s = file.read()
@@ -805,7 +811,7 @@ def parse_token(filename: str, replace_includes: bool = True, content: Optional[
805
811
 
806
812
  try:
807
813
  sql = ""
808
- doc = parse(s, basepath=basepath, replace_includes=replace_includes)
814
+ doc = parse(s, basepath=basepath, replace_includes=replace_includes, skip_eval=skip_eval)
809
815
  except ParseException as e:
810
816
  raise click.ClickException(
811
817
  FeedbackManager.error_parsing_file(
@@ -835,14 +841,20 @@ def _unquote(x: str):
835
841
  return x
836
842
 
837
843
 
838
- def eval_var(s: str) -> str:
844
+ def eval_var(s: str, skip: bool = False) -> str:
845
+ if skip:
846
+ return s
839
847
  # replace ENV variables
840
848
  # it's probably a bad idea to allow to get any env var
841
849
  return Template(s).safe_substitute(os.environ)
842
850
 
843
851
 
844
852
  def parse(
845
- s: str, default_node: Optional[str] = None, basepath: str = ".", replace_includes: bool = True
853
+ s: str,
854
+ default_node: Optional[str] = None,
855
+ basepath: str = ".",
856
+ replace_includes: bool = True,
857
+ skip_eval: bool = False,
846
858
  ) -> Datafile: # noqa: C901
847
859
  """
848
860
  Parses `s` string into a document
@@ -898,7 +910,7 @@ def parse(
898
910
  def assign_var(v: str) -> Callable[[VarArg(str), KwArg(Any)], None]:
899
911
  def _f(*args: str, **kwargs: Any):
900
912
  s = _unquote((" ".join(args)).strip())
901
- parser_state.current_node[v.lower()] = eval_var(s)
913
+ parser_state.current_node[v.lower()] = eval_var(s, skip=skip_eval)
902
914
 
903
915
  return _f
904
916
 
@@ -1029,12 +1041,12 @@ def parse(
1029
1041
  def set_engine(*args: str, **kwargs: Any) -> None:
1030
1042
  __init_engine("ENGINE")
1031
1043
  engine_type = _unquote((" ".join(args)).strip())
1032
- parser_state.current_node["engine"]["type"] = engine_type
1044
+ parser_state.current_node["engine"]["type"] = eval_var(engine_type, skip=skip_eval)
1033
1045
 
1034
1046
  def add_engine_var(v: str) -> Callable[[VarArg(str), KwArg(Any)], None]:
1035
1047
  def _f(*args: str, **kwargs: Any):
1036
1048
  __init_engine(f"ENGINE_{v}".upper())
1037
- engine_arg = _unquote((" ".join(args)).strip())
1049
+ engine_arg = eval_var(_unquote((" ".join(args)).strip()), skip=skip_eval)
1038
1050
  parser_state.current_node["engine"]["args"].append((v, engine_arg))
1039
1051
 
1040
1052
  return _f
@@ -4470,11 +4482,12 @@ async def format_datasource(
4470
4482
  replace_includes: bool = False,
4471
4483
  datafile: Optional[Datafile] = None,
4472
4484
  for_deploy_diff: bool = False,
4485
+ skip_eval: bool = False,
4473
4486
  ) -> str:
4474
4487
  if datafile:
4475
4488
  doc = datafile
4476
4489
  else:
4477
- doc = parse_datasource(filename, replace_includes=replace_includes)
4490
+ doc = parse_datasource(filename, replace_includes=replace_includes, skip_eval=skip_eval)
4478
4491
 
4479
4492
  file_parts: List[str] = []
4480
4493
  if for_diff:
@@ -4707,11 +4720,12 @@ async def format_pipe(
4707
4720
  replace_includes: bool = False,
4708
4721
  datafile: Optional[Datafile] = None,
4709
4722
  for_deploy_diff: bool = False,
4723
+ skip_eval: bool = False,
4710
4724
  ) -> str:
4711
4725
  if datafile:
4712
4726
  doc = datafile
4713
4727
  else:
4714
- doc = parse_pipe(filename, replace_includes=replace_includes)
4728
+ doc = parse_pipe(filename, replace_includes=replace_includes, skip_eval=skip_eval)
4715
4729
 
4716
4730
  file_parts: List[str] = []
4717
4731
  format_sources(file_parts, doc)
@@ -4738,7 +4752,7 @@ async def format_pipe(
4738
4752
  if "." in include_file
4739
4753
  else eval_var(include_file)
4740
4754
  )
4741
- included_pipe = parse_pipe(include_file)
4755
+ included_pipe = parse_pipe(include_file, skip_eval=skip_eval)
4742
4756
  pipe_nodes = doc.nodes.copy()
4743
4757
  for included_node in included_pipe.nodes.copy():
4744
4758
  unrolled_included_node = next(
@@ -205,7 +205,7 @@ class FeedbackManager:
205
205
  error_connection_already_exists = error_message(
206
206
  "Connection {name} already exists. Use 'tb connection ls' to list your connections"
207
207
  )
208
- error_connection_does_not_exists = error_message("Connection {connection} does not exist")
208
+ error_connection_does_not_exists = error_message("Connection {connection_id} does not exist")
209
209
  error_connection_create = error_message("Connection {connection_name} could not be created: {error}")
210
210
  error_connection_integration_not_available = error_message("Connection could not be created: {error}")
211
211
  error_workspace = error_message("Workspace {workspace} not found. use 'tb workspace ls' to list your workspaces")
@@ -1655,6 +1655,11 @@ def get_var_names_and_types(t, node_id=None): # noqa: C901
1655
1655
  raise SQLTemplateException(e)
1656
1656
 
1657
1657
 
1658
+ @lru_cache(maxsize=256)
1659
+ def get_var_names_and_types_cached(t: Template):
1660
+ return get_var_names_and_types(t)
1661
+
1662
+
1658
1663
  def wrap_vars(t):
1659
1664
  def _n(chunks, v):
1660
1665
  for x in chunks:
@@ -1746,8 +1751,55 @@ def get_template_and_variables(sql: str, name: Optional[str]):
1746
1751
  raise SQLTemplateException(e)
1747
1752
 
1748
1753
 
1754
+ def preprocess_variables(variables: dict, t: Template):
1755
+ """
1756
+ >>> preprocess_variables({"test": "1,2"}, Template("select * from table where f = {{Array(test, 'String')}}"))
1757
+ {'test': ['1', '2']}
1758
+ >>> preprocess_variables({"test": ['1', '2']}, Template("select * from table where f = {{Array(test, 'String')}}"))
1759
+ {'test': ['1', '2']}
1760
+ >>> preprocess_variables({"test": [1,2]}, Template("select * from table where f = {{Array(test, 'String')}}"))
1761
+ {'test': ['1', '2']}
1762
+ >>> preprocess_variables({"test": '24'}, Template("select * from table where f = {{Int32(test)}}"))
1763
+ {}
1764
+ >>> preprocess_variables({"test": "1,2,3"}, Template("select * from table where f = {{Array(test,'Int32')}}"))
1765
+ {'test': [1, 2, 3]}
1766
+ >>> preprocess_variables({"test": "1,2,msg"}, Template("select * from table where f = {{Array(test,'Int32')}}"))
1767
+ {}
1768
+ """
1769
+ template_variables = get_var_names_and_types_cached(t)
1770
+ processed_variables = {}
1771
+ for variable, value in variables.items():
1772
+ try:
1773
+ template_vars = [t_var for t_var in template_variables if t_var["name"] == variable] or None
1774
+ if template_vars is None or value is None:
1775
+ continue
1776
+
1777
+ t_var = template_vars[0]
1778
+ var_type = t_var.get("type")
1779
+ if var_type is None:
1780
+ continue
1781
+
1782
+ # For now, we only preprocess Array types
1783
+ match = re.match(r"Array\((\w+)\)", var_type)
1784
+ if match is None:
1785
+ continue
1786
+
1787
+ array_type = match.group(1)
1788
+ array_fn = type_fns.get("Array")
1789
+ parsed_exp = array_fn(value, array_type)
1790
+ processed_variables[variable] = ast.literal_eval(parsed_exp)
1791
+ except Exception:
1792
+ continue
1793
+
1794
+ return processed_variables
1795
+
1796
+
1749
1797
  def render_sql_template(
1750
- sql: str, variables: Optional[dict] = None, test_mode: bool = False, name: Optional[str] = None
1798
+ sql: str,
1799
+ variables: Optional[dict] = None,
1800
+ test_mode: bool = False,
1801
+ name: Optional[str] = None,
1802
+ preprocess_variables_flag: bool = False,
1751
1803
  ) -> Tuple[str, dict]:
1752
1804
  """
1753
1805
  >>> render_sql_template("select * from table where f = {{Float32(foo)}}", { 'foo': -1 })
@@ -1934,10 +1986,28 @@ def render_sql_template(
1934
1986
  Traceback (most recent call last):
1935
1987
  ...
1936
1988
  tinybird.sql_template.SQLTemplateException: Template Syntax Error: Error parsing JSON: '' - Expecting value: line 1 column 1 (char 0)
1989
+ >>> render_sql_template("% {% if defined(test) %}{% set _groupByCSV = ','.join(test) %} SELECT test as aa, {{Array(test, 'String')}} as test, {{_groupByCSV}} as a {% end %}", {"test": "1,2"}, preprocess_variables_flag=True)
1990
+ ("% SELECT test as aa, ['1','2'] as test, '1,2' as a ", {})
1991
+ >>> render_sql_template("% {% if defined(test) %}{% set _groupByCSV = ','.join(test) %} SELECT test as aa, {{Array(test, 'String')}} as test, {{_groupByCSV}} as a {% end %}", {"test": ["1","2"]}, preprocess_variables_flag=True)
1992
+ ("% SELECT test as aa, ['1','2'] as test, '1,2' as a ", {})
1993
+ >>> render_sql_template("% {% if defined(test) %}{% set _total = sum(test) %} SELECT test as aa, {{Array(test, 'Int32')}} as test, {{_total}} as a {% end %}", {"test": "1,2"}, preprocess_variables_flag=True)
1994
+ ('% SELECT test as aa, [1,2] as test, 3 as a ', {})
1995
+ >>> render_sql_template("% {% if defined(test) %}{% set _groupByCSV = ','.join(test) %} SELECT test as aa, {{Array(test, 'String')}} as test, {{_groupByCSV}} as a {% end %}", {"test": "1,2"})
1996
+ ("% SELECT test as aa, ['1','2'] as test, '1,,,2' as a ", {})
1997
+ >>> render_sql_template("% {% if defined(test) %}{% set _groupByCSV = ','.join(test) %} SELECT test as aa, {{Array(test, 'String')}} as test, {{_groupByCSV}} as a {% end %}", {"test": ["1","2"]})
1998
+ ("% SELECT test as aa, ['1','2'] as test, '1,2' as a ", {})
1999
+ >>> render_sql_template("% {% if defined(test) %}{% set _total = sum(test) %} SELECT test as aa, {{Array(test, 'Int32')}} as test, {{_total}} as a {% end %}", {"test": "1,2"})
2000
+ Traceback (most recent call last):
2001
+ ...
2002
+ ValueError: unsupported operand type(s) for +: 'int' and 'str'
1937
2003
  """
1938
2004
 
1939
2005
  t, template_variables = get_template_and_variables(sql, name)
1940
2006
 
2007
+ if preprocess_variables_flag and variables is not None:
2008
+ processed_variables = preprocess_variables(variables, t)
2009
+ variables.update(processed_variables)
2010
+
1941
2011
  if test_mode:
1942
2012
 
1943
2013
  def dummy(*args, **kwargs):
@@ -164,6 +164,7 @@ def replace_tables(
164
164
  valid_tables: Optional[Set[Tuple[str, str]]] = None,
165
165
  output_one_line: bool = False,
166
166
  timestamp: Optional[datetime] = None,
167
+ preprocess_variables_flag: bool = False,
167
168
  ) -> str:
168
169
  """
169
170
  Given a query and a list of table replacements, returns the query after applying the table replacements.
@@ -797,9 +797,9 @@ async def fmt(
797
797
  click.echo(filename)
798
798
  extensions = Path(filename).suffixes
799
799
  if is_file_a_datasource(filename):
800
- result = await format_datasource(filename)
800
+ result = await format_datasource(filename, skip_eval=True)
801
801
  elif (".pipe" in extensions) or (".incl" in extensions):
802
- result = await format_pipe(filename, line_length)
802
+ result = await format_pipe(filename, line_length, skip_eval=True)
803
803
  else:
804
804
  click.echo("Unsupported file type. Supported files types are: .pipe, .incl and .datasource")
805
805
  return None
@@ -394,25 +394,39 @@ async def connection_create_bigquery(ctx: Context, no_validate: bool) -> None:
394
394
 
395
395
 
396
396
  @connection.command(name="rm")
397
- @click.argument("connection_id")
397
+ @click.argument("connection_id_or_name")
398
398
  @click.option(
399
399
  "--force", default=False, help="Force connection removal even if there are datasources currently using it"
400
400
  )
401
401
  @click.pass_context
402
402
  @coro
403
- async def connection_rm(ctx: Context, connection_id: str, force: bool) -> None:
403
+ async def connection_rm(ctx: Context, connection_id_or_name: str, force: bool) -> None:
404
404
  """Remove a connection."""
405
405
 
406
406
  obj: Dict[str, Any] = ctx.ensure_object(dict)
407
407
  client: TinyB = obj["client"]
408
408
 
409
409
  try:
410
- await client.connector_delete(connection_id)
410
+ await client.connector_delete(connection_id_or_name)
411
411
  except DoesNotExistException:
412
- raise CLIConnectionException(FeedbackManager.error_connection_does_not_exists(connection_id=connection_id))
412
+ connections = await client.connections()
413
+ connection = next(
414
+ (connection for connection in connections if connection["name"] == connection_id_or_name), None
415
+ )
416
+ if connection:
417
+ try:
418
+ await client.connector_delete(connection["id"])
419
+ except DoesNotExistException:
420
+ raise CLIConnectionException(
421
+ FeedbackManager.error_connection_does_not_exists(connection_id=connection_id_or_name)
422
+ )
423
+ else:
424
+ raise CLIConnectionException(
425
+ FeedbackManager.error_connection_does_not_exists(connection_id=connection_id_or_name)
426
+ )
413
427
  except Exception as e:
414
428
  raise CLIConnectionException(FeedbackManager.error_exception(error=e))
415
- click.echo(FeedbackManager.success_delete_connection(connection_id=connection_id))
429
+ click.echo(FeedbackManager.success_delete_connection(connection_id=connection_id_or_name))
416
430
 
417
431
 
418
432
  @connection.command(name="ls")
@@ -698,6 +712,7 @@ async def connection_create_gcs(
698
712
  @click.option("--connection-name", default=None, help="The name of the connection to identify it in Tinybird")
699
713
  @click.option("--role-arn", default=None, help="The ARN of the IAM role to use for the connection")
700
714
  @click.option("--region", default=None, help="The Amazon S3 region where the bucket is located")
715
+ @click.option("--policy", default="write", help="The Amazon S3 access policy: write or read")
701
716
  @click.option(
702
717
  "--no-validate", is_flag=True, default=False, help="Do not validate S3 permissions during connection creation"
703
718
  )
@@ -708,6 +723,7 @@ async def connection_create_s3_iamrole(
708
723
  connection_name: Optional[str] = "",
709
724
  role_arn: Optional[str] = "",
710
725
  region: Optional[str] = "",
726
+ policy: str = "write",
711
727
  no_validate: Optional[bool] = False,
712
728
  ) -> None:
713
729
  """
@@ -723,7 +739,12 @@ async def connection_create_s3_iamrole(
723
739
  async def get_s3_policies():
724
740
  s3_access_policy: Dict[str, Any] = {}
725
741
  try:
726
- s3_access_policy = await client.get_s3_access_policy()
742
+ if policy == "write":
743
+ s3_access_policy = await client.get_s3_access_write_policy()
744
+ elif policy == "read":
745
+ s3_access_policy = await client.get_s3_access_read_policy()
746
+ else:
747
+ raise Exception(f"Access policy {policy} not supported. Choose from 'read' or 'write'")
727
748
  if not len(s3_access_policy) > 0:
728
749
  raise Exception("S3 Integration not supported in this region")
729
750
  except Exception as e:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tinybird-cli
3
- Version: 3.8.1.dev1
3
+ Version: 3.8.2.dev2
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/cli/introduction.html
6
6
  Author: Tinybird
@@ -16,9 +16,25 @@ Tinybird CLI
16
16
  The Tinybird command-line tool allows you to use all the Tinybird functionality directly from the command line. Additionally, it includes several functions to create and manage data projects easily.
17
17
 
18
18
  Changelog
19
-
20
19
  ---------
21
20
 
21
+ 3.8.2.dev2
22
+ ************
23
+
24
+ - `Added` New `--policy` option for `create s3_iamrole` command that will generate different hints depending on the case
25
+
26
+ 3.8.2.dev1
27
+ ************
28
+
29
+ - `Added` Support for connection names when doing `tb connection rm`
30
+
31
+ 3.8.1
32
+ ************
33
+
34
+ - `Fixed` Avoid system vars evaluation when doing `tb fmt`
35
+ - `Fixed` environment variables substitution for Data Source engine parameters.
36
+
37
+
22
38
  3.8.0
23
39
  ************
24
40