tinybird-cli 5.4.1.dev2__tar.gz → 5.4.1.dev4__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-5.4.1.dev2 → tinybird-cli-5.4.1.dev4}/PKG-INFO +11 -1
  2. {tinybird-cli-5.4.1.dev2 → tinybird-cli-5.4.1.dev4}/tinybird/__cli__.py +2 -2
  3. {tinybird-cli-5.4.1.dev2 → tinybird-cli-5.4.1.dev4}/tinybird/datafile.py +8 -14
  4. {tinybird-cli-5.4.1.dev2 → tinybird-cli-5.4.1.dev4}/tinybird/feedback_manager.py +0 -1
  5. {tinybird-cli-5.4.1.dev2 → tinybird-cli-5.4.1.dev4}/tinybird/sql_template.py +61 -16
  6. {tinybird-cli-5.4.1.dev2 → tinybird-cli-5.4.1.dev4}/tinybird/tb_cli_modules/common.py +27 -3
  7. {tinybird-cli-5.4.1.dev2 → tinybird-cli-5.4.1.dev4}/tinybird/tb_cli_modules/datasource.py +3 -0
  8. {tinybird-cli-5.4.1.dev2 → tinybird-cli-5.4.1.dev4}/tinybird/tb_cli_modules/workspace.py +3 -0
  9. {tinybird-cli-5.4.1.dev2 → tinybird-cli-5.4.1.dev4}/tinybird/tb_cli_modules/workspace_members.py +4 -0
  10. {tinybird-cli-5.4.1.dev2 → tinybird-cli-5.4.1.dev4}/tinybird_cli.egg-info/PKG-INFO +11 -1
  11. {tinybird-cli-5.4.1.dev2 → tinybird-cli-5.4.1.dev4}/setup.cfg +0 -0
  12. {tinybird-cli-5.4.1.dev2 → tinybird-cli-5.4.1.dev4}/tinybird/ch_utils/constants.py +0 -0
  13. {tinybird-cli-5.4.1.dev2 → tinybird-cli-5.4.1.dev4}/tinybird/ch_utils/engine.py +0 -0
  14. {tinybird-cli-5.4.1.dev2 → tinybird-cli-5.4.1.dev4}/tinybird/check_pypi.py +0 -0
  15. {tinybird-cli-5.4.1.dev2 → tinybird-cli-5.4.1.dev4}/tinybird/client.py +0 -0
  16. {tinybird-cli-5.4.1.dev2 → tinybird-cli-5.4.1.dev4}/tinybird/config.py +0 -0
  17. {tinybird-cli-5.4.1.dev2 → tinybird-cli-5.4.1.dev4}/tinybird/connectors.py +0 -0
  18. {tinybird-cli-5.4.1.dev2 → tinybird-cli-5.4.1.dev4}/tinybird/context.py +0 -0
  19. {tinybird-cli-5.4.1.dev2 → tinybird-cli-5.4.1.dev4}/tinybird/datatypes.py +0 -0
  20. {tinybird-cli-5.4.1.dev2 → tinybird-cli-5.4.1.dev4}/tinybird/git_settings.py +0 -0
  21. {tinybird-cli-5.4.1.dev2 → tinybird-cli-5.4.1.dev4}/tinybird/sql.py +0 -0
  22. {tinybird-cli-5.4.1.dev2 → tinybird-cli-5.4.1.dev4}/tinybird/sql_template_fmt.py +0 -0
  23. {tinybird-cli-5.4.1.dev2 → tinybird-cli-5.4.1.dev4}/tinybird/sql_toolset.py +0 -0
  24. {tinybird-cli-5.4.1.dev2 → tinybird-cli-5.4.1.dev4}/tinybird/syncasync.py +0 -0
  25. {tinybird-cli-5.4.1.dev2 → tinybird-cli-5.4.1.dev4}/tinybird/tb_cli.py +0 -0
  26. {tinybird-cli-5.4.1.dev2 → tinybird-cli-5.4.1.dev4}/tinybird/tb_cli_modules/auth.py +0 -0
  27. {tinybird-cli-5.4.1.dev2 → tinybird-cli-5.4.1.dev4}/tinybird/tb_cli_modules/branch.py +0 -0
  28. {tinybird-cli-5.4.1.dev2 → tinybird-cli-5.4.1.dev4}/tinybird/tb_cli_modules/cicd.py +0 -0
  29. {tinybird-cli-5.4.1.dev2 → tinybird-cli-5.4.1.dev4}/tinybird/tb_cli_modules/cli.py +0 -0
  30. {tinybird-cli-5.4.1.dev2 → tinybird-cli-5.4.1.dev4}/tinybird/tb_cli_modules/config.py +0 -0
  31. {tinybird-cli-5.4.1.dev2 → tinybird-cli-5.4.1.dev4}/tinybird/tb_cli_modules/connection.py +0 -0
  32. {tinybird-cli-5.4.1.dev2 → tinybird-cli-5.4.1.dev4}/tinybird/tb_cli_modules/exceptions.py +0 -0
  33. {tinybird-cli-5.4.1.dev2 → tinybird-cli-5.4.1.dev4}/tinybird/tb_cli_modules/job.py +0 -0
  34. {tinybird-cli-5.4.1.dev2 → tinybird-cli-5.4.1.dev4}/tinybird/tb_cli_modules/pipe.py +0 -0
  35. {tinybird-cli-5.4.1.dev2 → tinybird-cli-5.4.1.dev4}/tinybird/tb_cli_modules/regions.py +0 -0
  36. {tinybird-cli-5.4.1.dev2 → tinybird-cli-5.4.1.dev4}/tinybird/tb_cli_modules/telemetry.py +0 -0
  37. {tinybird-cli-5.4.1.dev2 → tinybird-cli-5.4.1.dev4}/tinybird/tb_cli_modules/test.py +0 -0
  38. {tinybird-cli-5.4.1.dev2 → tinybird-cli-5.4.1.dev4}/tinybird/tb_cli_modules/tinyunit/tinyunit.py +0 -0
  39. {tinybird-cli-5.4.1.dev2 → tinybird-cli-5.4.1.dev4}/tinybird/tb_cli_modules/tinyunit/tinyunit_lib.py +0 -0
  40. {tinybird-cli-5.4.1.dev2 → tinybird-cli-5.4.1.dev4}/tinybird/tb_cli_modules/token.py +0 -0
  41. {tinybird-cli-5.4.1.dev2 → tinybird-cli-5.4.1.dev4}/tinybird/tornado_template.py +0 -0
  42. {tinybird-cli-5.4.1.dev2 → tinybird-cli-5.4.1.dev4}/tinybird_cli.egg-info/SOURCES.txt +0 -0
  43. {tinybird-cli-5.4.1.dev2 → tinybird-cli-5.4.1.dev4}/tinybird_cli.egg-info/dependency_links.txt +0 -0
  44. {tinybird-cli-5.4.1.dev2 → tinybird-cli-5.4.1.dev4}/tinybird_cli.egg-info/entry_points.txt +0 -0
  45. {tinybird-cli-5.4.1.dev2 → tinybird-cli-5.4.1.dev4}/tinybird_cli.egg-info/requires.txt +0 -0
  46. {tinybird-cli-5.4.1.dev2 → tinybird-cli-5.4.1.dev4}/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.4.1.dev2
