tinybird 0.0.1.dev44__tar.gz → 0.0.1.dev47__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.

Files changed (102) hide show
  1. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/PKG-INFO +1 -1
  2. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/client.py +17 -1
  3. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/datafile.py +2 -0
  4. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/prompts.py +105 -12
  5. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/sql.py +3 -3
  6. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/__cli__.py +2 -2
  7. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/build.py +7 -4
  8. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/cli.py +17 -6
  9. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/common.py +1 -1
  10. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/create.py +32 -16
  11. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/datafile/common.py +1 -1
  12. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/datafile/format_datasource.py +1 -1
  13. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/datafile/format_pipe.py +4 -4
  14. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/datafile/pipe_checker.py +3 -3
  15. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/feedback_manager.py +5 -1
  16. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/local_common.py +10 -7
  17. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/pipe.py +6 -5
  18. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/shell.py +3 -3
  19. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/test.py +1 -1
  20. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/tinyunit/tinyunit.py +1 -1
  21. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/workspace.py +2 -1
  22. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb_cli_modules/cli.py +1 -1
  23. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird.egg-info/PKG-INFO +1 -1
  24. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/setup.cfg +0 -0
  25. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/__cli__.py +0 -0
  26. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/ch_utils/constants.py +0 -0
  27. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/ch_utils/engine.py +0 -0
  28. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/check_pypi.py +0 -0
  29. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/config.py +0 -0
  30. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/connectors.py +0 -0
  31. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/context.py +0 -0
  32. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/datatypes.py +0 -0
  33. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/feedback_manager.py +0 -0
  34. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/git_settings.py +0 -0
  35. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/sql_template.py +0 -0
  36. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/sql_template_fmt.py +0 -0
  37. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/sql_toolset.py +0 -0
  38. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/syncasync.py +0 -0
  39. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/cli.py +0 -0
  40. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/auth.py +0 -0
  41. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/cicd.py +0 -0
  42. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/config.py +0 -0
  43. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/copy.py +0 -0
  44. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/datafile/build.py +0 -0
  45. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/datafile/build_common.py +0 -0
  46. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/datafile/build_datasource.py +0 -0
  47. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/datafile/build_pipe.py +0 -0
  48. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/datafile/diff.py +0 -0
  49. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/datafile/exceptions.py +0 -0
  50. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/datafile/fixture.py +0 -0
  51. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/datafile/format_common.py +0 -0
  52. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/datafile/parse_datasource.py +0 -0
  53. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/datafile/parse_pipe.py +0 -0
  54. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/datafile/pull.py +0 -0
  55. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/datasource.py +0 -0
  56. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/deployment.py +0 -0
  57. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/endpoint.py +0 -0
  58. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/exceptions.py +0 -0
  59. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/fmt.py +0 -0
  60. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/job.py +0 -0
  61. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/llm.py +0 -0
  62. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/llm_utils.py +0 -0
  63. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/local.py +0 -0
  64. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/login.py +0 -0
  65. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/materialization.py +0 -0
  66. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/mock.py +0 -0
  67. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/project.py +0 -0
  68. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/regions.py +0 -0
  69. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/table.py +0 -0
  70. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/tag.py +0 -0
  71. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/telemetry.py +0 -0
  72. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/tinyunit/tinyunit_lib.py +0 -0
  73. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/token.py +0 -0
  74. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/update.py +0 -0
  75. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/watch.py +0 -0
  76. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb/modules/workspace_members.py +0 -0
  77. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb_cli.py +0 -0
  78. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb_cli_modules/auth.py +0 -0
  79. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb_cli_modules/branch.py +0 -0
  80. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb_cli_modules/cicd.py +0 -0
  81. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb_cli_modules/common.py +0 -0
  82. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb_cli_modules/config.py +0 -0
  83. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb_cli_modules/connection.py +0 -0
  84. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb_cli_modules/datasource.py +0 -0
  85. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb_cli_modules/exceptions.py +0 -0
  86. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb_cli_modules/fmt.py +0 -0
  87. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb_cli_modules/job.py +0 -0
  88. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb_cli_modules/pipe.py +0 -0
  89. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb_cli_modules/regions.py +0 -0
  90. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb_cli_modules/tag.py +0 -0
  91. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb_cli_modules/telemetry.py +0 -0
  92. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb_cli_modules/test.py +0 -0
  93. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb_cli_modules/tinyunit/tinyunit.py +0 -0
  94. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb_cli_modules/tinyunit/tinyunit_lib.py +0 -0
  95. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb_cli_modules/workspace.py +0 -0
  96. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tb_cli_modules/workspace_members.py +0 -0
  97. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird/tornado_template.py +0 -0
  98. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird.egg-info/SOURCES.txt +0 -0
  99. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird.egg-info/dependency_links.txt +0 -0
  100. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird.egg-info/entry_points.txt +0 -0
  101. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird.egg-info/requires.txt +0 -0
  102. {tinybird-0.0.1.dev44 → tinybird-0.0.1.dev47}/tinybird.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tinybird
3
- Version: 0.0.1.dev44
3
+ Version: 0.0.1.dev47
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/cli/introduction.html
6
6
  Author: Tinybird
@@ -1,6 +1,7 @@
1
1
  import asyncio
2
2
  import json
3
3
  import logging
4
+ import os
4
5
  import ssl
5
6
  from pathlib import Path
6
7
  from typing import Any, Callable, Dict, List, Mapping, Optional, Set, Union
@@ -91,6 +92,7 @@ class TinyB:
91
92
  disable_ssl_checks: bool = False,
92
93
  send_telemetry: bool = False,
93
94
  semver: Optional[str] = None,
95
+ env: Optional[str] = "production",
94
96
  ):
