tinybird 0.0.1.dev306__py3-none-any.whl → 1.0.5__py3-none-any.whl

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.
Files changed (45) hide show
  1. tinybird/datafile/common.py +4 -1
  2. tinybird/feedback_manager.py +3 -0
  3. tinybird/service_datasources.py +57 -8
  4. tinybird/sql_template.py +1 -1
  5. tinybird/sql_template_fmt.py +14 -4
  6. tinybird/tb/__cli__.py +2 -2
  7. tinybird/tb/cli.py +1 -0
  8. tinybird/tb/client.py +104 -22
  9. tinybird/tb/modules/agent/tools/execute_query.py +1 -1
  10. tinybird/tb/modules/agent/tools/request_endpoint.py +1 -1
  11. tinybird/tb/modules/branch.py +150 -0
  12. tinybird/tb/modules/build.py +51 -10
  13. tinybird/tb/modules/build_common.py +4 -2
  14. tinybird/tb/modules/cli.py +32 -10
  15. tinybird/tb/modules/common.py +161 -134
  16. tinybird/tb/modules/connection.py +125 -194
  17. tinybird/tb/modules/connection_kafka.py +382 -0
  18. tinybird/tb/modules/copy.py +3 -1
  19. tinybird/tb/modules/create.py +11 -0
  20. tinybird/tb/modules/datafile/build.py +1 -1
  21. tinybird/tb/modules/datafile/format_pipe.py +44 -5
  22. tinybird/tb/modules/datafile/playground.py +1 -1
  23. tinybird/tb/modules/datasource.py +475 -324
  24. tinybird/tb/modules/deployment.py +2 -0
  25. tinybird/tb/modules/deployment_common.py +81 -43
  26. tinybird/tb/modules/deprecations.py +4 -4
  27. tinybird/tb/modules/dev_server.py +33 -12
  28. tinybird/tb/modules/info.py +50 -7
  29. tinybird/tb/modules/job_common.py +15 -0
  30. tinybird/tb/modules/local.py +91 -21
  31. tinybird/tb/modules/local_common.py +320 -13
  32. tinybird/tb/modules/local_logs.py +209 -0
  33. tinybird/tb/modules/login.py +3 -2
  34. tinybird/tb/modules/login_common.py +252 -9
  35. tinybird/tb/modules/open.py +10 -5
  36. tinybird/tb/modules/project.py +14 -5
  37. tinybird/tb/modules/shell.py +14 -6
  38. tinybird/tb/modules/sink.py +3 -1
  39. tinybird/tb/modules/telemetry.py +7 -3
  40. tinybird/tb_cli_modules/telemetry.py +1 -1
  41. {tinybird-0.0.1.dev306.dist-info → tinybird-1.0.5.dist-info}/METADATA +29 -4
  42. {tinybird-0.0.1.dev306.dist-info → tinybird-1.0.5.dist-info}/RECORD +45 -41
  43. {tinybird-0.0.1.dev306.dist-info → tinybird-1.0.5.dist-info}/WHEEL +1 -1
  44. {tinybird-0.0.1.dev306.dist-info → tinybird-1.0.5.dist-info}/entry_points.txt +0 -0
  45. {tinybird-0.0.1.dev306.dist-info → tinybird-1.0.5.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,382 @@
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 re
7
+ from datetime import datetime
8
+ from typing import Any, Dict, Optional
9
+
10
+ import click
11
+ from click import Context
12
+ from confluent_kafka.admin import AdminClient
13
+
14
+ from tinybird.tb.client import TinyB
15
+ from tinybird.tb.modules.common import (
16
+ echo_safe_humanfriendly_tables_format_smart_table,
17
+ get_kafka_connection_name,
18
+ validate_kafka_bootstrap_servers,
19
+ )
20
+ from tinybird.tb.modules.create import generate_kafka_connection_with_secrets
21
+ from tinybird.tb.modules.exceptions import CLIConnectionException, CLIException
22
+ from tinybird.tb.modules.feedback_manager import FeedbackManager
23
+ from tinybird.tb.modules.project import Project
24
+ from tinybird.tb.modules.secret import save_secret_to_env_file
25
+ from tinybird.tb.modules.telemetry import add_telemetry_event
26
+
27
+
28
+ def connection_create_kafka(
29
+ ctx: Context,
30
+ connection_name: Optional[str] = None,
31
+ bootstrap_servers: Optional[str] = None,
32
+ key: Optional[str] = None,
33
+ secret: Optional[str] = None,
34
+ auto_offset_reset: Optional[str] = None,
35
+ schema_registry_url: Optional[str] = None,
36
+ sasl_mechanism: Optional[str] = None,
37
+ security_protocol: Optional[str] = None,
38
+ ssl_ca_pem: Optional[str] = None,
39
+ ) -> dict[str, Any]:
40
+ obj: Dict[str, Any] = ctx.ensure_object(dict)
41
+ click.echo(FeedbackManager.gray(message="\n» Creating Kafka connection..."))
42
+ project: Project = ctx.ensure_object(dict)["project"]
43
+ name = get_kafka_connection_name(project.folder, connection_name)
44
+ error: Optional[str] = None
45
+ ssl_ca_pem_required = False
46
+
47
+ if not bootstrap_servers:
48
+ default_bootstrap_servers = "localhost:9092"
49
+ bootstrap_servers = click.prompt(
50
+ FeedbackManager.highlight(
51
+ message=f"? Bootstrap servers (comma-separated list of host:port pairs) [{default_bootstrap_servers}]"
52
+ ),
53
+ default=default_bootstrap_servers,
54
+ show_default=False,
55
+ )
56
+
57
+ assert isinstance(bootstrap_servers, str)
58
+
59
+ servers_with_ssl_ca_pem_required = "aivencloud.com"
60
+
61
+ ssl_ca_pem_required = any(server in bootstrap_servers for server in servers_with_ssl_ca_pem_required)
62
+
63
+ try:
64
+ validate_kafka_bootstrap_servers(bootstrap_servers)
65
+ click.echo(FeedbackManager.success(message="✓ Server is valid"))
66
+ except CLIException as e:
67
+ error = str(e)
68
+ click.echo(FeedbackManager.error(message=error))
69
+ click.echo(FeedbackManager.warning(message="Process will continue, but the connection might not be valid."))
70
+ add_telemetry_event("connection_error", error=error)
71
+
72
+ secret_required = click.confirm(
73
+ FeedbackManager.info(message=" ? Do you want to store the bootstrap server in a .env.local file? [Y/n]"),
74
+ default=True,
75
+ show_default=False,
76
+ )
77
+ tb_secret_bootstrap_servers: Optional[str] = None
78
+ tb_secret_key: Optional[str] = None
79
+ tb_secret_secret: Optional[str] = None
80
+ tb_secret_ssl_ca_pem: Optional[str] = None
81
+
82
+ if secret_required:
83
+ tb_secret_bootstrap_servers = str(click.prompt(FeedbackManager.info(message=" ? Secret name")))
84
+ try:
85
+ save_secret_to_env_file(project=project, name=tb_secret_bootstrap_servers, value=bootstrap_servers)
86
+ except Exception as e:
87
+ raise CLIConnectionException(FeedbackManager.error(message=str(e)))
88
+
89
+ key = click.prompt(FeedbackManager.highlight(message="? Kafka key"))
90
+
91
+ assert isinstance(key, str)
92
+
93
+ secret_required = click.confirm(
94
+ FeedbackManager.info(message=" ? Do you want to store the Kafka key in a .env.local file? [Y/n]"),
95
+ default=True,
96
+ show_default=False,
97
+ )
98
+
99
+ if secret_required:
100
+ tb_secret_key = str(click.prompt(FeedbackManager.info(message=" ? Secret name")))
101
+ try:
102
+ save_secret_to_env_file(project=project, name=tb_secret_key, value=key)
103
+ except Exception as e:
104
+ raise CLIConnectionException(FeedbackManager.error(message=str(e)))
105
+
106
+ secret = secret or click.prompt(FeedbackManager.highlight(message="? Kafka secret"), hide_input=True)
107
+
108
+ assert isinstance(secret, str)
109
+
110
+ secret_required = click.confirm(
111
+ FeedbackManager.info(message=" ? Do you want to store the Kafka secret in a .env.local file? [Y/n]"),
112
+ default=True,
113
+ show_default=False,
114
+ )
115
+
116
+ if secret_required:
117
+ tb_secret_secret = str(click.prompt(FeedbackManager.info(message=" ? Secret name")))
118
+ try:
119
+ save_secret_to_env_file(project=project, name=tb_secret_secret, value=secret)
120
+ except Exception as e:
121
+ raise CLIConnectionException(FeedbackManager.error(message=str(e)))
122
+
123
+ security_protocol_options = ["SASL_SSL", "PLAINTEXT"]
124
+ security_protocol = security_protocol or click.prompt(
125
+ FeedbackManager.highlight(message="? Security Protocol (SASL_SSL, PLAINTEXT) [SASL_SSL]"),
126
+ type=click.Choice(security_protocol_options),
127
+ show_default=False,
128
+ show_choices=False,
129
+ default="SASL_SSL",
130
+ )
131
+
132
+ if security_protocol not in security_protocol_options:
133
+ raise CLIConnectionException(FeedbackManager.error(message=f"Invalid security protocol: {security_protocol}"))
134
+
135
+ sasl_mechanism_options = ["PLAIN", "SCRAM-SHA-256", "SCRAM-SHA-512"]
136
+ sasl_mechanism = sasl_mechanism or click.prompt(
137
+ FeedbackManager.highlight(message="? SASL Mechanism (PLAIN, SCRAM-SHA-256, SCRAM-SHA-512) [PLAIN]"),
138
+ type=click.Choice(sasl_mechanism_options),
139
+ show_default=False,
140
+ show_choices=False,
141
+ default="PLAIN",
142
+ )
143
+
144
+ if sasl_mechanism not in sasl_mechanism_options:
145
+ raise CLIConnectionException(FeedbackManager.error(message=f"Invalid SASL mechanism: {sasl_mechanism}"))
146
+
147
+ if not schema_registry_url:
148
+ schema_registry_url = click.prompt(
149
+ FeedbackManager.highlight(message="? Schema Registry URL (optional)"),
150
+ default="",
151
+ show_default=False,
152
+ )
153
+
154
+ if ssl_ca_pem_required and not ssl_ca_pem:
155
+ yes = click.confirm(
156
+ FeedbackManager.highlight(
157
+ message="? CA certificate in PEM format (optional)", default=True, show_default=False
158
+ )
159
+ )
160
+ if yes:
161
+ ssl_ca_pem = click.edit(
162
+ "IMPORTANT: THIS LINE MUST BE DELETED. Enter your CA certificate value.", extension=".txt"
163
+ )
164
+ secret_required = click.confirm(
165
+ FeedbackManager.info(message=" ? Do you want to store the Kafka key in a .env.local file? [Y/n]"),
166
+ default=True,
167
+ show_default=False,
168
+ )
169
+ if secret_required and ssl_ca_pem:
170
+ tb_secret_ssl_ca_pem = str(click.prompt(FeedbackManager.info(message=" ? Secret name")))
171
+ try:
172
+ save_secret_to_env_file(project=project, name=tb_secret_ssl_ca_pem, value=ssl_ca_pem)
173
+ except Exception as e:
174
+ raise CLIConnectionException(FeedbackManager.error(message=str(e)))
175
+
176
+ create_in_cloud = (
177
+ click.confirm(
178
+ FeedbackManager.highlight(
179
+ message="? Would you like to create this connection in the cloud environment as well? [Y/n]"
180
+ ),
181
+ default=True,
182
+ show_default=False,
183
+ )
184
+ if obj["env"] == "local"
185
+ and (tb_secret_bootstrap_servers or tb_secret_key or tb_secret_secret or tb_secret_ssl_ca_pem)
186
+ else False
187
+ )
188
+
189
+ if create_in_cloud:
190
+ click.echo(FeedbackManager.gray(message="» Creating Secrets in cloud environment..."))
191
+ prod_config = obj["config"]
192
+ host = prod_config["host"]
193
+ token = prod_config["token"]
194
+ prod_client = TinyB(
195
+ token=token,
196
+ host=host,
197
+ staging=False,
198
+ )
199
+ if tb_secret_bootstrap_servers:
200
+ prod_client.create_secret(name=tb_secret_bootstrap_servers, value=bootstrap_servers)
201
+ if tb_secret_key:
202
+ prod_client.create_secret(name=tb_secret_key, value=key)
203
+ if tb_secret_secret:
204
+ prod_client.create_secret(name=tb_secret_secret, value=secret)
205
+ if tb_secret_ssl_ca_pem and ssl_ca_pem:
206
+ prod_client.create_secret(name=tb_secret_ssl_ca_pem, value=ssl_ca_pem)
207
+ click.echo(FeedbackManager.success(message="✓ Secrets created!"))
208
+
209
+ click.echo(FeedbackManager.gray(message="» Validating connection..."))
210
+
211
+ topics: list[str] = []
212
+ try:
213
+ topics = list_kafka_topics(bootstrap_servers, key, secret, security_protocol, sasl_mechanism, ssl_ca_pem)
214
+ click.echo(FeedbackManager.success(message="✓ Connection is valid"))
215
+ except Exception as e:
216
+ error = str(e)
217
+ click.echo(FeedbackManager.error(message=f"Connection is not valid: {e}"))
218
+ add_telemetry_event("connection_error", error=error)
219
+
220
+ generate_kafka_connection_with_secrets(
221
+ name=name,
222
+ bootstrap_servers=bootstrap_servers,
223
+ tb_secret_bootstrap_servers=tb_secret_bootstrap_servers,
224
+ key=key,
225
+ tb_secret_key=tb_secret_key,
226
+ secret=secret,
227
+ tb_secret_secret=tb_secret_secret,
228
+ security_protocol=security_protocol,
229
+ sasl_mechanism=sasl_mechanism,
230
+ ssl_ca_pem=ssl_ca_pem,
231
+ tb_secret_ssl_ca_pem=tb_secret_ssl_ca_pem,
232
+ schema_registry_url=schema_registry_url,
233
+ folder=project.folder,
234
+ )
235
+ click.echo(FeedbackManager.info_file_created(file=f"connections/{name}.connection"))
236
+ if error:
237
+ click.echo(
238
+ FeedbackManager.warning(
239
+ message="Connection created, but some credentials are missing or invalid. Check https://www.tinybird.co/docs/forward/get-data-in/connectors/kafka#kafka-connection-settings for more details."
240
+ )
241
+ )
242
+ else:
243
+ click.echo(FeedbackManager.success(message="✓ Connection created!"))
244
+
245
+ return {
246
+ "name": name,
247
+ "bootstrap_servers": bootstrap_servers,
248
+ "key": key,
249
+ "secret": secret,
250
+ "sasl_mechanism": sasl_mechanism,
251
+ "security_protocol": security_protocol,
252
+ "topics": topics,
253
+ "error": error,
254
+ }
255
+
256
+
257
+ def list_kafka_topics(
258
+ bootstrap_servers, sasl_username, sasl_password, security_protocol, sasl_mechanism, ssl_ca_pem
259
+ ) -> list[str]:
260
+ conf = {
261
+ "bootstrap.servers": bootstrap_servers,
262
+ "security.protocol": security_protocol,
263
+ "sasl.mechanism": sasl_mechanism,
264
+ "sasl.username": sasl_username,
265
+ "sasl.password": sasl_password,
266
+ "log_level": 0,
267
+ }
268
+
269
+ if ssl_ca_pem:
270
+ conf["ssl.ca.pem"] = re.sub(r"\\n", r"\n", ssl_ca_pem)
271
+
272
+ client = AdminClient(conf)
273
+ metadata = client.list_topics(timeout=5)
274
+ return list(metadata.topics.keys())
275
+
276
+
277
+ def generate_kafka_group_id(topic: str):
278
+ return f"{topic}_{int(datetime.timestamp(datetime.now()))}"
279
+
280
+
281
+ def select_topic(kafka_topic: Optional[str], connection_id: str, client: TinyB) -> str:
282
+ if kafka_topic:
283
+ topics = client.kafka_list_topics(connection_id)
284
+ if kafka_topic not in topics:
285
+ raise CLIConnectionException(
286
+ FeedbackManager.error(message=f"Topic '{kafka_topic}' not found. Topics available: {', '.join(topics)}")
287
+ )
288
+ topic = kafka_topic
289
+ else:
290
+ topics = client.kafka_list_topics(connection_id)
291
+ click.echo(FeedbackManager.highlight(message="? Select a Kafka topic:"))
292
+ topic_index = -1
293
+ while topic_index == -1:
294
+ for index, topic in enumerate(topics):
295
+ click.echo(f" [{index + 1}] {topic}")
296
+ topic_index = click.prompt("\nSelect topic", default=1)
297
+ try:
298
+ topic = topics[int(topic_index) - 1]
299
+ except Exception:
300
+ topic_index = -1
301
+
302
+ if not topic:
303
+ raise CLIConnectionException(FeedbackManager.error(message="Topic is required."))
304
+
305
+ return topic
306
+
307
+
308
+ def select_group_id(kafka_group_id: Optional[str], topic: str, connection_id: str, client: TinyB) -> str:
309
+ group_id = kafka_group_id
310
+ is_valid = False
311
+ while not is_valid:
312
+ if not group_id:
313
+ default_group_id = generate_kafka_group_id(topic)
314
+ else:
315
+ default_group_id = group_id
316
+
317
+ group_id = click.prompt(
318
+ FeedbackManager.highlight(message="? Enter a Kafka group ID"),
319
+ default=default_group_id,
320
+ show_default=True,
321
+ )
322
+
323
+ assert isinstance(group_id, str)
324
+
325
+ click.echo(FeedbackManager.gray(message=f"» Validating group ID '{group_id}'..."))
326
+ try:
327
+ client.kafka_preview_group(connection_id, topic, group_id)
328
+ is_valid = True
329
+ click.echo(FeedbackManager.success(message=f"✓ Group ID '{group_id}' is valid."))
330
+ except Exception as e:
331
+ click.echo(FeedbackManager.error(message=str(e)))
332
+ group_id = None # Reset to prompt again
333
+
334
+ if not group_id:
335
+ raise CLIConnectionException(FeedbackManager.error(message="Group ID is required."))
336
+
337
+ return group_id
338
+
339
+
340
+ def echo_kafka_data(connection_id: str, connection_name: str, topic: str, group_id: str, client: TinyB) -> None:
341
+ click.echo(
342
+ FeedbackManager.highlight(
343
+ message=f"» Previewing data for connection '{connection_name}' with topic '{topic}' and group ID '{group_id}'..."
344
+ )
345
+ )
346
+ response = client.kafka_preview_topic(connection_id, topic, group_id)
347
+ preview = response.get("preview", {})
348
+ data = preview.get("data", [])
349
+ column_names = [col["name"] for col in preview.get("meta", [])]
350
+
351
+ # Convert each row dictionary to a list of values ordered by column names
352
+ data_as_lists = []
353
+ for row in data:
354
+ if isinstance(row, dict):
355
+ # Convert dict to list of values in column order
356
+ row_values = [row.get(col_name, "") for col_name in column_names]
357
+ data_as_lists.append(row_values)
358
+ else:
359
+ # If it's already a list, keep it as is
360
+ data_as_lists.append(row)
361
+
362
+ if not data_as_lists and not column_names:
363
+ click.echo(FeedbackManager.warning(message="No data to preview."))
364
+ return
365
+
366
+ echo_safe_humanfriendly_tables_format_smart_table(data_as_lists, column_names)
367
+
368
+
369
+ def select_connection(
370
+ connection_id: Optional[str], connection_type: str, connections: list[dict[str, Any]], client: TinyB
371
+ ) -> dict[str, Any]:
372
+ click.echo(FeedbackManager.highlight(message=f"? Select a {connection_type.capitalize()} connection:"))
373
+ connection_index = -1
374
+ while connection_index == -1:
375
+ for index, conn in enumerate(connections):
376
+ click.echo(f" [{index + 1}] {conn['name']}")
377
+ connection_index = click.prompt("\nSelect connection", default=1)
378
+ try:
379
+ connection = connections[int(connection_index) - 1]
380
+ except Exception:
381
+ connection_index = -1
382
+ return connection
@@ -16,6 +16,7 @@ from tinybird.tb.modules.cli import cli
16
16
  from tinybird.tb.modules.common import echo_safe_humanfriendly_tables_format_smart_table, wait_job
17
17
  from tinybird.tb.modules.exceptions import CLIPipeException
18
18
  from tinybird.tb.modules.feedback_manager import FeedbackManager
19
+ from tinybird.tb.modules.job_common import echo_job_url
19
20
 
20
21
 
21
22
  @cli.group()
@@ -89,15 +90,16 @@ def copy_run(ctx: click.Context, pipe_name_or_id: str, wait: bool, mode: str, pa
89
90
  params = dict(key_value.split("=") for key_value in param) if param else {}
90
91
  click.echo(FeedbackManager.highlight(message=f"\n» Running on-demand copy '{pipe_name_or_id}'"))
91
92
  client: TinyB = ctx.ensure_object(dict)["client"]
93
+ config = ctx.ensure_object(dict)["config"]
92
94
 
93
95
  try:
94
96
  response = client.pipe_run(pipe_name_or_id, "copy", params, mode)
95
97
  job_id = response["job"]["job_id"]
96
98
  job_url = response["job"]["job_url"]
99
+ echo_job_url(client.token, client.host, config.get("name") or "", job_url)
97
100
  target_datasource_id = response["tags"]["copy_target_datasource"]
98
101
  target_datasource = client.get_datasource(target_datasource_id)
99
102
  target_datasource_name = target_datasource["name"]
100
- click.echo(FeedbackManager.gray(message="Job URL: ") + FeedbackManager.info(message=f"{job_url}"))
101
103
  click.echo(FeedbackManager.success(message=f"✓ Copy to '{target_datasource_name}' job created"))
102
104
 
103
105
  if wait:
@@ -524,6 +524,9 @@ def generate_kafka_connection_with_secrets(
524
524
  tb_secret_secret: Optional[str],
525
525
  security_protocol: str,
526
526
  sasl_mechanism: str,
527
+ ssl_ca_pem: Optional[str],
528
+ tb_secret_ssl_ca_pem: Optional[str],
529
+ schema_registry_url: Optional[str],
527
530
  folder: str,
528
531
  ) -> Path:
529
532
  kafka_bootstrap_servers = (
@@ -531,6 +534,7 @@ def generate_kafka_connection_with_secrets(
531
534
  )
532
535
  kafka_key = inject_tb_secret(tb_secret_key) if tb_secret_key else key
533
536
  kafka_secret = inject_tb_secret(tb_secret_secret) if tb_secret_secret else secret
537
+ kafka_ssl_ca_pem = inject_tb_secret(tb_secret_ssl_ca_pem) if tb_secret_ssl_ca_pem else ssl_ca_pem
534
538
  content = f"""TYPE kafka
535
539
  KAFKA_BOOTSTRAP_SERVERS {kafka_bootstrap_servers}
536
540
  KAFKA_SECURITY_PROTOCOL {security_protocol or "SASL_SSL"}
@@ -538,6 +542,13 @@ KAFKA_SASL_MECHANISM {sasl_mechanism or "PLAIN"}
538
542
  KAFKA_KEY {kafka_key}
539
543
  KAFKA_SECRET {kafka_secret}
540
544
  """
545
+ if schema_registry_url:
546
+ content += f"""KAFKA_SCHEMA_REGISTRY_URL {schema_registry_url}\n"""
547
+ if kafka_ssl_ca_pem:
548
+ content += f"""KAFKA_SSL_CA_PEM >\n {kafka_ssl_ca_pem}\n"""
549
+ content += """# Learn more at https://www.tinybird.co/docs/forward/get-data-in/connectors/kafka#kafka-connection-settings
550
+ """
551
+
541
552
  return generate_connection_file(name, content, folder, skip_feedback=True)
542
553
 
543
554
 
@@ -1048,7 +1048,7 @@ def process_file(
1048
1048
  #
1049
1049
  # Note: any unknown import_ parameter is leaved as is.
1050
1050
  for key in ImportReplacements.get_datafile_parameter_keys():
1051
- replacement, default_value = ImportReplacements.get_api_param_for_datafile_param(service, key)
1051
+ replacement, default_value = ImportReplacements.get_api_param_for_datafile_param(key)
1052
1052
  if not replacement:
1053
1053
  continue # We should not reach this never, but just in case...
1054
1054
 
@@ -25,11 +25,25 @@ from tinybird.tb.modules.datafile.format_datasource import format_engine
25
25
 
26
26
 
27
27
  def format_node_sql(
28
- file_parts: List[str], node: Dict[str, Any], line_length: Optional[int] = None, lower_keywords: bool = False
28
+ file_parts: List[str],
29
+ node: Dict[str, Any],
30
+ line_length: Optional[int] = None,
31
+ lower_keywords: bool = False,
32
+ resource_name: Optional[str] = None,
33
+ resource_source: Optional[str] = None,
29
34
  ) -> List[str]:
30
35
  file_parts.append("SQL >")
31
36
  file_parts.append(DATAFILE_NEW_LINE)
32
- file_parts.append(format_sql(node["sql"], DATAFILE_INDENT, line_length=line_length, lower_keywords=lower_keywords))
37
+ file_parts.append(
38
+ format_sql(
39
+ node["sql"],
40
+ DATAFILE_INDENT,
41
+ line_length=line_length,
42
+ lower_keywords=lower_keywords,
43
+ resource_name=resource_name,
44
+ resource_source=resource_source,
45
+ )
46
+ )
33
47
  file_parts.append(DATAFILE_NEW_LINE)
34
48
  file_parts.append(DATAFILE_NEW_LINE)
35
49
  return file_parts
@@ -93,8 +107,21 @@ def format_pipe_include(file_parts: List[str], node: Dict[str, Any], includes: D
93
107
  return file_parts
94
108
 
95
109
 
96
- def format_sql(sql: str, DATAFILE_INDENT: str, line_length: Optional[int] = None, lower_keywords: bool = False) -> str:
97
- sql = format_sql_template(sql.strip(), line_length=line_length, lower_keywords=lower_keywords)
110
+ def format_sql(
111
+ sql: str,
112
+ DATAFILE_INDENT: str,
113
+ line_length: Optional[int] = None,
114
+ lower_keywords: bool = False,
115
+ resource_name: Optional[str] = None,
116
+ resource_source: Optional[str] = None,
117
+ ) -> str:
118
+ sql = format_sql_template(
119
+ sql.strip(),
120
+ line_length=line_length,
121
+ lower_keywords=lower_keywords,
122
+ resource_name=resource_name,
123
+ resource_source=resource_source,
124
+ )
98
125
  return "\n".join([f"{DATAFILE_INDENT}{part}" for part in sql.split("\n") if len(part.strip())])
99
126
 
100
127
 
@@ -105,6 +132,8 @@ def format_node(
105
132
  line_length: Optional[int] = None,
106
133
  unroll_includes: bool = False,
107
134
  lower_keywords: bool = False,
135
+ resource_name: Optional[str] = None,
136
+ resource_source: Optional[str] = None,
108
137
  ) -> None:
109
138
  if not unroll_includes:
110
139
  format_pipe_include(file_parts, node, includes)
@@ -119,7 +148,14 @@ def format_node(
119
148
 
120
149
  Doc = namedtuple("Doc", ["description"])
121
150
  format_description(file_parts, Doc(node.get("description", "")))
122
- format_node_sql(file_parts, node, line_length=line_length, lower_keywords=lower_keywords)
151
+ format_node_sql(
152
+ file_parts,
153
+ node,
154
+ line_length=line_length,
155
+ lower_keywords=lower_keywords,
156
+ resource_name=resource_name,
157
+ resource_source=resource_source,
158
+ )
123
159
  format_node_type(file_parts, node)
124
160
 
125
161
 
@@ -132,6 +168,7 @@ def format_pipe(
132
168
  for_deploy_diff: bool = False,
133
169
  skip_eval: bool = False,
134
170
  content: Optional[str] = None,
171
+ resource_source: Optional[str] = None,
135
172
  ) -> str:
136
173
  if datafile:
137
174
  doc = datafile
@@ -178,6 +215,8 @@ def format_pipe(
178
215
  line_length=line_length,
179
216
  unroll_includes=unroll_includes,
180
217
  lower_keywords=bool(for_deploy_diff),
218
+ resource_name=filename,
219
+ resource_source=resource_source,
181
220
  )
182
221
 
183
222
  if not unroll_includes:
@@ -1200,7 +1200,7 @@ def process_file(
1200
1200
  #
1201
1201
  # Note: any unknown import_ parameter is leaved as is.
1202
1202
  for key in ImportReplacements.get_datafile_parameter_keys():
1203
- replacement, default_value = ImportReplacements.get_api_param_for_datafile_param(service, key)
1203
+ replacement, default_value = ImportReplacements.get_api_param_for_datafile_param(key)
1204
1204
  if not replacement:
1205
1205
  continue # We should not reach this never, but just in case...
1206
1206