3
+ Version: 5.4.1.dev4
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/cli/introduction.html
6
6
  Author: Tinybird
@@ -18,6 +18,16 @@ The Tinybird command-line tool allows you to use all the Tinybird functionality
18
18
  Changelog
19
19
  ----------
20
20
 
21
+ 5.4.1.dev4
22
+ **********
23
+
24
+ - `Improved` info and error messages when using "user token" vs "admin your@email.com" token
25
+
26
+ 5.4.1.dev3
27
+ **********
28
+
29
+ - `Changed` Internal change
30
+
21
31
  5.4.1.dev1
22
32
  **********
23
33
 
@@ -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.4.1.dev2'
8
- __revision__ = '5e2ca7a'
7
+ __version__ = '5.4.1.dev4'
8
+ __revision__ = 'f135c10'
@@ -3047,20 +3047,14 @@ async def new_ds(
3047
3047
  and params.get("connector")
3048
3048
  and params.get("bucket_uri")
3049
3049
  ):
3050
- data = await client.preview_bucket(params["connector"], params["bucket_uri"])
3051
- if data:
3052
- files = data.get("files", [])
3053
- if len(files) > 0:
3054
- file_name = files[0].get("name", "")
3055
- extension = file_name.split(".")[-1]
3056
- if extension == "gz":
3057
- extension = file_name.split(".")[-2]
3058
- valid_formats = ["csv", "json", "jsonl", "ndjson", "parquet"]
3059
- if extension not in valid_formats:
3060
- raise Exception(
3061
- FeedbackManager.error_format(extension=extension, valid_formats=valid_formats)
3062
- )
3063
- params["format"] = extension
3050
+ bucket_uri = params.get("bucket_uri")
3051
+ extension = bucket_uri.split(".")[-1]
3052
+ if extension == "gz":
3053
+ extension = bucket_uri.split(".")[-2]
3054
+ valid_formats = ["csv", "json", "jsonl", "ndjson", "parquet"]
3055
+ if extension not in valid_formats:
3056
+ raise Exception(FeedbackManager.error_format(extension=extension, valid_formats=valid_formats))
3057
+ params["format"] = extension
3064
3058
  datasource_response = await client.datasource_create_from_definition(params)
