tinybird 0.0.1.dev72__tar.gz → 0.0.1.dev73__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.
Potentially problematic release.
This version of tinybird might be problematic. Click here for more details.
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/PKG-INFO +1 -1
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/client.py +3 -1
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/prompts.py +54 -6
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/__cli__.py +2 -2
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/cli.py +1 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/build.py +2 -2
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/cli.py +6 -6
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/common.py +1 -2
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/config.py +1 -1
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/copy.py +1 -1
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/datafile/build_pipe.py +35 -186
- tinybird-0.0.1.dev73/tinybird/tb/modules/datafile/playground.py +1400 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/datasource.py +1 -1
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/endpoint.py +1 -1
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/fmt.py +2 -2
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/local.py +2 -2
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/materialization.py +1 -1
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/mock.py +1 -1
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/pipe.py +1 -1
- tinybird-0.0.1.dev73/tinybird/tb/modules/playground.py +135 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/shell.py +6 -5
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/token.py +17 -3
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/watch.py +105 -1
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/workspace.py +1 -1
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird.egg-info/PKG-INFO +1 -1
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird.egg-info/SOURCES.txt +2 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/setup.cfg +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/__cli__.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/ch_utils/constants.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/ch_utils/engine.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/check_pypi.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/config.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/connectors.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/context.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/datafile.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/datatypes.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/feedback_manager.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/git_settings.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/sql.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/sql_template.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/sql_template_fmt.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/sql_toolset.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/syncasync.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/auth.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/cicd.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/create.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/datafile/build.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/datafile/build_common.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/datafile/build_datasource.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/datafile/common.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/datafile/diff.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/datafile/exceptions.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/datafile/fixture.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/datafile/format_common.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/datafile/format_datasource.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/datafile/format_pipe.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/datafile/parse_datasource.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/datafile/parse_pipe.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/datafile/pipe_checker.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/datafile/pull.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/deployment.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/exceptions.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/feedback_manager.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/job.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/llm.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/llm_utils.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/local_common.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/login.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/project.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/regions.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/table.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/tag.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/telemetry.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/test.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/tinyunit/tinyunit.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/tinyunit/tinyunit_lib.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb/modules/workspace_members.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb_cli.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb_cli_modules/auth.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb_cli_modules/branch.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb_cli_modules/cicd.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb_cli_modules/cli.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb_cli_modules/common.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb_cli_modules/config.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb_cli_modules/connection.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb_cli_modules/datasource.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb_cli_modules/exceptions.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb_cli_modules/fmt.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb_cli_modules/job.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb_cli_modules/pipe.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb_cli_modules/regions.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb_cli_modules/tag.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb_cli_modules/telemetry.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb_cli_modules/test.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb_cli_modules/tinyunit/tinyunit.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb_cli_modules/tinyunit/tinyunit_lib.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb_cli_modules/workspace.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tb_cli_modules/workspace_members.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird/tornado_template.py +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird.egg-info/dependency_links.txt +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird.egg-info/entry_points.txt +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird.egg-info/requires.txt +0 -0
- {tinybird-0.0.1.dev72 → tinybird-0.0.1.dev73}/tinybird.egg-info/top_level.txt +0 -0
|
@@ -669,10 +669,12 @@ class TinyB:
|
|
|
669
669
|
async def pipe_unlink_materialized(self, pipe_name: str, node_id: str):
|
|
670
670
|
return await self._req(f"/v0/pipes/{pipe_name}/nodes/{node_id}/materialization", method="DELETE")
|
|
671
671
|
|
|
672
|
-
async def query(self, sql: str, pipeline: Optional[str] = None):
|
|
672
|
+
async def query(self, sql: str, pipeline: Optional[str] = None, playground: Optional[str] = None):
|
|
673
673
|
params = {}
|
|
674
674
|
if pipeline:
|
|
675
675
|
params = {"pipeline": pipeline}
|
|
676
|
+
if playground:
|
|
677
|
+
params = {"playground": playground}
|
|
676
678
|
params.update({"release_replacements": "true"})
|
|
677
679
|
|
|
678
680
|
if len(sql) > TinyB.MAX_GET_LENGTH:
|
|
@@ -634,20 +634,42 @@ materialized_pipe_instructions = """
|
|
|
634
634
|
<materialized_pipe_instructions>
|
|
635
635
|
- Do not create materialized pipes by default, unless the user asks for it.
|
|
636
636
|
- In a .pipe file you can define how to materialize each row ingested in the earliest Data Source in the Pipe query to a materialized Data Source. Materialization happens at ingest.
|
|
637
|
-
- DATASOURCE: Required when TYPE is MATERIALIZED. Sets the
|
|
637
|
+
- DATASOURCE: Required when TYPE is MATERIALIZED. Sets the target Data Source for materialized nodes.
|
|
638
638
|
- TYPE MATERIALIZED is the type of the pipe and it is mandatory for materialized pipes.
|
|
639
|
-
- The content of the .pipe file must follow
|
|
640
|
-
|
|
641
|
-
|
|
639
|
+
- The content of the .pipe file must follow the materialized_pipe_content format.
|
|
640
|
+
- Use State modifier for the aggregated columns in the pipe.
|
|
641
|
+
- Keep the SQL query simple and avoid using complex queries with joins, subqueries, etc.
|
|
642
|
+
</materialized_pipe_instructions>
|
|
643
|
+
<materialized_pipe_content>
|
|
642
644
|
NODE daily_sales
|
|
643
645
|
SQL >
|
|
644
|
-
SELECT toStartOfDay(starting_date) day, country,
|
|
646
|
+
SELECT toStartOfDay(starting_date) day, country, sumState(sales) as total_sales
|
|
645
647
|
FROM teams
|
|
646
648
|
GROUP BY day, country
|
|
647
649
|
|
|
648
650
|
TYPE MATERIALIZED
|
|
649
651
|
DATASOURCE sales_by_hour
|
|
650
|
-
</
|
|
652
|
+
</materialized_pipe_content>
|
|
653
|
+
<target_datasource_instructions>
|
|
654
|
+
- The target datasource of a materialized pipe must have an AggregatingMergeTree engine.
|
|
655
|
+
- Use AggregateFunction for the aggregated columns in the pipe.
|
|
656
|
+
- Pipes using a materialized data source must use the Merge modifier in the SQL query for the aggregated columns. Example: sumMerge(total_sales)
|
|
657
|
+
- Put all dimensions in the ENGINE_SORTING_KEY, sorted from least to most cardinality.
|
|
658
|
+
</target_datasource_instructions>
|
|
659
|
+
<target_datasource_content>
|
|
660
|
+
SCHEMA >
|
|
661
|
+
`total_sales` AggregateFunction(sum, Float64),
|
|
662
|
+
`sales_count` AggregateFunction(count, UInt64),
|
|
663
|
+
`column_name_2` AggregateFunction(avg, Float64),
|
|
664
|
+
`dimension_1` String,
|
|
665
|
+
`dimension_2` String,
|
|
666
|
+
...
|
|
667
|
+
`date` DateTime
|
|
668
|
+
|
|
669
|
+
ENGINE "AggregatingMergeTree"
|
|
670
|
+
ENGINE_PARTITION_KEY "toYYYYMM(date)"
|
|
671
|
+
ENGINE_SORTING_KEY "date, dimension_1, dimension_2, ..."
|
|
672
|
+
</target_datasource_content>
|
|
651
673
|
"""
|
|
652
674
|
|
|
653
675
|
|
|
@@ -821,6 +843,10 @@ Follow these instructions when creating or updating .pipe files:
|
|
|
821
843
|
Follow these instructions when creating or updating .yaml files for tests:
|
|
822
844
|
{test_instructions}
|
|
823
845
|
</test_file_instructions>
|
|
846
|
+
<deployment_instruction>
|
|
847
|
+
Follow these instructions when evolving a datasource schema:
|
|
848
|
+
{deployment_instructions}
|
|
849
|
+
</deployment_instruction>
|
|
824
850
|
""".format(
|
|
825
851
|
base_command=base_command,
|
|
826
852
|
datasource_instructions=datasource_instructions,
|
|
@@ -831,6 +857,7 @@ Follow these instructions when creating or updating .yaml files for tests:
|
|
|
831
857
|
copy_pipe_instructions=copy_pipe_instructions,
|
|
832
858
|
materialized_pipe_instructions=materialized_pipe_instructions,
|
|
833
859
|
test_instructions=test_instructions,
|
|
860
|
+
deployment_instructions=deployment_instructions,
|
|
834
861
|
)
|
|
835
862
|
|
|
836
863
|
|
|
@@ -879,3 +906,24 @@ test_instructions = """
|
|
|
879
906
|
|
|
880
907
|
</test_file_format>
|
|
881
908
|
"""
|
|
909
|
+
|
|
910
|
+
deployment_instructions = """
|
|
911
|
+
- When you make schema changes that are incompatible with the old schema, you must use a forward query in your data source. Forward queries are necessary when introducing breaking changes. Otherwise, your deployment will fail due to a schema mismatch.
|
|
912
|
+
- Forward queries translate the old schema to a new one that you define in the .datasource file. This helps you evolve your schema while continuing to ingest data.
|
|
913
|
+
Follow these steps to evolve your schema using a forward query:
|
|
914
|
+
- Edit the .datasource file to add a forward query.
|
|
915
|
+
- Run tb deploy --check to validate the deployment before creating it.
|
|
916
|
+
- Deploy and promote your changes in Tinybird Cloud using {base_command} --cloud deploy.
|
|
917
|
+
<forward_query_example>
|
|
918
|
+
SCHEMA >
|
|
919
|
+
`timestamp` DateTime `json:$.timestamp`,
|
|
920
|
+
`session_id` UUID `json:$.session_id`,
|
|
921
|
+
`action` String `json:$.action`,
|
|
922
|
+
`version` String `json:$.version`,
|
|
923
|
+
`payload` String `json:$.payload`
|
|
924
|
+
|
|
925
|
+
FORWARD_QUERY >
|
|
926
|
+
select timestamp, toUUID(session_id) as session_id, action, version, payload
|
|
927
|
+
</forward_query_example>
|
|
928
|
+
</deployment_instruction>
|
|
929
|
+
"""
|
|
@@ -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__ = '0.0.1.
|
|
8
|
-
__revision__ = '
|
|
7
|
+
__version__ = '0.0.1.dev73'
|
|
8
|
+
__revision__ = 'a843588'
|
|
@@ -20,6 +20,7 @@ import tinybird.tb.modules.login
|
|
|
20
20
|
import tinybird.tb.modules.materialization
|
|
21
21
|
import tinybird.tb.modules.mock
|
|
22
22
|
import tinybird.tb.modules.pipe
|
|
23
|
+
import tinybird.tb.modules.playground
|
|
23
24
|
import tinybird.tb.modules.tag
|
|
24
25
|
import tinybird.tb.modules.test
|
|
25
26
|
import tinybird.tb.modules.token
|
|
@@ -41,7 +41,7 @@ def build(ctx: click.Context, watch: bool) -> None:
|
|
|
41
41
|
)
|
|
42
42
|
|
|
43
43
|
|
|
44
|
-
@cli.command("dev", help="Build the project server side and watch for changes")
|
|
44
|
+
@cli.command("dev", help="Build the project server side and watch for changes.")
|
|
45
45
|
@click.pass_context
|
|
46
46
|
def dev(ctx: click.Context) -> None:
|
|
47
47
|
project: Project = ctx.ensure_object(dict)["project"]
|
|
@@ -240,7 +240,7 @@ def process(
|
|
|
240
240
|
def run_watch(
|
|
241
241
|
project: Project, tb_client: TinyB, process: Callable[[bool, Optional[str], Optional[str]], None]
|
|
242
242
|
) -> None:
|
|
243
|
-
shell = Shell(project=project, tb_client=tb_client)
|
|
243
|
+
shell = Shell(project=project, tb_client=tb_client, playground=False)
|
|
244
244
|
click.echo(FeedbackManager.gray(message="\nWatching for changes..."))
|
|
245
245
|
watcher_thread = threading.Thread(
|
|
246
246
|
target=watch_project,
|
|
@@ -52,11 +52,11 @@ VERSION = f"{__cli__.__version__} (rev {__cli__.__revision__})"
|
|
|
52
52
|
default=False,
|
|
53
53
|
help="Prints internal representation, can be combined with any command to get more information.",
|
|
54
54
|
)
|
|
55
|
-
@click.option("--token", help="Use auth token, defaults to TB_TOKEN envvar, then to the .tinyb file")
|
|
55
|
+
@click.option("--token", help="Use auth token, defaults to TB_TOKEN envvar, then to the .tinyb file.")
|
|
56
56
|
@click.option("--host", help="Use custom host, defaults to TB_HOST envvar, then to https://api.tinybird.co")
|
|
57
|
-
@click.option("--show-tokens", is_flag=True, default=False, help="Enable the output of tokens")
|
|
58
|
-
@click.option("--cloud/--local", is_flag=True, default=False, help="Run against cloud or local")
|
|
59
|
-
@click.option("--build", is_flag=True, default=False, help="Run against build mode")
|
|
57
|
+
@click.option("--show-tokens", is_flag=True, default=False, help="Enable the output of tokens.")
|
|
58
|
+
@click.option("--cloud/--local", is_flag=True, default=False, help="Run against cloud or local.")
|
|
59
|
+
@click.option("--build", is_flag=True, default=False, help="Run against build mode.")
|
|
60
60
|
@click.option("--staging", is_flag=True, default=False, help="Run against a staging deployment.")
|
|
61
61
|
@click.version_option(version=VERSION)
|
|
62
62
|
@click.pass_context
|
|
@@ -189,7 +189,7 @@ async def dependencies(
|
|
|
189
189
|
|
|
190
190
|
@cli.command(
|
|
191
191
|
name="diff",
|
|
192
|
-
short_help="Diff local datafiles to the corresponding remote files in the workspace.
|
|
192
|
+
short_help="Diff local datafiles to the corresponding remote files in the workspace. Only diffs VERSION and SCHEMA for .datasource files.",
|
|
193
193
|
)
|
|
194
194
|
@click.argument("filename", type=click.Path(exists=True), nargs=-1, required=False)
|
|
195
195
|
@click.option(
|
|
@@ -394,7 +394,7 @@ async def create_ctx_client(ctx: Context, config: Dict[str, Any], cloud: bool, b
|
|
|
394
394
|
if command in commands_without_ctx_client:
|
|
395
395
|
return None
|
|
396
396
|
|
|
397
|
-
commands_always_cloud = ["pull"]
|
|
397
|
+
commands_always_cloud = ["pull", "playground"]
|
|
398
398
|
commands_always_build = ["build", "test", "dev"]
|
|
399
399
|
commands_always_local = ["create"]
|
|
400
400
|
if (
|
|
@@ -244,8 +244,7 @@ class CatchAuthExceptions(AliasedGroup):
|
|
|
244
244
|
formatter.write_text(
|
|
245
245
|
"""
|
|
246
246
|
Tinybird collects anonymous usage data and errors to improve the command
|
|
247
|
-
line experience. To opt-out, set TB_CLI_TELEMETRY_OPTOUT
|
|
248
|
-
variable to '1' or 'true'."""
|
|
247
|
+
line experience. To opt-out, set TB_CLI_TELEMETRY_OPTOUT to '1' or 'true'."""
|
|
249
248
|
)
|
|
250
249
|
formatter.write_paragraph()
|
|
251
250
|
|
|
@@ -77,7 +77,7 @@ class CLIConfig:
|
|
|
77
77
|
_global: Optional["CLIConfig"] = None
|
|
78
78
|
_projects: Dict[str, "CLIConfig"] = {}
|
|
79
79
|
|
|
80
|
-
def __init__(self, path: Optional[str], parent: Optional["CLIConfig"] = None) -> None:
|
|
80
|
+
def __init__(self, path: Optional[str] = None, parent: Optional["CLIConfig"] = None) -> None:
|
|
81
81
|
self._path = path
|
|
82
82
|
self._parent = parent
|
|
83
83
|
self._values: Dict[str, ConfigValue] = {}
|
|
@@ -9,12 +9,11 @@ import requests
|
|
|
9
9
|
from croniter import croniter
|
|
10
10
|
|
|
11
11
|
from tinybird.client import DoesNotExistException, TinyB
|
|
12
|
-
from tinybird.tb.modules.common import
|
|
12
|
+
from tinybird.tb.modules.common import requests_delete, requests_get, wait_job
|
|
13
|
+
from tinybird.tb.modules.config import CLIConfig
|
|
13
14
|
from tinybird.tb.modules.datafile.common import ON_DEMAND, CopyModes, CopyParameters, PipeNodeTypes, PipeTypes
|
|
14
|
-
from tinybird.tb.modules.datafile.pipe_checker import PipeCheckerRunner
|
|
15
15
|
from tinybird.tb.modules.exceptions import CLIPipeException
|
|
16
16
|
from tinybird.tb.modules.feedback_manager import FeedbackManager
|
|
17
|
-
from tinybird.tb.modules.table import format_pretty_table
|
|
18
17
|
|
|
19
18
|
|
|
20
19
|
async def new_pipe(
|
|
@@ -47,7 +46,8 @@ async def new_pipe(
|
|
|
47
46
|
):
|
|
48
47
|
# TODO use tb_client instead of calling the urls directly.
|
|
49
48
|
host = tb_client.host
|
|
50
|
-
|
|
49
|
+
config = CLIConfig().get_project_config()
|
|
50
|
+
token = config.get_user_token()
|
|
51
51
|
|
|
52
52
|
headers = {"Authorization": f"Bearer {token}"}
|
|
53
53
|
|
|
@@ -55,11 +55,16 @@ async def new_pipe(
|
|
|
55
55
|
cli_params["cli_version"] = tb_client.version
|
|
56
56
|
cli_params["description"] = p.get("description", "")
|
|
57
57
|
cli_params["ignore_sql_errors"] = "true" if ignore_sql_errors else "false"
|
|
58
|
+
cli_params["workspace_id"] = config.get("id", None)
|
|
58
59
|
|
|
59
|
-
r: requests.Response = await requests_get(f"{host}/v0/
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
r: requests.Response = await requests_get(f"{host}/v0/playgrounds?{urlencode(cli_params)}", headers=headers)
|
|
61
|
+
current_pipe = None
|
|
62
|
+
pipe_exists = False
|
|
63
|
+
playgrounds_response = r.json() if r.status_code == 200 else None
|
|
64
|
+
if playgrounds_response:
|
|
65
|
+
playgrounds = playgrounds_response["playgrounds"]
|
|
66
|
+
current_pipe = next((play for play in playgrounds if play["name"] == p["name"] + "__tb__playground"), None)
|
|
67
|
+
pipe_exists = current_pipe is not None
|
|
63
68
|
|
|
64
69
|
is_materialized = any([node.get("params", {}).get("type", None) == "materialized" for node in p["nodes"]])
|
|
65
70
|
copy_node = next((node for node in p["nodes"] if node.get("params", {}).get("type", None) == "copy"), None)
|
|
@@ -75,27 +80,7 @@ async def new_pipe(
|
|
|
75
80
|
# TODO: this should create a different node and rename it to the final one on success
|
|
76
81
|
if check and not populate:
|
|
77
82
|
if not is_materialized and not copy_node and not sink_node and not stream_node:
|
|
78
|
-
|
|
79
|
-
p,
|
|
80
|
-
host,
|
|
81
|
-
token,
|
|
82
|
-
populate,
|
|
83
|
-
tb_client,
|
|
84
|
-
only_response_times=only_response_times,
|
|
85
|
-
limit=tests_to_run,
|
|
86
|
-
relative_change=tests_relative_change,
|
|
87
|
-
sample_by_params=tests_to_sample_by_params,
|
|
88
|
-
matches=tests_filter_by,
|
|
89
|
-
failfast=tests_failfast,
|
|
90
|
-
validate_processed_bytes=tests_validate_processed_bytes,
|
|
91
|
-
ignore_order=tests_ignore_order,
|
|
92
|
-
token_for_requests_to_check=(
|
|
93
|
-
await get_token_from_main_branch(tb_client)
|
|
94
|
-
if not tests_check_requests_from_branch
|
|
95
|
-
else None
|
|
96
|
-
),
|
|
97
|
-
current_pipe=current_pipe,
|
|
98
|
-
)
|
|
83
|
+
pass
|
|
99
84
|
else:
|
|
100
85
|
if is_materialized:
|
|
101
86
|
await check_materialized(
|
|
@@ -167,9 +152,27 @@ async def new_pipe(
|
|
|
167
152
|
post_headers.update(headers)
|
|
168
153
|
|
|
169
154
|
try:
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
)
|
|
155
|
+
user_client = deepcopy(tb_client)
|
|
156
|
+
config = CLIConfig().get_project_config()
|
|
157
|
+
user_client.token = config.get_user_token()
|
|
158
|
+
params["workspace_id"] = config.get("id", None)
|
|
159
|
+
body["name"] = p["name"] + "__tb__playground"
|
|
160
|
+
|
|
161
|
+
if pipe_exists and current_pipe:
|
|
162
|
+
data = await user_client._req(
|
|
163
|
+
f"/v0/playgrounds/{current_pipe['id']}?{urlencode(params)}",
|
|
164
|
+
method="PUT",
|
|
165
|
+
headers=post_headers,
|
|
166
|
+
data=json.dumps(body),
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
else:
|
|
170
|
+
data = await user_client._req(
|
|
171
|
+
f"/v0/playgrounds?{urlencode(params)}",
|
|
172
|
+
method="POST",
|
|
173
|
+
headers=post_headers,
|
|
174
|
+
data=json.dumps(body),
|
|
175
|
+
)
|
|
173
176
|
except Exception as e:
|
|
174
177
|
raise click.ClickException(FeedbackManager.error_pushing_pipe(pipe=p["name"], error=str(e)))
|
|
175
178
|
|
|
@@ -296,160 +299,6 @@ async def get_token_from_main_branch(branch_tb_client: TinyB) -> Optional[str]:
|
|
|
296
299
|
return token_from_main_branch
|
|
297
300
|
|
|
298
301
|
|
|
299
|
-
async def check_pipe(
|
|
300
|
-
pipe,
|
|
301
|
-
host: str,
|
|
302
|
-
token: str,
|
|
303
|
-
populate: bool,
|
|
304
|
-
cl: TinyB,
|
|
305
|
-
limit: int = 0,
|
|
306
|
-
relative_change: float = 0.01,
|
|
307
|
-
sample_by_params: int = 0,
|
|
308
|
-
only_response_times=False,
|
|
309
|
-
matches: Optional[List[str]] = None,
|
|
310
|
-
failfast: bool = False,
|
|
311
|
-
validate_processed_bytes: bool = False,
|
|
312
|
-
ignore_order: bool = False,
|
|
313
|
-
token_for_requests_to_check: Optional[str] = None,
|
|
314
|
-
current_pipe: Optional[Dict[str, Any]] = None,
|
|
315
|
-
):
|
|
316
|
-
checker_pipe = deepcopy(pipe)
|
|
317
|
-
checker_pipe["name"] = f"{checker_pipe['name']}__checker"
|
|
318
|
-
|
|
319
|
-
if current_pipe:
|
|
320
|
-
pipe_type = current_pipe["type"]
|
|
321
|
-
if pipe_type == PipeTypes.COPY:
|
|
322
|
-
await cl.pipe_remove_copy(current_pipe["id"], current_pipe["copy_node"])
|
|
323
|
-
if pipe_type == PipeTypes.DATA_SINK:
|
|
324
|
-
await cl.pipe_remove_sink(current_pipe["id"], current_pipe["sink_node"])
|
|
325
|
-
if pipe_type == PipeTypes.STREAM:
|
|
326
|
-
await cl.pipe_remove_stream(current_pipe["id"], current_pipe["stream_node"])
|
|
327
|
-
|
|
328
|
-
# In case of doing --force for a materialized view, checker is being created as standard pipe
|
|
329
|
-
for node in checker_pipe["nodes"]:
|
|
330
|
-
node["params"]["type"] = PipeNodeTypes.STANDARD
|
|
331
|
-
|
|
332
|
-
if populate:
|
|
333
|
-
raise click.ClickException(FeedbackManager.error_check_pipes_populate())
|
|
334
|
-
|
|
335
|
-
runner = PipeCheckerRunner(pipe["name"], host)
|
|
336
|
-
headers = (
|
|
337
|
-
{"Authorization": f"Bearer {token_for_requests_to_check}"}
|
|
338
|
-
if token_for_requests_to_check
|
|
339
|
-
else {"Authorization": f"Bearer {token}"}
|
|
340
|
-
)
|
|
341
|
-
|
|
342
|
-
sql_for_coverage, sql_latest_requests = runner.get_sqls_for_requests_to_check(
|
|
343
|
-
matches or [], sample_by_params, limit
|
|
344
|
-
)
|
|
345
|
-
|
|
346
|
-
params = {"q": sql_for_coverage if limit == 0 and sample_by_params > 0 else sql_latest_requests}
|
|
347
|
-
r: requests.Response = await requests_get(
|
|
348
|
-
f"{host}/v0/sql?{urlencode(params)}", headers=headers, verify=not getenv_bool("TB_DISABLE_SSL_CHECKS", False)
|
|
349
|
-
)
|
|
350
|
-
|
|
351
|
-
# If we get a timeout, fallback to just the last requests
|
|
352
|
-
|
|
353
|
-
if not r or r.status_code == 408:
|
|
354
|
-
params = {"q": sql_latest_requests}
|
|
355
|
-
r = await requests_get(
|
|
356
|
-
f"{host}/v0/sql?{urlencode(params)}",
|
|
357
|
-
headers=headers,
|
|
358
|
-
verify=not getenv_bool("TB_DISABLE_SSL_CHECKS", False),
|
|
359
|
-
)
|
|
360
|
-
|
|
361
|
-
if not r or r.status_code != 200:
|
|
362
|
-
raise click.ClickException(FeedbackManager.error_check_pipes_api(pipe=pipe["name"]))
|
|
363
|
-
|
|
364
|
-
pipe_requests_to_check: List[Dict[str, Any]] = []
|
|
365
|
-
for row in r.json().get("data", []):
|
|
366
|
-
for i in range(len(row["endpoint_url"])):
|
|
367
|
-
pipe_requests_to_check += [
|
|
368
|
-
{
|
|
369
|
-
"endpoint_url": f"{host}{row['endpoint_url'][i]}",
|
|
370
|
-
"pipe_request_params": row["pipe_request_params"][i],
|
|
371
|
-
"http_method": row["http_method"],
|
|
372
|
-
}
|
|
373
|
-
]
|
|
374
|
-
|
|
375
|
-
if not pipe_requests_to_check:
|
|
376
|
-
return
|
|
377
|
-
|
|
378
|
-
await new_pipe(checker_pipe, cl, force=True, check=False, populate=populate)
|
|
379
|
-
|
|
380
|
-
runner_response = runner.run_pipe_checker(
|
|
381
|
-
pipe_requests_to_check,
|
|
382
|
-
checker_pipe["name"],
|
|
383
|
-
token,
|
|
384
|
-
only_response_times,
|
|
385
|
-
ignore_order,
|
|
386
|
-
validate_processed_bytes,
|
|
387
|
-
relative_change,
|
|
388
|
-
failfast,
|
|
389
|
-
)
|
|
390
|
-
|
|
391
|
-
try:
|
|
392
|
-
if runner_response.metrics_summary and runner_response.metrics_timing:
|
|
393
|
-
column_names_tests = ["Test Run", "Test Passed", "Test Failed", "% Test Passed", "% Test Failed"]
|
|
394
|
-
click.echo("\n==== Test Metrics ====\n")
|
|
395
|
-
click.echo(
|
|
396
|
-
format_pretty_table(
|
|
397
|
-
[
|
|
398
|
-
[
|
|
399
|
-
runner_response.metrics_summary["run"],
|
|
400
|
-
runner_response.metrics_summary["passed"],
|
|
401
|
-
runner_response.metrics_summary["failed"],
|
|
402
|
-
runner_response.metrics_summary["percentage_passed"],
|
|
403
|
-
runner_response.metrics_summary["percentage_failed"],
|
|
404
|
-
]
|
|
405
|
-
],
|
|
406
|
-
column_names=column_names_tests,
|
|
407
|
-
)
|
|
408
|
-
)
|
|
409
|
-
|
|
410
|
-
column_names_timing = ["Timing Metric (s)", "Current", "New"]
|
|
411
|
-
click.echo("\n==== Response Time Metrics ====\n")
|
|
412
|
-
click.echo(
|
|
413
|
-
format_pretty_table(
|
|
414
|
-
[
|
|
415
|
-
[metric, runner_response.metrics_timing[metric][0], runner_response.metrics_timing[metric][1]]
|
|
416
|
-
for metric in [
|
|
417
|
-
"min response time",
|
|
418
|
-
"max response time",
|
|
419
|
-
"mean response time",
|
|
420
|
-
"median response time",
|
|
421
|
-
"p90 response time",
|
|
422
|
-
"min read bytes",
|
|
423
|
-
"max read bytes",
|
|
424
|
-
"mean read bytes",
|
|
425
|
-
"median read bytes",
|
|
426
|
-
"p90 read bytes",
|
|
427
|
-
]
|
|
428
|
-
],
|
|
429
|
-
column_names=column_names_timing,
|
|
430
|
-
)
|
|
431
|
-
)
|
|
432
|
-
except Exception:
|
|
433
|
-
pass
|
|
434
|
-
|
|
435
|
-
if not runner_response.was_successfull:
|
|
436
|
-
for failure in runner_response.failed:
|
|
437
|
-
try:
|
|
438
|
-
click.echo("==== Test FAILED ====\n")
|
|
439
|
-
click.echo(failure["name"])
|
|
440
|
-
click.echo(FeedbackManager.error_check_pipe(error=failure["error"]))
|
|
441
|
-
click.echo("=====================\n\n\n")
|
|
442
|
-
except Exception:
|
|
443
|
-
pass
|
|
444
|
-
raise RuntimeError("Invalid results, you can bypass checks by running push with the --no-check flag")
|
|
445
|
-
|
|
446
|
-
# Only delete if no errors, so we can check results after failure
|
|
447
|
-
headers = {"Authorization": f"Bearer {token}"}
|
|
448
|
-
r = await requests_delete(f"{host}/v0/pipes/{checker_pipe['name']}", headers=headers)
|
|
449
|
-
if r.status_code != 204:
|
|
450
|
-
click.echo(FeedbackManager.warning_check_pipe(content=r.content))
|
|
451
|
-
|
|
452
|
-
|
|
453
302
|
async def check_materialized(pipe, host, token, cl, override_datasource=False, current_pipe=None):
|
|
454
303
|
checker_pipe = deepcopy(pipe)
|
|
455
304
|
checker_pipe["name"] = f"{checker_pipe['name']}__checker"
|