tinybird 0.0.1.dev35__tar.gz → 0.0.1.dev37__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 (104) hide show
  1. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/PKG-INFO +1 -1
  2. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/client.py +1 -1
  3. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/prompts.py +4 -0
  4. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/__cli__.py +2 -2
  5. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/cli.py +1 -0
  6. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/build.py +66 -35
  7. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/build_client.py +9 -15
  8. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/cli.py +20 -105
  9. tinybird-0.0.1.dev37/tinybird/tb/modules/copy.py +68 -0
  10. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/create.py +4 -4
  11. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/datafile/build.py +2 -2
  12. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/deployment.py +23 -25
  13. tinybird-0.0.1.dev37/tinybird/tb/modules/llm.py +32 -0
  14. tinybird-0.0.1.dev37/tinybird/tb/modules/llm_utils.py +108 -0
  15. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/local_common.py +2 -2
  16. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/mock.py +3 -4
  17. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/pipe.py +3 -8
  18. tinybird-0.0.1.dev37/tinybird/tb/modules/project.py +44 -0
  19. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/shell.py +16 -33
  20. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/test.py +2 -2
  21. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/token.py +1 -1
  22. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/update.py +4 -4
  23. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/watch.py +18 -12
  24. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird.egg-info/PKG-INFO +1 -1
  25. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird.egg-info/SOURCES.txt +2 -0
  26. tinybird-0.0.1.dev35/tinybird/tb/modules/llm.py +0 -38
  27. tinybird-0.0.1.dev35/tinybird/tb/modules/llm_utils.py +0 -24
  28. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/setup.cfg +0 -0
  29. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/__cli__.py +0 -0
  30. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/ch_utils/constants.py +0 -0
  31. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/ch_utils/engine.py +0 -0
  32. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/check_pypi.py +0 -0
  33. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/config.py +0 -0
  34. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/connectors.py +0 -0
  35. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/context.py +0 -0
  36. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/datafile.py +0 -0
  37. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/datatypes.py +0 -0
  38. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/feedback_manager.py +0 -0
  39. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/git_settings.py +0 -0
  40. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/sql.py +0 -0
  41. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/sql_template.py +0 -0
  42. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/sql_template_fmt.py +0 -0
  43. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/sql_toolset.py +0 -0
  44. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/syncasync.py +0 -0
  45. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/auth.py +0 -0
  46. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/cicd.py +0 -0
  47. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/common.py +0 -0
  48. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/config.py +0 -0
  49. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/datafile/build_common.py +0 -0
  50. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/datafile/build_datasource.py +0 -0
  51. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/datafile/build_pipe.py +0 -0
  52. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/datafile/common.py +0 -0
  53. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/datafile/diff.py +0 -0
  54. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/datafile/exceptions.py +0 -0
  55. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/datafile/fixture.py +0 -0
  56. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/datafile/format_common.py +0 -0
  57. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/datafile/format_datasource.py +0 -0
  58. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/datafile/format_pipe.py +0 -0
  59. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/datafile/parse_datasource.py +0 -0
  60. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/datafile/parse_pipe.py +0 -0
  61. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/datafile/pipe_checker.py +0 -0
  62. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/datafile/pull.py +0 -0
  63. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/datasource.py +0 -0
  64. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/endpoint.py +0 -0
  65. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/exceptions.py +0 -0
  66. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/feedback_manager.py +0 -0
  67. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/fmt.py +0 -0
  68. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/job.py +0 -0
  69. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/local.py +0 -0
  70. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/login.py +0 -0
  71. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/regions.py +0 -0
  72. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/table.py +0 -0
  73. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/tag.py +0 -0
  74. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/telemetry.py +0 -0
  75. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/tinyunit/tinyunit.py +0 -0
  76. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/tinyunit/tinyunit_lib.py +0 -0
  77. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/workspace.py +0 -0
  78. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb/modules/workspace_members.py +0 -0
  79. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb_cli.py +0 -0
  80. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb_cli_modules/auth.py +0 -0
  81. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb_cli_modules/branch.py +0 -0
  82. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb_cli_modules/cicd.py +0 -0
  83. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb_cli_modules/cli.py +0 -0
  84. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb_cli_modules/common.py +0 -0
  85. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb_cli_modules/config.py +0 -0
  86. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb_cli_modules/connection.py +0 -0
  87. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb_cli_modules/datasource.py +0 -0
  88. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb_cli_modules/exceptions.py +0 -0
  89. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb_cli_modules/fmt.py +0 -0
  90. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb_cli_modules/job.py +0 -0
  91. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb_cli_modules/pipe.py +0 -0
  92. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb_cli_modules/regions.py +0 -0
  93. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb_cli_modules/tag.py +0 -0
  94. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb_cli_modules/telemetry.py +0 -0
  95. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb_cli_modules/test.py +0 -0
  96. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb_cli_modules/tinyunit/tinyunit.py +0 -0
  97. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb_cli_modules/tinyunit/tinyunit_lib.py +0 -0
  98. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb_cli_modules/workspace.py +0 -0
  99. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tb_cli_modules/workspace_members.py +0 -0
  100. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird/tornado_template.py +0 -0
  101. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird.egg-info/dependency_links.txt +0 -0
  102. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird.egg-info/entry_points.txt +0 -0
  103. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/tinybird.egg-info/requires.txt +0 -0
  104. {tinybird-0.0.1.dev35 → tinybird-0.0.1.dev37}/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.dev35