3065
3059
  datasource = datasource_response.get("datasource", {})
3066
3060
 
@@ -654,7 +654,6 @@ Ready? """
654
654
  info_dependency_list_item = info_message("** --- {dependency}")
655
655
  info_no_dependencies_found = info_message("** No dependencies found for {dependency}")
656
656
  info_no_compatible_dependencies_found = info_message("** Data Sources with incompatible partitions found:")
657
- info_admin_token = info_message("** Go to {host}/tokens, copy the admin token and paste it")
658
657
  info_append_data = info_message("** => If you want to insert data use: $ tb datasource append")
659
658
  info_datasources = info_message("** Data Sources:")
660
659
  info_connections = info_message("** Connections:")
@@ -20,6 +20,7 @@ from .tornado_template import VALID_CUSTOM_FUNCTION_NAMES, SecurityException, Te
20
20
  TB_SECRET_IN_TEST_MODE = "tb_secret_dont_raise"
21
21
  TB_SECRET_PREFIX = "tb_secret_"
22
22
  CH_PARAM_PREFIX = "param_"
23
+ REQUIRED_PARAM_NOT_DEFINED = "Required parameter is not defined"
23
24
 
24
25
 
25
26
  def secret_template_key(secret_name: str) -> str:
@@ -129,9 +130,7 @@ def transform_type(
129
130
  x = default
130
131
  if x is None:
131
132
  if defined:
132
- raise SQLTemplateException(
133
- "Required parameter is not defined", documentation="/cli/advanced-templates.html"
134
- )
133
+ raise SQLTemplateException(REQUIRED_PARAM_NOT_DEFINED, documentation="/cli/advanced-templates.html")
135
134
  else:
136
135
  return None
137
136
  if tester == "String":
@@ -378,7 +377,7 @@ def array_type(types): # noqa: C901
378
377
  if x is None:
379
378
  if defined:
380
379
  raise SQLTemplateException(
381
- "Required parameter is not defined", documentation="/cli/advanced-templates.html"
380
+ REQUIRED_PARAM_NOT_DEFINED, documentation="/cli/advanced-templates.html"
382
381
  )
383
382
  else:
384
383
  return None
@@ -1866,26 +1865,25 @@ def get_template_and_variables(sql: str, name: Optional[str], escape_arrays: boo
1866
1865
  raise SQLTemplateException(e)
1867
1866
 
1868
1867
 
1869
- def preprocess_variables(variables: dict, t: Template):
1868
+ def preprocess_variables(variables: dict, template_variables_with_types: List[dict]):
1870
1869
  """
1871
- >>> preprocess_variables({"test": "1,2"}, Template("select * from table where f = {{Array(test, 'String')}}"))
1870
+ >>> preprocess_variables({"test": '24'}, [{"name": "test", "type": "Int32", "default": None}])
1871
+ {}
1872
+ >>> preprocess_variables({"test": "1,2"}, [{"name": "test", "type": "Array(String)", "default": None}])
1872
1873
  {'test': ['1', '2']}
1873
- >>> preprocess_variables({"test": ['1', '2']}, Template("select * from table where f = {{Array(test, 'String')}}"))
1874
+ >>> preprocess_variables({"test": ['1', '2']}, [{"name": "test", "type": "Array(String)", "default": None}])
1874
1875
  {'test': ['1', '2']}
1875
- >>> preprocess_variables({"test": [1,2]}, Template("select * from table where f = {{Array(test, 'String')}}"))
1876
+ >>> preprocess_variables({"test": [1,2]}, [{"name": "test", "type": "Array(String)", "default": None}])
1876
1877
  {'test': ['1', '2']}
