tinybird 0.0.1.dev190__py3-none-any.whl → 0.0.1.dev192__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.
Potentially problematic release.
This version of tinybird might be problematic. Click here for more details.
- tinybird/prompts.py +19 -0
- tinybird/tb/__cli__.py +2 -2
- tinybird/tb/modules/build.py +1 -2
- tinybird/tb/modules/cli.py +12 -17
- tinybird/tb/modules/common.py +6 -2
- tinybird/tb/modules/datasource.py +57 -16
- tinybird/tb/modules/deployment.py +30 -17
- tinybird/tb/modules/feedback_manager.py +3 -0
- tinybird/tb/modules/info.py +8 -3
- tinybird/tb/modules/project.py +12 -3
- {tinybird-0.0.1.dev190.dist-info → tinybird-0.0.1.dev192.dist-info}/METADATA +1 -1
- {tinybird-0.0.1.dev190.dist-info → tinybird-0.0.1.dev192.dist-info}/RECORD +15 -15
- {tinybird-0.0.1.dev190.dist-info → tinybird-0.0.1.dev192.dist-info}/WHEEL +0 -0
- {tinybird-0.0.1.dev190.dist-info → tinybird-0.0.1.dev192.dist-info}/entry_points.txt +0 -0
- {tinybird-0.0.1.dev190.dist-info → tinybird-0.0.1.dev192.dist-info}/top_level.txt +0 -0
tinybird/prompts.py
CHANGED
|
@@ -1010,3 +1010,22 @@ Current README.md file:
|
|
|
1010
1010
|
<readme>[readme content here]</readme>
|
|
1011
1011
|
</readme_instructions>
|
|
1012
1012
|
"""
|
|
1013
|
+
|
|
1014
|
+
|
|
1015
|
+
def quarantine_prompt(datasource_definition: str) -> str:
|
|
1016
|
+
return f"""
|
|
1017
|
+
- You are an expert in Tinybird.
|
|
1018
|
+
- You are given a list of rows that went to quarantine during ingestion because of data quality issues.
|
|
1019
|
+
- Return the errors in a human readable format so the user can understand what is the problem and fix it.
|
|
1020
|
+
- Be concise and to the point.
|
|
1021
|
+
- Do not mention clickhouse tables. Refer to data sources instead.
|
|
1022
|
+
- The possible fixes recommended fixes are:
|
|
1023
|
+
- Changing a column type in the datasource definition: tell the user what to change in the datasource file, then build again before appending the data.
|
|
1024
|
+
- Changing the data because of the wrong data type: tell the user what to change in the data file, then append the data again.
|
|
1025
|
+
- The format of the response always inside the tag <quarantine_errors>[response]</quarantine_errors>
|
|
1026
|
+
- Do not use markdown format in the response, because it is a CLI output.
|
|
1027
|
+
- The datasource definition is the following:
|
|
1028
|
+
<datasource_definition>
|
|
1029
|
+
{datasource_definition}
|
|
1030
|
+
</datasource_definition>
|
|
1031
|
+
"""
|
tinybird/tb/__cli__.py
CHANGED
|
@@ -4,5 +4,5 @@ __description__ = 'Tinybird Command Line Tool'
|
|
|
4
4
|
__url__ = 'https://www.tinybird.co/docs/forward/commands'
|
|
5
5
|
__author__ = 'Tinybird'
|
|
6
6
|
__author_email__ = 'support@tinybird.co'
|
|
7
|
-
__version__ = '0.0.1.
|
|
8
|
-
__revision__ = '
|
|
7
|
+
__version__ = '0.0.1.dev192'
|
|
8
|
+
__revision__ = '116c2dc'
|
tinybird/tb/modules/build.py
CHANGED
|
@@ -258,7 +258,6 @@ def show_data(tb_client: TinyB, filename: str, diff: Optional[str] = None):
|
|
|
258
258
|
table_name = diff
|
|
259
259
|
resource_path = Path(filename)
|
|
260
260
|
resource_name = resource_path.stem
|
|
261
|
-
resource_content = resource_path.read_text()
|
|
262
261
|
|
|
263
262
|
pipeline = resource_name if filename.endswith(".pipe") else None
|
|
264
263
|
|
|
@@ -269,7 +268,7 @@ def show_data(tb_client: TinyB, filename: str, diff: Optional[str] = None):
|
|
|
269
268
|
|
|
270
269
|
res = asyncio.run(tb_client.query(sql, pipeline=pipeline))
|
|
271
270
|
print_table_formatted(res, table_name)
|
|
272
|
-
if Project.
|
|
271
|
+
if Project.get_pipe_type(filename) == "endpoint":
|
|
273
272
|
example_params = {
|
|
274
273
|
"format": "json",
|
|
275
274
|
"pipe": resource_name,
|
tinybird/tb/modules/cli.py
CHANGED
|
@@ -28,7 +28,9 @@ from tinybird.tb.modules.common import (
|
|
|
28
28
|
CLIException,
|
|
29
29
|
_get_tb_client,
|
|
30
30
|
coro,
|
|
31
|
+
echo_json,
|
|
31
32
|
echo_safe_format_table,
|
|
33
|
+
force_echo,
|
|
32
34
|
getenv_bool,
|
|
33
35
|
try_update_config_with_remote,
|
|
34
36
|
)
|
|
@@ -72,7 +74,7 @@ VERSION = f"{__cli__.__version__} (rev {__cli__.__revision__})"
|
|
|
72
74
|
@click.option("--cloud/--local", is_flag=True, default=False, help="Run against cloud or local.")
|
|
73
75
|
@click.option("--staging", is_flag=True, default=False, help="Run against a staging deployment.")
|
|
74
76
|
@click.option(
|
|
75
|
-
"--output", type=click.Choice(["json", "
|
|
77
|
+
"--output", type=click.Choice(["human", "json", "csv"], case_sensitive=False), default="human", help="Output format"
|
|
76
78
|
)
|
|
77
79
|
@click.option("--max-depth", type=int, default=3, help="Maximum depth of the project files.")
|
|
78
80
|
@click.version_option(version=VERSION)
|
|
@@ -95,7 +97,7 @@ async def cli(
|
|
|
95
97
|
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.
|
|
96
98
|
"""
|
|
97
99
|
# We need to unpatch for our tests not to break
|
|
98
|
-
if output
|
|
100
|
+
if output != "human":
|
|
99
101
|
__hide_click_output()
|
|
100
102
|
else:
|
|
101
103
|
if show_tokens or not cloud or ctx.invoked_subcommand == "build":
|
|
@@ -195,17 +197,10 @@ async def pull(ctx: Context, force: bool, fmt: bool) -> None:
|
|
|
195
197
|
|
|
196
198
|
@cli.command()
|
|
197
199
|
@click.argument("query", required=False)
|
|
198
|
-
@click.option("--
|
|
200
|
+
@click.option("--rows-limit", default=100, help="Max number of rows retrieved")
|
|
199
201
|
@click.option("--pipeline", default=None, help="The name of the pipe to run the SQL Query")
|
|
200
202
|
@click.option("--pipe", default=None, help="The path to the .pipe file to run the SQL Query of a specific NODE")
|
|
201
203
|
@click.option("--node", default=None, help="The NODE name")
|
|
202
|
-
@click.option(
|
|
203
|
-
"--format",
|
|
204
|
-
"format_",
|
|
205
|
-
type=click.Choice(["json", "csv", "human"], case_sensitive=False),
|
|
206
|
-
default="human",
|
|
207
|
-
help="Output format",
|
|
208
|
-
)
|
|
209
204
|
@click.option("--stats/--no-stats", default=False, help="Show query stats")
|
|
210
205
|
@click.pass_context
|
|
211
206
|
@coro
|
|
@@ -216,13 +211,13 @@ async def sql(
|
|
|
216
211
|
pipeline: Optional[str],
|
|
217
212
|
pipe: Optional[str],
|
|
218
213
|
node: Optional[str],
|
|
219
|
-
format_: str,
|
|
220
214
|
stats: bool,
|
|
221
215
|
) -> None:
|
|
222
216
|
"""Run SQL query over data sources and pipes."""
|
|
223
|
-
|
|
224
217
|
client = ctx.ensure_object(dict)["client"]
|
|
225
|
-
|
|
218
|
+
output = ctx.ensure_object(dict)["output"]
|
|
219
|
+
|
|
220
|
+
req_format = "CSVWithNames" if output == "csv" else "JSON"
|
|
226
221
|
res = None
|
|
227
222
|
try:
|
|
228
223
|
if query:
|
|
@@ -275,11 +270,11 @@ async def sql(
|
|
|
275
270
|
bytes_read = humanfriendly.format_size(stats_dict["bytes_read"])
|
|
276
271
|
click.echo(FeedbackManager.info_query_stats(seconds=seconds, rows=rows_read, bytes=bytes_read))
|
|
277
272
|
|
|
278
|
-
if
|
|
279
|
-
|
|
273
|
+
if output == "csv":
|
|
274
|
+
force_echo(str(res))
|
|
280
275
|
elif isinstance(res, dict) and "data" in res and res["data"]:
|
|
281
|
-
if
|
|
282
|
-
|
|
276
|
+
if output == "json":
|
|
277
|
+
echo_json(res, indent=8)
|
|
283
278
|
else:
|
|
284
279
|
dd = []
|
|
285
280
|
for d in res["data"]:
|
tinybird/tb/modules/common.py
CHANGED
|
@@ -2250,5 +2250,9 @@ def get_error_event(error: str) -> Tuple[str, str]:
|
|
|
2250
2250
|
return error_event, silent_error_msg
|
|
2251
2251
|
|
|
2252
2252
|
|
|
2253
|
-
def
|
|
2254
|
-
click.echo(
|
|
2253
|
+
def force_echo(string: str) -> None:
|
|
2254
|
+
click.echo(string, force_output=True) # type: ignore
|
|
2255
|
+
|
|
2256
|
+
|
|
2257
|
+
def echo_json(data: Dict[str, Any], indent: Union[None, int, str] = None) -> None:
|
|
2258
|
+
force_echo(json.dumps(data, indent=indent))
|
|
@@ -18,6 +18,7 @@ import humanfriendly
|
|
|
18
18
|
import requests
|
|
19
19
|
from click import Context
|
|
20
20
|
|
|
21
|
+
from tinybird.prompts import quarantine_prompt
|
|
21
22
|
from tinybird.syncasync import sync_to_async
|
|
22
23
|
from tinybird.tb.client import AuthNoTokenException, DoesNotExistException, TinyB
|
|
23
24
|
from tinybird.tb.modules.cli import cli
|
|
@@ -42,7 +43,10 @@ from tinybird.tb.modules.datafile.common import get_name_version
|
|
|
42
43
|
from tinybird.tb.modules.datafile.fixture import persist_fixture
|
|
43
44
|
from tinybird.tb.modules.exceptions import CLIDatasourceException
|
|
44
45
|
from tinybird.tb.modules.feedback_manager import FeedbackManager
|
|
46
|
+
from tinybird.tb.modules.llm import LLM
|
|
47
|
+
from tinybird.tb.modules.llm_utils import extract_xml
|
|
45
48
|
from tinybird.tb.modules.project import Project
|
|
49
|
+
from tinybird.tb.modules.telemetry import add_telemetry_event
|
|
46
50
|
|
|
47
51
|
|
|
48
52
|
@cli.group()
|
|
@@ -151,6 +155,7 @@ async def datasource_append(
|
|
|
151
155
|
"""
|
|
152
156
|
env: str = ctx.ensure_object(dict)["env"]
|
|
153
157
|
client: TinyB = ctx.obj["client"]
|
|
158
|
+
project: Project = ctx.ensure_object(dict)["project"]
|
|
154
159
|
|
|
155
160
|
# If data is passed as argument, we detect if it's a JSON object, a URL or a file
|
|
156
161
|
if data:
|
|
@@ -244,15 +249,10 @@ async def datasource_append(
|
|
|
244
249
|
|
|
245
250
|
if events:
|
|
246
251
|
click.echo(FeedbackManager.highlight(message=f"\n» Sending events to {datasource_name}"))
|
|
247
|
-
try:
|
|
248
|
-
json_data = json.loads(events)
|
|
249
|
-
except Exception:
|
|
250
|
-
raise CLIDatasourceException(FeedbackManager.error(message="Invalid events data"))
|
|
251
|
-
|
|
252
252
|
response = await sync_to_async(requests.post)(
|
|
253
253
|
f"{client.host}/v0/events?name={datasource_name}",
|
|
254
254
|
headers={"Authorization": f"Bearer {client.token}"},
|
|
255
|
-
|
|
255
|
+
data=events,
|
|
256
256
|
)
|
|
257
257
|
|
|
258
258
|
try:
|
|
@@ -269,21 +269,32 @@ async def datasource_append(
|
|
|
269
269
|
)
|
|
270
270
|
)
|
|
271
271
|
if quarantined_rows > 0:
|
|
272
|
-
|
|
272
|
+
click.echo(
|
|
273
273
|
FeedbackManager.error(
|
|
274
|
-
message=f"{quarantined_rows} row{'' if quarantined_rows == 1 else 's'} went to quarantine"
|
|
274
|
+
message=f"✗ {quarantined_rows} row{'' if quarantined_rows == 1 else 's'} went to quarantine"
|
|
275
275
|
)
|
|
276
276
|
)
|
|
277
|
+
await analyze_quarantine(datasource_name, project, client)
|
|
278
|
+
return
|
|
277
279
|
else:
|
|
278
280
|
click.echo(FeedbackManager.highlight(message=f"\n» Appending data to {datasource_name}"))
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
281
|
+
try:
|
|
282
|
+
await push_data(
|
|
283
|
+
client,
|
|
284
|
+
datasource_name,
|
|
285
|
+
data,
|
|
286
|
+
mode="append",
|
|
287
|
+
concurrency=concurrency,
|
|
288
|
+
silent=True,
|
|
289
|
+
)
|
|
290
|
+
except Exception as e:
|
|
291
|
+
is_quarantined = "quarantine" in str(e)
|
|
292
|
+
click.echo(FeedbackManager.error(message="✗ " + str(e)))
|
|
293
|
+
if is_quarantined:
|
|
294
|
+
await analyze_quarantine(datasource_name, project, client)
|
|
295
|
+
return
|
|
296
|
+
else:
|
|
297
|
+
raise e
|
|
287
298
|
click.echo(FeedbackManager.success(message="✓ Rows appended!"))
|
|
288
299
|
|
|
289
300
|
|
|
@@ -902,3 +913,33 @@ def generate_short_id():
|
|
|
902
913
|
|
|
903
914
|
def generate_kafka_group_id(topic: str):
|
|
904
915
|
return f"{topic}_{int(datetime.timestamp(datetime.now()))}"
|
|
916
|
+
|
|
917
|
+
|
|
918
|
+
async def analyze_quarantine(datasource_name: str, project: Project, client: TinyB):
|
|
919
|
+
config = CLIConfig.get_project_config()
|
|
920
|
+
res = await client.query(
|
|
921
|
+
f"SELECT * FROM {datasource_name}_quarantine ORDER BY insertion_date DESC LIMIT 1 FORMAT JSON"
|
|
922
|
+
)
|
|
923
|
+
quarantine_data = res["data"]
|
|
924
|
+
error_message = json.dumps(res["data"])
|
|
925
|
+
user_token = config.get_user_token()
|
|
926
|
+
click.echo(FeedbackManager.gray(message=f"\n» Analyzing errors in {datasource_name}_quarantine..."))
|
|
927
|
+
if user_token:
|
|
928
|
+
llm = LLM(user_token=user_token, host=config.get_client().host)
|
|
929
|
+
ds_filenames = project.get_datasource_files()
|
|
930
|
+
datasource_definition = next(
|
|
931
|
+
(Path(f).read_text() for f in ds_filenames if f.endswith(f"{datasource_name}.datasource")), ""
|
|
932
|
+
)
|
|
933
|
+
response_llm = llm.ask(
|
|
934
|
+
system_prompt=quarantine_prompt(datasource_definition),
|
|
935
|
+
prompt=f"The quarantine errors are:\n{json.dumps(quarantine_data)}",
|
|
936
|
+
)
|
|
937
|
+
response = extract_xml(response_llm, "quarantine_errors")
|
|
938
|
+
error_message += "\n" + response
|
|
939
|
+
click.echo(response)
|
|
940
|
+
else:
|
|
941
|
+
echo_safe_humanfriendly_tables_format_smart_table(
|
|
942
|
+
data=[d.values() for d in res["data"]], column_names=res["data"][0].keys()
|
|
943
|
+
)
|
|
944
|
+
|
|
945
|
+
add_telemetry_event("datasource_error", error=f"quarantine_error: {error_message}")
|
|
@@ -15,7 +15,7 @@ from tinybird.tb.modules.common import (
|
|
|
15
15
|
get_display_cloud_host,
|
|
16
16
|
sys_exit,
|
|
17
17
|
)
|
|
18
|
-
from tinybird.tb.modules.feedback_manager import FeedbackManager
|
|
18
|
+
from tinybird.tb.modules.feedback_manager import FeedbackManager, bcolors
|
|
19
19
|
from tinybird.tb.modules.project import Project
|
|
20
20
|
|
|
21
21
|
|
|
@@ -229,10 +229,17 @@ def promote_deployment(host: Optional[str], headers: dict, wait: bool) -> None:
|
|
|
229
229
|
|
|
230
230
|
last_deployment = result.get("deployment")
|
|
231
231
|
if last_deployment.get("status") == "deleted":
|
|
232
|
-
click.echo(FeedbackManager.success(message="✓ Deployment is live!"))
|
|
232
|
+
click.echo(FeedbackManager.success(message=f"✓ Deployment #{candidate_deployment.get('id')} is live!"))
|
|
233
233
|
break
|
|
234
234
|
|
|
235
235
|
time.sleep(5)
|
|
236
|
+
if last_deployment.get("id") == "0":
|
|
237
|
+
# This is the first deployment, so we prompt the user to ingest data
|
|
238
|
+
click.echo(
|
|
239
|
+
FeedbackManager.info(
|
|
240
|
+
message="A deployment with no data is useless. Learn how to ingest at https://www.tinybird.co/docs/forward/get-data-in"
|
|
241
|
+
)
|
|
242
|
+
)
|
|
236
243
|
|
|
237
244
|
|
|
238
245
|
# TODO(eclbg): This logic should be in the server, and there should be a dedicated endpoint for discarding a
|
|
@@ -590,8 +597,8 @@ def create_deployment(
|
|
|
590
597
|
if status == "success":
|
|
591
598
|
host = get_display_cloud_host(client.host)
|
|
592
599
|
click.echo(
|
|
593
|
-
FeedbackManager.
|
|
594
|
-
+
|
|
600
|
+
FeedbackManager.info(message="Deployment URL: ")
|
|
601
|
+
+ f"{bcolors.UNDERLINE}{host}/{config.get('name')}/deployments/{deployment.get('id')}{bcolors.ENDC}"
|
|
595
602
|
)
|
|
596
603
|
|
|
597
604
|
if wait:
|
|
@@ -655,40 +662,46 @@ def create_deployment(
|
|
|
655
662
|
|
|
656
663
|
def print_changes(result: dict, project: Project) -> None:
|
|
657
664
|
deployment = result.get("deployment", {})
|
|
658
|
-
resources_columns = ["status", "name", "path"]
|
|
665
|
+
resources_columns = ["status", "name", "type", "path"]
|
|
659
666
|
resources: list[list[Union[str, None]]] = []
|
|
660
667
|
tokens_columns = ["Change", "Token name", "Added permissions", "Removed permissions"]
|
|
661
668
|
tokens: list[Tuple[str, str, str, str]] = []
|
|
662
669
|
|
|
663
670
|
for ds in deployment.get("new_datasource_names", []):
|
|
664
|
-
resources.append(["new", ds, project.get_resource_path(ds, "datasource")])
|
|
671
|
+
resources.append(["new", ds, "datasource", project.get_resource_path(ds, "datasource")])
|
|
665
672
|
|
|
666
673
|
for p in deployment.get("new_pipe_names", []):
|
|
667
|
-
|
|
674
|
+
path = str(project.get_resource_path(p, "pipe"))
|
|
675
|
+
pipe_type = project.get_pipe_type(path)
|
|
676
|
+
resources.append(["new", p, pipe_type, path])
|
|
668
677
|
|
|
669
678
|
for dc in deployment.get("new_data_connector_names", []):
|
|
670
|
-
resources.append(["new", dc, project.get_resource_path(dc, "connection")])
|
|
679
|
+
resources.append(["new", dc, "connection", project.get_resource_path(dc, "connection")])
|
|
671
680
|
|
|
672
681
|
for ds in deployment.get("changed_datasource_names", []):
|
|
673
|
-
resources.append(["modified", ds, project.get_resource_path(ds, "datasource")])
|
|
682
|
+
resources.append(["modified", ds, "datasource", project.get_resource_path(ds, "datasource")])
|
|
674
683
|
|
|
675
684
|
for p in deployment.get("changed_pipe_names", []):
|
|
676
|
-
|
|
685
|
+
path = str(project.get_resource_path(p, "pipe"))
|
|
686
|
+
pipe_type = project.get_pipe_type(path)
|
|
687
|
+
resources.append(["modified", p, pipe_type, path])
|
|
677
688
|
|
|
678
689
|
for dc in deployment.get("changed_data_connector_names", []):
|
|
679
|
-
resources.append(["modified", dc, project.get_resource_path(dc, "connection")])
|
|
690
|
+
resources.append(["modified", dc, "connection", project.get_resource_path(dc, "connection")])
|
|
680
691
|
|
|
681
692
|
for ds in deployment.get("disconnected_data_source_names", []):
|
|
682
|
-
resources.append(["modified", ds, project.get_resource_path(ds, "datasource")])
|
|
693
|
+
resources.append(["modified", ds, "datasource", project.get_resource_path(ds, "datasource")])
|
|
683
694
|
|
|
684
695
|
for ds in deployment.get("deleted_datasource_names", []):
|
|
685
|
-
resources.append(["deleted", ds, project.get_resource_path(ds, "datasource")])
|
|
696
|
+
resources.append(["deleted", ds, "datasource", project.get_resource_path(ds, "datasource")])
|
|
686
697
|
|
|
687
698
|
for p in deployment.get("deleted_pipe_names", []):
|
|
688
|
-
|
|
699
|
+
path = str(project.get_resource_path(p, "pipe"))
|
|
700
|
+
pipe_type = project.get_pipe_type(path)
|
|
701
|
+
resources.append(["deleted", p, pipe_type, path])
|
|
689
702
|
|
|
690
703
|
for dc in deployment.get("deleted_data_connector_names", []):
|
|
691
|
-
resources.append(["deleted", dc, project.get_resource_path(dc, "connection")])
|
|
704
|
+
resources.append(["deleted", dc, "connection", project.get_resource_path(dc, "connection")])
|
|
692
705
|
|
|
693
706
|
for token_change in deployment.get("token_changes", []):
|
|
694
707
|
token_name = token_change.get("token_name")
|
|
@@ -707,9 +720,9 @@ def print_changes(result: dict, project: Project) -> None:
|
|
|
707
720
|
click.echo(FeedbackManager.info(message="\n* Changes to be deployed:"))
|
|
708
721
|
echo_safe_humanfriendly_tables_format_smart_table(resources, column_names=resources_columns)
|
|
709
722
|
else:
|
|
710
|
-
click.echo(FeedbackManager.
|
|
723
|
+
click.echo(FeedbackManager.gray(message="\n* No changes to be deployed"))
|
|
711
724
|
if tokens:
|
|
712
725
|
click.echo(FeedbackManager.info(message="\n* Changes in tokens to be deployed:"))
|
|
713
726
|
echo_safe_humanfriendly_tables_format_smart_table(tokens, column_names=tokens_columns)
|
|
714
727
|
else:
|
|
715
|
-
click.echo(FeedbackManager.
|
|
728
|
+
click.echo(FeedbackManager.gray(message="* No changes in tokens to be deployed"))
|
|
@@ -437,6 +437,9 @@ class FeedbackManager:
|
|
|
437
437
|
error_tag_not_found = error_message("Tag {tag_name} not found.")
|
|
438
438
|
error_build_failed = error_message("Build failed")
|
|
439
439
|
error_request_failed = error_message("Request failed with status code {status_code}, please try again later.")
|
|
440
|
+
error_invalid_output_format = error_message(
|
|
441
|
+
"Invalid output format for this command. Supported formats are: {formats}"
|
|
442
|
+
)
|
|
440
443
|
|
|
441
444
|
info_incl_relative_path = info_message("** Relative path {path} does not exist, skipping.")
|
|
442
445
|
info_ignoring_incl_file = info_message(
|
tinybird/tb/modules/info.py
CHANGED
|
@@ -6,7 +6,7 @@ import click
|
|
|
6
6
|
from tinybird.tb.client import TinyB
|
|
7
7
|
from tinybird.tb.config import get_display_cloud_host
|
|
8
8
|
from tinybird.tb.modules.cli import CLIConfig, cli
|
|
9
|
-
from tinybird.tb.modules.common import coro, echo_json, format_robust_table
|
|
9
|
+
from tinybird.tb.modules.common import coro, echo_json, force_echo, format_robust_table
|
|
10
10
|
from tinybird.tb.modules.feedback_manager import FeedbackManager
|
|
11
11
|
from tinybird.tb.modules.local_common import TB_LOCAL_ADDRESS, get_tinybird_local_config
|
|
12
12
|
from tinybird.tb.modules.project import Project
|
|
@@ -19,14 +19,19 @@ async def info(ctx: click.Context) -> None:
|
|
|
19
19
|
"""Get information about the project that is currently being used"""
|
|
20
20
|
ctx_config = ctx.ensure_object(dict)["config"]
|
|
21
21
|
project: Project = ctx.ensure_object(dict)["project"]
|
|
22
|
-
|
|
22
|
+
output = ctx.ensure_object(dict)["output"]
|
|
23
|
+
|
|
24
|
+
if output not in {"human", "json"}:
|
|
25
|
+
force_echo(FeedbackManager.error_invalid_output_format(formats=", ".join(["human", "json"])))
|
|
26
|
+
return
|
|
27
|
+
|
|
23
28
|
click.echo(FeedbackManager.highlight(message="» Tinybird Cloud:"))
|
|
24
29
|
cloud_table, cloud_columns = await get_cloud_info(ctx_config)
|
|
25
30
|
click.echo(FeedbackManager.highlight(message="\n» Tinybird Local:"))
|
|
26
31
|
local_table, local_columns = await get_local_info(ctx_config)
|
|
27
32
|
click.echo(FeedbackManager.highlight(message="\n» Project:"))
|
|
28
33
|
project_table, project_columns = await get_project_info(project.folder)
|
|
29
|
-
if
|
|
34
|
+
if output == "json":
|
|
30
35
|
cloud_data = {}
|
|
31
36
|
if cloud_columns and cloud_table and isinstance(cloud_table, list) and len(cloud_table) > 0:
|
|
32
37
|
cloud_data = {column: cloud_table[0][i] for i, column in enumerate(cloud_columns)}
|
tinybird/tb/modules/project.py
CHANGED
|
@@ -51,7 +51,9 @@ class Project:
|
|
|
51
51
|
return project_files
|
|
52
52
|
|
|
53
53
|
def get_resource_path(self, resource_name: str, resource_type: str) -> Optional[str]:
|
|
54
|
-
full_path = next(
|
|
54
|
+
full_path = next(
|
|
55
|
+
(p for p in self.get_project_files() if p.endswith("/" + resource_name + f".{resource_type}")), ""
|
|
56
|
+
)
|
|
55
57
|
if not full_path:
|
|
56
58
|
return None
|
|
57
59
|
return Path(full_path).relative_to(self.path).as_posix()
|
|
@@ -114,8 +116,15 @@ class Project:
|
|
|
114
116
|
return datafiles
|
|
115
117
|
|
|
116
118
|
@staticmethod
|
|
117
|
-
def
|
|
118
|
-
|
|
119
|
+
def get_pipe_type(path: str) -> str:
|
|
120
|
+
content = Path(path).read_text()
|
|
121
|
+
if re.search(r"TYPE endpoint", content, re.IGNORECASE):
|
|
122
|
+
return "endpoint"
|
|
123
|
+
elif re.search(r"TYPE materialized", content, re.IGNORECASE):
|
|
124
|
+
return "materialization"
|
|
125
|
+
elif re.search(r"TYPE copy", content, re.IGNORECASE):
|
|
126
|
+
return "copy"
|
|
127
|
+
return "pipe"
|
|
119
128
|
|
|
120
129
|
@staticmethod
|
|
121
130
|
def is_kafka_connection(content: str) -> bool:
|
|
@@ -3,7 +3,7 @@ tinybird/context.py,sha256=FfqYfrGX_I7PKGTQo93utaKPDNVYWelg4Hsp3evX5wM,1291
|
|
|
3
3
|
tinybird/datatypes.py,sha256=r4WCvspmrXTJHiPjjyOTiZyZl31FO3Ynkwq4LQsYm6E,11059
|
|
4
4
|
tinybird/feedback_manager.py,sha256=1INQFfRfuMCb9lfB8KNf4r6qC2khW568hoHjtk-wshI,69305
|
|
5
5
|
tinybird/git_settings.py,sha256=Sw_8rGmribEFJ4Z_6idrVytxpFYk7ez8ei0qHULzs3E,3934
|
|
6
|
-
tinybird/prompts.py,sha256=
|
|
6
|
+
tinybird/prompts.py,sha256=rpgvMpR103niDnoDMSz8hRAYBdgfrorfD5-7g23BMQQ,37596
|
|
7
7
|
tinybird/sql.py,sha256=C_B81wwv3BsqyXGhF5oTk9DcTUkrp7NwIFqSzd3Dmjc,47854
|
|
8
8
|
tinybird/sql_template.py,sha256=hWW8JawSWLl9GeWPYkC_Yrxj7P0MHEVMJ0Px9bedEgM,99817
|
|
9
9
|
tinybird/sql_template_fmt.py,sha256=KUHdj5rYCYm_rKKdXYSJAE9vIyXUQLB0YSZnUXHeBlY,10196
|
|
@@ -12,27 +12,27 @@ tinybird/syncasync.py,sha256=IPnOx6lMbf9SNddN1eBtssg8vCLHMt76SuZ6YNYm-Yk,27761
|
|
|
12
12
|
tinybird/tornado_template.py,sha256=jjNVDMnkYFWXflmT8KU_Ssbo5vR8KQq3EJMk5vYgXRw,41959
|
|
13
13
|
tinybird/ch_utils/constants.py,sha256=aYvg2C_WxYWsnqPdZB1ZFoIr8ZY-XjUXYyHKE9Ansj0,3890
|
|
14
14
|
tinybird/ch_utils/engine.py,sha256=X4tE9OrfaUy6kO9cqVEzyI9cDcmOF3IAssRRzsTsfEQ,40781
|
|
15
|
-
tinybird/tb/__cli__.py,sha256=
|
|
15
|
+
tinybird/tb/__cli__.py,sha256=WyfYqfkf_ILLl0K6xlcfKKfD_0fTf__KCCa29CBGHmw,247
|
|
16
16
|
tinybird/tb/check_pypi.py,sha256=rW4QmDRbtgKdUUwJCnBkVjmTjZSZGN-XgZhx7vMkC0w,1009
|
|
17
17
|
tinybird/tb/cli.py,sha256=u3eGOhX0MHkuT6tiwaZ0_3twqLmqKXDAOxF7yV_Nn9Q,1075
|
|
18
18
|
tinybird/tb/client.py,sha256=CO-dQw8h28X6T6IO-Z79yPBKaJQT1Rwya5b6gexvw58,56491
|
|
19
19
|
tinybird/tb/config.py,sha256=jT9xndpeCY_g0HdB5qE2EquC0TFRRnkPnQFWZWd04jo,3998
|
|
20
|
-
tinybird/tb/modules/build.py,sha256=
|
|
20
|
+
tinybird/tb/modules/build.py,sha256=Deuq1Qk7c5Uapz92Vn9Bp7CrjDylRWER5hBvKGS3QxE,19370
|
|
21
21
|
tinybird/tb/modules/cicd.py,sha256=Njb6eZOHHbUkoJJx6KoixO9PsfA_T-3Ybkya9-50Ca8,7328
|
|
22
|
-
tinybird/tb/modules/cli.py,sha256=
|
|
23
|
-
tinybird/tb/modules/common.py,sha256=
|
|
22
|
+
tinybird/tb/modules/cli.py,sha256=LW77oBqvZ6QB5X96158Kp13Tl24vKdxlyWvsSawpmCo,15399
|
|
23
|
+
tinybird/tb/modules/common.py,sha256=2NRDRll0czmYjwLh3qv3DYL9aP8XgRkRAv5S3meCGfM,84062
|
|
24
24
|
tinybird/tb/modules/config.py,sha256=ziqW_t_mRVvWOd85VoB4vKyvgMkEfpXDf9H4v38p2xc,11422
|
|
25
25
|
tinybird/tb/modules/connection.py,sha256=z1xWP2gtjKEbjc4ZF1aD7QUgl8V--wf2IRNy-4sRFm8,9779
|
|
26
26
|
tinybird/tb/modules/copy.py,sha256=2Mm4FWKehOG7CoOhiF1m9UZJgJn0W1_cMolqju8ONYg,5805
|
|
27
27
|
tinybird/tb/modules/create.py,sha256=2uW-4t7c7e4xkZ-GpK_8XaA-nuXwklq7rTks4k6qrtI,20917
|
|
28
|
-
tinybird/tb/modules/datasource.py,sha256=
|
|
29
|
-
tinybird/tb/modules/deployment.py,sha256=
|
|
28
|
+
tinybird/tb/modules/datasource.py,sha256=Uomj3bpVL5m4A0_ezWFXvQAjVER4EfmDz5gFkfGR7eo,37027
|
|
29
|
+
tinybird/tb/modules/deployment.py,sha256=aVVg-ZqpZSNp8f8RQflAKijQsyRs9W6tys-xzHUUrMI,28169
|
|
30
30
|
tinybird/tb/modules/deprecations.py,sha256=rrszC1f_JJeJ8mUxGoCxckQTJFBCR8wREf4XXXN-PRc,4507
|
|
31
31
|
tinybird/tb/modules/dev_server.py,sha256=57FCKuWpErwYUYgHspYDkLWEm9F4pbvVOtMrFXX1fVU,10129
|
|
32
32
|
tinybird/tb/modules/endpoint.py,sha256=XySDt3pk66vxOZ0egUfz4bY8bEk3BjOXkv-L0OIJ3sc,12083
|
|
33
33
|
tinybird/tb/modules/exceptions.py,sha256=5jK91w1LPmtqIUfDpHe_Op5OxGz8-p1BPgtLREMIni0,5217
|
|
34
|
-
tinybird/tb/modules/feedback_manager.py,sha256=
|
|
35
|
-
tinybird/tb/modules/info.py,sha256=
|
|
34
|
+
tinybird/tb/modules/feedback_manager.py,sha256=cnMGTCXa04Zmwq0tPHuAeq_hxqTz3F8c7mXqTJ-MZsw,76920
|
|
35
|
+
tinybird/tb/modules/info.py,sha256=qsXr2g45jRv2xD4Kt2sgsT0AR8sjwrAZf2s7mI-zxL4,6113
|
|
36
36
|
tinybird/tb/modules/infra.py,sha256=fve30Gj3mG9zbquGxS2e4ipcOYOxviWQCpNFfEzJN_Q,33195
|
|
37
37
|
tinybird/tb/modules/job.py,sha256=AsUCRNzy7HG5oJ4fyk9NpIm5NtNJgBZSy8MtJdXBe5A,3167
|
|
38
38
|
tinybird/tb/modules/llm.py,sha256=KfsCYmKeW1VQz0iDZhGKCRkQv_Y3kTHh6JuxvofOguE,1076
|
|
@@ -45,7 +45,7 @@ tinybird/tb/modules/materialization.py,sha256=QJX5kCPhhm6IXBO1JsalVfbQdypCe_eOUD
|
|
|
45
45
|
tinybird/tb/modules/mock.py,sha256=IyHweMUM6bUH8IhyiX2tTMpdVpTFUeAJ41lZ5P42-HQ,5303
|
|
46
46
|
tinybird/tb/modules/open.py,sha256=OuctINN77oexpSjth9uoIZPCelKO4Li-yyVxeSnk1io,1371
|
|
47
47
|
tinybird/tb/modules/pipe.py,sha256=AQKEDagO6e3psPVjJkS_MDbn8aK-apAiLp26k7jgAV0,2432
|
|
48
|
-
tinybird/tb/modules/project.py,sha256=
|
|
48
|
+
tinybird/tb/modules/project.py,sha256=eUO8WKfnBIEz9WofOdgXDiEl26dLJdK-3i3O8OHUOLk,5514
|
|
49
49
|
tinybird/tb/modules/regions.py,sha256=QjsL5H6Kg-qr0aYVLrvb1STeJ5Sx_sjvbOYO0LrEGMk,166
|
|
50
50
|
tinybird/tb/modules/secret.py,sha256=WsqzxxLh9W_jkuHL2JofMXdIJy0lT5WEI-7bQSIDgAc,2921
|
|
51
51
|
tinybird/tb/modules/shell.py,sha256=Zd_4Ak_5tKVX-cw6B4ag36xZeEGHeh-jZpAsIXkoMoE,14116
|
|
@@ -80,8 +80,8 @@ tinybird/tb_cli_modules/config.py,sha256=IsgdtFRnUrkY8-Zo32lmk6O7u3bHie1QCxLwgp4
|
|
|
80
80
|
tinybird/tb_cli_modules/exceptions.py,sha256=pmucP4kTF4irIt7dXiG-FcnI-o3mvDusPmch1L8RCWk,3367
|
|
81
81
|
tinybird/tb_cli_modules/regions.py,sha256=QjsL5H6Kg-qr0aYVLrvb1STeJ5Sx_sjvbOYO0LrEGMk,166
|
|
82
82
|
tinybird/tb_cli_modules/telemetry.py,sha256=Hh2Io8ZPROSunbOLuMvuIFU4TqwWPmQTqal4WS09K1A,10449
|
|
83
|
-
tinybird-0.0.1.
|
|
84
|
-
tinybird-0.0.1.
|
|
85
|
-
tinybird-0.0.1.
|
|
86
|
-
tinybird-0.0.1.
|
|
87
|
-
tinybird-0.0.1.
|
|
83
|
+
tinybird-0.0.1.dev192.dist-info/METADATA,sha256=eZrRBg8bCyPv90gKQtHPx47LdUlGlk6nIoAU18AjYf4,1608
|
|
84
|
+
tinybird-0.0.1.dev192.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
85
|
+
tinybird-0.0.1.dev192.dist-info/entry_points.txt,sha256=LwdHU6TfKx4Qs7BqqtaczEZbImgU7Abe9Lp920zb_fo,43
|
|
86
|
+
tinybird-0.0.1.dev192.dist-info/top_level.txt,sha256=VqqqEmkAy7UNaD8-V51FCoMMWXjLUlR0IstvK7tJYVY,54
|
|
87
|
+
tinybird-0.0.1.dev192.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|