tinybird 0.0.1.dev66__tar.gz → 0.0.1.dev68__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 (103) hide show
  1. tinybird-0.0.1.dev68/PKG-INFO +64 -0
  2. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/prompts.py +3 -3
  3. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/__cli__.py +2 -2
  4. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/cli.py +1 -1
  5. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/common.py +72 -0
  6. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/create.py +42 -80
  7. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/datafile/common.py +0 -2
  8. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/deployment.py +47 -10
  9. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/mock.py +11 -39
  10. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/test.py +38 -70
  11. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/watch.py +25 -12
  12. tinybird-0.0.1.dev68/tinybird.egg-info/PKG-INFO +64 -0
  13. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird.egg-info/requires.txt +1 -0
  14. tinybird-0.0.1.dev66/PKG-INFO +0 -24
  15. tinybird-0.0.1.dev66/tinybird.egg-info/PKG-INFO +0 -24
  16. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/setup.cfg +0 -0
  17. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/__cli__.py +0 -0
  18. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/ch_utils/constants.py +0 -0
  19. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/ch_utils/engine.py +0 -0
  20. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/check_pypi.py +0 -0
  21. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/client.py +0 -0
  22. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/config.py +0 -0
  23. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/connectors.py +0 -0
  24. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/context.py +0 -0
  25. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/datafile.py +0 -0
  26. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/datatypes.py +0 -0
  27. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/feedback_manager.py +0 -0
  28. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/git_settings.py +0 -0
  29. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/sql.py +0 -0
  30. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/sql_template.py +0 -0
  31. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/sql_template_fmt.py +0 -0
  32. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/sql_toolset.py +0 -0
  33. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/syncasync.py +0 -0
  34. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/cli.py +0 -0
  35. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/auth.py +0 -0
  36. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/build.py +0 -0
  37. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/cicd.py +0 -0
  38. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/config.py +0 -0
  39. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/copy.py +0 -0
  40. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/datafile/build.py +0 -0
  41. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/datafile/build_common.py +0 -0
  42. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/datafile/build_datasource.py +0 -0
  43. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/datafile/build_pipe.py +0 -0
  44. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/datafile/diff.py +0 -0
  45. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/datafile/exceptions.py +0 -0
  46. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/datafile/fixture.py +0 -0
  47. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/datafile/format_common.py +0 -0
  48. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/datafile/format_datasource.py +0 -0
  49. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/datafile/format_pipe.py +0 -0
  50. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/datafile/parse_datasource.py +0 -0
  51. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/datafile/parse_pipe.py +0 -0
  52. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/datafile/pipe_checker.py +0 -0
  53. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/datafile/pull.py +0 -0
  54. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/datasource.py +0 -0
  55. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/endpoint.py +0 -0
  56. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/exceptions.py +0 -0
  57. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/feedback_manager.py +0 -0
  58. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/fmt.py +0 -0
  59. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/job.py +0 -0
  60. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/llm.py +0 -0
  61. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/llm_utils.py +0 -0
  62. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/local.py +0 -0
  63. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/local_common.py +0 -0
  64. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/login.py +0 -0
  65. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/materialization.py +0 -0
  66. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/pipe.py +0 -0
  67. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/project.py +0 -0
  68. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/regions.py +0 -0
  69. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/shell.py +0 -0
  70. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/table.py +0 -0
  71. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/tag.py +0 -0
  72. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/telemetry.py +0 -0
  73. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/tinyunit/tinyunit.py +0 -0
  74. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/tinyunit/tinyunit_lib.py +0 -0
  75. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/token.py +0 -0
  76. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/workspace.py +0 -0
  77. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb/modules/workspace_members.py +0 -0
  78. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb_cli.py +0 -0
  79. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb_cli_modules/auth.py +0 -0
  80. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb_cli_modules/branch.py +0 -0
  81. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb_cli_modules/cicd.py +0 -0
  82. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb_cli_modules/cli.py +0 -0
  83. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb_cli_modules/common.py +0 -0
  84. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb_cli_modules/config.py +0 -0
  85. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb_cli_modules/connection.py +0 -0
  86. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb_cli_modules/datasource.py +0 -0
  87. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb_cli_modules/exceptions.py +0 -0
  88. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb_cli_modules/fmt.py +0 -0
  89. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb_cli_modules/job.py +0 -0
  90. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb_cli_modules/pipe.py +0 -0
  91. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb_cli_modules/regions.py +0 -0
  92. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb_cli_modules/tag.py +0 -0
  93. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb_cli_modules/telemetry.py +0 -0
  94. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb_cli_modules/test.py +0 -0
  95. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb_cli_modules/tinyunit/tinyunit.py +0 -0
  96. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb_cli_modules/tinyunit/tinyunit_lib.py +0 -0
  97. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb_cli_modules/workspace.py +0 -0
  98. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tb_cli_modules/workspace_members.py +0 -0
  99. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird/tornado_template.py +0 -0
  100. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird.egg-info/SOURCES.txt +0 -0
  101. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird.egg-info/dependency_links.txt +0 -0
  102. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird.egg-info/entry_points.txt +0 -0
  103. {tinybird-0.0.1.dev66 → tinybird-0.0.1.dev68}/tinybird.egg-info/top_level.txt +0 -0