1877
- >>> preprocess_variables({"test": '24'}, Template("select * from table where f = {{Int32(test)}}"))
1878
- {}
1879
- >>> preprocess_variables({"test": "1,2,3"}, Template("select * from table where f = {{Array(test,'Int32')}}"))
1878
+ >>> preprocess_variables({"test": "1,2,3"}, [{"name": "test", "type": "Array(Int32)", "default": None}])
1880
1879
  {'test': [1, 2, 3]}
1881
- >>> preprocess_variables({"test": "1,2,msg"}, Template("select * from table where f = {{Array(test,'Int32')}}"))
1880
+ >>> preprocess_variables({"test": "1,2,msg"}, [{"name": "test", "type": "Array(Int32)", "default": None}])
1882
1881
  {}
1883
1882
  """
1884
- template_variables = get_var_names_and_types_cached(t)
1885
1883
  processed_variables = {}
1886
1884
  for variable, value in variables.items():
1887
1885
  try:
1888
- template_vars = [t_var for t_var in template_variables if t_var["name"] == variable] or None
1886
+ template_vars = [t_var for t_var in template_variables_with_types if t_var["name"] == variable] or None
1889
1887
  if template_vars is None or value is None:
1890
1888
  continue
1891
1889
 
@@ -1909,6 +1907,43 @@ def preprocess_variables(variables: dict, t: Template):
1909
1907
  return processed_variables
1910
1908
 
1911
1909
 
1910
+ def format_SQLTemplateException_message(e: SQLTemplateException, vars_and_types: Optional[dict] = None):
1911
+ def join_with_different_last_separator(items, separator=", ", last_separator=" and "):
1912
+ if not items:
1913
+ return ""
1914
+ if len(items) == 1:
1915
+ return items[0]
1916
+
1917
+ result = separator.join(items[:-1])
1918
+ return result + last_separator + items[-1]
1919
+
1920
+ message = str(e)
1921
+ var_names = ""
1922
+
1923
+ try:
1924
+ if REQUIRED_PARAM_NOT_DEFINED in message and vars_and_types:
1925
+ vars_with_default_none = []
1926
+ for item in vars_and_types:
1927
+ if (
1928
+ item.get("default") is None
1929
+ and item.get("used_in", None) is None
1930
+ and item.get("name") not in vars_with_default_none
1931
+ ):
1932
+ vars_with_default_none.append(item["name"])
1933
+
1934
+ var_names = join_with_different_last_separator(vars_with_default_none)
1935
+ except Exception:
1936
+ pass
1937
+
1938
+ if var_names:
1939
+ raise SQLTemplateException(
1940
+ f"{REQUIRED_PARAM_NOT_DEFINED}. Check the parameters {join_with_different_last_separator(vars_with_default_none)}. Please provide a value or set a default value in the pipe code.",
1941
+ e.documentation,
1942
+ )
1943
+ else:
1944
+ raise e
1945
+
1946
+
1912
1947
  def render_sql_template(
1913
1948
  sql: str,
1914
1949
  variables: Optional[dict] = None,
@@ -2131,6 +2166,14 @@ def render_sql_template(
2131
2166
  Traceback (most recent call last):
2132
2167
  ...
2133
2168
  tinybird.sql_template.SQLTemplateException: Template Syntax Error: Cannot access secret 'test'. Check the secret exists in the Workspace and the token has the required scope.
2169
+ >>> render_sql_template("select * from table where str = {{String(test)}} and category = {{String(category, 'shirts')}} and color = {{ Int32(color)}}", test_mode=False)
2170
+ Traceback (most recent call last):
2171
+ ...
2172
+ tinybird.sql_template.SQLTemplateException: Template Syntax Error: Required parameter is not defined. Check the parameters test and color. Please provide a value or set a default value in the pipe code.
2173
+ >>> render_sql_template("select columns(cols, 'salary') from table where str = {{String(test)}}", test_mode=False)
2174
+ Traceback (most recent call last):
2175
+ ...
2176
+ tinybird.sql_template.SQLTemplateException: Template Syntax Error: Required parameter is not defined. Check the parameters test. Please provide a value or set a default value in the pipe code.
2134
2177
  """
2135
2178
  escape_split_to_array = ff_split_to_array_escape.get(False)
2136
2179
  bypass_preprocess_variables = ff_preprocess_parameters_circuit_breaker.get(False)