3
+ Version: 0.0.1.dev37
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/cli/introduction.html
6
6
  Author: Tinybird
@@ -378,7 +378,7 @@ class TinyB:
378
378
  return res
379
379
 
380
380
  async def pipe_file(self, pipe: str):
381
- return await self._req(f"/v0/pipes/{pipe}.pipe")
381
+ return await self._req(f"/v1/pipes/{pipe}.pipe")
382
382
 
383
383
  async def datasource_file(self, datasource: str):
384
384
  try:
@@ -641,6 +641,7 @@ The previous instructions are explanations of how things work in Tinybird. Answe
641
641
 
642
642
  datasource_instructions = """
643
643
  <datasource_file_instructions>
644
+ - Content cannot be empty.
644
645
  - The datasource names must be unique.
645
646
  - No indentation is allowed for property names: DESCRIPTION, SCHEMA, ENGINE, ENGINE_PARTITION_KEY, ENGINE_SORTING_KEY, etc.
646
647
  - Use MergeTree engine by default.
@@ -673,6 +674,7 @@ DESCRIPTION >
673
674
  NODE node_1
674
675
  SQL >
675
676
  [sql query using clickhouse syntax and tinybird templating syntax and starting always with SELECT or %\nSELECT]
677
+ TYPE endpoint
676
678
 
677
679
  </pipe_content>
678
680
  """
@@ -685,6 +687,7 @@ pipe_instructions = """
685
687
  - Avoid more than one node per pipe unless it is really necessary or requested by the user.
686
688
  - No indentation is allowed for property names: DESCRIPTION, NODE, SQL, TYPE, etc.
687
689
  - Allowed TYPE values are: endpoint, copy, materialized, sink
690
+ - Add always the output node in the TYPE section.
688
691
  </pipe_file_instructions>
689
692
  """
690
693
 
@@ -701,6 +704,7 @@ sql_instructions = """
701
704
  </valid_query_with_parameters_with_%_on_top>
702
705
  - The Parameter functions like this one {{{{String(my_param_name,default_value)}}}} can be one of the following: String, DateTime, Date, Float32, Float64, Int, Integer, UInt8, UInt16, UInt32, UInt64, UInt128, UInt256, Int8, Int16, Int32, Int64, Int128, Int256
703
706
  - Parameter names must be different from column names. Pass always the param name and a default value to the function.
707
+ - Use ALWAYS hardcoded values for default values for parameters.
704
708
  - Code inside the template {{{{template_expression}}}} follows the rules of Tornado templating language so no module is allowed to be imported. So for example you can't use now() as default value for a DateTime parameter. You need an if else block like this:
705
709
  <invalid_condition_with_now>
706
710
  AND timestamp BETWEEN {{DateTime(start_date, now() - interval 30 day)}} AND {{DateTime(end_date, now())}}
@@ -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.dev35'
8
- __revision__ = '5046116'
7
+ __version__ = '0.0.1.dev37'
8
+ __revision__ = '255b7f6'
@@ -9,6 +9,7 @@ import tinybird.tb.modules.build
9
9
  import tinybird.tb.modules.build_client
10
10
  import tinybird.tb.modules.cli
11
11
  import tinybird.tb.modules.common
12
+ import tinybird.tb.modules.copy
12
13
  import tinybird.tb.modules.create
13
14
  import tinybird.tb.modules.datasource
14
15
  import tinybird.tb.modules.deployment
@@ -1,11 +1,10 @@
1
1
  import asyncio
2
- import glob
3
2
  import json
4
3
  import logging
5
4
  import threading
6
5
  import time
7
6
  from pathlib import Path
8
- from typing import List
7
+ from typing import Optional
9
8
 
10
9
  import click
11
10
  import requests
@@ -13,55 +12,53 @@ import requests
13
12
  from tinybird.client import TinyB
14
13
  from tinybird.tb.modules.cli import cli
15
14
  from tinybird.tb.modules.common import push_data
15
+ from tinybird.tb.modules.datafile.fixture import build_fixture_name, get_fixture_dir
16
16
  from tinybird.tb.modules.feedback_manager import FeedbackManager
17
17
  from tinybird.tb.modules.local_common import get_tinybird_local_client
18
+ from tinybird.tb.modules.project import Project
18
19
  from tinybird.tb.modules.shell import Shell
19
20
  from tinybird.tb.modules.watch import watch_project
20
21
 
21
22
 
22
23
  @cli.command()
23
- @click.option("--folder", type=str, default=".")
24
24
  @click.option("--watch", is_flag=True, default=False, help="Watch for changes and rebuild automatically")
25
- def build(folder: str, watch: bool) -> None:
25
+ @click.pass_context
26
+ def build(ctx: click.Context, watch: bool) -> None:
26
27
  """