@@ -0,0 +1,64 @@
1
+ Metadata-Version: 2.1
2
+ Name: tinybird
3
+ Version: 0.0.1.dev68
4
+ Summary: Tinybird Command Line Tool
5
+ Home-page: https://www.tinybird.co/docs/cli/introduction.html
6
+ Author: Tinybird
7
+ Author-email: support@tinybird.co
8
+ Requires-Python: >=3.9, <3.13
9
+ Description-Content-Type: text/x-rst
10
+ Requires-Dist: aiofiles==24.1.0
11
+ Requires-Dist: anthropic==0.42.0
12
+ Requires-Dist: click<8.2,>=8.1.6
13
+ Requires-Dist: clickhouse-toolset==0.33.dev0
14
+ Requires-Dist: colorama==0.4.6
15
+ Requires-Dist: cryptography~=41.0.0
16
+ Requires-Dist: croniter==1.3.8
17
+ Requires-Dist: docker==7.1.0
18
+ Requires-Dist: GitPython~=3.1.32
19
+ Requires-Dist: humanfriendly~=8.2
20
+ Requires-Dist: prompt_toolkit==3.0.48
21
+ Requires-Dist: pydantic~=2.8.0
22
+ Requires-Dist: pyperclip==1.8.2
23
+ Requires-Dist: pyyaml<6.1,>=6.0
24
+ Requires-Dist: requests<3,>=2.28.1
25
+ Requires-Dist: shandy-sqlfmt==0.11.1
26
+ Requires-Dist: shandy-sqlfmt[jinjafmt]==0.11.1
27
+ Requires-Dist: toposort==1.10
28
+ Requires-Dist: tornado~=6.0.0
29
+ Requires-Dist: urllib3<2,>=1.26.14
30
+ Requires-Dist: watchdog==6.0.0
31
+ Requires-Dist: wheel
32
+ Requires-Dist: packaging<24,>=23.1
33
+ Requires-Dist: llm>=0.19
34
+ Requires-Dist: thefuzz==0.22.1
35
+ Provides-Extra: bigquery
36
+ Requires-Dist: gsutil==4.58; extra == "bigquery"
37
+ Requires-Dist: google-api-python-client==2.0.2; extra == "bigquery"
38
+ Requires-Dist: google-auth==1.27.1; extra == "bigquery"
39
+ Requires-Dist: google-auth-httplib2==0.1.0; extra == "bigquery"
40
+ Requires-Dist: google-cloud-storage==2.4.0; extra == "bigquery"
41
+ Requires-Dist: google-cloud-bigquery==2.11.0; extra == "bigquery"
42
+ Provides-Extra: snowflake
43
+ Requires-Dist: snowflake-connector-python~=3.12.3; extra == "snowflake"
44
+ Requires-Dist: gsutil==4.58; extra == "snowflake"
45
+ Requires-Dist: google-api-python-client==2.0.2; extra == "snowflake"
46
+ Requires-Dist: google-auth==1.27.1; extra == "snowflake"
47
+ Requires-Dist: google-auth-httplib2==0.1.0; extra == "snowflake"
48
+ Requires-Dist: google-cloud-storage==2.4.0; extra == "snowflake"
49
+ Requires-Dist: oauth2client==3.0.0; extra == "snowflake"
50
+ Requires-Dist: chardet<4,>=3.0.2; extra == "snowflake"
51
+ Requires-Dist: pyOpenSSL<20.0.0,>=16.2.0; extra == "snowflake"
52
+
53
+ Tinybird CLI
54
+ =============
55
+
56
+ The Tinybird command-line tool allows you to use all the Tinybird functionality directly from the command line. Additionally, it includes several functions to create and manage data projects easily.
57
+
58
+ Changelog
59
+ ----------
60
+
61
+ 0.0.1dev1
62
+ ***********
63
+
64
+ * Initial release of the Tinybird CLI
@@ -699,8 +699,8 @@ pipe_instructions = """
699
699
  - Nodes can't have the same exact name as the Pipe they belong to.
700
700
  - Avoid more than one node per pipe unless it is really necessary or requested by the user.
701
701
  - No indentation is allowed for property names: DESCRIPTION, NODE, SQL, TYPE, etc.
702
- - Allowed TYPE values are: endpoint, copy, materialized, sink
703
- - Add always the output node in the TYPE section.
702
+ - Allowed TYPE values are: endpoint, copy, materialized.
703
+ - Add always the output node in the TYPE section or in the last node of the pipe.
704
704
  </pipe_file_instructions>
705
705
  """