@@ -2138,9 +2181,10 @@ def render_sql_template(
2138
2181
  t, template_variables, variable_warnings = get_template_and_variables(
2139
2182
  sql, name, escape_arrays=escape_split_to_array
2140
2183
  )
2184
+ template_variables_with_types = get_var_names_and_types_cached(t)
2141
2185
 
2142
2186
  if not bypass_preprocess_variables and variables is not None:
2143
- processed_variables = preprocess_variables(variables, t)
2187
+ processed_variables = preprocess_variables(variables, template_variables_with_types)
2144
2188
  variables.update(processed_variables)
2145
2189
 
2146
2190
  if test_mode:
@@ -2188,7 +2232,8 @@ def render_sql_template(
2188
2232
  return sql, template_execution_results, variable_warnings
2189
2233
  except NameError as e:
2190
2234
  raise SQLTemplateException(e, documentation="/cli/advanced-templates.html#defined")
2191
- except SQLTemplateException:
2235
+ except SQLTemplateException as e:
2236
+ format_SQLTemplateException_message(e, vars_and_types=template_variables_with_types)
2192
2237
  raise
2193
2238
  except Exception as e:
2194
2239
  # errors might vary here, we need to support as much as possible
@@ -811,7 +811,7 @@ async def authenticate(ctx, host, token=None, regions=None, interactive=False, t
811
811
  if not token:
812
812
  tokens_url = urljoin(ui_host, "tokens")
813
813
  token = click.prompt(
814
- f"\nCopy the admin token from {tokens_url} and paste it here { f'OR press enter to use the token from .tinyb file' if default_password else ''}",
814
+ f"\nCopy the \"admin your@email\" token from {tokens_url} and paste it here { f'OR press enter to use the token from .tinyb file' if default_password else ''}",
815
815
  hide_input=True,
816
816
  show_default=False,
817
817
  default=default_password,
@@ -860,13 +860,37 @@ async def authenticate(ctx, host, token=None, regions=None, interactive=False, t
860
860
 
861
861
  def ask_for_user_token(action: str, ui_host: str) -> str:
862
862
  return click.prompt(
863
- f"\nIn order to {action} we need your user token. Copy it from {ui_host}/tokens and paste it here",
863
+ f'\nUse the token called "user token" in order to {action}. Copy it from {ui_host}/tokens and paste it here',
864
864
  hide_input=True,
865
865
  show_default=False,
866
866
  default=None,
867
867
  )
868
868
 
869
869
 
870
+ async def check_user_token(ctx: Context, token: str):
871
+ client: TinyB = ctx.ensure_object(dict)["client"]
872
+ try:
873
+ user_client: TinyB = deepcopy(client)
874
+ user_client.token = token
875
+
876
+ is_authenticated = await user_client.check_auth_login()
877
+ except Exception as e:
878
+ raise CLIWorkspaceException(FeedbackManager.error_exception(error=str(e)))
879
+
880
+ if not is_authenticated.get("is_valid", False):
881
+ raise CLIWorkspaceException(
882
+ FeedbackManager.error_exception(
883
+ error='Invalid token. Please, be sure you are using the "user token" instead of the "admin your@email" token.'
884
+ )
885
+ )
886
+ if is_authenticated.get("is_valid") and not is_authenticated.get("is_user", False):
887
+ raise CLIWorkspaceException(
888
+ FeedbackManager.error_exception(
889
+ error='Invalid user authentication. Please, be sure you are using the "user token" instead of the "admin your@email" token.'
890
+ )
891
+ )
892
+
893
+
870
894
  async def get_available_starterkits(ctx: Context) -> List[Dict[str, Any]]:
871
895
  ctx_dict = ctx.ensure_object(dict)
872
896
  available_starterkits = ctx_dict.get("available_starterkits", None)
@@ -1826,7 +1850,7 @@ async def try_update_config_with_remote(
1826
1850
  def ask_for_admin_token_interactively(ui_host: str, default_token: Optional[str]) -> str:
1827
1851
  return (
1828
1852
  click.prompt(
1829
- f"\nCopy the admin token from {ui_host}/tokens and paste it here { f'OR press enter to use the token from .tinyb file' if default_token else ''}",
1853
+ f"\nCopy the \"admin your@email\" token from {ui_host}/tokens and paste it here { f'OR press enter to use the token from .tinyb file' if default_token else ''}",
1830
1854
  hide_input=True,
1831
1855
  show_default=False,
1832
1856
  default=default_token,
@@ -27,6 +27,7 @@ from tinybird.tb_cli_modules.common import (
27
27
  _generate_datafile,
28
28
  ask_for_user_token,
29
29
  autocomplete_topics,
30
+ check_user_token,
30
31
  coro,
31
32
  echo_safe_humanfriendly_tables_format_smart_table,
32
33
  get_config_and_hosts,
@@ -631,6 +632,7 @@ async def datasource_share(ctx: Context, datasource_name: str, workspace_name_or
631
632
 
632
633
  if not user_token:
633
634
  user_token = ask_for_user_token("share a Data Source", ui_host)
635
+ await check_user_token(ctx, user_token)
634
636
 
635
637
  client.token = user_token
636
638
 
@@ -696,6 +698,7 @@ async def datasource_unshare(ctx: Context, datasource_name: str, workspace_name_
696
698
 
697
699
  if not user_token:
698
700
  user_token = ask_for_user_token("unshare a Data Source", ui_host)
701
+ await check_user_token(ctx, user_token)
699
702
 
700
703
  client.token = user_token
701
704
 
@@ -16,6 +16,7 @@ from tinybird.tb_cli_modules.common import (
16
16
  _get_config,
17
17
  _get_workspace_plan_name,
18
18
  ask_for_user_token,
19
+ check_user_token,
19
20
  coro,
20
21
  create_workspace_interactive,
21
22
  create_workspace_non_interactive,
@@ -216,6 +217,7 @@ async def create_workspace(
216
217
  user_token = ask_for_user_token("create a new workspace", ui_host)
217
218
  if not user_token:
218
219
  return
220
+ await check_user_token(ctx, user_token)
219
221
 
220
222
  # If we have at least workspace_name, we start the non interactive
221
223
  # process, creating an empty workspace
@@ -247,6 +249,7 @@ async def delete_workspace(
247
249
 
248
250
  if not user_token:
249
251
  user_token = ask_for_user_token("delete a workspace", ui_host)
252
+ await check_user_token(ctx, user_token)
250
253
 
251
254
  workspaces = (await client.user_workspaces()).get("workspaces", [])
252
255
  workspace_to_delete = next(
@@ -14,6 +14,7 @@ from tinybird.client import TinyB
14
14
  from tinybird.feedback_manager import FeedbackManager
15
15
  from tinybird.tb_cli_modules.common import (
16
16
  ask_for_user_token,
17
+ check_user_token,
17
18
  coro,
18
19
  echo_safe_humanfriendly_tables_format_smart_table,
19
20
  get_config_and_hosts,
@@ -81,6 +82,7 @@ async def add_members_to_workspace(
81
82
  else:
82
83
  if not user_token:
83
84
  user_token = ask_for_user_token(f"add users to {cmd_ctx.workspace['name']}", cmd_ctx.ui_host)
85
+ await check_user_token(ctx, user_token)
84
86
 
85
87
  user_client: TinyB = deepcopy(cmd_ctx.client)
86
88
  user_client.token = user_token
@@ -130,6 +132,7 @@ async def remove_members_from_workspace(ctx: Context, members_emails: str, user_
130
132
  else:
131
133
  if not user_token:
132
134
  user_token = ask_for_user_token(f"remove users from {cmd_ctx.workspace['name']}", cmd_ctx.ui_host)
135
+ await check_user_token(ctx, user_token)
133
136
 
134
137
  if len(non_valid_users) > 0:
135
138
  click.echo(
@@ -179,6 +182,7 @@ async def set_workspace_member_role(ctx: Context, role: str, members_emails: str
179
182
  else:
180
183
  if not user_token:
181
184
  user_token = ask_for_user_token(f"change user roles in {cmd_ctx.workspace['name']}", cmd_ctx.ui_host)
185
+ await check_user_token(ctx, user_token)
182
186
 
183
187
  if len(non_valid_users) > 0:
184
188
  click.echo(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tinybird-cli
3
- Version: 5.4.1.dev2
3
+ Version: 5.4.1.dev4
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/cli/introduction.html
6
6
  Author: Tinybird
@@ -18,6 +18,16 @@ The Tinybird command-line tool allows you to use all the Tinybird functionality
18
18
  Changelog
19
19
  ----------
20
20
 
21
+ 5.4.1.dev4
22
+ **********
23
+
24
+ - `Improved` info and error messages when using "user token" vs "admin your@email.com" token
25
+
26
+ 5.4.1.dev3
27
+ **********
28
+
29
+ - `Changed` Internal change
30
+
21
31
  5.4.1.dev1
22
32
  **********
23
33