tinybird 0.0.1.dev42__tar.gz → 0.0.1.dev43__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.
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/PKG-INFO +1 -1
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/client.py +1 -1
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/connectors.py +3 -3
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/datafile.py +11 -11
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/feedback_manager.py +1 -1
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/prompts.py +1 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/sql.py +1 -1
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/sql_template_fmt.py +1 -1
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/__cli__.py +2 -2
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/build.py +29 -16
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/build_client.py +1 -1
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/cicd.py +2 -2
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/cli.py +6 -4
- tinybird-0.0.1.dev43/tinybird/tb/modules/copy.py +159 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/datafile/build.py +52 -26
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/deployment.py +86 -61
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/endpoint.py +1 -1
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/llm_utils.py +2 -2
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/pipe.py +0 -90
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/project.py +25 -1
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/watch.py +54 -5
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb_cli_modules/cli.py +1 -1
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb_cli_modules/common.py +1 -1
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb_cli_modules/pipe.py +1 -1
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb_cli_modules/tinyunit/tinyunit.py +1 -1
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tornado_template.py +2 -2
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird.egg-info/PKG-INFO +1 -1
- tinybird-0.0.1.dev42/tinybird/tb/modules/copy.py +0 -68
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/setup.cfg +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/__cli__.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/ch_utils/constants.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/ch_utils/engine.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/check_pypi.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/config.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/context.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/datatypes.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/git_settings.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/sql_template.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/sql_toolset.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/syncasync.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/cli.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/auth.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/common.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/config.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/create.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/datafile/build_common.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/datafile/build_datasource.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/datafile/build_pipe.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/datafile/common.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/datafile/diff.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/datafile/exceptions.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/datafile/fixture.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/datafile/format_common.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/datafile/format_datasource.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/datafile/format_pipe.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/datafile/parse_datasource.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/datafile/parse_pipe.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/datafile/pipe_checker.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/datafile/pull.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/datasource.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/exceptions.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/feedback_manager.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/fmt.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/job.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/llm.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/local.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/local_common.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/login.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/mock.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/regions.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/shell.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/table.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/tag.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/telemetry.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/test.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/tinyunit/tinyunit.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/tinyunit/tinyunit_lib.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/token.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/update.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/workspace.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb/modules/workspace_members.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb_cli.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb_cli_modules/auth.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb_cli_modules/branch.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb_cli_modules/cicd.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb_cli_modules/config.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb_cli_modules/connection.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb_cli_modules/datasource.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb_cli_modules/exceptions.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb_cli_modules/fmt.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb_cli_modules/job.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb_cli_modules/regions.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb_cli_modules/tag.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb_cli_modules/telemetry.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb_cli_modules/test.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb_cli_modules/tinyunit/tinyunit_lib.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb_cli_modules/workspace.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird/tb_cli_modules/workspace_members.py +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird.egg-info/SOURCES.txt +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird.egg-info/dependency_links.txt +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird.egg-info/entry_points.txt +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird.egg-info/requires.txt +0 -0
- {tinybird-0.0.1.dev42 → tinybird-0.0.1.dev43}/tinybird.egg-info/top_level.txt +0 -0
|
@@ -72,7 +72,7 @@ def parse_error_response(response: Response) -> str:
|
|
|
72
72
|
if content.get("error", None):
|
|
73
73
|
error = content["error"]
|
|
74
74
|
if content.get("errors", None):
|
|
75
|
-
error += f
|
|
75
|
+
error += f" -> errors: {content.get('errors')}"
|
|
76
76
|
else:
|
|
77
77
|
error = json.dumps(response, indent=4)
|
|
78
78
|
return error
|
|
@@ -246,7 +246,7 @@ class BigQuery(Connector):
|
|
|
246
246
|
uri='{self.gcs.gs_url()}{destination}*.csv',
|
|
247
247
|
format='CSV',
|
|
248
248
|
overwrite=true,
|
|
249
|
-
header={
|
|
249
|
+
header={"true" if with_headers else "false"},
|
|
250
250
|
field_delimiter=',') AS
|
|
251
251
|
{sql}
|
|
252
252
|
"""
|
|
@@ -319,7 +319,7 @@ class Snowflake(Connector):
|
|
|
319
319
|
|
|
320
320
|
def create_stage(self):
|
|
321
321
|
sql = f"""
|
|
322
|
-
create stage "{self.options[
|
|
322
|
+
create stage "{self.options["schema"]}".{self.stage()}
|
|
323
323
|
url='{self.gcs.gcs_url()}'
|
|
324
324
|
storage_integration = {self.storage_integration()};
|
|
325
325
|
"""
|
|
@@ -337,7 +337,7 @@ class Snowflake(Connector):
|
|
|
337
337
|
from ({sql})
|
|
338
338
|
overwrite = true
|
|
339
339
|
file_format = (TYPE=CSV COMPRESSION=NONE ESCAPE_UNENCLOSED_FIELD=NONE FIELD_DELIMITER='|' FIELD_OPTIONALLY_ENCLOSED_BY='"' null_if=())
|
|
340
|
-
header = {"true" if with_headers else "false"
|
|
340
|
+
header = {"true" if with_headers else "false"}
|
|
341
341
|
max_file_size = 2500000000;
|
|
342
342
|
"""
|
|
343
343
|
self.execute(sql)
|
|
@@ -2175,8 +2175,8 @@ class PipeCheckerRunner:
|
|
|
2175
2175
|
AND extractURLParameter(assumeNotNull(url), 'debug') <> 'query'
|
|
2176
2176
|
AND error = 0
|
|
2177
2177
|
AND not mapContains(parameters, '__tb__semver')
|
|
2178
|
-
{" AND " + " AND ".join([f"mapContains(pipe_request_params, '{match}')" for match in matches]) if matches and len(matches) > 0 else
|
|
2179
|
-
{
|
|
2178
|
+
{" AND " + " AND ".join([f"mapContains(pipe_request_params, '{match}')" for match in matches]) if matches and len(matches) > 0 else ""}
|
|
2179
|
+
{extra_where_clause}
|
|
2180
2180
|
Limit 5000000 -- Enough to bring data while not processing all requests from highly used pipes
|
|
2181
2181
|
)
|
|
2182
2182
|
group by request_param_names, http_method
|
|
@@ -2202,7 +2202,7 @@ class PipeCheckerRunner:
|
|
|
2202
2202
|
AND extractURLParameter(assumeNotNull(url), 'debug') <> 'query'
|
|
2203
2203
|
AND error = 0
|
|
2204
2204
|
AND not mapContains(parameters, '__tb__semver')
|
|
2205
|
-
{" AND " + " AND ".join([f"mapContains(pipe_request_params, '{match}')" for match in matches]) if matches and len(matches) > 0 else
|
|
2205
|
+
{" AND " + " AND ".join([f"mapContains(pipe_request_params, '{match}')" for match in matches]) if matches and len(matches) > 0 else ""}
|
|
2206
2206
|
{extra_where_clause}
|
|
2207
2207
|
LIMIT {limit}
|
|
2208
2208
|
)
|
|
@@ -4425,7 +4425,7 @@ async def folder_push(
|
|
|
4425
4425
|
name=(
|
|
4426
4426
|
name
|
|
4427
4427
|
if to_run[name]["version"] is None
|
|
4428
|
-
else f
|
|
4428
|
+
else f"{name}__v{to_run[name]['version']}"
|
|
4429
4429
|
)
|
|
4430
4430
|
)
|
|
4431
4431
|
)
|
|
@@ -4442,7 +4442,7 @@ async def folder_push(
|
|
|
4442
4442
|
if raise_on_exists:
|
|
4443
4443
|
raise AlreadyExistsException(
|
|
4444
4444
|
FeedbackManager.warning_name_already_exists(
|
|
4445
|
-
name=name if to_run[name]["version"] is None else f
|
|
4445
|
+
name=name if to_run[name]["version"] is None else f"{name}__v{to_run[name]['version']}"
|
|
4446
4446
|
)
|
|
4447
4447
|
)
|
|
4448
4448
|
else:
|
|
@@ -4457,7 +4457,7 @@ async def folder_push(
|
|
|
4457
4457
|
name=(
|
|
4458
4458
|
name
|
|
4459
4459
|
if to_run[name]["version"] is None
|
|
4460
|
-
else f
|
|
4460
|
+
else f"{name}__v{to_run[name]['version']}"
|
|
4461
4461
|
)
|
|
4462
4462
|
)
|
|
4463
4463
|
)
|
|
@@ -5013,7 +5013,7 @@ async def format_engine(
|
|
|
5013
5013
|
else:
|
|
5014
5014
|
if node.get("engine", None):
|
|
5015
5015
|
empty = '""'
|
|
5016
|
-
file_parts.append(f
|
|
5016
|
+
file_parts.append(f"ENGINE {node['engine']['type']}" if node.get("engine", {}).get("type") else empty)
|
|
5017
5017
|
file_parts.append(DATAFILE_NEW_LINE)
|
|
5018
5018
|
for arg in sorted(node["engine"].get("args", [])):
|
|
5019
5019
|
elem = ", ".join([x.strip() for x in arg[1].split(",")])
|
|
@@ -5030,7 +5030,7 @@ async def format_node_type(file_parts: List[str], node: Dict[str, Any]) -> List[
|
|
|
5030
5030
|
if node_type == PipeNodeTypes.MATERIALIZED:
|
|
5031
5031
|
file_parts.append(node_type_upper)
|
|
5032
5032
|
file_parts.append(DATAFILE_NEW_LINE)
|
|
5033
|
-
file_parts.append(f
|
|
5033
|
+
file_parts.append(f"DATASOURCE {node['datasource']}")
|
|
5034
5034
|
file_parts.append(DATAFILE_NEW_LINE)
|
|
5035
5035
|
await format_engine(file_parts, node)
|
|
5036
5036
|
|
|
@@ -5038,10 +5038,10 @@ async def format_node_type(file_parts: List[str], node: Dict[str, Any]) -> List[
|
|
|
5038
5038
|
if node_type == PipeNodeTypes.COPY:
|
|
5039
5039
|
file_parts.append(node_type_upper)
|
|
5040
5040
|
file_parts.append(DATAFILE_NEW_LINE)
|
|
5041
|
-
file_parts.append(f
|
|
5041
|
+
file_parts.append(f"TARGET_DATASOURCE {node['target_datasource']}")
|
|
5042
5042
|
if node.get("mode"):
|
|
5043
5043
|
file_parts.append(DATAFILE_NEW_LINE)
|
|
5044
|
-
file_parts.append(f
|
|
5044
|
+
file_parts.append(f"COPY_MODE {node.get('mode')}")
|
|
5045
5045
|
|
|
5046
5046
|
if node.get(CopyParameters.COPY_SCHEDULE):
|
|
5047
5047
|
is_ondemand = node[CopyParameters.COPY_SCHEDULE].lower() == ON_DEMAND
|
|
@@ -5095,7 +5095,7 @@ async def format_node(
|
|
|
5095
5095
|
if item and not unroll_includes:
|
|
5096
5096
|
return
|
|
5097
5097
|
|
|
5098
|
-
file_parts.append(f
|
|
5098
|
+
file_parts.append(f"NODE {node['name'].strip()}")
|
|
5099
5099
|
file_parts.append(DATAFILE_NEW_LINE)
|
|
5100
5100
|
|
|
5101
5101
|
from collections import namedtuple
|
|
@@ -951,7 +951,7 @@ Ready? """
|
|
|
951
951
|
)
|
|
952
952
|
success_datasource_alter = success_message("** The Data Source has been correctly updated.")
|
|
953
953
|
success_datasource_kafka_connected = success_message(
|
|
954
|
-
"** Data Source '{id}' created\n
|
|
954
|
+
"** Data Source '{id}' created\n** Kafka streaming connection configured successfully!"
|
|
955
955
|
)
|
|
956
956
|
success_datasource_shared = success_message(
|
|
957
957
|
"** The Data Source {datasource} has been correctly shared with {workspace}"
|
|
@@ -373,6 +373,7 @@ You are a Tinybird expert. You will be given a pipe containing different nodes w
|
|
|
373
373
|
- The parameter within Tinybird templating syntax looks like this one {{String(my_param_name, default_value)}}.
|
|
374
374
|
- If there are no parameters, you can omit parameters and generate a single test.
|
|
375
375
|
- The format of the parameters is the following: ?param1=value1¶m2=value2¶m3=value3
|
|
376
|
+
- If some parameters are provided by the user and you need to use them, preserve in the same format as they were provided, like case sensitive.
|
|
376
377
|
</instructions>
|
|
377
378
|
|
|
378
379
|
This is an example of a test with parameters:
|
|
@@ -241,7 +241,7 @@ def format_parse_error(
|
|
|
241
241
|
message += f" found at position {adjusted_position - len(keyword)}"
|
|
242
242
|
else:
|
|
243
243
|
message += (
|
|
244
|
-
f" found {repr(table_structure[i]) if len(table_structure)>i else 'EOF'} at position {adjusted_position}"
|
|
244
|
+
f" found {repr(table_structure[i]) if len(table_structure) > i else 'EOF'} at position {adjusted_position}"
|
|
245
245
|
)
|
|
246
246
|
return message
|
|
247
247
|
|
|
@@ -100,7 +100,7 @@ def _format_jinja_node(self, node: Node, max_length: int) -> bool:
|
|
|
100
100
|
parts = tag.code.split("\n")
|
|
101
101
|
prefix = INDENT * (node.depth[0] + node.depth[1])
|
|
102
102
|
if len(parts) > 1:
|
|
103
|
-
tag.code = "\n".join([f
|
|
103
|
+
tag.code = "\n".join([f"{prefix if i != 0 else ''}{part}" for i, part in enumerate(parts)])
|
|
104
104
|
|
|
105
105
|
node.value = str(tag)
|
|
106
106
|
|
|
@@ -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.
|
|
8
|
-
__revision__ = '
|
|
7
|
+
__version__ = '0.0.1.dev43'
|
|
8
|
+
__revision__ = 'ecb1311'
|
|
@@ -31,17 +31,16 @@ def build(ctx: click.Context, watch: bool) -> None:
|
|
|
31
31
|
project: Project = ctx.ensure_object(dict)["project"]
|
|
32
32
|
tb_client = asyncio.run(get_tinybird_local_client(str(project.path)))
|
|
33
33
|
click.echo(FeedbackManager.highlight(message="\n» Building project..."))
|
|
34
|
-
|
|
35
34
|
time_start = time.time()
|
|
36
35
|
|
|
37
|
-
def process(file_changed: Optional[str] = None) -> None:
|
|
36
|
+
def process(file_changed: Optional[str] = None, diff: Optional[str] = None) -> None:
|
|
38
37
|
if file_changed and file_changed.endswith(".ndjson"):
|
|
39
38
|
rebuild_fixture(project, tb_client, file_changed)
|
|
40
39
|
else:
|
|
41
40
|
build_project(project, tb_client)
|
|
42
41
|
try:
|
|
43
42
|
if file_changed:
|
|
44
|
-
build_and_print_resource(tb_client, file_changed)
|
|
43
|
+
build_and_print_resource(tb_client, file_changed, diff)
|
|
45
44
|
except Exception:
|
|
46
45
|
pass
|
|
47
46
|
|
|
@@ -79,6 +78,9 @@ def build_project(project: Project, tb_client: TinyB) -> None:
|
|
|
79
78
|
project_path = project.path
|
|
80
79
|
project_files = project.get_project_files()
|
|
81
80
|
|
|
81
|
+
if not project_files:
|
|
82
|
+
return
|
|
83
|
+
|
|
82
84
|
for file_path in project_files:
|
|
83
85
|
relative_path = str(Path(file_path).relative_to(project_path))
|
|
84
86
|
fd = open(file_path, "rb")
|
|
@@ -102,16 +104,19 @@ def build_project(project: Project, tb_client: TinyB) -> None:
|
|
|
102
104
|
datasources = result.get("datasources", [])
|
|
103
105
|
pipes = result.get("pipes", [])
|
|
104
106
|
for ds in datasources:
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
107
|
+
ds_path_str: Optional[str] = next(
|
|
108
|
+
(p for p in project_files if p.endswith(ds.get("name") + ".datasource")), None
|
|
109
|
+
)
|
|
110
|
+
if ds_path_str:
|
|
111
|
+
ds_path = Path(ds_path_str)
|
|
112
|
+
ds_path_str = ds_path_str.replace(f"{project.folder}/", "")
|
|
113
|
+
click.echo(FeedbackManager.info(message=f"✓ {ds_path_str} created"))
|
|
109
114
|
for pipe in pipes:
|
|
110
115
|
pipe_name = pipe.get("name")
|
|
111
|
-
|
|
112
|
-
if
|
|
113
|
-
|
|
114
|
-
click.echo(FeedbackManager.info(message=f"✓ {
|
|
116
|
+
pipe_path_str: Optional[str] = next((p for p in project_files if p.endswith(pipe_name + ".pipe")), None)
|
|
117
|
+
if pipe_path_str:
|
|
118
|
+
pipe_path_str = pipe_path_str.replace(f"{project.folder}/", "")
|
|
119
|
+
click.echo(FeedbackManager.info(message=f"✓ {pipe_path_str} created"))
|
|
115
120
|
|
|
116
121
|
try:
|
|
117
122
|
for filename in project_files:
|
|
@@ -185,9 +190,17 @@ def rebuild_fixture(project: Project, tb_client: TinyB, fixture: str) -> None:
|
|
|
185
190
|
click.echo(FeedbackManager.error_exception(error=e))
|
|
186
191
|
|
|
187
192
|
|
|
188
|
-
def build_and_print_resource(tb_client: TinyB, filename: str):
|
|
193
|
+
def build_and_print_resource(tb_client: TinyB, filename: str, diff: Optional[str] = None):
|
|
194
|
+
table_name = diff
|
|
189
195
|
resource_path = Path(filename)
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
196
|
+
resource_name = resource_path.stem
|
|
197
|
+
|
|
198
|
+
pipeline = resource_name if filename.endswith(".pipe") else None
|
|
199
|
+
|
|
200
|
+
if not table_name:
|
|
201
|
+
table_name = resource_name
|
|
202
|
+
|
|
203
|
+
sql = f"SELECT * FROM {table_name} FORMAT JSON"
|
|
204
|
+
|
|
205
|
+
res = asyncio.run(tb_client.query(sql, pipeline=pipeline))
|
|
206
|
+
print_table_formatted(res, table_name)
|
|
@@ -166,7 +166,7 @@ def build_client(
|
|
|
166
166
|
build_ok = asyncio.run(build_once(filenames))
|
|
167
167
|
|
|
168
168
|
if watch:
|
|
169
|
-
shell = Shell(project=project,
|
|
169
|
+
shell = Shell(project=project, tb_client=tb_client)
|
|
170
170
|
click.echo(FeedbackManager.gray(message="\nWatching for changes..."))
|
|
171
171
|
watcher_thread = threading.Thread(
|
|
172
172
|
target=watch_files, args=(filenames, process, shell, project, build_ok), daemon=True
|
|
@@ -39,7 +39,7 @@ jobs:
|
|
|
39
39
|
working-directory: '{{ data_project_dir }}'
|
|
40
40
|
services:
|
|
41
41
|
tinybird:
|
|
42
|
-
image: tinybirdco/tinybird-local:
|
|
42
|
+
image: tinybirdco/tinybird-local:beta
|
|
43
43
|
ports:
|
|
44
44
|
- 80:80
|
|
45
45
|
steps:
|
|
@@ -83,7 +83,7 @@ tinybird_ci_workflow:
|
|
|
83
83
|
- tb build
|
|
84
84
|
- tb test run
|
|
85
85
|
services:
|
|
86
|
-
- name: tinybirdco/tinybird-local:
|
|
86
|
+
- name: tinybirdco/tinybird-local:beta
|
|
87
87
|
alias: tinybird-local
|
|
88
88
|
"""
|
|
89
89
|
|
|
@@ -66,15 +66,17 @@ VERSION = f"{__cli__.__version__} (rev {__cli__.__revision__})"
|
|
|
66
66
|
@click.option("--host", help="Use custom host, defaults to TB_HOST envvar, then to https://api.tinybird.co")
|
|
67
67
|
@click.option("--show-tokens", is_flag=True, default=False, help="Enable the output of tokens")
|
|
68
68
|
@click.option("--prod/--local", is_flag=True, default=False, help="Run against production or local")
|
|
69
|
-
@click.option("--folder", type=str,
|
|
69
|
+
@click.option("--folder", type=str, help="Folder where files will be placed")
|
|
70
70
|
@click.version_option(version=VERSION)
|
|
71
71
|
@click.pass_context
|
|
72
72
|
@coro
|
|
73
|
-
async def cli(
|
|
73
|
+
async def cli(
|
|
74
|
+
ctx: Context, debug: bool, token: str, host: str, show_tokens: bool, prod: bool, folder: Optional[str]
|
|
75
|
+
) -> None:
|
|
74
76
|
"""
|
|
75
77
|
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.
|
|
76
78
|
"""
|
|
77
|
-
project = Project(folder=folder)
|
|
79
|
+
project = Project(folder=folder or os.getcwd())
|
|
78
80
|
# We need to unpatch for our tests not to break
|
|
79
81
|
if show_tokens or not prod or ctx.invoked_subcommand == "build":
|
|
80
82
|
__unpatch_click_output()
|
|
@@ -87,7 +89,7 @@ async def cli(ctx: Context, debug: bool, token: str, host: str, show_tokens: boo
|
|
|
87
89
|
if debug:
|
|
88
90
|
logging.basicConfig(level=logging.DEBUG)
|
|
89
91
|
|
|
90
|
-
config_temp = CLIConfig.get_project_config(project.path)
|
|
92
|
+
config_temp = CLIConfig.get_project_config(str(project.path))
|
|
91
93
|
if token:
|
|
92
94
|
config_temp.set_token(token)
|
|
93
95
|
if host:
|
|
@@ -0,0 +1,159 @@
|
|
|
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
|
+
from typing import Optional, Tuple
|
|
9
|
+
|
|
10
|
+
import click
|
|
11
|
+
from click import Context
|
|
12
|
+
|
|
13
|
+
from tinybird.client import AuthNoTokenException, TinyB
|
|
14
|
+
from tinybird.tb.modules.cli import cli
|
|
15
|
+
from tinybird.tb.modules.common import coro, echo_safe_humanfriendly_tables_format_smart_table, wait_job
|
|
16
|
+
from tinybird.tb.modules.datafile.common import get_name_version
|
|
17
|
+
from tinybird.tb.modules.exceptions import CLIPipeException
|
|
18
|
+
from tinybird.tb.modules.feedback_manager import FeedbackManager
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@cli.group(hidden=True)
|
|
22
|
+
@click.pass_context
|
|
23
|
+
def copy(ctx):
|
|
24
|
+
"""Copy pipe commands"""
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@copy.command(name="ls")
|
|
28
|
+
@click.option("--match", default=None, help="Retrieve any resourcing matching the pattern. eg --match _test")
|
|
29
|
+
@click.option(
|
|
30
|
+
"--format",
|
|
31
|
+
"format_",
|
|
32
|
+
type=click.Choice(["json"], case_sensitive=False),
|
|
33
|
+
default=None,
|
|
34
|
+
help="Force a type of the output",
|
|
35
|
+
)
|
|
36
|
+
@click.pass_context
|
|
37
|
+
@coro
|
|
38
|
+
async def copy_ls(ctx: Context, match: str, format_: str):
|
|
39
|
+
"""List copy pipes"""
|
|
40
|
+
|
|
41
|
+
client: TinyB = ctx.ensure_object(dict)["client"]
|
|
42
|
+
pipes = await client.pipes(dependencies=False, node_attrs="name", attrs="name,updated_at,type")
|
|
43
|
+
copies = [p for p in pipes if p.get("type") == "copy"]
|
|
44
|
+
copies = sorted(copies, key=lambda p: p["updated_at"])
|
|
45
|
+
columns = ["name", "updated at", "nodes"]
|
|
46
|
+
table_human_readable = []
|
|
47
|
+
table_machine_readable = []
|
|
48
|
+
pattern = re.compile(match) if match else None
|
|
49
|
+
for t in copies:
|
|
50
|
+
tk = get_name_version(t["name"])
|
|
51
|
+
if pattern and not pattern.search(tk["name"]):
|
|
52
|
+
continue
|
|
53
|
+
table_human_readable.append((tk["name"], t["updated_at"][:-7], len(t["nodes"])))
|
|
54
|
+
table_machine_readable.append(
|
|
55
|
+
{
|
|
56
|
+
"name": tk["name"],
|
|
57
|
+
"updated at": t["updated_at"][:-7],
|
|
58
|
+
"nodes": len(t["nodes"]),
|
|
59
|
+
}
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
if not format_:
|
|
63
|
+
click.echo(FeedbackManager.info_pipes())
|
|
64
|
+
echo_safe_humanfriendly_tables_format_smart_table(table_human_readable, column_names=columns)
|
|
65
|
+
click.echo("\n")
|
|
66
|
+
elif format_ == "json":
|
|
67
|
+
click.echo(json.dumps({"pipes": table_machine_readable}, indent=2))
|
|
68
|
+
else:
|
|
69
|
+
raise CLIPipeException(FeedbackManager.error_pipe_ls_type())
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@copy.command(name="run", short_help="Run an on-demand copy job")
|
|
73
|
+
@click.argument("pipe_name_or_id")
|
|
74
|
+
@click.option("--wait", is_flag=True, default=False, help="Wait for the copy job to finish")
|
|
75
|
+
@click.option(
|
|
76
|
+
"--mode", type=click.Choice(["append", "replace"], case_sensitive=True), default=None, help="Copy strategy"
|
|
77
|
+
)
|
|
78
|
+
@click.option("--yes", is_flag=True, default=False, help="Do not ask for confirmation")
|
|
79
|
+
@click.option(
|
|
80
|
+
"--param",
|
|
81
|
+
nargs=1,
|
|
82
|
+
type=str,
|
|
83
|
+
multiple=True,
|
|
84
|
+
default=None,
|
|
85
|
+
help="Key and value of the params you want the Copy pipe to be called with. For example: tb pipe copy run <my_copy_pipe> --param foo=bar",
|
|
86
|
+
)
|
|
87
|
+
@click.pass_context
|
|
88
|
+
@coro
|
|
89
|
+
async def copy_run(
|
|
90
|
+
ctx: click.Context, pipe_name_or_id: str, wait: bool, mode: str, yes: bool, param: Optional[Tuple[str]]
|
|
91
|
+
):
|
|
92
|
+
"""Run an on-demand copy job"""
|
|
93
|
+
|
|
94
|
+
params = dict(key_value.split("=") for key_value in param) if param else {}
|
|
95
|
+
|
|
96
|
+
if yes or click.confirm(FeedbackManager.warning_confirm_copy_pipe(pipe=pipe_name_or_id)):
|
|
97
|
+
click.echo(FeedbackManager.info_copy_job_running(pipe=pipe_name_or_id))
|
|
98
|
+
client: TinyB = ctx.ensure_object(dict)["client"]
|
|
99
|
+
|
|
100
|
+
try:
|
|
101
|
+
response = await client.pipe_run_copy(pipe_name_or_id, params, mode)
|
|
102
|
+
|
|
103
|
+
job_id = response["job"]["job_id"]
|
|
104
|
+
job_url = response["job"]["job_url"]
|
|
105
|
+
target_datasource_id = response["tags"]["copy_target_datasource"]
|
|
106
|
+
target_datasource = await client.get_datasource(target_datasource_id)
|
|
107
|
+
target_datasource_name = target_datasource["name"]
|
|
108
|
+
click.echo(
|
|
109
|
+
FeedbackManager.success_copy_job_created(target_datasource=target_datasource_name, job_url=job_url)
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
if wait:
|
|
113
|
+
await wait_job(client, job_id, job_url, "** Copying data")
|
|
114
|
+
click.echo(FeedbackManager.success_data_copied_to_ds(target_datasource=target_datasource_name))
|
|
115
|
+
|
|
116
|
+
except AuthNoTokenException:
|
|
117
|
+
raise
|
|
118
|
+
except Exception as e:
|
|
119
|
+
raise CLIPipeException(FeedbackManager.error_creating_copy_job(error=e))
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
@copy.command(name="resume", short_help="Resume a paused copy pipe")
|
|
123
|
+
@click.argument("pipe_name_or_id")
|
|
124
|
+
@click.pass_context
|
|
125
|
+
@coro
|
|
126
|
+
async def copy_resume(ctx: click.Context, pipe_name_or_id: str):
|
|
127
|
+
"""Resume a paused copy pipe"""
|
|
128
|
+
|
|
129
|
+
click.echo(FeedbackManager.info_copy_pipe_resuming(pipe=pipe_name_or_id))
|
|
130
|
+
client: TinyB = ctx.ensure_object(dict)["client"]
|
|
131
|
+
|
|
132
|
+
try:
|
|
133
|
+
await client.pipe_resume_copy(pipe_name_or_id)
|
|
134
|
+
click.echo(FeedbackManager.success_copy_pipe_resumed(pipe=pipe_name_or_id))
|
|
135
|
+
|
|
136
|
+
except AuthNoTokenException:
|
|
137
|
+
raise
|
|
138
|
+
except Exception as e:
|
|
139
|
+
raise CLIPipeException(FeedbackManager.error_resuming_copy_pipe(error=e))
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
@copy.command(name="pause", short_help="Pause a running copy pipe")
|
|
143
|
+
@click.argument("pipe_name_or_id")
|
|
144
|
+
@click.pass_context
|
|
145
|
+
@coro
|
|
146
|
+
async def copy_pause(ctx: click.Context, pipe_name_or_id: str):
|
|
147
|
+
"""Pause a running copy pipe"""
|
|
148
|
+
|
|
149
|
+
click.echo(FeedbackManager.info_copy_pipe_pausing(pipe=pipe_name_or_id))
|
|
150
|
+
client: TinyB = ctx.ensure_object(dict)["client"]
|
|
151
|
+
|
|
152
|
+
try:
|
|
153
|
+
await client.pipe_pause_copy(pipe_name_or_id)
|
|
154
|
+
click.echo(FeedbackManager.success_copy_pipe_paused(pipe=pipe_name_or_id))
|
|
155
|
+
|
|
156
|
+
except AuthNoTokenException:
|
|
157
|
+
raise
|
|
158
|
+
except Exception as e:
|
|
159
|
+
raise CLIPipeException(FeedbackManager.error_pausing_copy_pipe(error=e))
|
|
@@ -707,6 +707,7 @@ async def process(
|
|
|
707
707
|
):
|
|
708
708
|
name, kind = filename.rsplit(".", 1)
|
|
709
709
|
warnings = []
|
|
710
|
+
embedded_datasources = {} if embedded_datasources is None else embedded_datasources
|
|
710
711
|
|
|
711
712
|
try:
|
|
712
713
|
res = await process_file(
|
|
@@ -806,31 +807,45 @@ async def get_processed(
|
|
|
806
807
|
to_run: Optional[Dict[str, Any]] = None,
|
|
807
808
|
vendor_paths: Optional[List[Tuple[str, str]]] = None,
|
|
808
809
|
processed: Optional[Set[str]] = None,
|
|
809
|
-
tb_client: TinyB = None,
|
|
810
|
+
tb_client: Optional[TinyB] = None,
|
|
810
811
|
skip_connectors: bool = False,
|
|
811
812
|
current_ws: Optional[Dict[str, Any]] = None,
|
|
812
813
|
fork_downstream: Optional[bool] = False,
|
|
813
814
|
is_internal: Optional[bool] = False,
|
|
814
815
|
dir_path: Optional[str] = None,
|
|
815
|
-
embedded_datasources: Optional[Dict[str, Any]] = None,
|
|
816
|
+
embedded_datasources: Optional[Dict[str, Dict[str, Any]]] = None,
|
|
816
817
|
):
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
if dep_map is None
|
|
820
|
-
|
|
821
|
-
if
|
|
822
|
-
|
|
823
|
-
if processed is None:
|
|
824
|
-
processed = set()
|
|
818
|
+
# Initialize with proper type annotations
|
|
819
|
+
deps_list: List[str] = [] if deps is None else deps
|
|
820
|
+
dep_map_dict: Dict[str, Any] = {} if dep_map is None else dep_map
|
|
821
|
+
to_run_dict: Dict[str, Any] = {} if to_run is None else to_run
|
|
822
|
+
processed_set: Set[str] = set() if processed is None else processed
|
|
823
|
+
embedded_ds: Dict[str, Dict[str, Any]] = {} if embedded_datasources is None else embedded_datasources
|
|
825
824
|
|
|
826
825
|
for filename in filenames:
|
|
827
826
|
# just process changed filenames (tb deploy and --only-changes)
|
|
828
|
-
if changed:
|
|
827
|
+
if changed is not None:
|
|
829
828
|
resource = Path(filename).resolve().stem
|
|
830
829
|
if resource in changed and (not changed[resource] or changed[resource] in ["shared", "remote"]):
|
|
831
830
|
continue
|
|
832
831
|
if os.path.isdir(filename):
|
|
833
|
-
await get_processed(
|
|
832
|
+
await get_processed(
|
|
833
|
+
filenames=get_project_filenames(filename),
|
|
834
|
+
changed=changed,
|
|
835
|
+
verbose=verbose,
|
|
836
|
+
deps=deps_list,
|
|
837
|
+
dep_map=dep_map_dict,
|
|
838
|
+
to_run=to_run_dict,
|
|
839
|
+
vendor_paths=vendor_paths,
|
|
840
|
+
processed=processed_set,
|
|
841
|
+
tb_client=tb_client,
|
|
842
|
+
skip_connectors=skip_connectors,
|
|
843
|
+
current_ws=current_ws,
|
|
844
|
+
fork_downstream=fork_downstream,
|
|
845
|
+
is_internal=is_internal,
|
|
846
|
+
dir_path=dir_path,
|
|
847
|
+
embedded_datasources=embedded_ds,
|
|
848
|
+
)
|
|
834
849
|
else:
|
|
835
850
|
if verbose:
|
|
836
851
|
click.echo(FeedbackManager.info_processing_file(filename=filename))
|
|
@@ -838,12 +853,15 @@ async def get_processed(
|
|
|
838
853
|
if ".incl" in filename:
|
|
839
854
|
click.echo(FeedbackManager.warning_skipping_include_file(file=filename))
|
|
840
855
|
|
|
856
|
+
if tb_client is None:
|
|
857
|
+
raise ValueError("tb_client cannot be None")
|
|
858
|
+
|
|
841
859
|
name, warnings = await process(
|
|
842
860
|
filename=filename,
|
|
843
861
|
tb_client=tb_client,
|
|
844
|
-
deps=
|
|
845
|
-
dep_map=
|
|
846
|
-
to_run=
|
|
862
|
+
deps=deps_list,
|
|
863
|
+
dep_map=dep_map_dict,
|
|
864
|
+
to_run=to_run_dict,
|
|
847
865
|
vendor_paths=vendor_paths,
|
|
848
866
|
skip_connectors=skip_connectors,
|
|
849
867
|
current_ws=current_ws,
|
|
@@ -852,9 +870,9 @@ async def get_processed(
|
|
|
852
870
|
is_internal=is_internal,
|
|
853
871
|
dir_path=dir_path,
|
|
854
872
|
verbose=verbose,
|
|
855
|
-
embedded_datasources=
|
|
873
|
+
embedded_datasources=embedded_ds,
|
|
856
874
|
)
|
|
857
|
-
|
|
875
|
+
processed_set.add(name)
|
|
858
876
|
|
|
859
877
|
if verbose:
|
|
860
878
|
if len(warnings) == 1:
|
|
@@ -890,7 +908,7 @@ async def build_graph(
|
|
|
890
908
|
to_run: Dict[str, Any] = {}
|
|
891
909
|
deps: List[str] = []
|
|
892
910
|
dep_map: Dict[str, Any] = {}
|
|
893
|
-
embedded_datasources = {}
|
|
911
|
+
embedded_datasources: Dict[str, Dict[str, Any]] = {}
|
|
894
912
|
|
|
895
913
|
# These dictionaries are used to store all the resources and there dependencies for the whole project
|
|
896
914
|
# This is used for the downstream dependency graph
|
|
@@ -919,17 +937,18 @@ async def build_graph(
|
|
|
919
937
|
all_dep_map = all_dependencies_graph.dep_map
|
|
920
938
|
all_resources = all_dependencies_graph.to_run
|
|
921
939
|
|
|
922
|
-
processed = set()
|
|
940
|
+
processed: Set[str] = set()
|
|
923
941
|
|
|
924
942
|
await get_processed(
|
|
925
943
|
filenames=filenames,
|
|
926
|
-
tb_client=tb_client,
|
|
927
944
|
changed=changed,
|
|
945
|
+
verbose=verbose,
|
|
928
946
|
deps=deps,
|
|
929
947
|
dep_map=dep_map,
|
|
930
948
|
to_run=to_run,
|
|
931
949
|
vendor_paths=vendor_paths,
|
|
932
950
|
processed=processed,
|
|
951
|
+
tb_client=tb_client,
|
|
933
952
|
skip_connectors=skip_connectors,
|
|
934
953
|
current_ws=current_ws,
|
|
935
954
|
fork_downstream=fork_downstream,
|
|
@@ -1198,18 +1217,22 @@ async def process_file(
|
|
|
1198
1217
|
raise Exception(f"Invalid import schedule: '{cron}'. Valid values are: {valid_values}")
|
|
1199
1218
|
|
|
1200
1219
|
if cron == ON_DEMAND_CRON:
|
|
1220
|
+
if import_params is None:
|
|
1221
|
+
import_params = {}
|
|
1201
1222
|
import_params["import_schedule"] = ON_DEMAND_CRON_EXPECTED_BY_THE_API
|
|
1223
|
+
|
|
1202
1224
|
if cron == AUTO_CRON:
|
|
1203
1225
|
period: int = DEFAULT_CRON_PERIOD
|
|
1204
1226
|
|
|
1205
|
-
if current_ws:
|
|
1227
|
+
if current_ws is not None:
|
|
1206
1228
|
workspaces = (await tb_client.user_workspaces()).get("workspaces", [])
|
|
1207
1229
|
workspace_rate_limits: Dict[str, Dict[str, int]] = next(
|
|
1208
1230
|
(w.get("rate_limits", {}) for w in workspaces if w["id"] == current_ws["id"]), {}
|
|
1209
1231
|
)
|
|
1210
|
-
|
|
1211
|
-
"
|
|
1212
|
-
|
|
1232
|
+
if workspace_rate_limits:
|
|
1233
|
+
rate_limit_config = workspace_rate_limits.get("api_datasources_create_append_replace", {})
|
|
1234
|
+
if rate_limit_config:
|
|
1235
|
+
period = rate_limit_config.get("period", DEFAULT_CRON_PERIOD)
|
|
1213
1236
|
|
|
1214
1237
|
def seconds_to_cron_expression(seconds: int) -> str:
|
|
1215
1238
|
minutes = seconds // 60
|
|
@@ -1223,10 +1246,13 @@ async def process_file(
|
|
|
1223
1246
|
return f"*/{minutes} * * * *"
|
|
1224
1247
|
return f"*/{seconds} * * * *"
|
|
1225
1248
|
|
|
1249
|
+
if import_params is None:
|
|
1250
|
+
import_params = {}
|
|
1226
1251
|
import_params["import_schedule"] = seconds_to_cron_expression(period)
|
|
1227
1252
|
|
|
1228
|
-
|
|
1229
|
-
|
|
1253
|
+
# Include all import_ parameters in the datasource params
|
|
1254
|
+
if import_params is not None:
|
|
1255
|
+
params.update(import_params)
|
|
1230
1256
|
|
|
1231
1257
|
# Substitute the import parameters with the ones used by the
|
|
1232
1258
|
# import API:
|