tinybird-cli 4.1.2.dev0__tar.gz → 5.0.0__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.
- {tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/PKG-INFO +17 -3
- {tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/tinybird/__cli__.py +2 -2
- {tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/tinybird/ch_utils/constants.py +1 -0
- {tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/tinybird/client.py +21 -9
- {tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/tinybird/datafile.py +44 -7
- {tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/tinybird/feedback_manager.py +8 -1
- {tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/tinybird/sql_toolset.py +69 -16
- {tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/tinybird/tb_cli_modules/pipe.py +8 -0
- {tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/tinybird/tb_cli_modules/regions.py +5 -5
- {tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/tinybird/tb_cli_modules/workspace_members.py +8 -3
- {tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/tinybird_cli.egg-info/PKG-INFO +17 -3
- {tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/setup.cfg +0 -0
- {tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/tinybird/ch_utils/engine.py +0 -0
- {tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/tinybird/check_pypi.py +0 -0
- {tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/tinybird/config.py +0 -0
- {tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/tinybird/connectors.py +0 -0
- {tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/tinybird/context.py +0 -0
- {tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/tinybird/datatypes.py +0 -0
- {tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/tinybird/git_settings.py +0 -0
- {tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/tinybird/sql.py +0 -0
- {tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/tinybird/sql_template.py +0 -0
- {tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/tinybird/sql_template_fmt.py +0 -0
- {tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/tinybird/syncasync.py +0 -0
- {tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/tinybird/tb_cli.py +0 -0
- {tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/tinybird/tb_cli_modules/auth.py +0 -0
- {tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/tinybird/tb_cli_modules/branch.py +0 -0
- {tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/tinybird/tb_cli_modules/cicd.py +0 -0
- {tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/tinybird/tb_cli_modules/cli.py +0 -0
- {tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/tinybird/tb_cli_modules/common.py +0 -0
- {tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/tinybird/tb_cli_modules/config.py +0 -0
- {tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/tinybird/tb_cli_modules/connection.py +0 -0
- {tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/tinybird/tb_cli_modules/datasource.py +0 -0
- {tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/tinybird/tb_cli_modules/exceptions.py +0 -0
- {tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/tinybird/tb_cli_modules/job.py +0 -0
- {tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/tinybird/tb_cli_modules/telemetry.py +0 -0
- {tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/tinybird/tb_cli_modules/test.py +0 -0
- {tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/tinybird/tb_cli_modules/tinyunit/tinyunit.py +0 -0
- {tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/tinybird/tb_cli_modules/tinyunit/tinyunit_lib.py +0 -0
- {tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/tinybird/tb_cli_modules/token.py +0 -0
- {tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/tinybird/tb_cli_modules/workspace.py +0 -0
- {tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/tinybird/tornado_template.py +0 -0
- {tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/tinybird_cli.egg-info/SOURCES.txt +0 -0
- {tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/tinybird_cli.egg-info/dependency_links.txt +0 -0
- {tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/tinybird_cli.egg-info/entry_points.txt +0 -0
- {tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/tinybird_cli.egg-info/requires.txt +0 -0
- {tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/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
|
+
Version: 5.0.0
|
|
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,20 @@ The Tinybird command-line tool allows you to use all the Tinybird functionality
|
|
|
18
18
|
Changelog
|
|
19
19
|
----------
|
|
20
20
|
|
|
21
|
+
5.0.0
|
|
22
|
+
******
|
|
23
|
+
|
|
24
|
+
- `Breaking change` Make `insertion_date` column explicit. This column is no longer inferred, it must be present in the Data Source file.
|
|
25
|
+
|
|
26
|
+
Detailed info at https://www.tinybird.co/docs/changelog/2024-06-17-insertion_date-deprecation
|
|
27
|
+
|
|
28
|
+
- `Added` parameter to `tb pipe regression-test` --relative-change
|
|
29
|
+
- `Added` `tb push` displays warnings when using a reserved parameter in a pipe.
|
|
30
|
+
- `Added` --role parameter to `tb workspace members add`
|
|
31
|
+
- `Changed` Point region hosts to new `app.tinybird.co` domain
|
|
32
|
+
- `Improved` help message of `tb workspace members set-role` now displays the 3 valid roles: viewer|guest|admin.
|
|
33
|
+
- `Improved` syntax error messages when parsing endpoints.
|
|
34
|
+
|
|
21
35
|
4.1.1
|
|
22
36
|
************
|
|
23
37
|
|
|
@@ -27,7 +41,7 @@ Changelog
|
|
|
27
41
|
************
|
|
28
42
|
|
|
29
43
|
- `Added` `tb token create` command to be able to create static and JWT tokens from the CLI. You can check more information at https://www.tinybird.co/blog-posts/jwt-api-endpoints-public-beta
|
|
30
|
-
- `Fixed` `tb init --git` to pin `tinybird-cli>=4,<5` in `requirements.txt` to avoid issues with the latest version of the CLI.
|
|
44
|
+
- `Fixed` `tb init --git` to pin `tinybird-cli>=4,<5` in `requirements.txt` to avoid issues with the latest version of the CLI.
|
|
31
45
|
|
|
32
46
|
4.0.0
|
|
33
47
|
************
|
|
@@ -85,7 +99,7 @@ This is a major release, please read the commands affected below and consider up
|
|
|
85
99
|
************
|
|
86
100
|
|
|
87
101
|
- `Changed` fixed major version of tinybird-cli to lower than 4 when using `tb init --git`
|
|
88
|
-
- `Changed` behavior when running `tb deploy` on a branch to push the connection settings to the backend. This change is the backend that decides what to do.
|
|
102
|
+
- `Changed` behavior when running `tb deploy` on a branch to push the connection settings to the backend. This change is the backend that decides what to do.
|
|
89
103
|
- `Changed` `tb pipe sink` commands are now available
|
|
90
104
|
- `Fixed` regression tests query when filtering by specific parameter
|
|
91
105
|
- `Fixed` Avoid `tb fmt` to error if there's a `CASE` in the sql
|
|
@@ -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__ = '
|
|
8
|
-
__revision__ = '
|
|
7
|
+
__version__ = '5.0.0'
|
|
8
|
+
__revision__ = '2af0ad5'
|
|
@@ -4,6 +4,7 @@ LIVE_WS_NAME = "live"
|
|
|
4
4
|
SNAPSHOT_WS_NAME = "snapshot"
|
|
5
5
|
|
|
6
6
|
ENABLED_TABLE_FUNCTIONS = {"generateRandom", "null", "numbers", "numbers_mt", "values", "zeros", "zeros_mt"}
|
|
7
|
+
COPY_ENABLED_TABLE_FUNCTIONS = frozenset(["postgresql"])
|
|
7
8
|
|
|
8
9
|
ENABLED_SYSTEM_TABLES = {
|
|
9
10
|
"functions",
|
|
@@ -11,6 +11,7 @@ import requests.adapters
|
|
|
11
11
|
from requests import Response
|
|
12
12
|
from urllib3 import Retry
|
|
13
13
|
|
|
14
|
+
from tinybird.ch_utils.constants import COPY_ENABLED_TABLE_FUNCTIONS
|
|
14
15
|
from tinybird.syncasync import sync_to_async
|
|
15
16
|
from tinybird.tb_cli_modules.regions import fill_with_public_regions
|
|
16
17
|
from tinybird.tb_cli_modules.telemetry import add_telemetry_event
|
|
@@ -749,10 +750,12 @@ class TinyB(object):
|
|
|
749
750
|
async def delete_branch(self, id: str):
|
|
750
751
|
return await self._req(f"/v0/environments/{id}", method="DELETE")
|
|
751
752
|
|
|
752
|
-
async def add_users_to_workspace(self, workspace: Dict[str, Any], users_emails: List[str]):
|
|
753
|
+
async def add_users_to_workspace(self, workspace: Dict[str, Any], users_emails: List[str], role: Optional[str]):
|
|
753
754
|
users = ",".join(users_emails)
|
|
754
755
|
return await self._req(
|
|
755
|
-
f"/v0/workspaces/{workspace['id']}/users/",
|
|
756
|
+
f"/v0/workspaces/{workspace['id']}/users/",
|
|
757
|
+
method="PUT",
|
|
758
|
+
data={"operation": "add", "users": users, "role": role},
|
|
756
759
|
)
|
|
757
760
|
|
|
758
761
|
async def remove_users_from_workspace(self, workspace: Dict[str, Any], users_emails: List[str]):
|
|
@@ -1033,23 +1036,32 @@ class TinyB(object):
|
|
|
1033
1036
|
return result["q"]
|
|
1034
1037
|
|
|
1035
1038
|
@staticmethod
|
|
1036
|
-
def _sql_get_used_tables_local(sql: str, raising: bool = False) -> List[str]:
|
|
1039
|
+
def _sql_get_used_tables_local(sql: str, raising: bool = False, is_copy: Optional[bool] = False) -> List[str]:
|
|
1037
1040
|
from tinybird.sql_toolset import sql_get_used_tables
|
|
1038
1041
|
|
|
1039
|
-
tables = sql_get_used_tables(
|
|
1042
|
+
tables = sql_get_used_tables(
|
|
1043
|
+
sql, raising, table_functions=False, function_allow_list=COPY_ENABLED_TABLE_FUNCTIONS if is_copy else None
|
|
1044
|
+
)
|
|
1040
1045
|
return [t[1] if t[0] == "" else f"{t[0]}.{t[1]}" for t in tables]
|
|
1041
1046
|
|
|
1042
|
-
async def _sql_get_used_tables_remote(
|
|
1043
|
-
|
|
1047
|
+
async def _sql_get_used_tables_remote(
|
|
1048
|
+
self, sql: str, raising: bool = False, is_copy: Optional[bool] = False
|
|
1049
|
+
) -> List[str]:
|
|
1050
|
+
params = {
|
|
1051
|
+
"q": sql,
|
|
1052
|
+
"raising": "true" if raising else "false",
|
|
1053
|
+
"table_functions": "false",
|
|
1054
|
+
"is_copy": "true" if is_copy else "false",
|
|
1055
|
+
}
|
|
1044
1056
|
result = await self._req("/v0/sql_tables", data=params, method="POST")
|
|
1045
1057
|
return [t[1] if t[0] == "" else f"{t[0]}.{t[1]}" for t in result["tables"]]
|
|
1046
1058
|
|
|
1047
1059
|
# Get used tables from a query. Does not include table functions
|
|
1048
|
-
async def sql_get_used_tables(self, sql: str, raising: bool = False) -> List[str]:
|
|
1060
|
+
async def sql_get_used_tables(self, sql: str, raising: bool = False, is_copy: Optional[bool] = False) -> List[str]:
|
|
1049
1061
|
try:
|
|
1050
|
-
return self._sql_get_used_tables_local(sql, raising)
|
|
1062
|
+
return self._sql_get_used_tables_local(sql, raising, is_copy)
|
|
1051
1063
|
except ModuleNotFoundError:
|
|
1052
|
-
return await self._sql_get_used_tables_remote(sql, raising)
|
|
1064
|
+
return await self._sql_get_used_tables_remote(sql, raising, is_copy)
|
|
1053
1065
|
|
|
1054
1066
|
@staticmethod
|
|
1055
1067
|
def _replace_tables_local(q: str, replacements):
|
|
@@ -711,6 +711,7 @@ class Datafile:
|
|
|
711
711
|
self.raw: Optional[List[str]] = None
|
|
712
712
|
self.includes: Dict[str, Any] = {}
|
|
713
713
|
self.shared_with: List[str] = []
|
|
714
|
+
self.warnings: List[str] = []
|
|
714
715
|
|
|
715
716
|
def validate(self) -> None:
|
|
716
717
|
for x in self.nodes:
|
|
@@ -773,7 +774,8 @@ def parse_pipe(
|
|
|
773
774
|
for node in doc.nodes:
|
|
774
775
|
sql = node.get("sql", "")
|
|
775
776
|
if sql.strip()[0] == "%":
|
|
776
|
-
sql, _,
|
|
777
|
+
sql, _, variable_warnings = render_sql_template(sql[1:], test_mode=True, name=node["name"])
|
|
778
|
+
doc.warnings = variable_warnings
|
|
777
779
|
# it'll fail with a ModuleNotFoundError when the toolset is not available but it returns the parsed doc
|
|
778
780
|
from tinybird.sql_toolset import format_sql as toolset_format_sql
|
|
779
781
|
|
|
@@ -1474,6 +1476,7 @@ async def process_file(
|
|
|
1474
1476
|
deps = []
|
|
1475
1477
|
nodes: List[Dict[str, Any]] = []
|
|
1476
1478
|
|
|
1479
|
+
is_copy = any([node for node in doc.nodes if node.get("type", "standard").lower() == PipeNodeTypes.COPY])
|
|
1477
1480
|
for node in doc.nodes:
|
|
1478
1481
|
sql = node["sql"]
|
|
1479
1482
|
node_type = node.get("type", "standard").lower()
|
|
@@ -1502,7 +1505,7 @@ async def process_file(
|
|
|
1502
1505
|
sql_rendered = sql
|
|
1503
1506
|
|
|
1504
1507
|
try:
|
|
1505
|
-
dependencies = await tb_client.sql_get_used_tables(sql_rendered, raising=True)
|
|
1508
|
+
dependencies = await tb_client.sql_get_used_tables(sql_rendered, raising=True, is_copy=is_copy)
|
|
1506
1509
|
deps += [t for t in dependencies if t not in [n["name"] for n in doc.nodes]]
|
|
1507
1510
|
|
|
1508
1511
|
except Exception as e:
|
|
@@ -1565,6 +1568,7 @@ async def process_file(
|
|
|
1565
1568
|
"deps": [x for x in set(deps)],
|
|
1566
1569
|
"tokens": doc.tokens,
|
|
1567
1570
|
"description": description,
|
|
1571
|
+
"warnings": doc.warnings,
|
|
1568
1572
|
}
|
|
1569
1573
|
]
|
|
1570
1574
|
elif DataFileExtensions.TOKEN in filename:
|
|
@@ -1766,6 +1770,7 @@ class PipeChecker(unittest.TestCase):
|
|
|
1766
1770
|
only_response_times: bool,
|
|
1767
1771
|
ignore_order: bool,
|
|
1768
1772
|
validate_processed_bytes: bool,
|
|
1773
|
+
relative_change: float,
|
|
1769
1774
|
*args,
|
|
1770
1775
|
**kwargs,
|
|
1771
1776
|
) -> None:
|
|
@@ -1784,6 +1789,7 @@ class PipeChecker(unittest.TestCase):
|
|
|
1784
1789
|
self.only_response_times = only_response_times
|
|
1785
1790
|
self.ignore_order = ignore_order
|
|
1786
1791
|
self.validate_processed_bytes = validate_processed_bytes
|
|
1792
|
+
self.relative_change = relative_change
|
|
1787
1793
|
|
|
1788
1794
|
parsed = urlparse(self.current_pipe_url)
|
|
1789
1795
|
self.checker_pipe_url = f"{parsed.scheme}://{parsed.netloc}/v0/pipes/{self.checker_pipe_name}.json"
|
|
@@ -1901,12 +1907,13 @@ class PipeChecker(unittest.TestCase):
|
|
|
1901
1907
|
for _, (current_data_e, check_fixtures_data_e) in enumerate(zip(current_data, checker_data)):
|
|
1902
1908
|
self.assertEqual(list(current_data_e.keys()), list(check_fixtures_data_e.keys()))
|
|
1903
1909
|
for x in current_data_e.keys():
|
|
1904
|
-
if isinstance(current_data_e[x], float):
|
|
1910
|
+
if isinstance(current_data_e[x], (float, int)):
|
|
1905
1911
|
d = abs(current_data_e[x] - check_fixtures_data_e[x])
|
|
1912
|
+
|
|
1906
1913
|
try:
|
|
1907
1914
|
self.assertLessEqual(
|
|
1908
1915
|
d / current_data_e[x],
|
|
1909
|
-
|
|
1916
|
+
self.relative_change,
|
|
1910
1917
|
f"key {x}. old value: {current_data_e[x]}, new value: {check_fixtures_data_e[x]}\n{self.diff(current_data_e, check_fixtures_data_e)}",
|
|
1911
1918
|
)
|
|
1912
1919
|
except ZeroDivisionError:
|
|
@@ -2057,6 +2064,7 @@ class PipeCheckerRunner:
|
|
|
2057
2064
|
only_response_times: bool,
|
|
2058
2065
|
ignore_order: bool,
|
|
2059
2066
|
validate_processed_bytes: bool,
|
|
2067
|
+
relative_change: float,
|
|
2060
2068
|
) -> PipeChecker:
|
|
2061
2069
|
return PipeChecker(
|
|
2062
2070
|
request,
|
|
@@ -2066,6 +2074,7 @@ class PipeCheckerRunner:
|
|
|
2066
2074
|
only_response_times,
|
|
2067
2075
|
ignore_order,
|
|
2068
2076
|
validate_processed_bytes,
|
|
2077
|
+
relative_change,
|
|
2069
2078
|
)
|
|
2070
2079
|
|
|
2071
2080
|
def _delta_percentage(self, checker: float, current: float) -> float:
|
|
@@ -2085,6 +2094,7 @@ class PipeCheckerRunner:
|
|
|
2085
2094
|
only_response_times: bool,
|
|
2086
2095
|
ignore_order: bool,
|
|
2087
2096
|
validate_processed_bytes: bool,
|
|
2097
|
+
relative_change: float,
|
|
2088
2098
|
failfast: bool,
|
|
2089
2099
|
custom_output: bool = False,
|
|
2090
2100
|
debug: bool = False,
|
|
@@ -2123,7 +2133,13 @@ class PipeCheckerRunner:
|
|
|
2123
2133
|
for _, request in enumerate(pipe_requests_to_check):
|
|
2124
2134
|
suite.addTest(
|
|
2125
2135
|
self._get_checker(
|
|
2126
|
-
request,
|
|
2136
|
+
request,
|
|
2137
|
+
checker_pipe_name,
|
|
2138
|
+
token,
|
|
2139
|
+
only_response_times,
|
|
2140
|
+
ignore_order,
|
|
2141
|
+
validate_processed_bytes,
|
|
2142
|
+
relative_change,
|
|
2127
2143
|
)
|
|
2128
2144
|
)
|
|
2129
2145
|
|
|
@@ -2264,6 +2280,7 @@ async def check_pipe(
|
|
|
2264
2280
|
populate: bool,
|
|
2265
2281
|
cl: TinyB,
|
|
2266
2282
|
limit: int = 0,
|
|
2283
|
+
relative_change: float = 0.01,
|
|
2267
2284
|
sample_by_params: int = 0,
|
|
2268
2285
|
only_response_times=False,
|
|
2269
2286
|
matches: Optional[List[str]] = None,
|
|
@@ -2342,6 +2359,7 @@ async def check_pipe(
|
|
|
2342
2359
|
only_response_times,
|
|
2343
2360
|
ignore_order,
|
|
2344
2361
|
validate_processed_bytes,
|
|
2362
|
+
relative_change,
|
|
2345
2363
|
failfast,
|
|
2346
2364
|
)
|
|
2347
2365
|
|
|
@@ -2556,6 +2574,7 @@ async def new_pipe(
|
|
|
2556
2574
|
run_tests: bool = False,
|
|
2557
2575
|
as_standard: bool = False,
|
|
2558
2576
|
tests_to_run: int = 0,
|
|
2577
|
+
tests_relative_change: float = 0.01,
|
|
2559
2578
|
tests_to_sample_by_params: int = 0,
|
|
2560
2579
|
tests_filter_by: Optional[List[str]] = None,
|
|
2561
2580
|
tests_failfast: bool = False,
|
|
@@ -2600,6 +2619,7 @@ async def new_pipe(
|
|
|
2600
2619
|
tb_client,
|
|
2601
2620
|
only_response_times=only_response_times,
|
|
2602
2621
|
limit=tests_to_run,
|
|
2622
|
+
relative_change=tests_relative_change,
|
|
2603
2623
|
sample_by_params=tests_to_sample_by_params,
|
|
2604
2624
|
matches=tests_filter_by,
|
|
2605
2625
|
failfast=tests_failfast,
|
|
@@ -3206,6 +3226,7 @@ async def exec_file(
|
|
|
3206
3226
|
run_tests=False,
|
|
3207
3227
|
as_standard=False,
|
|
3208
3228
|
tests_to_run: int = 0,
|
|
3229
|
+
tests_relative_change: float = 0.01,
|
|
3209
3230
|
tests_to_sample_by_params: int = 0,
|
|
3210
3231
|
tests_filter_by: Optional[List[str]] = None,
|
|
3211
3232
|
tests_failfast: bool = False,
|
|
@@ -3235,6 +3256,7 @@ async def exec_file(
|
|
|
3235
3256
|
run_tests=run_tests,
|
|
3236
3257
|
as_standard=as_standard,
|
|
3237
3258
|
tests_to_run=tests_to_run,
|
|
3259
|
+
tests_relative_change=tests_relative_change,
|
|
3238
3260
|
tests_to_sample_by_params=tests_to_sample_by_params,
|
|
3239
3261
|
tests_filter_by=tests_filter_by,
|
|
3240
3262
|
tests_failfast=tests_failfast,
|
|
@@ -3587,6 +3609,7 @@ async def build_graph(
|
|
|
3587
3609
|
workspace_lib_paths: Optional[List[Tuple[str, str]]],
|
|
3588
3610
|
):
|
|
3589
3611
|
name, kind = filename.rsplit(".", 1)
|
|
3612
|
+
warnings = []
|
|
3590
3613
|
|
|
3591
3614
|
try:
|
|
3592
3615
|
res = await process_file(
|
|
@@ -3607,6 +3630,7 @@ async def build_graph(
|
|
|
3607
3630
|
|
|
3608
3631
|
for r in res:
|
|
3609
3632
|
fn = r["resource_name"]
|
|
3633
|
+
warnings = r.get("warnings", [])
|
|
3610
3634
|
if changed and fn in changed and (not changed[fn] or changed[fn] in ["shared", "remote"]):
|
|
3611
3635
|
continue
|
|
3612
3636
|
|
|
@@ -3655,7 +3679,7 @@ async def build_graph(
|
|
|
3655
3679
|
r["shared_with"] = mapped_workspaces
|
|
3656
3680
|
|
|
3657
3681
|
dep_map[fn] = set(dep_list)
|
|
3658
|
-
return os.path.basename(name)
|
|
3682
|
+
return os.path.basename(name), warnings
|
|
3659
3683
|
|
|
3660
3684
|
processed = set()
|
|
3661
3685
|
|
|
@@ -3675,9 +3699,20 @@ async def build_graph(
|
|
|
3675
3699
|
if ".incl" in filename:
|
|
3676
3700
|
click.echo(FeedbackManager.warning_skipping_include_file(file=filename))
|
|
3677
3701
|
|
|
3678
|
-
name = await process(filename, deps, dep_map, to_run, workspace_lib_paths)
|
|
3702
|
+
name, warnings = await process(filename, deps, dep_map, to_run, workspace_lib_paths)
|
|
3679
3703
|
processed.add(name)
|
|
3680
3704
|
|
|
3705
|
+
if verbose:
|
|
3706
|
+
if len(warnings) == 1:
|
|
3707
|
+
click.echo(FeedbackManager.warning_pipe_restricted_param(word=warnings[0]))
|
|
3708
|
+
elif len(warnings) > 1:
|
|
3709
|
+
click.echo(
|
|
3710
|
+
FeedbackManager.warning_pipe_restricted_params(
|
|
3711
|
+
words=", ".join(["'{}'".format(param) for param in warnings[:-1]]),
|
|
3712
|
+
last_word=warnings[-1],
|
|
3713
|
+
)
|
|
3714
|
+
)
|
|
3715
|
+
|
|
3681
3716
|
await get_processed(filenames=filenames)
|
|
3682
3717
|
|
|
3683
3718
|
if process_dependencies:
|
|
@@ -3805,6 +3840,7 @@ async def folder_push(
|
|
|
3805
3840
|
raise_on_exists: bool = False,
|
|
3806
3841
|
verbose: bool = True,
|
|
3807
3842
|
tests_to_run: int = 0,
|
|
3843
|
+
tests_relative_change: float = 0.01,
|
|
3808
3844
|
tests_sample_by_params: int = 0,
|
|
3809
3845
|
tests_filter_by: Optional[List[str]] = None,
|
|
3810
3846
|
tests_failfast: bool = False,
|
|
@@ -3991,6 +4027,7 @@ async def folder_push(
|
|
|
3991
4027
|
run_tests,
|
|
3992
4028
|
as_standard,
|
|
3993
4029
|
tests_to_run,
|
|
4030
|
+
tests_relative_change,
|
|
3994
4031
|
tests_sample_by_params,
|
|
3995
4032
|
tests_filter_by,
|
|
3996
4033
|
tests_failfast,
|
|
@@ -261,7 +261,7 @@ class FeedbackManager:
|
|
|
261
261
|
"** Unable to switch to 'main' Workspace. Need to authenticate again, use 'tb auth"
|
|
262
262
|
)
|
|
263
263
|
error_parsing_node_with_unclosed_if = error_message(
|
|
264
|
-
|
|
264
|
+
"Missing {{%end%}} block in parsing node '{node}' from pipe '{pipe}':\n Missing in line '{lineno}' of the SQL:\n '{sql}'"
|
|
265
265
|
)
|
|
266
266
|
error_unknown_connection = error_message("Unknown connection '{connection}' in Data Source '{datasource}'.")
|
|
267
267
|
error_unknown_kafka_connection = error_message(
|
|
@@ -554,6 +554,13 @@ Ready? """
|
|
|
554
554
|
warning_sink_no_connection = warning_message(
|
|
555
555
|
"** Warning: pipe '{pipe_name}' does not have an associated Sink connection."
|
|
556
556
|
)
|
|
557
|
+
warning_pipe_restricted_param = warning_message(
|
|
558
|
+
"** The parameter name '{word}' is a reserved word. Please, choose another name or the pipe will not work as expected."
|
|
559
|
+
)
|
|
560
|
+
warning_pipe_restricted_params = warning_message(
|
|
561
|
+
"** The parameter names {words} and '{last_word}' are reserved words. Please, choose another name or the pipe will not work as expected."
|
|
562
|
+
)
|
|
563
|
+
|
|
557
564
|
info_fixtures_branch = info_message("** Data Fixtures are only pushed to Branches")
|
|
558
565
|
info_materialize_push_datasource_exists = warning_message("** Data Source {name} already exists")
|
|
559
566
|
info_materialize_push_datasource_override = prompt_message(
|
|
@@ -3,20 +3,26 @@ import logging
|
|
|
3
3
|
from collections import defaultdict
|
|
4
4
|
from datetime import datetime
|
|
5
5
|
from functools import lru_cache
|
|
6
|
-
from typing import Any, List, Optional, Set, Tuple
|
|
6
|
+
from typing import Any, FrozenSet, List, Optional, Set, Tuple
|
|
7
7
|
|
|
8
8
|
from chtoolset import query as chquery
|
|
9
9
|
from toposort import toposort
|
|
10
10
|
|
|
11
|
-
from tinybird.ch_utils.constants import ENABLED_TABLE_FUNCTIONS
|
|
11
|
+
from tinybird.ch_utils.constants import COPY_ENABLED_TABLE_FUNCTIONS, ENABLED_TABLE_FUNCTIONS
|
|
12
12
|
|
|
13
13
|
VALID_REMOTE = "VALID_REMOTE"
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
class InvalidFunction(ValueError):
|
|
17
17
|
def __init__(self, msg: str = "", table_function_names: str = ""):
|
|
18
|
+
if any([fn for fn in COPY_ENABLED_TABLE_FUNCTIONS if fn in msg]):
|
|
19
|
+
msg = msg.replace("is restricted", "is restricted to Copy Pipes")
|
|
20
|
+
|
|
18
21
|
if table_function_names:
|
|
19
|
-
|
|
22
|
+
if "postgresql" in table_function_names:
|
|
23
|
+
self.msg = "The postgresql table function is only allowed in Copy Pipes"
|
|
24
|
+
else:
|
|
25
|
+
self.msg = f"The query uses disabled table functions: '{table_function_names}'"
|
|
20
26
|
else:
|
|
21
27
|
self.msg = msg
|
|
22
28
|
super().__init__(self.msg)
|
|
@@ -58,7 +64,11 @@ def format_where_for_mutation_command(where_clause: str) -> str:
|
|
|
58
64
|
|
|
59
65
|
@lru_cache(maxsize=2**13)
|
|
60
66
|
def sql_get_used_tables_cached(
|
|
61
|
-
sql: str,
|
|
67
|
+
sql: str,
|
|
68
|
+
raising: bool = False,
|
|
69
|
+
default_database: str = "",
|
|
70
|
+
table_functions: bool = True,
|
|
71
|
+
function_allow_list: Optional[FrozenSet[str]] = None,
|
|
62
72
|
) -> List[Tuple[str, str, str]]:
|
|
63
73
|
"""
|
|
64
74
|
>>> sql_get_used_tables("SELECT 1 FROM the_table")
|
|
@@ -79,7 +89,11 @@ def sql_get_used_tables_cached(
|
|
|
79
89
|
[('d_d3926a', 't_976af08ec4b547419e729c63e754b17b', '')]
|
|
80
90
|
"""
|
|
81
91
|
try:
|
|
82
|
-
|
|
92
|
+
_function_allow_list = list() if function_allow_list is None else list(function_allow_list)
|
|
93
|
+
|
|
94
|
+
tables: List[Tuple[str, str, str]] = chquery.tables(
|
|
95
|
+
sql, default_database=default_database, function_allow_list=_function_allow_list
|
|
96
|
+
)
|
|
83
97
|
if not table_functions:
|
|
84
98
|
return [(t[0], t[1], "") for t in tables if t[0] or t[1]]
|
|
85
99
|
return tables
|
|
@@ -93,10 +107,17 @@ def sql_get_used_tables_cached(
|
|
|
93
107
|
|
|
94
108
|
|
|
95
109
|
def sql_get_used_tables(
|
|
96
|
-
sql: str,
|
|
110
|
+
sql: str,
|
|
111
|
+
raising: bool = False,
|
|
112
|
+
default_database: str = "",
|
|
113
|
+
table_functions: bool = True,
|
|
114
|
+
function_allow_list: Optional[FrozenSet[str]] = None,
|
|
97
115
|
) -> List[Tuple[str, str, str]]:
|
|
98
|
-
|
|
99
|
-
|
|
116
|
+
hashable_list = frozenset() if function_allow_list is None else function_allow_list
|
|
117
|
+
|
|
118
|
+
return copy.copy(
|
|
119
|
+
sql_get_used_tables_cached(sql, raising, default_database, table_functions, function_allow_list=hashable_list)
|
|
120
|
+
)
|
|
100
121
|
|
|
101
122
|
|
|
102
123
|
class ReplacementsDict(dict):
|
|
@@ -150,9 +171,18 @@ def replace_tables_chquery_cached(
|
|
|
150
171
|
default_database: str = "",
|
|
151
172
|
output_one_line: bool = False,
|
|
152
173
|
timestamp: Optional[datetime] = None,
|
|
174
|
+
function_allow_list: Optional[FrozenSet[str]] = None,
|
|
153
175
|
) -> str:
|
|
154
176
|
replacements = dict(sorted_replacements) if sorted_replacements else {}
|
|
155
|
-
|
|
177
|
+
_function_allow_list = list() if function_allow_list is None else list(function_allow_list)
|
|
178
|
+
|
|
179
|
+
return chquery.replace_tables(
|
|
180
|
+
sql,
|
|
181
|
+
replacements,
|
|
182
|
+
default_database=default_database,
|
|
183
|
+
one_line=output_one_line,
|
|
184
|
+
function_allow_list=_function_allow_list,
|
|
185
|
+
)
|
|
156
186
|
|
|
157
187
|
|
|
158
188
|
def replace_tables(
|
|
@@ -164,15 +194,19 @@ def replace_tables(
|
|
|
164
194
|
valid_tables: Optional[Set[Tuple[str, str]]] = None,
|
|
165
195
|
output_one_line: bool = False,
|
|
166
196
|
timestamp: Optional[datetime] = None,
|
|
197
|
+
function_allow_list: Optional[FrozenSet[str]] = None,
|
|
167
198
|
) -> str:
|
|
168
199
|
"""
|
|
169
200
|
Given a query and a list of table replacements, returns the query after applying the table replacements.
|
|
170
201
|
It takes into account dependencies between replacement subqueries (if any)
|
|
171
202
|
It also validates the sql to verify it's valid and doesn't use unknown or prohibited functions
|
|
172
203
|
"""
|
|
204
|
+
hashable_list = frozenset() if function_allow_list is None else function_allow_list
|
|
173
205
|
if not replacements:
|
|
174
206
|
# Always call replace_tables to do validation and formatting
|
|
175
|
-
return replace_tables_chquery_cached(
|
|
207
|
+
return replace_tables_chquery_cached(
|
|
208
|
+
sql, None, output_one_line=output_one_line, timestamp=timestamp, function_allow_list=hashable_list
|
|
209
|
+
)
|
|
176
210
|
|
|
177
211
|
_replaced_with = set()
|
|
178
212
|
_replacements = ReplacementsDict()
|
|
@@ -182,14 +216,24 @@ def replace_tables(
|
|
|
182
216
|
_replaced_with.add(r)
|
|
183
217
|
|
|
184
218
|
deps: defaultdict = defaultdict(set)
|
|
185
|
-
_tables = sql_get_used_tables(
|
|
219
|
+
_tables = sql_get_used_tables(
|
|
220
|
+
sql,
|
|
221
|
+
default_database=default_database,
|
|
222
|
+
raising=True,
|
|
223
|
+
table_functions=check_functions,
|
|
224
|
+
function_allow_list=function_allow_list,
|
|
225
|
+
)
|
|
186
226
|
seen_tables = set()
|
|
187
227
|
table: Tuple[str, str] | Tuple[str, str, str]
|
|
228
|
+
if function_allow_list is None:
|
|
229
|
+
_enabled_table_functions = ENABLED_TABLE_FUNCTIONS
|
|
230
|
+
else:
|
|
231
|
+
_enabled_table_functions = ENABLED_TABLE_FUNCTIONS.union(set(function_allow_list))
|
|
188
232
|
while _tables:
|
|
189
233
|
table = _tables.pop()
|
|
190
234
|
if len(table) == 3:
|
|
191
235
|
first_table, second_table, last_table = table
|
|
192
|
-
if last_table and last_table not in
|
|
236
|
+
if last_table and last_table not in _enabled_table_functions:
|
|
193
237
|
raise InvalidFunction(table_function_names=last_table)
|
|
194
238
|
if first_table or second_table:
|
|
195
239
|
table = (first_table, second_table)
|
|
@@ -204,7 +248,7 @@ def replace_tables(
|
|
|
204
248
|
if len(dependent_table) == 3:
|
|
205
249
|
if (
|
|
206
250
|
dependent_table[2]
|
|
207
|
-
and dependent_table[2] not in
|
|
251
|
+
and dependent_table[2] not in _enabled_table_functions
|
|
208
252
|
and not (dependent_table[2] in ["cluster"] and replacement[0] == VALID_REMOTE)
|
|
209
253
|
):
|
|
210
254
|
raise InvalidFunction(table_function_names=dependent_table[2])
|
|
@@ -219,7 +263,9 @@ def replace_tables(
|
|
|
219
263
|
deps_sorted = list(reversed(list(toposort(deps))))
|
|
220
264
|
|
|
221
265
|
if not deps_sorted:
|
|
222
|
-
return replace_tables_chquery_cached(
|
|
266
|
+
return replace_tables_chquery_cached(
|
|
267
|
+
sql, None, output_one_line=output_one_line, timestamp=timestamp, function_allow_list=hashable_list
|
|
268
|
+
)
|
|
223
269
|
|
|
224
270
|
for current_deps in deps_sorted:
|
|
225
271
|
current_replacements = {}
|
|
@@ -251,10 +297,17 @@ def replace_tables(
|
|
|
251
297
|
# We need to transform the dictionary into something cacheable, so a sorted tuple of tuples it is
|
|
252
298
|
r = tuple(sorted([(k, v) for k, v in current_replacements.items()]))
|
|
253
299
|
sql = replace_tables_chquery_cached(
|
|
254
|
-
sql,
|
|
300
|
+
sql,
|
|
301
|
+
r,
|
|
302
|
+
default_database=default_database,
|
|
303
|
+
output_one_line=output_one_line,
|
|
304
|
+
timestamp=timestamp,
|
|
305
|
+
function_allow_list=hashable_list,
|
|
255
306
|
)
|
|
256
307
|
else:
|
|
257
|
-
sql = replace_tables_chquery_cached(
|
|
308
|
+
sql = replace_tables_chquery_cached(
|
|
309
|
+
sql, None, output_one_line=output_one_line, timestamp=timestamp, function_allow_list=hashable_list
|
|
310
|
+
)
|
|
258
311
|
|
|
259
312
|
return sql
|
|
260
313
|
|
|
@@ -539,6 +539,12 @@ async def print_pipe(ctx: Context, pipe: str, query: str, format_: str):
|
|
|
539
539
|
help="When set, the checker will get Main Workspace requests",
|
|
540
540
|
hidden=True,
|
|
541
541
|
)
|
|
542
|
+
@click.option(
|
|
543
|
+
"--relative-change",
|
|
544
|
+
type=float,
|
|
545
|
+
default=0.01,
|
|
546
|
+
help="When set, the checker will validate the new version has less than this distance with the current version",
|
|
547
|
+
)
|
|
542
548
|
@click.pass_context
|
|
543
549
|
@coro
|
|
544
550
|
async def regression_test(
|
|
@@ -556,6 +562,7 @@ async def regression_test(
|
|
|
556
562
|
ignore_order: bool,
|
|
557
563
|
validate_processed_bytes: bool,
|
|
558
564
|
check_requests_from_main: bool,
|
|
565
|
+
relative_change: float,
|
|
559
566
|
):
|
|
560
567
|
"""
|
|
561
568
|
Run regression tests on Tinybird
|
|
@@ -583,6 +590,7 @@ async def regression_test(
|
|
|
583
590
|
no_versions=no_versions,
|
|
584
591
|
run_tests=True,
|
|
585
592
|
tests_to_run=limit,
|
|
593
|
+
tests_relative_change=relative_change,
|
|
586
594
|
tests_sample_by_params=sample_by_params,
|
|
587
595
|
tests_filter_by=match,
|
|
588
596
|
tests_failfast=failfast,
|
|
@@ -16,35 +16,35 @@ regions_list: List[Region] = [
|
|
|
16
16
|
"name": "europe-west3",
|
|
17
17
|
"provider": "gcp",
|
|
18
18
|
"api_host": "https://api.tinybird.co",
|
|
19
|
-
"host": "https://
|
|
19
|
+
"host": "https://app.tinybird.co/gcp/europe-west3",
|
|
20
20
|
"default_password": "",
|
|
21
21
|
},
|
|
22
22
|
{
|
|
23
23
|
"name": "us-east4",
|
|
24
24
|
"provider": "gcp",
|
|
25
25
|
"api_host": "https://api.us-east.tinybird.co",
|
|
26
|
-
"host": "https://
|
|
26
|
+
"host": "https://app.tinybird.co/gcp/us-east4",
|
|
27
27
|
"default_password": "",
|
|
28
28
|
},
|
|
29
29
|
{
|
|
30
30
|
"name": "us-east-1",
|
|
31
31
|
"provider": "aws",
|
|
32
32
|
"api_host": "https://api.us-east.aws.tinybird.co",
|
|
33
|
-
"host": "https://
|
|
33
|
+
"host": "https://app.tinybird.co/aws/us-east-1",
|
|
34
34
|
"default_password": "",
|
|
35
35
|
},
|
|
36
36
|
{
|
|
37
37
|
"name": "us-west-2",
|
|
38
38
|
"provider": "aws",
|
|
39
39
|
"api_host": "https://api.us-west-2.aws.tinybird.co",
|
|
40
|
-
"host": "https://
|
|
40
|
+
"host": "https://app.tinybird.co/aws/us-west-2",
|
|
41
41
|
"default_password": "",
|
|
42
42
|
},
|
|
43
43
|
{
|
|
44
44
|
"name": "eu-central-1",
|
|
45
45
|
"provider": "aws",
|
|
46
46
|
"api_host": "https://api.eu-central-1.aws.tinybird.co",
|
|
47
|
-
"host": "https://
|
|
47
|
+
"host": "https://app.tinybird.co/aws/eu-central-1",
|
|
48
48
|
"default_password": "",
|
|
49
49
|
},
|
|
50
50
|
]
|
|
@@ -22,6 +22,8 @@ from tinybird.tb_cli_modules.common import (
|
|
|
22
22
|
from tinybird.tb_cli_modules.exceptions import CLIWorkspaceMembersException
|
|
23
23
|
from tinybird.tb_cli_modules.workspace import workspace
|
|
24
24
|
|
|
25
|
+
ROLES = ["viewer", "guest", "admin"]
|
|
26
|
+
|
|
25
27
|
|
|
26
28
|
@dataclass
|
|
27
29
|
class WorkspaceMemberCommandContext:
|
|
@@ -55,10 +57,13 @@ def members(ctx: Context) -> None:
|
|
|
55
57
|
|
|
56
58
|
@members.command(name="add", short_help="Adds members to the current Workspace")
|
|
57
59
|
@click.argument("members_emails")
|
|
60
|
+
@click.option("--role", is_flag=False, default=None, help="Role for the members being added", type=click.Choice(ROLES))
|
|
58
61
|
@click.option("--user_token", is_flag=False, default=None, help="When passed, we won't prompt asking for it")
|
|
59
62
|
@click.pass_context
|
|
60
63
|
@coro
|
|
61
|
-
async def add_members_to_workspace(
|
|
64
|
+
async def add_members_to_workspace(
|
|
65
|
+
ctx: Context, members_emails: str, user_token: Optional[str], role: Optional[str]
|
|
66
|
+
) -> None:
|
|
62
67
|
"""Adds members to the current Workspace."""
|
|
63
68
|
|
|
64
69
|
cmd_ctx = await get_command_context(ctx)
|
|
@@ -79,7 +84,7 @@ async def add_members_to_workspace(ctx: Context, members_emails: str, user_token
|
|
|
79
84
|
|
|
80
85
|
user_client: TinyB = deepcopy(cmd_ctx.client)
|
|
81
86
|
user_client.token = user_token
|
|
82
|
-
await user_client.add_users_to_workspace(cmd_ctx.workspace, users_to_add)
|
|
87
|
+
await user_client.add_users_to_workspace(cmd_ctx.workspace, users_to_add, role)
|
|
83
88
|
msg = (
|
|
84
89
|
FeedbackManager.success_workspace_user_added(user=users_to_add[0], workspace_name=cmd_ctx.workspace["name"])
|
|
85
90
|
if len(users_to_add) == 1
|
|
@@ -149,7 +154,7 @@ async def remove_members_from_workspace(ctx: Context, members_emails: str, user_
|
|
|
149
154
|
|
|
150
155
|
|
|
151
156
|
@members.command(name="set-role", short_help="Sets the role for existing workspace members")
|
|
152
|
-
@click.argument("role", required=True, type=click.Choice(
|
|
157
|
+
@click.argument("role", required=True, type=click.Choice(ROLES))
|
|
153
158
|
@click.argument("members_emails", required=True, type=str)
|
|
154
159
|
@click.option("--user_token", is_flag=False, default=None, help="When passed, we won't prompt asking for it")
|
|
155
160
|
@click.pass_context
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: tinybird-cli
|
|
3
|
-
Version:
|
|
3
|
+
Version: 5.0.0
|
|
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,20 @@ The Tinybird command-line tool allows you to use all the Tinybird functionality
|
|
|
18
18
|
Changelog
|
|
19
19
|
----------
|
|
20
20
|
|
|
21
|
+
5.0.0
|
|
22
|
+
******
|
|
23
|
+
|
|
24
|
+
- `Breaking change` Make `insertion_date` column explicit. This column is no longer inferred, it must be present in the Data Source file.
|
|
25
|
+
|
|
26
|
+
Detailed info at https://www.tinybird.co/docs/changelog/2024-06-17-insertion_date-deprecation
|
|
27
|
+
|
|
28
|
+
- `Added` parameter to `tb pipe regression-test` --relative-change
|
|
29
|
+
- `Added` `tb push` displays warnings when using a reserved parameter in a pipe.
|
|
30
|
+
- `Added` --role parameter to `tb workspace members add`
|
|
31
|
+
- `Changed` Point region hosts to new `app.tinybird.co` domain
|
|
32
|
+
- `Improved` help message of `tb workspace members set-role` now displays the 3 valid roles: viewer|guest|admin.
|
|
33
|
+
- `Improved` syntax error messages when parsing endpoints.
|
|
34
|
+
|
|
21
35
|
4.1.1
|
|
22
36
|
************
|
|
23
37
|
|
|
@@ -27,7 +41,7 @@ Changelog
|
|
|
27
41
|
************
|
|
28
42
|
|
|
29
43
|
- `Added` `tb token create` command to be able to create static and JWT tokens from the CLI. You can check more information at https://www.tinybird.co/blog-posts/jwt-api-endpoints-public-beta
|
|
30
|
-
- `Fixed` `tb init --git` to pin `tinybird-cli>=4,<5` in `requirements.txt` to avoid issues with the latest version of the CLI.
|
|
44
|
+
- `Fixed` `tb init --git` to pin `tinybird-cli>=4,<5` in `requirements.txt` to avoid issues with the latest version of the CLI.
|
|
31
45
|
|
|
32
46
|
4.0.0
|
|
33
47
|
************
|
|
@@ -85,7 +99,7 @@ This is a major release, please read the commands affected below and consider up
|
|
|
85
99
|
************
|
|
86
100
|
|
|
87
101
|
- `Changed` fixed major version of tinybird-cli to lower than 4 when using `tb init --git`
|
|
88
|
-
- `Changed` behavior when running `tb deploy` on a branch to push the connection settings to the backend. This change is the backend that decides what to do.
|
|
102
|
+
- `Changed` behavior when running `tb deploy` on a branch to push the connection settings to the backend. This change is the backend that decides what to do.
|
|
89
103
|
- `Changed` `tb pipe sink` commands are now available
|
|
90
104
|
- `Fixed` regression tests query when filtering by specific parameter
|
|
91
105
|
- `Fixed` Avoid `tb fmt` to error if there's a `CASE` in the sql
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tinybird-cli-4.1.2.dev0 → tinybird-cli-5.0.0}/tinybird/tb_cli_modules/tinyunit/tinyunit_lib.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|