27
28
  Validate and build the project server side.
28
29
  """
29
30
 
30
- tb_client = asyncio.run(get_tinybird_local_client(folder))
31
+ project: Project = ctx.ensure_object(dict)["project"]
32
+ tb_client = asyncio.run(get_tinybird_local_client(str(project.path)))
31
33
  click.echo(FeedbackManager.highlight(message="\n» Building project..."))
32
34
 
33
- def process() -> None:
34
- build_project(folder, tb_client)
35
-
36
35
  time_start = time.time()
36
+
37
+ def process(file_changed: Optional[str] = None) -> None:
38
+ if file_changed and file_changed.endswith(".ndjson"):
39
+ rebuild_fixture(project, tb_client, file_changed)
40
+ else:
41
+ build_project(project, tb_client)
42
+
37
43
  process()
44
+
38
45
  time_end = time.time()
39
46
  elapsed_time = time_end - time_start
40
47
  click.echo(FeedbackManager.success(message=f"\n✓ Build completed in {elapsed_time:.1f}s"))
41
48
 
42
49
  if watch:
43
- shell = Shell(folder=folder, client=tb_client)
50
+ shell = Shell(project=project, client=tb_client)
44
51
  click.echo(FeedbackManager.gray(message="\nWatching for changes..."))
45
52
  watcher_thread = threading.Thread(
46
53
  target=watch_project,
47
- args=(shell, process, folder),
54
+ args=(shell, process, project),
48
55
  daemon=True,
49
56
  )
50
57
  watcher_thread.start()
51
58
  shell.run()
52
59
 
53
60
 
54
- def get_project_files(project_path: Path) -> List[str]:
55
- project_file_extensions = ("datasource", "pipe")
56
- project_files = []
57
- for extension in project_file_extensions:
58
- for project_file in glob.glob(f"{project_path}/**/*.{extension}", recursive=True):
59
- logging.debug(f"Found project file: {project_file}")
60
- project_files.append(project_file)
61
- return project_files
62
-
63
-
64
- def build_project(folder: str, tb_client: TinyB) -> None:
61
+ def build_project(project: Project, tb_client: TinyB) -> None:
65
62
  MULTIPART_BOUNDARY_DATA_PROJECT = "data_project://"
66
63
  DATAFILE_TYPE_TO_CONTENT_TYPE = {
67
64
  ".datasource": "text/plain",
@@ -75,8 +72,8 @@ def build_project(folder: str, tb_client: TinyB) -> None:
75
72
  ("context://", ("cli-version", "1.0.0", "text/plain")),
76
73
  ]
77
74
  fds = []
78
- project_path = Path(folder)
79
- project_files = get_project_files(project_path)
75
+ project_path = project.path
76
+ project_files = project.get_project_files()
80
77
 
81
78
  for file_path in project_files:
82
79
  relative_path = str(Path(file_path).relative_to(project_path))
@@ -103,15 +100,29 @@ def build_project(folder: str, tb_client: TinyB) -> None:
103
100
  for ds in datasources:
104
101
  ds_path = next((p for p in project_files if p.endswith(ds.get("name") + ".datasource")), None)
105
102
  if ds_path:
106
- ds_path = ds_path.replace(f"{folder}/", "")
103
+ ds_path = ds_path.replace(f"{project.folder}/", "")
107
104
  click.echo(FeedbackManager.info(message=f"✓ {ds_path} created"))
108
105
  for pipe in pipes:
109
106
  pipe_name = pipe.get("name")
110
107
  pipe_path = next((p for p in project_files if p.endswith(pipe_name + ".pipe")), None)
111
108
  if pipe_path:
112
- pipe_path = pipe_path.replace(f"{folder}/", "")
109
+ pipe_path = pipe_path.replace(f"{project.folder}/", "")
113
110
  click.echo(FeedbackManager.info(message=f"✓ {pipe_path} created"))
114
111
 
112
+ for filename in project_files:
113
+ if filename.endswith(".datasource"):
114
+ ds_path = Path(filename)
115
+ ds_name = ds_path.stem
116
+ name = build_fixture_name(filename, ds_name, ds_path.read_text())
117
+ fixture_folder = get_fixture_dir(project.folder)
118
+ fixture_path = fixture_folder / f"{name}.ndjson"
119
+
120
+ if not fixture_path.exists():
121
+ fixture_path = fixture_folder / f"{ds_name}.ndjson"
122
+
123
+ if fixture_path.exists():
124
+ append_fixture(tb_client, ds_name, str(fixture_path))
125
+
115
126
  elif build_result == "failed":
116
127
  click.echo(FeedbackManager.error(message="Build failed"))
117
128
  build_errors = result.get("errors")
@@ -121,24 +132,44 @@ def build_project(folder: str, tb_client: TinyB) -> None:
121
132
  click.echo(FeedbackManager.error(message=error_msg))
122
133
  else:
123
134
  click.echo(FeedbackManager.error(message=f"Unknown build result. Error: {result.get('error')}"))
135
+
124
136
  except Exception as e:
125
- click.echo(FeedbackManager.error_exception(error="Error building project: " + str(e)))
137
+ click.echo(FeedbackManager.error_exception(error="Error: " + str(e)))
126
138
  finally:
127
139
  for fd in fds:
128
140
  fd.close()
129
141
 
130
142
 
131
- async def append_fixture(
143
+ def append_fixture(
132
144
  tb_client: TinyB,
133
145
  datasource_name: str,
134
146
  url: str,
135
147
  ):
136
- await tb_client.datasource_truncate(datasource_name)
137
- await push_data(
138
- tb_client,
139
- datasource_name,
140
- url,
141
- mode="append",
142
- concurrency=1,
143
- silent=True,
148
+ asyncio.run(tb_client.datasource_truncate(datasource_name))
149
+ asyncio.run(
150
+ push_data(
151
+ tb_client,
152
+ datasource_name,
153
+ url,
154
+ mode="append",
155
+ concurrency=1,
156
+ silent=True,
157
+ )
144
158
  )
159
+
160
+
161
+ def rebuild_fixture(project: Project, tb_client: TinyB, fixture_path: str) -> None:
162
+ fixture_path = Path(fixture_path)
163
+ datasources_path = Path(project.folder) / "datasources"
164
+ ds_name = fixture_path.stem
165
+
166
+ if ds_name not in project.datasources:
167
+ try:
168
+ ds_name = "_".join(fixture_path.stem.split("_")[:-1])
169
+ except Exception:
170
+ pass
171
+
172
+ ds_path = datasources_path / f"{ds_name}.datasource"
173
+
174
+ if ds_path.exists():
175
+ append_fixture(tb_client, ds_name, str(fixture_path))
@@ -13,13 +13,14 @@ from tinybird.config import FeatureFlags
13
13
  from tinybird.tb.modules.cli import cli
14
14
  from tinybird.tb.modules.common import push_data
15
15
  from tinybird.tb.modules.datafile.build import folder_build
16
- from tinybird.tb.modules.datafile.common import get_project_filenames, get_project_fixtures, has_internal_datafiles
16
+ from tinybird.tb.modules.datafile.common import get_project_fixtures, has_internal_datafiles
17
17
  from tinybird.tb.modules.datafile.exceptions import ParseException
18
18
  from tinybird.tb.modules.datafile.fixture import build_fixture_name, get_fixture_dir
19
19
  from tinybird.tb.modules.datafile.parse_datasource import parse_datasource
20
20
  from tinybird.tb.modules.datafile.parse_pipe import parse_pipe
21
21
  from tinybird.tb.modules.feedback_manager import FeedbackManager
22
22
  from tinybird.tb.modules.local_common import get_tinybird_local_client
23
+ from tinybird.tb.modules.project import Project
23
24
  from tinybird.tb.modules.shell import Shell, print_table_formatted
24
25
  from tinybird.tb.modules.watch import watch_files
25
26
 
@@ -45,9 +46,6 @@ def check_filenames(filenames: List[str]):
45
46
  incl_suffix = ".incl"
46
47
 
47
48
  for filename in filenames:
48
- if os.path.isdir(filename):
49
- check_filenames(filenames=get_project_filenames(filename))
50
-
51
49
  file_suffix = Path(filename).suffix
52
50
  if file_suffix == incl_suffix:
53
51
  continue
@@ -60,23 +58,19 @@ def check_filenames(filenames: List[str]):
60
58
 
61
59
 
62
60
  @cli.command()
63
- @click.option(
64
- "--folder",
65
- default=".",
66
- help="Folder from where to execute the command. By default the current folder.",
67
- hidden=True,
68
- type=click.types.STRING,
69
- )
70
61
  @click.option(
71
62
  "--watch",
72
63
  is_flag=True,
73
64
  help="Watch for changes in the files and rebuild them.",
74
65
  )
66
+ @click.pass_context
75
67
  def build_client(
76
- folder: str,
68
+ ctx: click.Context,
77
69
  watch: bool,
78
70
  ) -> None:
79
71
  """Build the project in Tinybird Local."""
72
+ project: Project = ctx.ensure_object(dict)["project"]
73
+ folder = project.folder
80
74
  ignore_sql_errors = FeatureFlags.ignore_sql_errors()
81
75
  context.disable_template_security_validation.set(True)
82
76
  is_internal = has_internal_datafiles(folder)
@@ -130,7 +124,7 @@ def build_client(
130
124
  if not filename.endswith(".ndjson"):
131
125
  await build_and_print_resource(tb_client, filename)
132
126
 
133
- datafiles = get_project_filenames(folder)
127
+ datafiles = project.get_project_files()
134
128
  fixtures = get_project_fixtures(folder)
135
129
  filenames = datafiles + fixtures
136
130
 
@@ -172,10 +166,10 @@ def build_client(
172
166
  build_ok = asyncio.run(build_once(filenames))
173
167
 
174
168
  if watch:
175
- shell = Shell(folder=folder, client=tb_client)
169
+ shell = Shell(project=project, client=tb_client)
176
170
  click.echo(FeedbackManager.gray(message="\nWatching for changes..."))
177
171
  watcher_thread = threading.Thread(
178
- target=watch_files, args=(filenames, process, shell, folder, build_ok), daemon=True
172
+ target=watch_files, args=(filenames, process, shell, project, build_ok), daemon=True
179
173
  )
180
174
  watcher_thread.start()
181
175
  shell.run()
@@ -35,11 +35,7 @@ from tinybird.tb.modules.common import (
35
35
  )
36
36
  from tinybird.tb.modules.config import CLIConfig
37
37
  from tinybird.tb.modules.datafile.build import build_graph
38
- from tinybird.tb.modules.datafile.common import (
39
- Datafile,
40
- DatafileSyntaxError,
41
- get_project_filenames,
42
- )
38
+ from tinybird.tb.modules.datafile.common import Datafile, DatafileSyntaxError
43
39
  from tinybird.tb.modules.datafile.diff import diff_command
44
40
  from tinybird.tb.modules.datafile.exceptions import (
45
41
  ParseException,
@@ -50,6 +46,7 @@ from tinybird.tb.modules.datafile.pull import folder_pull
50
46
  from tinybird.tb.modules.feedback_manager import FeedbackManager
51
47
  from tinybird.tb.modules.llm import LLM
52
48
  from tinybird.tb.modules.local_common import get_tinybird_local_client
49
+ from tinybird.tb.modules.project import Project
53
50
 
54
51
  __old_click_echo = click.echo
55
52
  __old_click_secho = click.secho
@@ -67,61 +64,17 @@ VERSION = f"{__cli__.__version__} (rev {__cli__.__revision__})"
67
64
  )
68
65
  @click.option("--token", help="Use auth token, defaults to TB_TOKEN envvar, then to the .tinyb file")
69
66
  @click.option("--host", help="Use custom host, defaults to TB_HOST envvar, then to https://api.tinybird.co")
70
- @click.option("--gcp-project-id", help="The Google Cloud project ID", hidden=True)
71
- @click.option(
72
- "--gcs-bucket", help="The Google Cloud Storage bucket to write temp files when using the connectors", hidden=True
73
- )
74
- @click.option(
75
- "--google-application-credentials",
76
- envvar="GOOGLE_APPLICATION_CREDENTIALS",
77
- help="Set GOOGLE_APPLICATION_CREDENTIALS",
78
- hidden=True,
79
- )
80
- @click.option("--sf-account", help="The Snowflake Account (e.g. your-domain.west-europe.azure)", hidden=True)
81
- @click.option("--sf-warehouse", help="The Snowflake warehouse name", hidden=True)
82
- @click.option("--sf-database", help="The Snowflake database name", hidden=True)
83
- @click.option("--sf-schema", help="The Snowflake schema name", hidden=True)
84
- @click.option("--sf-role", help="The Snowflake role name", hidden=True)
85
- @click.option("--sf-user", help="The Snowflake user name", hidden=True)
86
- @click.option("--sf-password", help="The Snowflake password", hidden=True)
87
- @click.option(
88
- "--sf-storage-integration",
89
- help="The Snowflake GCS storage integration name (leave empty to auto-generate one)",
90
- hidden=True,
91
- )
92
- @click.option("--sf-stage", help="The Snowflake GCS stage name (leave empty to auto-generate one)", hidden=True)
93
- @click.option(
94
- "--with-headers", help="Flag to enable connector to export with headers", is_flag=True, default=False, hidden=True
95
- )
96
67
  @click.option("--show-tokens", is_flag=True, default=False, help="Enable the output of tokens")
97
68
  @click.option("--prod/--local", is_flag=True, default=False, help="Run against production or local")
69
+ @click.option("--folder", type=str, default=os.getcwd(), help="Folder where files will be placed")
98
70
  @click.version_option(version=VERSION)
99
71
  @click.pass_context
100
72
  @coro
101
- async def cli(
102
- ctx: Context,
103
- debug: bool,
104
- token: str,
105
- host: str,
106
- gcp_project_id: str,
107
- gcs_bucket: str,
108
- google_application_credentials: str,
109
- sf_account: str,
110
- sf_warehouse: str,
111
- sf_database: str,
112
- sf_schema: str,
113
- sf_role: str,
114
- sf_user: str,
115
- sf_password: str,
116
- sf_storage_integration: str,
117
- sf_stage,
118
- with_headers: bool,
119
- show_tokens: bool,
120
- prod: bool,
121
- ) -> None:
73
+ async def cli(ctx: Context, debug: bool, token: str, host: str, show_tokens: bool, prod: bool, folder: str) -> None:
122
74
  """