706
706
 
@@ -788,7 +788,7 @@ When you need to work with resources or data in cloud, add always the --cloud fl
788
788
  ├── endpoints
789
789
  ├── fixtures
790
790
  ├── materializations
791
- ├── sinks
791
+ ├── pipes
792
792
  └── tests
793
793
  - The local development server will be available at http://localhost:80. Even if some response uses another base url, use always http://localhost:80.
794
794
  - After every change in your .datasource, .pipe or .ndjson files, run `{base_command} build` to build the project locally.
@@ -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.dev66'
8
- __revision__ = '449a9aa'
7
+ __version__ = '0.0.1.dev68'
8
+ __revision__ = 'e28d545'
@@ -392,7 +392,7 @@ async def create_ctx_client(ctx: Context, config: Dict[str, Any], cloud: bool, b
392
392
  return None
393
393
 
394
394
  commands_always_cloud = ["pull"]
395
- commands_always_build = ["build", "test"]
395
+ commands_always_build = ["build", "test", "dev"]
396
396
  commands_always_local = ["create", "mock"]
397
397
  if (
398
398
  (cloud or command in commands_always_cloud)
@@ -33,6 +33,7 @@ from click import Context
33
33
  from click._termui_impl import ProgressBar
34
34
  from humanfriendly.tables import format_pretty_table
35
35
  from packaging.version import Version
36
+ from thefuzz import process
36
37
 
37
38
  from tinybird.client import (
38
39
  AuthException,
@@ -277,6 +278,77 @@ variable to '1' or 'true'."""
277
278
 
278
279
  sys.exit(exit_code)
279
280
 
281
+ def get_command(self, ctx: click.Context, cmd_name: str) -> Optional[click.Command]:
282
+ """
283
+ Override get_command to add suggestion functionality.
284
+
285
+ Args:
286
+ ctx: Click context
287
+ cmd_name: Command name entered by user
288
+
289
+ Returns:
290
+ Click command if found, None otherwise
291
+ """
292
+ # First, try to get the command normally
293
+ rv = click.Group.get_command(self, ctx, cmd_name)
294
+ if rv is not None:
295
+ return rv
296
+
297
+ # Get all available commands
298
+ commands: List[str] = self.list_commands(ctx)
299
+
300
+ # Find closest matching command using thefuzz
301
+ matches = process.extract(cmd_name, commands, limit=1)
302
+ if not matches:
303
+ return None
304
+
305
+ suggestion, score = matches[0]
306
+
307
+ # Only suggest if the similarity score is high enough (adjust threshold as needed)
308
+ if score >= 65:
309
+ # Initialize lists for arguments before and after command
310
+ args_before_cmd: List[str] = []
311
+ args_after_cmd: List[str] = []
312
+
313
+ # Reconstruct the full command with all arguments
314
+ full_command = ctx.command_path # Gets the base command (e.g., 'tb')
315
+
316
+ # Add any options/arguments that came before the command
317
+ if ctx.args:
318
+ found_cmd = False
319
+ for arg in ctx.args:
320
+ if arg == cmd_name:
321
+ found_cmd = True
322
+ elif found_cmd:
323
+ args_after_cmd.append(arg)
324
+ else:
325
+ args_before_cmd.append(arg)
326
+
327
+ if args_before_cmd:
328
+ full_command += " " + " ".join(args_before_cmd)
329
+
330
+ # Add any options from params
331
+ if ctx.params:
332
+ for param, value in ctx.params.items():
333
+ if value is True:
334
+ full_command += f" --{param}"
335
+ elif value is not False and value is not None:
336
+ full_command += f" --{param} {value}"
337
+
338
+ # Add the suggested command
339
+ full_command += f" {suggestion}"
340
+
341
+ # Add any arguments that came after the command
342
+ if args_after_cmd:
343
+ full_command += " " + " ".join(args_after_cmd)
344
+
345
+ click.echo(f"\nWARNING: '{cmd_name}' is not a valid command. ", nl=False)
346
+ # Ask for confirmation
347
+ if click.confirm(f"Execute '{full_command}' instead?"):
348
+ return click.Group.get_command(self, ctx, suggestion)
349
+
350
+ return None
351
+
280
352
 
281
353
  def load_connector_config(ctx: Context, connector_name: str, debug: bool, check_uninstalled: bool = False):
282
354
  config_file = Path(getcwd()) / f".tinyb_{connector_name}"
@@ -35,17 +35,9 @@ from tinybird.tb.modules.project import Project
35
35
  )
36
36
  @click.option("--rows", type=int, default=10, help="Number of events to send")
37
37
  @click.option("--source", type=str, default="tb", help="Source of the command")
38
- @click.option("--skip", is_flag=True, default=False, help="Skip following up on the generated resources")
39
38
  @click.pass_context
40
39
  @coro
41
- async def create(
42
- ctx: click.Context,
43
- data: Optional[str],
44
- prompt: Optional[str],
45
- rows: int,
46
- source: str,
47
- skip: bool,
48
- ) -> None:
40
+ async def create(ctx: click.Context, data: Optional[str], prompt: Optional[str], rows: int, source: str) -> None:
49
41
  """Initialize a new project."""
50
42
  project: Project = ctx.ensure_object(dict)["project"]
51
43
  local_client: TinyB = ctx.ensure_object(dict)["client"]
@@ -80,7 +72,7 @@ async def create(
80
72
  result = ""
81
73
  if data or prompt:
82
74
  click.echo(FeedbackManager.highlight(message="\n» Creating resources..."))
83
- result = await create_resources(local_client, tb_client, user_token, data, prompt, folder, skip)
75
+ result = await create_resources(local_client, tb_client, user_token, data, prompt, folder)
84
76
  click.echo(FeedbackManager.success(message="✓ Done!\n"))
85
77
 
86
78
  if not already_has_cicd(folder):
@@ -125,7 +117,7 @@ async def create(
125
117
  click.echo(FeedbackManager.error(message=f"Error: {str(e)}"))
126
118
 
127
119
 
128
- PROJECT_PATHS = ("datasources", "endpoints", "materializations", "copies", "sinks", "fixtures", "tests")
120
+ PROJECT_PATHS = ("datasources", "endpoints", "materializations", "copies", "pipes", "fixtures", "tests")
129
121
 
130
122
 
131
123
  def validate_project_structure(folder: str) -> bool:
@@ -166,7 +158,6 @@ async def create_resources(
166
158
  data: Optional[str],
167
159
  prompt: Optional[str],
168
160
  folder: str,
169
- skip: bool,
170
161
  ):
171
162
  result = ""
172
163
  folder_path = Path(folder)
@@ -215,73 +206,39 @@ TYPE ENDPOINT
215
206
  ]
216
207
  )
217
208
  llm = LLM(user_token=user_token, host=tb_client.host)
218
- result = ""
219
- iterations = 0
220
- history = ""
221
- generated_paths: list[Path] = []
222
-
223
- while iterations < 10:
224
- feedback = ""
225
- if iterations > 0:
226
- feedback = click.prompt("\nFollow-up instructions or continue", default="continue")
227
- if iterations > 0 and (not feedback or feedback in ("continue", "ok", "exit", "quit", "q")):
228
- break
229
- else:
230
- if iterations > 0:
231
- click.echo(FeedbackManager.highlight(message="\n» Creating resources..."))
232
- for path in generated_paths:
233
- path.unlink()
234
- generated_paths = []
235
-
236
- save_context(prompt, feedback)
237
- result = llm.ask(system_prompt=create_prompt(resources_xml, feedback, history), prompt=prompt)
238
- result = extract_xml(result, "response")
239
- history = (
240
- history
241
- + f"""
242
- <result_iteration_{iterations}>
243
- {result}
244
- </result_iteration_{iterations}>
245
- """
246
- )
247
- resources = parse_xml(result, "resource")
248
- datasources = []
249
- pipes = []
250
- for resource_xml in resources:
251
- resource_type = extract_xml(resource_xml, "type")
252
- name = extract_xml(resource_xml, "name")
253
- content = extract_xml(resource_xml, "content")
254
- resource = {
255
- "name": name,
256
- "content": content,
257
- }
258
- if resource_type.lower() == "datasource":
259
- datasources.append(resource)
260
- elif resource_type.lower() == "pipe":
261
- pipes.append(resource)
262
-
263
- for ds in datasources:
264
- content = ds["content"].replace("```", "")
265
- filename = f"{ds['name']}.datasource"
266
- datasource_path = generate_datafile(
267
- content,
268
- filename=filename,
269
- data=None,
270
- _format="ndjson",
271
- force=True,
272
- folder=folder,
273
- )
274
- generated_paths.append(datasource_path)
275
- for pipe in pipes:
276
- content = pipe["content"].replace("```", "")
277
- pipe_path = generate_pipe_file(pipe["name"], content, folder)
278
- generated_paths.append(pipe_path)
279
- if skip:
280
- break
281
- iterations += 1
282
-
283
- if iterations == 10:
284
- click.echo(FeedbackManager.info(message="Too many iterations. Change the prompt and try again."))
209
+ result = llm.ask(system_prompt=create_prompt(resources_xml), prompt=prompt)
210
+ result = extract_xml(result, "response")
211
+ resources = parse_xml(result, "resource")
212
+ datasources = []
213
+ pipes = []
214
+ for resource_xml in resources:
215
+ resource_type = extract_xml(resource_xml, "type")
216
+ name = extract_xml(resource_xml, "name")
217
+ content = extract_xml(resource_xml, "content")
218
+ resource = {
219
+ "name": name,
220
+ "content": content,
221
+ }
222
+ if resource_type.lower() == "datasource":
223
+ datasources.append(resource)
224
+ elif resource_type.lower() == "pipe":
225
+ pipes.append(resource)
226
+
227
+ for ds in datasources:
228
+ content = ds["content"].replace("```", "")
229
+ filename = f"{ds['name']}.datasource"
230
+ generate_datafile(
231
+ content,
232
+ filename=filename,
233
+ data=None,
234
+ _format="ndjson",
235
+ force=True,
236
+ folder=folder,
237
+ )
238
+
239
+ for pipe in pipes:
240
+ content = pipe["content"].replace("```", "")
241
+ generate_pipe_file(pipe["name"], content, folder)
285
242
 
286
243
  return result
287
244
 
@@ -313,14 +270,19 @@ def generate_pipe_file(name: str, content: str, folder: str) -> Path:
313
270
  def is_sink(content: str) -> bool:
314
271
  return re.search(r"TYPE sink", content, re.IGNORECASE) is not None
315
272
 
273
+ def is_endpoint(content: str) -> bool:
274
+ return re.search(r"TYPE endpoint", content, re.IGNORECASE) is not None
275
+
316
276
  if is_copy(content):
317
277
  pathname = "copies"
318
278
  elif is_materialization(content):
319
279
  pathname = "materializations"
320
280
  elif is_sink(content):
321
281
  pathname = "sinks"
322
- else:
282
+ elif is_endpoint(content):
323
283
  pathname = "endpoints"
284
+ else:
285
+ pathname = "pipes"
324
286
 
325
287
  base = Path(folder) / pathname
326
288
  if not base.exists():
@@ -232,8 +232,6 @@ class Datafile:
232
232
  node = self.nodes[0]
233
233
  if "schema" not in node:
234
234
  raise DatafileValidationError("SCHEMA is mandatory")
235
- if "engine" not in node:
236
- raise DatafileValidationError("ENGINE is mandatory")
237
235
  else:
238
236
  # We cannot validate a datafile whose kind is unknown
239
237
  pass
@@ -58,6 +58,8 @@ def promote_deployment(host: Optional[str], headers: dict, wait: bool) -> None:
58
58
  result = r.json()
59
59
  logging.debug(json.dumps(result, indent=2))
60
60
 
61
+ click.echo(FeedbackManager.success(message="Deployment promotion successfully started"))
62
+
61
63
  if wait:
62
64
  while True:
63
65
  TINYBIRD_API_URL = f"{host}/v1/deployments/{last_deployment.get('id')}"
@@ -67,12 +69,11 @@ def promote_deployment(host: Optional[str], headers: dict, wait: bool) -> None:
67
69
 
68
70
  last_deployment = result.get("deployment")
69
71
  if last_deployment.get("status") == "deleted":
72
+ click.echo(FeedbackManager.success(message="Deployment promoted successfully"))
70
73
  break
71
74
 
72
75
  time.sleep(5)
73
76
 
74
- click.echo(FeedbackManager.success(message="Deployment promoted successfully"))
75
-
76
77
 
77
78
  # TODO(eclbg): This logic should be in the server, and there should be a dedicated endpoint for rolling back a
78
79
  # deployment
@@ -117,6 +118,8 @@ def rollback_deployment(host: Optional[str], headers: dict, wait: bool) -> None:
117
118
  result = r.json()
118
119
  logging.debug(json.dumps(result, indent=2))
119
120
 
121
+ click.echo(FeedbackManager.success(message="Deployment rollback successfully started"))
122
+
120
123
  if wait:
121
124
  while True:
122
125
  TINYBIRD_API_URL = f"{host}/v1/deployments/{current_deployment.get('id')}"
@@ -126,11 +129,10 @@ def rollback_deployment(host: Optional[str], headers: dict, wait: bool) -> None:
126
129
 
127
130
  current_deployment = result.get("deployment")
128
131
  if current_deployment.get("status") == "deleted":
132
+ click.echo(FeedbackManager.success(message="Deployment rolled back successfully"))
129
133
  break
130
134
  time.sleep(5)
131
135
 
132
- click.echo(FeedbackManager.success(message="Deployment rolled back successfully"))
133
-
134
136
 
135
137
  @cli.group(name="deployment")
136
138
  def deployment_group() -> None:
@@ -159,12 +161,18 @@ def deployment_group() -> None:
159
161
  default=False,
160
162
  help="Validate the deployment before creating it. Disabled by default.",
161
163
  )
164
+ @click.option(
165
+ "--allow-remove-datasources/--no-allow-remove-datasources",
166
+ is_flag=True,
167
+ default=False,
168
+ help="Allow removing datasources. Disabled by default.",
169
+ )
162
170
  @click.pass_context
163
- def deployment_create(ctx: click.Context, wait: bool, auto: bool, check: bool) -> None:
171
+ def deployment_create(ctx: click.Context, wait: bool, auto: bool, check: bool, allow_remove_datasources: bool) -> None:
164
172
  """
165
173
  Validate and deploy the project server side.
166
174
  """
167
- create_deployment(ctx, wait, auto, check)
175
+ create_deployment(ctx, wait, auto, check, allow_remove_datasources)
168
176
 
169
177
 
170
178
  @deployment_group.command(name="ls")
@@ -187,6 +195,9 @@ def deployment_ls(ctx: click.Context) -> None:
187
195
  columns = ["ID", "Status", "Created at", "Live"]
188
196
  table = []
189
197
  for deployment in result.get("deployments"):
198
+ if deployment.get("id") == "0":
199
+ continue
200
+
190
201
  table.append(
191
202
  [
192
203
  deployment.get("id"),
@@ -258,15 +269,27 @@ def deployment_rollback(ctx: click.Context, wait: bool) -> None:
258
269
  default=False,
259
270
  help="Validate the deployment before creating it. Disabled by default.",
260
271
  )
272
+ @click.option(
273
+ "--allow-remove-datasources/--no-allow-remove-datasources",
274
+ is_flag=True,
275
+ default=False,
276
+ help="Allow removing datasources. Disabled by default.",
277
+ )
261
278
  @click.pass_context
262
- def deploy(ctx: click.Context, wait: bool, auto: bool, check: bool) -> None:
279
+ def deploy(ctx: click.Context, wait: bool, auto: bool, check: bool, allow_remove_datasources: bool) -> None:
263
280
  """
264
281
  Deploy the project.
265
282
  """
266
- create_deployment(ctx, wait, auto, check)
283
+ create_deployment(ctx, wait, auto, check, allow_remove_datasources)
267
284
 
268
285
 
269
- def create_deployment(ctx: click.Context, wait: bool, auto: bool, check: Optional[bool] = None) -> None:
286
+ def create_deployment(
287
+ ctx: click.Context,
288
+ wait: bool,
289
+ auto: bool,
290
+ check: Optional[bool] = None,
291
+ allow_remove_datasources: Optional[bool] = None,
292
+ ) -> None:
270
293
  # TODO: This code is duplicated in build_server.py
271
294
  # Should be refactored to be shared
272
295
  MULTIPART_BOUNDARY_DATA_PROJECT = "data_project://"
@@ -297,21 +320,31 @@ def create_deployment(ctx: click.Context, wait: bool, auto: bool, check: Optiona
297
320
  if check:
298
321
  click.echo(FeedbackManager.highlight(message="\n» Validating deployment...\n"))
299
322
  params["check"] = "true"
323
+ if allow_remove_datasources:
324
+ params["allow_remove_datasources"] = "true"
300
325
  r = requests.post(TINYBIRD_API_URL, files=files, headers=HEADERS, params=params)
301
326
  result = r.json()
302
327
  logging.debug(json.dumps(result, indent=2))
303
328
 
304
329
  if check:
305
330
  print_changes(result, project)
331
+ feedback = result.get("deployment", {}).get("feedback", [])
332
+ for f in feedback:
333
+ click.echo(
334
+ FeedbackManager.warning(message=f"△ {f.get('level')}: {f.get('resource')}: {f.get('message')}")
335
+ )
336
+
306
337
  status = result.get("result")
307
338
  if status == "success":
308
339
  click.echo(FeedbackManager.success(message="\n✓ Deployment is valid"))
309
340
  else:
310
341
  click.echo(FeedbackManager.error(message="\n✗ Deployment is not valid"))
342
+
311
343
  return
312
344
 
313
345
  deploy_result = result.get("result")
314
346
  if deploy_result == "success":
347
+ print_changes(result, project)
315
348
  click.echo(FeedbackManager.success(message="Deployment submitted successfully"))
316
349
  deployment = result.get("deployment")
317
350
  feedback = deployment.get("feedback", [])
@@ -394,4 +427,8 @@ def print_changes(result: dict, project: Project) -> None:
394
427
  for p in deployment.get("deleted_pipe_names", []):
395
428
  resources.append(["deleted", p, project.get_resource_path(p, "pipe")])
396
429
 
397
- echo_safe_humanfriendly_tables_format_smart_table(resources, column_names=columns)
430
+ if resources:
431
+ click.echo(FeedbackManager.highlight(message="\n» Changes to be deployed...\n"))
432
+ echo_safe_humanfriendly_tables_format_smart_table(resources, column_names=columns)
433
+ else:
434
+ click.echo(FeedbackManager.highlight(message="\n» No changes to be deployed\n"))
@@ -2,7 +2,6 @@ import glob
2
2
  import logging
3
3
  import os
4
4
  from pathlib import Path
5
- from typing import Optional
6
5
 
7
6
  import click
8
7
 
@@ -27,10 +26,9 @@ from tinybird.tb.modules.project import Project
27
26
  default="",
28
27
  help="Extra context to use for data generation",
29
28
  )
30
- @click.option("--skip", is_flag=True, default=False, help="Skip following up on the generated data")
31
29
  @click.pass_context
32
30
  @coro
33
- async def mock(ctx: click.Context, datasource: str, rows: int, prompt: str, skip: bool) -> None:
31
+ async def mock(ctx: click.Context, datasource: str, rows: int, prompt: str) -> None:
34
32
  """Generate sample data for a datasource.
35
33
 
36
34
  Args:
@@ -80,44 +78,18 @@ async def mock(ctx: click.Context, datasource: str, rows: int, prompt: str, skip
80
78
  return
81
79
  llm = LLM(user_token=user_token, host=user_client.host)
82
80
  prompt = f"<datasource_schema>{datasource_content}</datasource_schema>\n<user_input>{prompt}</user_input>"
83
- iterations = 0
84
- history = ""
85
- fixture_path: Optional[Path] = None
86
81
  sql = ""
87
- while iterations < 10:
88
- feedback = ""
89
- if iterations > 0:
90
- feedback = click.prompt("\nFollow-up instructions or continue", default="continue")
91
- if iterations > 0 and (not feedback or feedback in ("continue", "ok", "exit", "quit", "q")):
92
- break
93
- else:
94
- if iterations > 0:
95
- if fixture_path:
96
- fixture_path.unlink()
97
- fixture_path = None
98
- click.echo(FeedbackManager.highlight(message=f"\n» Creating fixture for {datasource_name}..."))
99
82
 
100
- response = llm.ask(system_prompt=mock_prompt(rows, feedback, history), prompt=prompt)
101
- sql = extract_xml(response, "sql")
102
- result = await tb_client.query(f"{sql} FORMAT JSON")
103
- data = result.get("data", [])[:rows]
104
- fixture_path = persist_fixture(datasource_name, data, folder)
105
- click.echo(FeedbackManager.info(message=f"✓ /fixtures/{datasource_name}.ndjson created"))
106
-
107
- if os.environ.get("TB_DEBUG", "") != "":
108
- logging.debug(sql)
109
-
110
- history = (
111
- history
112
- + f"""
113
- <result_iteration_{iterations}>
114
- {response}
115
- </result_iteration_{iterations}>
116
- """
117
- )
118
- if skip:
119
- break
120
- iterations += 1
83
+ click.echo(FeedbackManager.highlight(message=f"\n» Creating fixture for {datasource_name}..."))
84
+ response = llm.ask(system_prompt=mock_prompt(rows), prompt=prompt)
85
+ sql = extract_xml(response, "sql")
86
+ result = await tb_client.query(f"{sql} FORMAT JSON")
87
+ data = result.get("data", [])[:rows]
88
+ persist_fixture(datasource_name, data, folder)
89
+ click.echo(FeedbackManager.info(message=f"✓ /fixtures/{datasource_name}.ndjson created"))
90
+
91
+ if os.environ.get("TB_DEBUG", "") != "":
92
+ logging.debug(sql)
121
93
 
122
94
  click.echo(FeedbackManager.success(message=f"✓ Sample data for {datasource_name} created with {rows} rows"))
123
95