tinybird 0.0.1.dev5__py3-none-any.whl → 0.0.1.dev7__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/__cli__.py +7 -8
- tinybird/tb/cli.py +28 -0
- tinybird/{tb_cli_modules → tb/modules}/auth.py +5 -5
- tinybird/{tb_cli_modules → tb/modules}/branch.py +5 -25
- tinybird/{tb_cli_modules → tb/modules}/build.py +10 -21
- tinybird/tb/modules/cicd.py +271 -0
- tinybird/{tb_cli_modules → tb/modules}/cli.py +20 -140
- tinybird/tb/modules/common.py +2110 -0
- tinybird/tb/modules/config.py +352 -0
- tinybird/{tb_cli_modules → tb/modules}/connection.py +4 -4
- tinybird/{tb_cli_modules → tb/modules}/create.py +20 -20
- tinybird/tb/modules/datafile/build.py +2103 -0
- tinybird/tb/modules/datafile/build_common.py +118 -0
- tinybird/tb/modules/datafile/build_datasource.py +403 -0
- tinybird/tb/modules/datafile/build_pipe.py +648 -0
- tinybird/tb/modules/datafile/common.py +897 -0
- tinybird/tb/modules/datafile/diff.py +197 -0
- tinybird/tb/modules/datafile/exceptions.py +23 -0
- tinybird/tb/modules/datafile/format_common.py +66 -0
- tinybird/tb/modules/datafile/format_datasource.py +160 -0
- tinybird/tb/modules/datafile/format_pipe.py +195 -0
- tinybird/tb/modules/datafile/parse_datasource.py +41 -0
- tinybird/tb/modules/datafile/parse_pipe.py +69 -0
- tinybird/tb/modules/datafile/pipe_checker.py +560 -0
- tinybird/tb/modules/datafile/pull.py +157 -0
- tinybird/{tb_cli_modules → tb/modules}/datasource.py +7 -6
- tinybird/tb/modules/exceptions.py +91 -0
- tinybird/{tb_cli_modules → tb/modules}/fmt.py +6 -3
- tinybird/{tb_cli_modules → tb/modules}/job.py +3 -3
- tinybird/{tb_cli_modules → tb/modules}/llm.py +1 -1
- tinybird/{tb_cli_modules → tb/modules}/local.py +9 -5
- tinybird/{tb_cli_modules → tb/modules}/mock.py +5 -5
- tinybird/{tb_cli_modules → tb/modules}/pipe.py +11 -5
- tinybird/{tb_cli_modules → tb/modules}/prompts.py +1 -1
- tinybird/tb/modules/regions.py +9 -0
- tinybird/{tb_cli_modules → tb/modules}/tag.py +2 -2
- tinybird/tb/modules/telemetry.py +310 -0
- tinybird/{tb_cli_modules → tb/modules}/test.py +5 -5
- tinybird/{tb_cli_modules → tb/modules}/tinyunit/tinyunit.py +1 -1
- tinybird/{tb_cli_modules → tb/modules}/token.py +3 -3
- tinybird/{tb_cli_modules → tb/modules}/workspace.py +5 -5
- tinybird/{tb_cli_modules → tb/modules}/workspace_members.py +4 -4
- tinybird/tb_cli_modules/common.py +9 -25
- tinybird/tb_cli_modules/config.py +0 -8
- {tinybird-0.0.1.dev5.dist-info → tinybird-0.0.1.dev7.dist-info}/METADATA +1 -1
- tinybird-0.0.1.dev7.dist-info/RECORD +71 -0
- tinybird-0.0.1.dev7.dist-info/entry_points.txt +2 -0
- tinybird/datafile.py +0 -6123
- tinybird/tb_cli.py +0 -28
- tinybird-0.0.1.dev5.dist-info/RECORD +0 -52
- tinybird-0.0.1.dev5.dist-info/entry_points.txt +0 -2
- /tinybird/{tb_cli_modules → tb/modules}/table.py +0 -0
- /tinybird/{tb_cli_modules → tb/modules}/tinyunit/tinyunit_lib.py +0 -0
- {tinybird-0.0.1.dev5.dist-info → tinybird-0.0.1.dev7.dist-info}/WHEEL +0 -0
- {tinybird-0.0.1.dev5.dist-info → tinybird-0.0.1.dev7.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
from copy import deepcopy
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from enum import Enum
|
|
6
|
+
from typing import Any, Dict, List, Optional, Tuple, Union
|
|
7
|
+
from urllib.parse import urlparse
|
|
8
|
+
|
|
9
|
+
from packaging import version
|
|
10
|
+
|
|
11
|
+
import tinybird.client as tbc
|
|
12
|
+
from tinybird.config import CURRENT_VERSION, DEFAULT_API_HOST, DEFAULT_LOCALHOST
|
|
13
|
+
|
|
14
|
+
APP_CONFIG_NAME = "tinybird"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class FeatureFlags:
|
|
18
|
+
@classmethod
|
|
19
|
+
def ignore_sql_errors(cls) -> bool: # Context: #1155
|
|
20
|
+
return "TB_IGNORE_SQL_ERRORS" in os.environ
|
|
21
|
+
|
|
22
|
+
@classmethod
|
|
23
|
+
def is_localhost(cls) -> bool:
|
|
24
|
+
return "SET_LOCALHOST" in os.environ
|
|
25
|
+
|
|
26
|
+
@classmethod
|
|
27
|
+
def ignore_ssl_errors(cls) -> bool:
|
|
28
|
+
return os.environ.get("TB_DISABLE_SSL_CHECKS", "0") == "1"
|
|
29
|
+
|
|
30
|
+
@classmethod
|
|
31
|
+
def send_telemetry(cls) -> bool:
|
|
32
|
+
if os.environ.get("TB_CLI_TELEMETRY_OPTOUT", "0") == "1":
|
|
33
|
+
return False
|
|
34
|
+
if "x.y.z" in CURRENT_VERSION and os.environ.get("TB_CLI_TELEMETRY_SEND_IN_LOCAL", "0") == "0":
|
|
35
|
+
return False
|
|
36
|
+
return True
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def compare_versions(a: str, b: str) -> int:
|
|
40
|
+
va = version.parse(a)
|
|
41
|
+
vb = version.parse(b)
|
|
42
|
+
return -1 if va < vb else (1 if va > vb else 0)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class ConfigValueOrigin(Enum):
|
|
46
|
+
# Sources for config values (environment variables, .tinyb file or default value)
|
|
47
|
+
|
|
48
|
+
ENVIRONMENT: str = "env"
|
|
49
|
+
CONFIG: str = "conf"
|
|
50
|
+
DEFAULT: str = "default"
|
|
51
|
+
NONE: str = ""
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@dataclass
|
|
55
|
+
class ConfigValue:
|
|
56
|
+
name: str
|
|
57
|
+
value: Any
|
|
58
|
+
origin: ConfigValueOrigin
|
|
59
|
+
|
|
60
|
+
def as_tuple(self) -> Tuple[str, str]:
|
|
61
|
+
return (self.name, self.value)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def write_json_file(data: Dict[str, Any], path: str) -> None:
|
|
65
|
+
with open(path, "w") as file:
|
|
66
|
+
file.write(json.dumps(data, indent=4, sort_keys=True))
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class CLIConfig:
|
|
70
|
+
# Mapping between environment variables and config values
|
|
71
|
+
ENV_KEYS: Dict[str, str] = {
|
|
72
|
+
"token": "TB_TOKEN",
|
|
73
|
+
"user_token": "TB_USER_TOKEN",
|
|
74
|
+
"host": "TB_HOST",
|
|
75
|
+
"semver": "TB_SEMVER",
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
DEFAULTS: Dict[str, str] = {"host": DEFAULT_API_HOST if not FeatureFlags.is_localhost() else DEFAULT_LOCALHOST}
|
|
79
|
+
|
|
80
|
+
_global: Optional["CLIConfig"] = None
|
|
81
|
+
_projects: Dict[str, "CLIConfig"] = {}
|
|
82
|
+
|
|
83
|
+
def __init__(self, path: Optional[str], parent: Optional["CLIConfig"] = None) -> None:
|
|
84
|
+
self._path = path
|
|
85
|
+
self._parent = parent
|
|
86
|
+
self._values: Dict[str, ConfigValue] = {}
|
|
87
|
+
self._values["version"] = ConfigValue("version", CURRENT_VERSION, ConfigValueOrigin.DEFAULT)
|
|
88
|
+
self._workspaces: List[Dict[str, Any]] = []
|
|
89
|
+
|
|
90
|
+
if self._path:
|
|
91
|
+
self.override_with_file(self._path)
|
|
92
|
+
self.override_with_environment()
|
|
93
|
+
self.override_with_defaults()
|
|
94
|
+
|
|
95
|
+
def __str__(self) -> str:
|
|
96
|
+
return str(self.to_dict())
|
|
97
|
+
|
|
98
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
99
|
+
"""Helper to ease"""
|
|
100
|
+
result: Dict[str, Any] = self._parent.to_dict() if self._parent else {}
|
|
101
|
+
result.update(dict((v.name, deepcopy(v.value)) for v in self._values.values()))
|
|
102
|
+
return result
|
|
103
|
+
|
|
104
|
+
def __getitem__(self, key) -> Any:
|
|
105
|
+
"""Gets a config key value in this order:
|
|
106
|
+
- Environment
|
|
107
|
+
- Internal dict (by host)
|
|
108
|
+
- Parent dict (if has a parent)
|
|
109
|
+
- Default values
|
|
110
|
+
"""
|
|
111
|
+
if key in self._values:
|
|
112
|
+
return self._values[key].value
|
|
113
|
+
if self._parent:
|
|
114
|
+
return self._parent[key]
|
|
115
|
+
raise KeyError(key)
|
|
116
|
+
|
|
117
|
+
def __setitem__(self, key: str, value: Any) -> None:
|
|
118
|
+
self._values[key] = ConfigValue(key, value, ConfigValueOrigin.CONFIG)
|
|
119
|
+
|
|
120
|
+
def __contains__(self, key: str) -> bool:
|
|
121
|
+
return self.get_value_origin(key) != ConfigValueOrigin.NONE
|
|
122
|
+
|
|
123
|
+
def get(self, key: str, default: Any = None) -> Any:
|
|
124
|
+
try:
|
|
125
|
+
return self[key]
|
|
126
|
+
except KeyError:
|
|
127
|
+
return default
|
|
128
|
+
|
|
129
|
+
def get_value_origin(self, key: str) -> ConfigValueOrigin:
|
|
130
|
+
if key in self._values:
|
|
131
|
+
return self._values[key].origin
|
|
132
|
+
if self._parent:
|
|
133
|
+
return self._parent.get_value_origin(key)
|
|
134
|
+
else:
|
|
135
|
+
return ConfigValueOrigin.NONE
|
|
136
|
+
|
|
137
|
+
def persist_to_file(self, override_with_values: Optional["CLIConfig"] = None) -> None:
|
|
138
|
+
if not self._path:
|
|
139
|
+
raise ValueError("Cannot persist configuration: `path` is None")
|
|
140
|
+
|
|
141
|
+
if override_with_values:
|
|
142
|
+
self.update(override_with_values)
|
|
143
|
+
|
|
144
|
+
os.makedirs(os.path.dirname(self._path), exist_ok=True)
|
|
145
|
+
values = dict(v.as_tuple() for v in self._values.values())
|
|
146
|
+
write_json_file(values, self._path)
|
|
147
|
+
|
|
148
|
+
def override_with_file(self, path: str) -> bool:
|
|
149
|
+
"""Loads the contents of the passed file."""
|
|
150
|
+
try:
|
|
151
|
+
with open(path) as file:
|
|
152
|
+
values: Dict[str, Any] = json.loads(file.read())
|
|
153
|
+
for k, v in values.items():
|
|
154
|
+
self[k] = v
|
|
155
|
+
return True
|
|
156
|
+
except IOError:
|
|
157
|
+
return False
|
|
158
|
+
|
|
159
|
+
def override_with_environment(self) -> None:
|
|
160
|
+
"""Loads environment variables."""
|
|
161
|
+
for config_key, env_key in CLIConfig.ENV_KEYS.items():
|
|
162
|
+
env_value = os.environ.get(env_key, None)
|
|
163
|
+
if env_value:
|
|
164
|
+
self._values[config_key] = ConfigValue(config_key, env_value, ConfigValueOrigin.ENVIRONMENT)
|
|
165
|
+
|
|
166
|
+
def override_with_defaults(self) -> None:
|
|
167
|
+
"""Loads default values."""
|
|
168
|
+
for key, default_value in CLIConfig.DEFAULTS.items():
|
|
169
|
+
if key not in self._values:
|
|
170
|
+
self._values[key] = ConfigValue(key, default_value, ConfigValueOrigin.DEFAULT)
|
|
171
|
+
|
|
172
|
+
def set_token(self, token: Optional[str]) -> None:
|
|
173
|
+
self["token"] = token
|
|
174
|
+
|
|
175
|
+
def get_token(self) -> Optional[str]:
|
|
176
|
+
try:
|
|
177
|
+
return self["token"]
|
|
178
|
+
except KeyError:
|
|
179
|
+
return None
|
|
180
|
+
|
|
181
|
+
def set_semver(self, semver: Optional[str]) -> None:
|
|
182
|
+
self["semver"] = semver
|
|
183
|
+
|
|
184
|
+
def get_semver(self) -> Optional[str]:
|
|
185
|
+
try:
|
|
186
|
+
return self["semver"]
|
|
187
|
+
except KeyError:
|
|
188
|
+
return None
|
|
189
|
+
|
|
190
|
+
def set_token_for_host(self, token: Optional[str], host: Optional[str]) -> None:
|
|
191
|
+
"""Sets the token for the specified host.
|
|
192
|
+
|
|
193
|
+
Here we ask for the host explicitly to avoid mistakenly setting the token
|
|
194
|
+
for the wrong host.
|
|
195
|
+
"""
|
|
196
|
+
if not token and not host:
|
|
197
|
+
return
|
|
198
|
+
|
|
199
|
+
assert isinstance(host, str)
|
|
200
|
+
|
|
201
|
+
tokens: Dict[str, Optional[str]] = self.get("tokens", {})
|
|
202
|
+
tokens[host] = token
|
|
203
|
+
self["tokens"] = tokens
|
|
204
|
+
|
|
205
|
+
def get_token_for_host(self, host: str) -> Optional[str]:
|
|
206
|
+
try:
|
|
207
|
+
return self["tokens"][host]
|
|
208
|
+
except KeyError:
|
|
209
|
+
return None
|
|
210
|
+
|
|
211
|
+
def set_user_token(self, token: Optional[str]) -> None:
|
|
212
|
+
self["user_token"] = token
|
|
213
|
+
|
|
214
|
+
def get_user_token(self) -> Optional[str]:
|
|
215
|
+
try:
|
|
216
|
+
return self["user_token"]
|
|
217
|
+
except KeyError:
|
|
218
|
+
return None
|
|
219
|
+
|
|
220
|
+
def set_host(self, host: Optional[str]) -> None:
|
|
221
|
+
url_info = urlparse(host)
|
|
222
|
+
scheme: str = url_info.scheme.decode() if isinstance(url_info.scheme, bytes) else url_info.scheme
|
|
223
|
+
netloc: str = url_info.netloc.decode() if isinstance(url_info.netloc, bytes) else url_info.netloc
|
|
224
|
+
self["host"] = f"{scheme}://{netloc}"
|
|
225
|
+
|
|
226
|
+
def get_host(self, use_defaults_if_needed: bool = False) -> Optional[str]:
|
|
227
|
+
result: Optional[str] = self.get("host", None)
|
|
228
|
+
if result:
|
|
229
|
+
return result
|
|
230
|
+
if use_defaults_if_needed:
|
|
231
|
+
return CLIConfig.DEFAULTS["host"]
|
|
232
|
+
return None
|
|
233
|
+
|
|
234
|
+
def get_client(self, token: Optional[str] = None, host: Optional[str] = None) -> tbc.TinyB:
|
|
235
|
+
"""Returns a new TinyB client configured with:
|
|
236
|
+
|
|
237
|
+
- token:
|
|
238
|
+
- passed token
|
|
239
|
+
- OR the user token
|
|
240
|
+
- OR the current admin token
|
|
241
|
+
|
|
242
|
+
- host:
|
|
243
|
+
- passed host
|
|
244
|
+
- OR the current host
|
|
245
|
+
"""
|
|
246
|
+
host = host or self.get_host()
|
|
247
|
+
assert isinstance(host, str)
|
|
248
|
+
token = token or self.get_token() or self.get_token_for_host(host) or ""
|
|
249
|
+
assert isinstance(token, str)
|
|
250
|
+
|
|
251
|
+
return tbc.TinyB(
|
|
252
|
+
token,
|
|
253
|
+
host,
|
|
254
|
+
version=CURRENT_VERSION,
|
|
255
|
+
disable_ssl_checks=FeatureFlags.ignore_ssl_errors(),
|
|
256
|
+
send_telemetry=FeatureFlags.send_telemetry(),
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
def get_user_client(self, host: Optional[str] = None) -> tbc.TinyB:
|
|
260
|
+
return self.get_client(self.get_user_token(), host)
|
|
261
|
+
|
|
262
|
+
def set_workspace_token(self, workspace_id: str, token: str) -> None:
|
|
263
|
+
pass
|
|
264
|
+
|
|
265
|
+
async def get_workspace_token(
|
|
266
|
+
self, workspace_id: Optional[str] = None, host: Optional[str] = None
|
|
267
|
+
) -> Optional[str]:
|
|
268
|
+
"""Returns the token for the specific workspace on a host.
|
|
269
|
+
- If no workspace passed, it uses the current active workspace.
|
|
270
|
+
- If no host is passed, it uses the current one.
|
|
271
|
+
"""
|
|
272
|
+
|
|
273
|
+
# First, try to get any saved token for the host
|
|
274
|
+
if host:
|
|
275
|
+
try:
|
|
276
|
+
return self["tokens"][host]
|
|
277
|
+
except KeyError:
|
|
278
|
+
pass
|
|
279
|
+
|
|
280
|
+
# If we don't have an user_token, can't continue
|
|
281
|
+
if not self.get_user_token():
|
|
282
|
+
return None
|
|
283
|
+
|
|
284
|
+
if not workspace_id:
|
|
285
|
+
workspace_id = self.get("id", None)
|
|
286
|
+
if not workspace_id:
|
|
287
|
+
return None
|
|
288
|
+
|
|
289
|
+
client: tbc.TinyB = self.get_client(token=self.get_user_token(), host=host)
|
|
290
|
+
|
|
291
|
+
info: Dict[str, Any] = await client.user_workspaces_and_branches()
|
|
292
|
+
workspaces: List[Dict[str, Any]] = info["workspaces"]
|
|
293
|
+
|
|
294
|
+
result: Optional[str] = next(
|
|
295
|
+
(w["token"] for w in workspaces if workspace_id in (w.get("id"), w.get("name"))), None
|
|
296
|
+
)
|
|
297
|
+
if host:
|
|
298
|
+
self.set_token_for_host(result, host)
|
|
299
|
+
|
|
300
|
+
return result
|
|
301
|
+
|
|
302
|
+
def spawn(self) -> "CLIConfig":
|
|
303
|
+
return CLIConfig(path=None, parent=self)
|
|
304
|
+
|
|
305
|
+
def update(self, other: Union["CLIConfig", Dict[str, Any]]):
|
|
306
|
+
values = other if isinstance(other, dict) else other._values
|
|
307
|
+
for k, v in values.items():
|
|
308
|
+
self[k] = v
|
|
309
|
+
|
|
310
|
+
@staticmethod
|
|
311
|
+
def get_global_config(_path: Optional[str] = None) -> "CLIConfig":
|
|
312
|
+
"""Returns the user-specific config.
|
|
313
|
+
|
|
314
|
+
The data is cached between calls, so feel free to use it freely instead
|
|
315
|
+
of saving a reference.
|
|
316
|
+
|
|
317
|
+
Note: the `_path` argument is mainly intended to help during testing.
|
|
318
|
+
"""
|
|
319
|
+
if not CLIConfig._global:
|
|
320
|
+
path: Optional[str] = _path or os.environ.get("XDG_CONFIG_HOME", None)
|
|
321
|
+
if not path:
|
|
322
|
+
path = os.path.join(os.environ.get("HOME", "~"), ".config")
|
|
323
|
+
path = os.path.join(path, APP_CONFIG_NAME, ".tinyb")
|
|
324
|
+
CLIConfig._global = CLIConfig(path, parent=None)
|
|
325
|
+
return CLIConfig._global
|
|
326
|
+
|
|
327
|
+
@staticmethod
|
|
328
|
+
def get_project_config(working_dir: Optional[str] = None) -> "CLIConfig":
|
|
329
|
+
"""Returns the project-specific config located at `working_dir` (defaults to `os.getcwd()`)
|
|
330
|
+
|
|
331
|
+
The data is cached between calls, given the same `working_dir`.
|
|
332
|
+
"""
|
|
333
|
+
working_dir = working_dir or os.getcwd()
|
|
334
|
+
result = CLIConfig._projects.get(working_dir)
|
|
335
|
+
if not result:
|
|
336
|
+
path: str = os.path.join(working_dir, ".tinyb")
|
|
337
|
+
result = CLIConfig(path, parent=CLIConfig.get_global_config())
|
|
338
|
+
CLIConfig._projects[working_dir] = result
|
|
339
|
+
return result
|
|
340
|
+
|
|
341
|
+
@staticmethod
|
|
342
|
+
def get_llm_config(working_dir: Optional[str] = None) -> Dict[str, Any]:
|
|
343
|
+
return (
|
|
344
|
+
CLIConfig.get_project_config(working_dir)
|
|
345
|
+
.get("llms", {})
|
|
346
|
+
.get("openai", {"model": "gpt-4o-mini", "api_key": None})
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
@staticmethod
|
|
350
|
+
def reset() -> None:
|
|
351
|
+
CLIConfig._global = None
|
|
352
|
+
CLIConfig._projects.clear()
|
|
@@ -14,8 +14,8 @@ from click import Context
|
|
|
14
14
|
|
|
15
15
|
from tinybird.client import DoesNotExistException, TinyB
|
|
16
16
|
from tinybird.feedback_manager import FeedbackManager
|
|
17
|
-
from tinybird.
|
|
18
|
-
from tinybird.
|
|
17
|
+
from tinybird.tb.modules.cli import cli
|
|
18
|
+
from tinybird.tb.modules.common import (
|
|
19
19
|
ConnectionReplacements,
|
|
20
20
|
DataConnectorType,
|
|
21
21
|
_get_setting_value,
|
|
@@ -33,8 +33,8 @@ from tinybird.tb_cli_modules.common import (
|
|
|
33
33
|
validate_kafka_secret,
|
|
34
34
|
validate_string_connector_param,
|
|
35
35
|
)
|
|
36
|
-
from tinybird.
|
|
37
|
-
from tinybird.
|
|
36
|
+
from tinybird.tb.modules.exceptions import CLIConnectionException
|
|
37
|
+
from tinybird.tb.modules.telemetry import is_ci_environment
|
|
38
38
|
|
|
39
39
|
DATA_CONNECTOR_SETTINGS: Dict[DataConnectorType, List[str]] = {
|
|
40
40
|
DataConnectorType.KAFKA: [
|
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
import os
|
|
2
2
|
from os import getcwd
|
|
3
3
|
from pathlib import Path
|
|
4
|
-
from typing import
|
|
4
|
+
from typing import Optional
|
|
5
5
|
|
|
6
6
|
import click
|
|
7
7
|
from click import Context
|
|
8
8
|
|
|
9
9
|
from tinybird.client import TinyB
|
|
10
|
-
from tinybird.datafile import folder_build
|
|
11
10
|
from tinybird.feedback_manager import FeedbackManager
|
|
12
|
-
from tinybird.
|
|
13
|
-
from tinybird.
|
|
14
|
-
from tinybird.
|
|
15
|
-
from tinybird.
|
|
16
|
-
from tinybird.
|
|
17
|
-
from tinybird.
|
|
11
|
+
from tinybird.tb.modules.cicd import init_cicd
|
|
12
|
+
from tinybird.tb.modules.cli import cli
|
|
13
|
+
from tinybird.tb.modules.common import _generate_datafile, coro, generate_datafile, push_data
|
|
14
|
+
from tinybird.tb.modules.config import CLIConfig
|
|
15
|
+
from tinybird.tb.modules.datafile.build import folder_build
|
|
16
|
+
from tinybird.tb.modules.exceptions import CLIDatasourceException
|
|
17
|
+
from tinybird.tb.modules.llm import LLM
|
|
18
|
+
from tinybird.tb.modules.local import (
|
|
18
19
|
get_tinybird_local_client,
|
|
19
20
|
)
|
|
20
21
|
|
|
@@ -54,15 +55,10 @@ async def create(
|
|
|
54
55
|
click.echo(FeedbackManager.gray(message="Creating new project structure..."))
|
|
55
56
|
await project_create(tb_client, data, prompt, folder)
|
|
56
57
|
click.echo(FeedbackManager.success(message="✓ Scaffolding completed!\n"))
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
tb_client,
|
|
62
|
-
workspaces,
|
|
63
|
-
datasources,
|
|
64
|
-
pipes,
|
|
65
|
-
)
|
|
58
|
+
await folder_build(tb_client, folder=folder)
|
|
59
|
+
|
|
60
|
+
await init_cicd(data_project_dir=os.path.relpath(folder))
|
|
61
|
+
|
|
66
62
|
if data:
|
|
67
63
|
ds_name = os.path.basename(data.split(".")[0])
|
|
68
64
|
await append_datasource(ctx, tb_client, ds_name, data, None, None, False, 1)
|
|
@@ -98,8 +94,10 @@ async def project_create(
|
|
|
98
94
|
except FileExistsError:
|
|
99
95
|
click.echo(FeedbackManager.info_path_created(path=x))
|
|
100
96
|
|
|
101
|
-
def generate_pipe_file(name: str, content: str):
|
|
97
|
+
def generate_pipe_file(name: str, content: str, parent_dir: Optional[str] = None):
|
|
102
98
|
base = Path("endpoints")
|
|
99
|
+
if parent_dir:
|
|
100
|
+
base = Path(parent_dir) / base
|
|
103
101
|
if not base.exists():
|
|
104
102
|
base = Path()
|
|
105
103
|
f = base / (f"{name}.pipe")
|
|
@@ -164,8 +162,10 @@ SQL >
|
|
|
164
162
|
LIMIT 5
|
|
165
163
|
TYPE ENDPOINT
|
|
166
164
|
"""
|
|
167
|
-
generate_datafile(
|
|
168
|
-
|
|
165
|
+
generate_datafile(
|
|
166
|
+
events_ds, filename="events.datasource", data=None, _format="ndjson", force=force, parent_dir=folder
|
|
167
|
+
)
|
|
168
|
+
generate_pipe_file("top_airlines", top_airlines, parent_dir=folder)
|
|
169
169
|
|
|
170
170
|
|
|
171
171
|
async def append_datasource(
|