95
97
  ctx = ssl.create_default_context()
96
98
  ctx.check_hostname = False
@@ -102,6 +104,7 @@ class TinyB:
102
104
  self.disable_ssl_checks = disable_ssl_checks
103
105
  self.send_telemetry = send_telemetry
104
106
  self.semver = semver
107
+ self.env = env
105
108
 
106
109
  async def _req_raw(
107
110
  self,
@@ -687,7 +690,20 @@ class TinyB:
687
690
  return await self._req(f"/v0/jobs/{job_id}/cancel", method="POST", data=b"")
688
691
 
689
692
  async def user_workspaces(self):
690
- return await self._req("/v0/user/workspaces/?with_environments=false")
693
+ data = await self._req("/v0/user/workspaces/?with_environments=false")
694
+ # TODO: this is repeated in local_common.py but I'm avoiding circular imports
695
+ local_port = int(os.getenv("TB_LOCAL_PORT", 80))
696
+ local_host = f"http://localhost:{local_port}"
697
+ if local_host != self.host:
698
+ return data
699
+
700
+ local_workspaces = [
701
+ x
702
+ for x in data["workspaces"]
703
+ if x["name"].startswith("Tinybird_Local_") and not x["name"].startswith("Tinybird_Local_Build_")
704
+ ]
705
+
706
+ return {**data, "workspaces": local_workspaces}
691
707
 
692
708
  async def user_workspaces_and_branches(self):
693
709
  return await self._req("/v0/user/workspaces/?with_environments=true")
@@ -1218,6 +1218,8 @@ def parse(
1218
1218
  "kafka_store_raw_value": assign_var("kafka_store_raw_value"),
1219
1219
  "kafka_store_headers": assign_var("kafka_store_headers"),
1220
1220
  "kafka_store_binary_headers": assign_var("kafka_store_binary_headers"),
1221
+ "kafka_key_format": assign_var("kafka_key_format"),
1222
+ "kafka_value_format": assign_var("kafka_value_format"),
1221
1223
  "kafka_key_avro_deserialization": assign_var("kafka_key_avro_deserialization"),
1222
1224
  "kafka_ssl_ca_pem": assign_var("kafka_ssl_ca_pem"),
1223
1225
  "kafka_sasl_mechanism": assign_var("kafka_sasl_mechanism"),
@@ -1,3 +1,5 @@
1
+ from typing import Optional
2
+
1
3
  general_functions = [
2
4
  "BLAKE3",
3
5
  "CAST",
@@ -368,8 +370,7 @@ You are a Tinybird expert. You will be given a pipe containing different nodes w
368
370
 
369
371
  <instructions>
370
372
  - Every test name must be unique.
371
- - The test command must be a valid Tinybird command that can be run in the terminal.
372
- - The test command can have as many parameters as are needed to test the pipe.
373
+ - The test can have as many parameters as are needed to test the pipe.
373
374
  - The parameter within Tinybird templating syntax looks like this one {{String(my_param_name, default_value)}}.
374
375
  - If there are no parameters, you can omit parameters and generate a single test.
375
376
  - The format of the parameters is the following: ?param1=value1&param2=value2&param3=value3
@@ -759,6 +760,10 @@ sql_instructions = """
759
760
  AND {{{{DateTime(end_date)}}}}
760
761
  {{%end%}}
761
762
  </valid_condition_without_now>
763
+ - Parameters must not be quoted.
764
+ - When you use defined function with a paremeter inside, do NOT add quotes around the parameter:
765
+ <invalid_defined_function_with_parameter>{{% if defined('my_param') %}}</invalid_defined_function_with_parameter>
766
+ <valid_defined_function_without_parameter>{{% if defined(my_param) %}}</valid_defined_function_without_parameter>
762
767
  - Use datasource names as table names when doing SELECT statements.
763
768
  - Do not use pipe names as table names.
764
769
  - The available datasource names to use in the SQL are the ones present in the existing_resources section or the ones you will create.
@@ -776,7 +781,6 @@ sql_instructions = """
776
781
  - When aliasing a column, use first the column name and then the alias.
777
782
  - General functions and aggregate functions are case sensitive.
778
783
  - Character insensitive functions are case insensitive.
779
- - When you use defined function with a paremeter inside, do NOT add quotes around the parameter.
780
784
  - Parameters are never quoted in any case.
781
785
  </sql_instructions>
782
786
  """.format(
@@ -819,22 +823,111 @@ Use the following format to generate the response and do not wrap it in any othe
819
823
  )
820
824
 
821
825
 
822
- cursorrules_prompt = """
826
+ def rules_prompt(source: Optional[str] = None) -> str:
827
+ base_command = source or "tb"
828
+ return """
823
829
  You are an expert in SQL and Tinybird. Follow these instructions when working with .datasource and .pipe files:
824
830
 
831
+ <command_calling>
832
+ You have commands at your disposal to develop a tinybird project:
833
+ - {base_command} build: to build the project locally and check it works.
834
+ - {base_command} deployment create --wait --auto: to create a deployment and promote it automatically
835
+ - {base_command} test run: to run existing tests
836
+ - {base_command} --build endpoint url <pipe_name>: to get the url of an endpoint, token included.
837
+ - {base_command} --build endpoint data <pipe_name>: to get the data of an endpoint. You can pass parameters to the endpoint like this: {base_command} --build endpoint data <pipe_name> --param1 value1 --param2 value2
838
+ - {base_command} --build token ls: to list all the tokens
839
+ There are other commands that you can use, but these are the most common ones. Run `{base_command} -h` to see all the commands if needed.
840
+ When you need to check resources or data in the Tinybird environment that you updated with the build command, add always the --build flag before the command. Example: {base_command} --build datasource ls
841
+ </command_calling>
842
+ <development_instructions>
843
+ - When asking to create a tinybird data project, if the needed folders are not already created, use the following structure:
844
+ ├── copies
845
+ ├── datasources
846
+ ├── endpoints
847
+ ├── fixtures
848
+ ├── materializations
849
+ ├── sinks
850
+ └── tests
851
+ - The local development server will be available at http://localhost:80. Even if some response uses another base url, use always http://localhost:80.
852
+ - After every change in your .datasource, .pipe or .ndjson files, run `{base_command} build` to build the project locally.
853
+ - When you need to ingest data locally in a datasource, create a .ndjson file with the same name of the datasource and the data you want and run `{base_command} build` so the data is ingested.
854
+ - The format of the generated api endpoint urls is: http://localhost:80/v0/pipe/<pipe_name>.json?token=<token>
855
+ - Before running the tests, remember to have the project built with `{base_command} build` with the latest changes.
856
+ </development_instructions>
857
+
858
+ <datasource_file_instructions>
859
+ Follow these instructions when creating or updating .datasource files:
825
860
  {datasource_instructions}
861
+ </datasource_file_instructions>
862
+
863
+ <pipe_file_instructions>
864
+ Follow these instructions when creating or updating .pipe files:
826
865
  {pipe_instructions}
827
866
  {sql_instructions}
828
867
  {datasource_example}
829
868
  {pipe_example}
830
869
  {copy_pipe_instructions}
831
870
  {materialized_pipe_instructions}
871
+ </pipe_file_instructions>
872
+ <test_file_instructions>
873
+ Follow these instructions when creating or updating .yaml files for tests:
874
+ {test_instructions}
875
+ </test_file_instructions>
832
876
  """.format(
833
- datasource_instructions=datasource_instructions,
834
- pipe_instructions=pipe_instructions,
835
- sql_instructions=sql_instructions,
836
- datasource_example=datasource_example,
837
- pipe_example=pipe_example,
838
- copy_pipe_instructions=copy_pipe_instructions,
839
- materialized_pipe_instructions=materialized_pipe_instructions,
840
- )
877
+ base_command=base_command,
878
+ datasource_instructions=datasource_instructions,
879
+ pipe_instructions=pipe_instructions,
880
+ sql_instructions=sql_instructions,
881
+ datasource_example=datasource_example,
882
+ pipe_example=pipe_example,
883
+ copy_pipe_instructions=copy_pipe_instructions,
884
+ materialized_pipe_instructions=materialized_pipe_instructions,
885
+ test_instructions=test_instructions,
886
+ )
887
+
888
+
889
+ test_instructions = """
890
+ - The test file name must match the name of the pipe it is testing.
891
+ - Every scenario name must be unique inside the test file.
892
+ - When looking for the parameters available, you will find them in the pipes in the following format: {{{{String(my_param_name, default_value)}}}}.
893
+ - If there are no parameters, you can omit parameters and generate a single test.
894
+ - The format of the parameters is the following: param1=value1&param2=value2&param3=value3
895
+ - If some parameters are provided by the user and you need to use them, preserve in the same format as they were provided, like case sensitive
896
+ - Test as many scenarios as possible.
897
+ - The format of the test file is the following:
898
+ <test_file_format>
899
+ - name: kpis_single_day
900
+ description: Test hourly granularity for a single day
901
+ parameters: date_from=2024-01-01&date_to=2024-01-01
902
+ expected_result: |
903
+ {"date":"2024-01-01 00:00:00","visits":0,"pageviews":0,"bounce_rate":null,"avg_session_sec":0}
904
+ {"date":"2024-01-01 01:00:00","visits":0,"pageviews":0,"bounce_rate":null,"avg_session_sec":0}
905
+
906
+ - name: kpis_date_range
907
+ description: Test daily granularity for a date range
908
+ parameters: date_from=2024-01-01&date_to=2024-01-31
909
+ expected_result: |
910
+ {"date":"2024-01-01","visits":0,"pageviews":0,"bounce_rate":null,"avg_session_sec":0}
911
+ {"date":"2024-01-02","visits":0,"pageviews":0,"bounce_rate":null,"avg_session_sec":0}
912
+
913
+ - name: kpis_default_range
914
+ description: Test default behavior without date parameters (last 7 days)
915
+ parameters: ''
916
+ expected_result: |
917
+ {"date":"2025-01-10","visits":0,"pageviews":0,"bounce_rate":null,"avg_session_sec":0}
918
+ {"date":"2025-01-11","visits":0,"pageviews":0,"bounce_rate":null,"avg_session_sec":0}
919
+
920
+ - name: kpis_fixed_time
921
+ description: Test with fixed timestamp for consistent testing
922
+ parameters: fixed_time=2024-01-15T12:00:00
923
+ expected_result: ''
924
+
925
+ - name: kpis_single_day
926
+ description: Test single day with hourly granularity
927
+ parameters: date_from=2024-01-01&date_to=2024-01-01
928
+ expected_result: |
929
+ {"date":"2024-01-01 00:00:00","visits":0,"pageviews":0,"bounce_rate":null,"avg_session_sec":0}
930
+ {"date":"2024-01-01 01:00:00","visits":0,"pageviews":0,"bounce_rate":null,"avg_session_sec":0}
931
+
932
+ </test_file_format>
933
+ """
@@ -86,7 +86,7 @@ def get_format(sql: str) -> Optional[str]:
86
86
  """
87
87
  FORMAT_RE = r"\s+format\s+(\w+)\s*$"
88
88
  sql = sql.strip()
89
- format = re.findall(FORMAT_RE, sql, re.I)
89
+ format = re.findall(FORMAT_RE, sql, re.IGNORECASE)
90
90
  return format[0] if format else None
91
91
 
92
92
 
@@ -100,7 +100,7 @@ def get_format_group(sql: str) -> str:
100
100
  """
101
101
  FORMAT_RE = r"\s+format\s+(\w+)\s*$"
102
102
  sql = sql.strip()
103
- format = re.search(FORMAT_RE, sql, re.I)
103
+ format = re.search(FORMAT_RE, sql, re.IGNORECASE)
104
104
  return format.group() if format else ""
105
105
 
106
106
 
@@ -135,7 +135,7 @@ def remove_format(sql: str) -> str:
135
135
  """
136
136
  FORMAT_RE = r"\s+(format)\s+(\w+)\s*$"
137
137
  sql = sql.strip()
138
- return re.sub(FORMAT_RE, "", sql, flags=re.I)
138
+ return re.sub(FORMAT_RE, "", sql, flags=re.IGNORECASE)
139
139
 
140
140
 
141
141
  def col_name(name: str, backquotes: bool = True) -> str:
@@ -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.dev44'
8
- __revision__ = '637cb4c'
7
+ __version__ = '0.0.1.dev47'
8
+ __revision__ = '05e3b46'
@@ -28,10 +28,9 @@ def build(ctx: click.Context, watch: bool) -> None:
28
28
  """
29
29
  Validate and build the project server side.
30
30
  """
31
-
32
31
  project: Project = ctx.ensure_object(dict)["project"]
33
- tb_client = asyncio.run(get_tinybird_local_client(str(project.path)))
34
- click.echo(FeedbackManager.highlight(message="\n» Building project..."))
32
+ tb_client = asyncio.run(get_tinybird_local_client(str(project.path), build=True))
33
+ click.echo(FeedbackManager.highlight_building_project())
35
34
  time_start = time.time()
36
35
 
37
36
  def process(file_changed: Optional[str] = None, diff: Optional[str] = None) -> None:
@@ -72,6 +71,7 @@ def build_project(project: Project, tb_client: TinyB, file_changed: Optional[str
72
71
  TINYBIRD_API_URL = tb_client.host + "/v1/build"
73
72
  logging.debug(TINYBIRD_API_URL)
74
73
  TINYBIRD_API_KEY = tb_client.token
74
+ error = False
75
75
  try:
76
76
  files = [
77
77
  ("context://", ("cli-version", "1.0.0", "text/plain")),
@@ -141,13 +141,14 @@ def build_project(project: Project, tb_client: TinyB, file_changed: Optional[str
141
141
  pass
142
142
 
143
143
  elif build_result == "failed":
144
- click.echo(FeedbackManager.error(message="Build failed"))
145
144
  build_errors = result.get("errors")
145
+ error = True
146
146
  for build_error in build_errors:
147
147
  filename_bit = f"{build_error.get('filename', '')}"
148
148
  error_msg = ((filename_bit + "\n\n") if filename_bit else "") + build_error.get("error")
149
149
  click.echo(FeedbackManager.error(message=error_msg))
150
150
  else:
151
+ error = True
151
152
  click.echo(FeedbackManager.error(message=f"Unknown build result. Error: {result.get('error')}"))
152
153
 
153
154
  except Exception as e:
@@ -155,6 +156,8 @@ def build_project(project: Project, tb_client: TinyB, file_changed: Optional[str
155
156
  finally:
156
157
  for fd in fds:
157
158
  fd.close()
159
+ if error:
160
+ raise click.ClickException(FeedbackManager.error_build_failed())
158
161
 
159
162
 
160
163
  def append_fixture(
@@ -59,19 +59,27 @@ VERSION = f"{__cli__.__version__} (rev {__cli__.__revision__})"
59
59
  @click.option("--host", help="Use custom host, defaults to TB_HOST envvar, then to https://api.tinybird.co")
60
60
  @click.option("--show-tokens", is_flag=True, default=False, help="Enable the output of tokens")
61
61
  @click.option("--prod/--local", is_flag=True, default=False, help="Run against production or local")
62
+ @click.option("--build", is_flag=True, default=False, help="Run against build mode")
62
63
  @click.option("--folder", type=str, help="Folder where files will be placed")
63
64
  @click.version_option(version=VERSION)
64
65
  @click.pass_context
65
66
  @coro
66
67
  async def cli(
67
- ctx: Context, debug: bool, token: str, host: str, show_tokens: bool, prod: bool, folder: Optional[str]
68
+ ctx: Context,
69
+ debug: bool,
70
+ token: str,
71
+ host: str,
72
+ show_tokens: bool,
73
+ prod: bool,
74
+ build: bool,
75
+ folder: Optional[str],
68
76
  ) -> None:
69
77
  """
70
78
  Use `OBFUSCATE_REGEX_PATTERN` and `OBFUSCATE_PATTERN_SEPARATOR` environment variables to define a regex pattern and a separator (in case of a single string with multiple regex) to obfuscate secrets in the CLI output.
71
79
  """
72
80
  project = Project(folder=folder or os.getcwd())
73
81
  # We need to unpatch for our tests not to break
74
- if show_tokens or not prod or ctx.invoked_subcommand == "build":
82
+ if show_tokens or not prod or ctx.invoked_subcommand == "build" or build:
75
83
  __unpatch_click_output()
76
84
  else:
77
85
  __patch_click_output()
@@ -116,10 +124,13 @@ async def cli(
116
124
 
117
125
  logging.debug("debug enabled")
118
126
 
119
- skip_client = ctx.invoked_subcommand in ["auth", "check", "login", "workspace", "local", "build"]
120
- client = await create_ctx_client(config, prod, skip_client, project)
127
+ skip_client = ctx.invoked_subcommand in ["auth", "check", "login", "local", "build"]
128
+ client = await create_ctx_client(config, prod, build, skip_client, project)
121
129
 
122
130
  if client:
131
+ if not build:
132
+ target = config.get("name", "production") if prod else "Tinybird local"
133
+ click.echo(FeedbackManager.gray(message=f"Running against {target}\n"))
123
134
  ctx.ensure_object(dict)["client"] = client
124
135
 
125
136
  ctx.ensure_object(dict)["project"] = project
@@ -434,11 +445,11 @@ def __unpatch_click_output():
434
445
  click.secho = __old_click_secho
435
446
 
436
447
 
437
- async def create_ctx_client(config: Dict[str, Any], prod: bool, skip_client: bool, project: Project):
448
+ async def create_ctx_client(config: Dict[str, Any], prod: bool, build: bool, skip_client: bool, project: Project):
438
449
  if skip_client:
439
450
  return None
440
451
 
441
452
  if prod:
442
453
  return _get_tb_client(config.get("token", None), config["host"])
443
454
 
444
- return await get_tinybird_local_client(str(project.path))
455
+ return await get_tinybird_local_client(str(project.path), build=build)
@@ -1362,7 +1362,7 @@ async def try_update_config_with_remote(
1362
1362
  def ask_for_admin_token_interactively(ui_host: str, default_token: Optional[str]) -> str:
1363
1363
  return (
1364
1364
  click.prompt(
1365
- f"\nCopy the \"admin your@email\" token from {ui_host}/tokens and paste it here { 'OR press enter to use the token from .tinyb file' if default_token else ''}",
1365
+ f'\nCopy the "admin your@email" token from {ui_host}/tokens and paste it here {"OR press enter to use the token from .tinyb file" if default_token else ""}',
1366
1366
  hide_input=True,
1367
1367
  show_default=False,
1368
1368
  default=default_token,
@@ -7,7 +7,7 @@ from typing import Optional
7
7
  import click
8
8
 
9
9
  from tinybird.client import TinyB
10
- from tinybird.prompts import create_prompt, cursorrules_prompt, mock_prompt
10
+ from tinybird.prompts import create_prompt, mock_prompt, rules_prompt
11
11
  from tinybird.tb.modules.cicd import init_cicd
12
12
  from tinybird.tb.modules.cli import cli
13
13
  from tinybird.tb.modules.common import _generate_datafile, check_user_token_with_client, coro, generate_datafile
@@ -36,12 +36,13 @@ from tinybird.tb.modules.project import Project
36
36
  )
37
37
  @click.option(
38
38
  "--folder",
39
- default=".",
39
+ default="",
40
40
  type=click.Path(exists=False, file_okay=False),
41
41
  help="Folder where datafiles will be placed",
42
42
  )
43
43
  @click.option("--rows", type=int, default=10, help="Number of events to send")
44
44
  @click.option("--cursor", default=False, is_flag=True, help="Create .cursorrules file with Tinybird rules")
45
+ @click.option("--source", type=str, default="tb", help="Source of the command")
45
46
  @click.pass_context
46
47
  @coro
47
48
  async def create(
@@ -51,6 +52,7 @@ async def create(
51
52
  folder: Optional[str],
52
53
  rows: int,
53
54
  cursor: bool,
55
+ source: str,
54
56
  ) -> None:
55
57
  """Initialize a new project."""
56
58
  project: Project = ctx.ensure_object(dict)["project"]
@@ -83,9 +85,11 @@ async def create(
83
85
  create_project_structure(folder)
84
86
  click.echo(FeedbackManager.success(message="✓ Scaffolding completed!\n"))
85
87
 
86
- click.echo(FeedbackManager.highlight(message="\n» Creating resources..."))
87
- result = await create_resources(local_client, tb_client, user_token, data, prompt, folder)
88
- click.echo(FeedbackManager.success(message=" Done!\n"))
88
+ result = ""
89
+ if data or prompt:
90
+ click.echo(FeedbackManager.highlight(message="\n» Creating resources..."))
91
+ result = await create_resources(local_client, tb_client, user_token, data, prompt, folder)
92
+ click.echo(FeedbackManager.success(message="✓ Done!\n"))
89
93
 
90
94
  if not already_has_cicd(folder):
91
95
  click.echo(FeedbackManager.highlight(message="\n» Creating CI/CD files for GitHub and GitLab..."))
@@ -118,8 +122,8 @@ async def create(
118
122
  response = llm.ask(system_prompt=mock_prompt(rows), prompt=prompt)
119
123
  sql = extract_xml(response, "sql")
120
124
  sql = sql.split("FORMAT")[0]
121
- result = await local_client.query(f"{sql} FORMAT JSON")
122
- data = result.get("data", [])
125
+ query_result = await local_client.query(f"{sql} FORMAT JSON")
126
+ data = query_result.get("data", [])
123
127
  fixture_name = build_fixture_name(
124
128
  datasource_path.absolute().as_posix(), datasource_name, datasource_content
125
129
  )
@@ -127,9 +131,9 @@ async def create(
127
131
  persist_fixture(fixture_name, data, folder)
128
132
  click.echo(FeedbackManager.info(message=f"✓ /fixtures/{datasource_name}"))
129
133
 
130
- if cursor and not already_has_cursorrules(folder):
134
+ if cursor:
131
135
  click.echo(FeedbackManager.highlight(message="\n» Creating .cursorrules..."))
132
- create_cursorrules(folder)
136
+ create_rules(folder, source, "cursor")
133
137
  click.echo(FeedbackManager.success(message="✓ Done!\n"))
134
138
 
135
139
  except Exception as e:
@@ -336,13 +340,15 @@ def generate_pipe_file(name: str, content: str, folder: str) -> Path:
336
340
  return f.relative_to(folder)
337
341
 
338
342
 
339
- def already_has_cursorrules(folder: str) -> bool:
340
- return (Path(folder) / ".cursorrules").exists()
341
-
342
-
343
- def create_cursorrules(folder: str):
344
- cursorrules_file = Path(folder) / ".cursorrules"
345
- cursorrules_file.write_text(cursorrules_prompt)
343
+ def create_rules(folder: str, source: str, agent: str):
344
+ if agent == "cursor":
345
+ extension = ".cursorrules"
346
+ elif agent == "windsurf":
347
+ extension = ".windsurfrules"
348
+ else:
349
+ extension = ".txt"
350
+ rules_file = Path(folder) / extension
351
+ rules_file.write_text(rules_prompt(source))
346
352
 
347
353
 
348
354
  def get_context_file() -> Path:
@@ -360,3 +366,13 @@ def get_context() -> str:
360
366
  def save_context(prompt: str, feedback: str):
361
367
  context_file = get_context_file()
362
368
  context_file.write_text(f"- {prompt}\n{feedback}")
369
+
370
+
371
+ @cli.command("rules")
372
+ @click.option("--agent", type=str, default="cursor", help="Agent to use for rules")
373
+ @click.option("--source", type=str, default="tb", help="Source of the command")
374
+ @click.pass_context
375
+ def rules(ctx: click.Context, agent: str, source: str):
376
+ """Create agent rules for the project."""
377
+ project: Project = ctx.ensure_object(dict)["project"]
378
+ create_rules(str(project.path), source, agent)
@@ -526,7 +526,7 @@ def format_parse_error(
526
526
  message += f" found at position {adjusted_position - len(keyword)}"
527
527
  else:
528
528
  message += (
529
- f" found {repr(table_structure[i]) if len(table_structure)>i else 'EOF'} at position {adjusted_position}"
529
+ f" found {repr(table_structure[i]) if len(table_structure) > i else 'EOF'} at position {adjusted_position}"
530
530
  )
531
531
  return message
532
532
 
@@ -148,7 +148,7 @@ async def format_engine(
148
148
  else:
149
149
  if node.get("engine", None):
150
150
  empty = '""'
151
- file_parts.append(f'ENGINE {node["engine"]["type"]}' if node.get("engine", {}).get("type") else empty)
151
+ file_parts.append(f"ENGINE {node['engine']['type']}" if node.get("engine", {}).get("type") else empty)
152
152
  file_parts.append(DATAFILE_NEW_LINE)
153
153
  for arg in sorted(node["engine"].get("args", [])):
154
154
  elem = ", ".join([x.strip() for x in arg[1].split(",")])
@@ -42,7 +42,7 @@ async def format_node_type(file_parts: List[str], node: Dict[str, Any]) -> List[
42
42
  if node_type == PipeNodeTypes.MATERIALIZED:
43
43
  file_parts.append(node_type_upper)
44
44
  file_parts.append(DATAFILE_NEW_LINE)
45
- file_parts.append(f'DATASOURCE {node["datasource"]}')
45
+ file_parts.append(f"DATASOURCE {node['datasource']}")
46
46
  file_parts.append(DATAFILE_NEW_LINE)
47
47
  await format_engine(file_parts, node)
48
48
 
@@ -50,10 +50,10 @@ async def format_node_type(file_parts: List[str], node: Dict[str, Any]) -> List[
50
50
  if node_type == PipeNodeTypes.COPY:
51
51
  file_parts.append(node_type_upper)
52
52
  file_parts.append(DATAFILE_NEW_LINE)
53
- file_parts.append(f'TARGET_DATASOURCE {node["target_datasource"]}')
53
+ file_parts.append(f"TARGET_DATASOURCE {node['target_datasource']}")
54
54
  if node.get("mode"):
55
55
  file_parts.append(DATAFILE_NEW_LINE)
56
- file_parts.append(f'COPY_MODE {node.get("mode")}')
56
+ file_parts.append(f"COPY_MODE {node.get('mode')}")
57
57
 
58
58
  if node.get(CopyParameters.COPY_SCHEDULE):
59
59
  is_ondemand = node[CopyParameters.COPY_SCHEDULE].lower() == ON_DEMAND
@@ -112,7 +112,7 @@ async def format_node(
112
112
  if item and not unroll_includes:
113
113
  return
114
114
 
115
- file_parts.append(f'NODE {node["name"].strip()}')
115
+ file_parts.append(f"NODE {node['name'].strip()}")
116
116
  file_parts.append(DATAFILE_NEW_LINE)
117
117
 
118
118
  from collections import namedtuple
@@ -288,8 +288,8 @@ class PipeCheckerRunner:
288
288
  AND extractURLParameter(assumeNotNull(url), 'debug') <> 'query'
289
289
  AND error = 0
290
290
  AND not mapContains(parameters, '__tb__semver')
291
- {" AND " + " AND ".join([f"mapContains(pipe_request_params, '{match}')" for match in matches]) if matches and len(matches) > 0 else ''}
292
- { extra_where_clause }
291
+ {" AND " + " AND ".join([f"mapContains(pipe_request_params, '{match}')" for match in matches]) if matches and len(matches) > 0 else ""}
292
+ {extra_where_clause}
293
293
  Limit 5000000 -- Enough to bring data while not processing all requests from highly used pipes
294
294
  )
295
295
  group by request_param_names, http_method
@@ -315,7 +315,7 @@ class PipeCheckerRunner:
315
315
  AND extractURLParameter(assumeNotNull(url), 'debug') <> 'query'
316
316
  AND error = 0
317
317
  AND not mapContains(parameters, '__tb__semver')
318
- {" AND " + " AND ".join([f"mapContains(pipe_request_params, '{match}')" for match in matches]) if matches and len(matches) > 0 else ''}
318
+ {" AND " + " AND ".join([f"mapContains(pipe_request_params, '{match}')" for match in matches]) if matches and len(matches) > 0 else ""}
319
319
  {extra_where_clause}
320
320
  LIMIT {limit}
321
321
  )
@@ -432,6 +432,7 @@ class FeedbackManager:
432
432
  error_updating_tag = error_message("Error updating tag: {error}")
433
433
  error_tag_generic = error_message("There was an issue updating tags. {error}")
434
434
  error_tag_not_found = error_message("Tag {tag_name} not found.")
435
+ error_build_failed = error_message("Build failed")
435
436
 
436
437
  info_incl_relative_path = info_message("** Relative path {path} does not exist, skipping.")
437
438
  info_ignoring_incl_file = info_message(
@@ -849,6 +850,7 @@ Ready? """
849
850
  )
850
851
  info_tag_list = info_message("** Tags:")
851
852
  info_tag_resources = info_message("** Resources tagged by {tag_name}:")
853
+ info_build_failed = info_message("** Build failed")
852
854
  warning_no_release = warning_message(
853
855
  "** Warning: Workspace does not have Releases, run `tb init --git` to activate them."
854
856
  )
@@ -964,7 +966,7 @@ Ready? """
964
966
  )
965
967
  success_datasource_alter = success_message("** The Data Source has been correctly updated.")
966
968
  success_datasource_kafka_connected = success_message(
967
- "** Data Source '{id}' created\n" "** Kafka streaming connection configured successfully!"
969
+ "** Data Source '{id}' created\n** Kafka streaming connection configured successfully!"
968
970
  )
969
971
  success_datasource_shared = success_message(
970
972
  "** The Data Source {datasource} has been correctly shared with {workspace}"
@@ -1040,6 +1042,8 @@ Ready? """
1040
1042
 
1041
1043
  debug_running_file = print_message("** Running {file}", bcolors.CGREY)
1042
1044
 
1045
+ highlight_building_project = info_highlight_message("\n» Building project...")
1046
+
1043
1047
  success = success_message("{message}")
1044
1048
  info = info_message("{message}")
1045
1049
  highlight = info_highlight_message("{message}")
@@ -1,7 +1,6 @@
1
1
  import hashlib
2
2
  import logging
3
3
  import os
4
- from typing import Optional
5
4
 
6
5
  import requests
7
6
 
@@ -9,19 +8,19 @@ from tinybird.client import TinyB
9
8
  from tinybird.tb.modules.config import CLIConfig
10
9
  from tinybird.tb.modules.exceptions import CLIException
11
10
 
12
- TB_IMAGE_NAME = "tinybirdco/tinybird-local:beta"
11
+ TB_IMAGE_NAME = "registry.gitlab.com/tinybird/analytics/tinybird-local-jammy-3.11:beta"
13
12
  TB_CONTAINER_NAME = "tinybird-local"
14
13
  TB_LOCAL_PORT = int(os.getenv("TB_LOCAL_PORT", 80))
15
14
  TB_LOCAL_HOST = f"http://localhost:{TB_LOCAL_PORT}"
16
15
 
17
16
 
18
- async def get_tinybird_local_client(path: Optional[str] = None) -> TinyB:
17
+ async def get_tinybird_local_client(path: str, build: bool = False) -> TinyB:
19
18
  """Get a Tinybird client connected to the local environment."""
20
- config = await get_tinybird_local_config(path)
19
+ config = await get_tinybird_local_config(path, build=build)
21
20
  return config.get_client(host=TB_LOCAL_HOST)
22
21
 
23
22
 
24
- async def get_tinybird_local_config(path: Optional[str] = None) -> CLIConfig:
23
+ async def get_tinybird_local_config(path: str, build: bool = False) -> CLIConfig:
25
24
  """Craft a client config with a workspace name based on the path of the project files
26
25
 
27
26
  It uses the tokens from tinybird local
@@ -41,11 +40,15 @@ async def get_tinybird_local_config(path: Optional[str] = None) -> CLIConfig:
41
40
  if path:
42
41
  folder_hash = hashlib.sha256(path.encode()).hexdigest()
43
42
  user_client = config.get_client(host=TB_LOCAL_HOST, token=user_token)
44
- ws_name = f"Tinybird_Local_Build_{folder_hash}"
43
+ ws_name = f"Tinybird_Local_Build_{folder_hash}" if build else f"Tinybird_Local_{folder_hash}"
45
44
  logging.debug(f"Workspace used for build: {ws_name}")
46
45
 
47
46
  user_workspaces = requests.get(f"{TB_LOCAL_HOST}/v0/user/workspaces?token={user_token}").json()
48
- local_workspaces = [ws for ws in user_workspaces["workspaces"] if ws["name"].startswith(ws_name)]
47
+ local_workspaces = (
48
+ [ws for ws in user_workspaces["workspaces"] if ws["name"].startswith(ws_name)]
49
+ if user_workspaces.get("workspaces")
50
+ else []
51
+ )
49
52
  local_workspaces = sorted(local_workspaces, key=lambda x: x["name"])
50
53
 
51
54
  ws = None
@@ -20,10 +20,10 @@ from tinybird.tb.modules.exceptions import CLIPipeException
20
20
  from tinybird.tb.modules.feedback_manager import FeedbackManager
21
21
 
22
22
 
23
- @cli.group(hidden=True)
23
+ @cli.group(hidden=False)
24
24
  @click.pass_context
25
25
  def pipe(ctx):
26
- """Pipes commands"""
26
+ """Pipe commands"""
27
27
 
28
28
 
29
29
  @pipe.command(name="ls")
@@ -41,10 +41,10 @@ async def pipe_ls(ctx: Context, match: str, format_: str):
41
41
  """List pipes"""
42
42
 
43
43
  client: TinyB = ctx.ensure_object(dict)["client"]
44
- pipes = await client.pipes(dependencies=False, node_attrs="name", attrs="name,updated_at")
44
+ pipes = await client.pipes(dependencies=False, node_attrs="name", attrs="name,updated_at,type")
45
45
  pipes = sorted(pipes, key=lambda p: p["updated_at"])
46
46
 
47
- columns = ["name", "published date", "nodes"]
47
+ columns = ["name", "published date", "nodes", "type"]
48
48
  table_human_readable = []
49
49
  table_machine_readable = []
50
50
  pattern = re.compile(match) if match else None
@@ -52,12 +52,13 @@ async def pipe_ls(ctx: Context, match: str, format_: str):
52
52
  tk = get_name_version(t["name"])
53
53
  if pattern and not pattern.search(tk["name"]):
54
54
  continue
55
- table_human_readable.append((tk["name"], t["updated_at"][:-7], len(t["nodes"])))
55
+ table_human_readable.append((tk["name"], t["updated_at"][:-7], len(t["nodes"]), t["type"]))
56
56
  table_machine_readable.append(
57
57
  {
58
58
  "name": tk["name"],
59
59
  "published date": t["updated_at"][:-7],
60
60
  "nodes": len(t["nodes"]),
61
+ "type": t["type"],
61
62
  }
62
63
  )
63
64
 
@@ -245,7 +245,7 @@ class Shell:
245
245
  click.echo(FeedbackManager.error(message=f"'tb {arg}' command is not available in watch mode"))
246
246
 
247
247
  def handle_mock(self, arg):
248
- subprocess.run(f"tb --folder {self.project.folder} mock {arg}", shell=True, text=True)
248
+ subprocess.run(f"tb --build --folder {self.project.folder} mock {arg}", shell=True, text=True)
249
249
 
250
250
  def handle_tb(self, arg):
251
251
  click.echo("")
@@ -259,7 +259,7 @@ class Shell:
259
259
  elif arg.startswith("mock"):
260
260
  self.handle_mock(arg)
261
261
  else:
262
- subprocess.run(f"tb --folder {self.project.folder} {arg}", shell=True, text=True)
262
+ subprocess.run(f"tb --build --folder {self.project.folder} {arg}", shell=True, text=True)
263
263
 
264
264
  def default(self, argline):
265
265
  click.echo("")
@@ -271,7 +271,7 @@ class Shell:
271
271
  elif len(arg.split()) == 1 and arg in self.project.pipes + self.project.datasources:
272
272
  self.run_sql(f"select * from {arg}")
273
273
  else:
274
- subprocess.run(f"tb --folder {self.project.folder} {arg}", shell=True, text=True)
274
+ subprocess.run(f"tb --build --folder {self.project.folder} {arg}", shell=True, text=True)
275
275
 
276
276
  def run_sql(self, query, rows_limit=20):
277
277
  try:
@@ -102,7 +102,7 @@ async def test_create(ctx: click.Context, name_or_filename: str, prompt: str, sk
102
102
  pipe_path = root_path / pipe_path
103
103
  pipe_content = pipe_path.read_text()
104
104
 
105
- client = await get_tinybird_local_client(folder)
105
+ client = await get_tinybird_local_client(folder, build=True)
106
106
  pipe = await client._req(f"/v0/pipes/{pipe_name}")
107
107
  parameters = set([param["name"] for node in pipe["nodes"] for param in node["params"]])
108
108
 
@@ -155,7 +155,7 @@ def parse_file(file: str) -> Iterable[TestCase]:
155
155
  )
156
156
  except Exception as e:
157
157
  raise CLIException(
158
- f"""Error: {FeedbackManager.error_exception(error=e)} reading file, check "{file}"->"{definition.get('name')}" """
158
+ f"""Error: {FeedbackManager.error_exception(error=e)} reading file, check "{file}"->"{definition.get("name")}" """
159
159
  )
160
160
 
161
161
 
@@ -8,6 +8,7 @@ from typing import Optional
8
8
  import click
9
9
  from click import Context
10
10
 
11
+ from tinybird.client import TinyB
11
12
  from tinybird.config import get_display_host
12
13
  from tinybird.tb.modules.cli import cli
13
14
  from tinybird.tb.modules.common import (
@@ -41,7 +42,7 @@ async def workspace_ls(ctx: Context) -> None:
41
42
  """List all the workspaces you have access to in the account you're currently authenticated into."""
42
43
 
43
44
  config = CLIConfig.get_project_config()
44
- client = config.get_client()
45
+ client: TinyB = ctx.ensure_object(dict)["client"]
45
46
 
46
47
  response = await client.user_workspaces()
47
48
 
@@ -1231,7 +1231,7 @@ def __patch_click_output():
1231
1231
  if isinstance(substitution, str):
1232
1232
  msg = re.sub(pattern, substitution, str(msg))
1233
1233
  else:
1234
- msg = re.sub(pattern, lambda m: substitution(m.group(0)), str(msg)) # noqa
1234
+ msg = re.sub(pattern, lambda m: substitution(m.group(0)), str(msg)) # noqa: B023
1235
1235
  return msg
1236
1236
 
1237
1237
  def _obfuscate_echo(msg: Any, *args: Any, **kwargs: Any) -> None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tinybird
3
- Version: 0.0.1.dev44
3
+ Version: 0.0.1.dev47
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/cli/introduction.html
6
6
  Author: Tinybird
File without changes