123
75
  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.
124
76
  """
77
+ project = Project(folder=folder)
125
78
  # We need to unpatch for our tests not to break
126
79
  if show_tokens or not prod or ctx.invoked_subcommand == "build":
127
80
  __unpatch_click_output()
@@ -134,7 +87,7 @@ async def cli(
134
87
  if debug:
135
88
  logging.basicConfig(level=logging.DEBUG)
136
89
 
137
- config_temp = CLIConfig.get_project_config()
90
+ config_temp = CLIConfig.get_project_config(project.path)
138
91
  if token:
139
92
  config_temp.set_token(token)
140
93
  if host:
@@ -166,65 +119,27 @@ async def cli(
166
119
 
167
120
  ctx.ensure_object(dict)["config"] = config
168
121
 
169
- if ctx.invoked_subcommand == "auth":
170
- return
171
-
172
- from tinybird.connectors import create_connector
173
-
174
- if gcp_project_id and gcs_bucket and google_application_credentials and not sf_account:
175
- bq_config = {
176
- "project_id": gcp_project_id,
177
- "bucket_name": gcs_bucket,
178
- "service_account": google_application_credentials,
179
- "with_headers": with_headers,
180
- }
181
- ctx.ensure_object(dict)["bigquery"] = create_connector("bigquery", bq_config)
182
- if (
183
- sf_account
184
- and sf_warehouse
185
- and sf_database
186
- and sf_schema
187
- and sf_role
188
- and sf_user
189
- and sf_password
190
- and gcs_bucket
191
- and google_application_credentials
192
- and gcp_project_id
193
- ):
194
- sf_config = {
195
- "account": sf_account,
196
- "warehouse": sf_warehouse,
197
- "database": sf_database,
198
- "schema": sf_schema,
199
- "role": sf_role,
200
- "user": sf_user,
201
- "password": sf_password,
202
- "storage_integration": sf_storage_integration,
203
- "stage": sf_stage,
204
- "bucket_name": gcs_bucket,
205
- "service_account": google_application_credentials,
206
- "project_id": gcp_project_id,
207
- "with_headers": with_headers,
208
- }
209
- ctx.ensure_object(dict)["snowflake"] = create_connector("snowflake", sf_config)
210
-
211
122
  logging.debug("debug enabled")
212
123
 
213
- skip_client = ctx.invoked_subcommand in ["login", "workspace", "local", "build"]
214
- client = await create_ctx_client(config, prod, skip_client)
124
+ skip_client = ctx.invoked_subcommand in ["auth", "check", "login", "workspace", "local", "build"]
125
+ client = await create_ctx_client(config, prod, skip_client, project)
215
126
 
216
127
  if client:
217
128
  ctx.ensure_object(dict)["client"] = client
218
129
 
130
+ ctx.ensure_object(dict)["project"] = project
131
+
219
132
 
220
133
  @cli.command(hidden=True)
221
134
  @click.argument("filenames", type=click.Path(exists=True), nargs=-1, default=None)
222
135
  @click.option("--debug", is_flag=True, default=False, help="Print internal representation")
223
- def check(filenames: List[str], debug: bool) -> None:
136
+ @click.pass_context
137
+ def check(ctx: Context, filenames: List[str], debug: bool) -> None:
224
138
  """Check file syntax."""
225
139
 
226
140
  if not filenames:
227
- filenames = get_project_filenames(".")
141
+ project: Project = ctx.ensure_object(dict)["project"]
142
+ filenames = project.get_project_files()
228
143
 
229
144
  def process(filenames: Iterable):
230
145
  parser_matrix = {".pipe": parse_pipe, ".datasource": parse_datasource}
@@ -232,7 +147,7 @@ def check(filenames: List[str], debug: bool) -> None:
232
147
  try:
233
148
  for filename in filenames:
234
149
  if os.path.isdir(filename):
235
- process(filenames=get_project_filenames(filename))
150
+ process(filenames=filename)
236
151
 
237
152
  click.echo(FeedbackManager.info_processing_file(filename=filename))
238
153
 
@@ -487,7 +402,7 @@ async def sql(
487
402
 
488
403
  @cli.command(hidden=True)
489
404
  @click.argument("prompt")
490
- @click.option("--folder", default=".", help="The folder to use for the project")
405
+ @click.option("--folder", default=os.getcwd(), help="The folder to use for the project")
491
406
  @coro
492
407
  async def ask(prompt: str, folder: str) -> None:
493
408
  """Ask things about your data project."""
@@ -529,8 +444,8 @@ async def ask(prompt: str, folder: str) -> None:
529
444
  )
530
445
 
531
446
  client = config.get_client()
532
- llm = LLM(user_token=user_token, client=client)
533
- click.echo(await llm.ask(system_prompt=ask_prompt(resources_xml), prompt=prompt))
447
+ llm = LLM(user_token=user_token, host=client.host)
448
+ click.echo(llm.ask(system_prompt=ask_prompt(resources_xml), prompt=prompt))
534
449
  except Exception as e:
535
450
  raise CLIException(FeedbackManager.error_exception(error=e))
536
451
 
@@ -572,11 +487,11 @@ def __unpatch_click_output():
572
487
  click.secho = __old_click_secho
573
488
 
574
489
 
575
- async def create_ctx_client(config: Dict[str, Any], prod: bool, skip_client: bool):
490
+ async def create_ctx_client(config: Dict[str, Any], prod: bool, skip_client: bool, project: Project):
576
491
  if skip_client:
577
492
  return None
578
493
 
579
494
  if prod:
580
495
  return _get_tb_client(config.get("token", None), config["host"])
581
496
 
582
- return await get_tinybird_local_client()
497
+ return await get_tinybird_local_client(str(project.path))
@@ -0,0 +1,68 @@
1
+ # This is a command file for our CLI. Please keep it clean.
2
+ #
3
+ # - If it makes sense and only when strictly necessary, you can create utility functions in this file.
4
+ # - But please, **do not** interleave utility functions and command definitions.
5
+
6
+ import json
7
+ import re
8
+
9
+ import click
10
+ from click import Context
11
+
12
+ from tinybird.client import TinyB
13
+ from tinybird.tb.modules.cli import cli
14
+ from tinybird.tb.modules.common import coro, echo_safe_humanfriendly_tables_format_smart_table
15
+ from tinybird.tb.modules.datafile.common import get_name_version
16
+ from tinybird.tb.modules.exceptions import CLIPipeException
17
+ from tinybird.tb.modules.feedback_manager import FeedbackManager
18
+
19
+
20
+ @cli.group(hidden=True)
21
+ @click.pass_context
22
+ def copy(ctx):
23
+ """Copy pipe commands"""
24
+
25
+
26
+ @copy.command(name="ls")
27
+ @click.option("--match", default=None, help="Retrieve any resourcing matching the pattern. eg --match _test")
28
+ @click.option(
29
+ "--format",
30
+ "format_",
31
+ type=click.Choice(["json"], case_sensitive=False),
32
+ default=None,
33
+ help="Force a type of the output",
34
+ )
35
+ @click.pass_context
36
+ @coro
37
+ async def copy_ls(ctx: Context, match: str, format_: str):
38
+ """List copy pipes"""
39
+
40
+ client: TinyB = ctx.ensure_object(dict)["client"]
41
+ pipes = await client.pipes(dependencies=False, node_attrs="name", attrs="name,updated_at")
42
+ copies = [p for p in pipes if p.get("type") == "copy"]
43
+ copies = sorted(copies, key=lambda p: p["updated_at"])
44
+ columns = ["name", "updated at", "nodes", "url"]
45
+ table_human_readable = []
46
+ table_machine_readable = []
47
+ pattern = re.compile(match) if match else None
48
+ for t in copies:
49
+ tk = get_name_version(t["name"])
50
+ if pattern and not pattern.search(tk["name"]):
51
+ continue
52
+ table_human_readable.append((tk["name"], t["updated_at"][:-7], len(t["nodes"])))
53
+ table_machine_readable.append(
54
+ {
55
+ "name": tk["name"],
56
+ "updated at": t["updated_at"][:-7],
57
+ "nodes": len(t["nodes"]),
58
+ }
59
+ )
60
+
61
+ if not format_:
62
+ click.echo(FeedbackManager.info_pipes())
63
+ echo_safe_humanfriendly_tables_format_smart_table(table_human_readable, column_names=columns)
64
+ click.echo("\n")
65
+ elif format_ == "json":
66
+ click.echo(json.dumps({"pipes": table_machine_readable}, indent=2))
67
+ else:
68
+ raise CLIPipeException(FeedbackManager.error_pipe_ls_type())
@@ -103,13 +103,13 @@ async def create(
103
103
  datasource_files = [f for f in os.listdir(Path(folder) / "datasources") if f.endswith(".datasource")]
104
104
  for datasource_file in datasource_files:
105
105
  datasource_path = Path(folder) / "datasources" / datasource_file
106
- llm = LLM(user_token=user_token, client=tb_client)
106
+ llm = LLM(user_token=user_token, host=tb_client.host)
107
107
  datasource_name = datasource_path.stem
108
108
  datasource_content = datasource_path.read_text()
109
109
  has_json_path = "`json:" in datasource_content
110
110
  if has_json_path:
111
111
  prompt = f"<datasource_schema>{datasource_content}</datasource_schema>\n<user_input>{prompt}</user_input>"
112
- response = await llm.ask(system_prompt=mock_prompt(rows), prompt=prompt)
112
+ response = llm.ask(system_prompt=mock_prompt(rows), prompt=prompt)
113
113
  sql = extract_xml(response, "sql")
114
114
  sql = sql.split("FORMAT")[0]
115
115
  result = await local_client.query(f"{sql} FORMAT JSON")
@@ -205,8 +205,8 @@ TYPE ENDPOINT
205
205
  ]
206
206
  ]
207
207
  )
208
- llm = LLM(user_token=user_token, client=tb_client)
209
- result = await llm.ask(system_prompt=create_prompt(resources_xml), prompt=prompt)
208
+ llm = LLM(user_token=user_token, host=tb_client.host)
209
+ result = llm.ask(system_prompt=create_prompt(resources_xml), prompt=prompt)
210
210
  result = extract_xml(result, "response")
211
211
  resources = parse_xml(result, "resource")
212
212
  datasources = []
@@ -44,7 +44,7 @@ from tinybird.tb.modules.datafile.exceptions import AlreadyExistsException, Incl
44
44
  from tinybird.tb.modules.datafile.parse_datasource import parse_datasource
45
45
  from tinybird.tb.modules.datafile.parse_pipe import parse_pipe
46
46
  from tinybird.tb.modules.feedback_manager import FeedbackManager
47
- from tinybird.tb.modules.local_common import get_client_config_for_build
47
+ from tinybird.tb.modules.local_common import get_tinybird_local_config
48
48
 
49
49
 
50
50
  async def folder_build(
@@ -58,7 +58,7 @@ async def folder_build(
58
58
  local_ws: Optional[Dict[str, Any]] = None,
59
59
  watch: bool = False,
60
60
  ):
61
- config = await get_client_config_for_build(folder)
61
+ config = await get_tinybird_local_config(folder)
62
62
  build = True
63
63
  dry_run = False
64
64
  force = True