tinybird-cli 4.0.1.dev0__py3-none-any.whl → 4.0.1.dev2__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.
tinybird/__cli__.py CHANGED
@@ -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__ = '4.0.1.dev0'
8
- __revision__ = '2edda96'
7
+ __version__ = '4.0.1.dev2'
8
+ __revision__ = '2f7367b'
tinybird/client.py CHANGED
@@ -209,11 +209,21 @@ class TinyB(object):
209
209
  return None
210
210
 
211
211
  async def create_token(
212
- self, name: str, scope: str, origin_code: Optional[str], origin_resource_name_or_id: Optional[str]
212
+ self, name: str, scope: List[str], origin_code: Optional[str], origin_resource_name_or_id: Optional[str] = None
213
213
  ):
214
214
  origin = origin_code or "C" # == Origins.CUSTOM if none specified
215
+ params = {
216
+ "name": name,
217
+ "origin": origin,
218
+ }
219
+ if origin_resource_name_or_id:
220
+ params["resource_id"] = origin_resource_name_or_id
221
+
222
+ # TODO: We should support sending multiple scopes in the body of the request
223
+ url = f"/v0/tokens?{urlencode(params)}"
224
+ url = url + "&" + "&".join([f"scope={scope}" for scope in scope])
215
225
  return await self._req(
216
- f"/v0/tokens?name={name}&scope={scope}&origin={origin}&resource_id={origin_resource_name_or_id}",
226
+ url,
217
227
  method="POST",
218
228
  data="",
219
229
  )
@@ -1163,6 +1173,11 @@ class TinyB(object):
1163
1173
  params = self._token_to_params(token)
1164
1174
  return await self._req(f"/v0/tokens?{params}", method="POST", data="")
1165
1175
 
1176
+ async def create_jwt_token(self, name: str, expiration_time: int, scopes: List[Dict[str, Any]]):
1177
+ url_params = {"name": name, "expiration_time": expiration_time}
1178
+ body = json.dumps({"scopes": scopes})
1179
+ return await self._req(f"/v0/tokens?{urlencode(url_params)}", method="POST", data=body)
1180
+
1166
1181
  async def token_update(self, token: Dict[str, Any]):
1167
1182
  name = token["name"]
1168
1183
  params = self._token_to_params(token)
tinybird/context.py CHANGED
@@ -12,7 +12,6 @@ origin: ContextVar[str] = ContextVar("origin")
12
12
  request_id: ContextVar[str] = ContextVar("request_id")
13
13
  engine: ContextVar[str] = ContextVar("engine")
14
14
  wait_parameter: ContextVar[bool] = ContextVar("wait_parameter")
15
- wait_for_gatherer: ContextVar[bool] = ContextVar("wait_for_gatherer")
16
15
  api_host: ContextVar[str] = ContextVar("api_host")
17
16
  ff_split_to_array_escape: ContextVar[bool] = ContextVar("ff_split_to_array_escape")
18
17
  ff_preprocess_parameters_circuit_breaker: ContextVar[bool] = ContextVar("ff_preprocess_parameters_circuit_breaker")
tinybird/datafile.py CHANGED
@@ -2785,7 +2785,7 @@ async def new_pipe(
2785
2785
  click.echo(FeedbackManager.info_create_not_found_token(token=token_name))
2786
2786
  try:
2787
2787
  r = await tb_client.create_token(
2788
- token_name, f"PIPES:{tk['permissions']}:{p['name']}", "P", p["name"]
2788
+ token_name, [f"PIPES:{tk['permissions']}:{p['name']}"], "P", p["name"]
2789
2789
  )
2790
2790
  token = r["token"] # type: ignore
2791
2791
  except Exception as e:
@@ -2896,7 +2896,7 @@ async def new_ds(
2896
2896
  token_name = tk["token_name"]
2897
2897
  click.echo(FeedbackManager.info_create_not_found_token(token=token_name))
2898
2898
  # DS == token_origin.Origins.DATASOURCE
2899
- await client.create_token(token_name, f"DATASOURCES:{tk['permissions']}:{ds_name}", "DS", ds_name)
2899
+ await client.create_token(token_name, [f"DATASOURCES:{tk['permissions']}:{ds_name}"], "DS", ds_name)
2900
2900
  else:
2901
2901
  click.echo(FeedbackManager.info_create_found_token(token=token_name))
2902
2902
  scopes = [f"DATASOURCES:{tk['permissions']}:{ds_name}"]
@@ -348,6 +348,13 @@ class FeedbackManager:
348
348
  "{connector} Data sources require a post-release deployment. Increment the post-release number of the semver (for example: 0.0.1 -> 0.0.1-1) to do so. You can read more about post-releases at https://www.tinybird.co/docs/production/deployment-strategies"
349
349
  )
350
350
 
351
+ error_number_of_scopes_and_resources_mismatch = error_message(
352
+ "The number of --scope and --resource options must be the same"
353
+ )
354
+ error_number_of_fixed_params_and_resources_mismatch = error_message(
355
+ "The number of --fixed-params options must not exceed the number of --scope and --resource options."
356
+ )
357
+
351
358
  info_incl_relative_path = info_message("** Relative path {path} does not exist, skipping.")
352
359
  info_ignoring_incl_file = info_message(
353
360
  "** Ignoring file {filename}. .incl files are not checked independently. They are checked as part of the file that includes them. Please check the file that includes this .incl file."
@@ -422,6 +429,9 @@ Ready? """
422
429
  "You are going to manually update workspace commit reference manually, this is just for special occasions. Do you want to update current commit reference '{current_commit}' to '{new_commit}'?"
423
430
  )
424
431
 
432
+ warning_exchange = warning_message(
433
+ "Warning: Do you want to exchange Data Source {datasource_a} by Data Source {datasource_b}?"
434
+ )
425
435
  warning_no_test_results = warning_message("Warning: No test results to show")
426
436
  warning_using_branch_token = warning_message("** You're using the token defined in $TB_TOKEN.")
427
437
  warning_using_branch_host = warning_message("** You're using the token defined in $TB_HOST.")
@@ -17,7 +17,7 @@ class Provider(Enum):
17
17
 
18
18
  WORKFLOW_VERSION = "v3.1.0"
19
19
 
20
- DEFAULT_REQUIREMENTS_FILE = "tinybird-cli>=3,<4"
20
+ DEFAULT_REQUIREMENTS_FILE = "tinybird-cli>=4,<5"
21
21
 
22
22
  GITHUB_CI_YML = """
23
23
  ##################################################
@@ -1174,11 +1174,23 @@ class PlanName(Enum):
1174
1174
 
1175
1175
 
1176
1176
  def _get_workspace_plan_name(plan):
1177
+ """
1178
+ >>> _get_workspace_plan_name("dev")
1179
+ 'Build'
1180
+ >>> _get_workspace_plan_name("pro")
1181
+ 'Pro'
1182
+ >>> _get_workspace_plan_name("enterprise")
1183
+ 'Enterprise'
1184
+ >>> _get_workspace_plan_name("branch_enterprise")
1185
+ 'Enterprise'
1186
+ >>> _get_workspace_plan_name("other_plan")
1187
+ 'Custom'
1188
+ """
1177
1189
  if plan == "dev":
1178
1190
  return PlanName.DEV.value
1179
1191
  if plan == "pro":
1180
1192
  return PlanName.PRO.value
1181
- if plan == "enterprise":
1193
+ if plan in ("enterprise", "branch_enterprise"):
1182
1194
  return PlanName.ENTERPRISE.value
1183
1195
  return "Custom"
1184
1196
 
@@ -735,18 +735,20 @@ async def datasource_sync(ctx, datasource_name: str, yes: bool):
735
735
  raise CLIDatasourceException(FeedbackManager.error_syncing_datasource(datasource=datasource_name, error=str(e)))
736
736
 
737
737
 
738
- @datasource.command(name="exchange", hidden=True)
738
+ @datasource.command(name="exchange")
739
739
  @click.argument("datasource_a", required=True)
740
740
  @click.argument("datasource_b", required=True)
741
+ @click.option("--yes", is_flag=True, default=False, help="Do not ask for confirmation")
741
742
  @click.pass_context
742
743
  @coro
743
- async def datasource_exchange(ctx, datasource_a, datasource_b):
744
+ async def datasource_exchange(ctx, datasource_a: str, datasource_b: str, yes: bool):
744
745
  """Exchange two data sources"""
745
746
 
746
747
  client = ctx.obj["client"]
747
748
 
748
749
  try:
749
- await client.datasource_exchange(datasource_a, datasource_b)
750
+ if yes or click.confirm(FeedbackManager.warning_exchange(datasource_a=datasource_a, datasource_b=datasource_b)):
751
+ await client.datasource_exchange(datasource_a, datasource_b)
750
752
  except Exception as e:
751
753
  raise CLIDatasourceException(FeedbackManager.error_exception(error=e))
752
754
 
@@ -1,8 +1,10 @@
1
- from typing import Any, Dict, Optional
1
+ from datetime import datetime, timedelta, timezone
2
+ from typing import Any, Dict, List, Optional
2
3
 
3
4
  import click
4
5
  import pyperclip
5
6
  from click import Context
7
+ from humanfriendly import parse_timespan
6
8
 
7
9
  from tinybird.client import TinyB
8
10
  from tinybird.feedback_manager import FeedbackManager
@@ -29,7 +31,7 @@ async def token_ls(
29
31
  ctx: Context,
30
32
  match: Optional[str] = None,
31
33
  ) -> None:
32
- """List tokens."""
34
+ """List static tokens."""
33
35
 
34
36
  obj: Dict[str, Any] = ctx.ensure_object(dict)
35
37
  client: TinyB = obj["client"]
@@ -52,7 +54,7 @@ async def token_ls(
52
54
  @click.pass_context
53
55
  @coro
54
56
  async def token_rm(ctx: Context, token_id: str, yes: bool) -> None:
55
- """Remove a token."""
57
+ """Remove a static token."""
56
58
 
57
59
  obj: Dict[str, Any] = ctx.ensure_object(dict)
58
60
  client: TinyB = obj["client"]
@@ -72,7 +74,7 @@ async def token_rm(ctx: Context, token_id: str, yes: bool) -> None:
72
74
  @click.pass_context
73
75
  @coro
74
76
  async def token_refresh(ctx: Context, token_id: str, yes: bool) -> None:
75
- """Refresh a token."""
77
+ """Refresh a static token."""
76
78
 
77
79
  obj: Dict[str, Any] = ctx.ensure_object(dict)
78
80
  client: TinyB = obj["client"]
@@ -91,7 +93,7 @@ async def token_refresh(ctx: Context, token_id: str, yes: bool) -> None:
91
93
  @click.pass_context
92
94
  @coro
93
95
  async def token_scopes(ctx: Context, token_id: str) -> None:
94
- """List token scopes."""
96
+ """List static token scopes."""
95
97
 
96
98
  obj: Dict[str, Any] = ctx.ensure_object(dict)
97
99
  client: TinyB = obj["client"]
@@ -112,7 +114,7 @@ async def token_scopes(ctx: Context, token_id: str) -> None:
112
114
  @click.pass_context
113
115
  @coro
114
116
  async def token_copy(ctx: Context, token_id: str) -> None:
115
- """Copy a token."""
117
+ """Copy a static token."""
116
118
 
117
119
  obj: Dict[str, Any] = ctx.ensure_object(dict)
118
120
  client: TinyB = obj["client"]
@@ -125,3 +127,209 @@ async def token_copy(ctx: Context, token_id: str) -> None:
125
127
  except Exception as e:
126
128
  raise CLITokenException(FeedbackManager.error_exception(error=e))
127
129
  click.echo(FeedbackManager.success_copy_token(token=token_id))
130
+
131
+
132
+ def parse_ttl(ctx, param, value):
133
+ if value is None:
134
+ return None
135
+ try:
136
+ seconds = parse_timespan(value)
137
+ return timedelta(seconds=seconds)
138
+ except ValueError:
139
+ raise click.BadParameter(f"Invalid time to live format: {value}")
140
+
141
+
142
+ def parse_fixed_params(fixed_params_list):
143
+ parsed_params = []
144
+ for fixed_param in fixed_params_list:
145
+ param_dict = {}
146
+ for param in fixed_param.split(","):
147
+ key, value = param.split("=")
148
+ param_dict[key] = value
149
+ parsed_params.append(param_dict)
150
+ return parsed_params
151
+
152
+
153
+ @token.group(hidden=True)
154
+ @click.pass_context
155
+ def create(ctx: Context) -> None:
156
+ """Token creation commands.
157
+
158
+ You can create two types of tokens: JWT or Static.
159
+
160
+ * JWT tokens have a TTL and can only have the PIPES:READ scope.Their main use case is allow your users to call your endpoints without exposing your API key.
161
+
162
+ * Static tokens do not have a TTL and can have any valid scope (DATASOURCES:READ, DATASOURCES:APPEND, DATASOURCES:CREATE, DATASOURCES:DROP, PIPES:CREATE, PIPES:READ, PIPES:DROP).
163
+
164
+ Examples:
165
+
166
+ tb token create jwt my_jwt_token --ttl 1h --scope PIPES:READ --resource my_pipe
167
+
168
+ tb token create static my_static_token --scope PIPES:READ --resource my_pipe
169
+
170
+ tb token create static my_static_token --scope DATASOURCES:READ --resource my_datasource
171
+
172
+ tb token create static my_static_token --scope DATASOURCES:READ --resource my_datasource --filters "column_name=value"
173
+
174
+ """
175
+
176
+
177
+ @create.command(name="jwt")
178
+ @click.argument("name")
179
+ @click.option("--ttl", type=str, callback=parse_ttl, required=True, help="Time to live (e.g., '1h', '30min', '1d')")
180
+ @click.option(
181
+ "--scope",
182
+ multiple=True,
183
+ type=click.Choice(["PIPES:READ"]),
184
+ required=True,
185
+ help="Scope of the token (only PIPES:READ is allowed for JWT tokens)",
186
+ )
187
+ @click.option("--resource", multiple=True, required=True, help="Resource associated with the scope")
188
+ @click.option(
189
+ "--fixed-params", multiple=True, help="Fixed parameters in key=value format, multiple values separated by commas"
190
+ )
191
+ @click.pass_context
192
+ @coro
193
+ async def create_jwt_token(ctx: Context, name: str, ttl: timedelta, scope, resource, fixed_params) -> None:
194
+ """Create a JWT token with a TTL specify."""
195
+
196
+ obj: Dict[str, Any] = ctx.ensure_object(dict)
197
+ client: TinyB = obj["client"]
198
+
199
+ expiration_time = int((ttl + datetime.now(timezone.utc)).timestamp())
200
+ if len(scope) != len(resource):
201
+ raise CLITokenException(FeedbackManager.error_number_of_scopes_and_resources_mismatch())
202
+
203
+ # Ensure the number of fixed-params does not exceed the number of scope/resource pairs
204
+ if fixed_params and len(fixed_params) > len(scope):
205
+ raise CLITokenException(FeedbackManager.error_number_of_fixed_params_and_resources_mismatch())
206
+
207
+ # Parse fixed params
208
+ parsed_fixed_params = parse_fixed_params(fixed_params) if fixed_params else []
209
+
210
+ # Create a list of fixed params for each scope/resource pair, defaulting to empty dict if not provided
211
+ fixed_params_list: List[Dict[str, Any]] = [{}] * len(scope)
212
+ for i, params in enumerate(parsed_fixed_params):
213
+ fixed_params_list[i] = params
214
+
215
+ scopes = []
216
+ for sc, res, fparams in zip(scope, resource, fixed_params_list):
217
+ scopes.append(
218
+ {
219
+ "type": sc,
220
+ "resource": res,
221
+ "fixed_params": fparams,
222
+ }
223
+ )
224
+
225
+ try:
226
+ response = await client.create_jwt_token(name, expiration_time, scopes)
227
+ except Exception as e:
228
+ raise CLITokenException(FeedbackManager.error_exception(error=e))
229
+
230
+ click.echo("The token has been generated successfully.")
231
+ click.echo(
232
+ f"The token will expire at: {datetime.fromtimestamp(expiration_time).strftime('%Y-%m-%d %H:%M:%S')} UTC "
233
+ )
234
+ click.echo(f"The token is: {response['token']}")
235
+
236
+
237
+ # Valid scopes for static tokens
238
+ valid_scopes = [
239
+ "DATASOURCES:READ",
240
+ "DATASOURCES:APPEND",
241
+ "DATASOURCES:CREATE",
242
+ "DATASOURCES:DROP",
243
+ "PIPES:CREATE",
244
+ "PIPES:READ",
245
+ "PIPES:DROP",
246
+ ]
247
+
248
+
249
+ # As we are passing dynamic options to the command, we need to create a custom class to handle the help message
250
+ class DynamicOptionsCommand(click.Command):
251
+ def get_help(self, ctx):
252
+ # Usage
253
+ usage = "Usage: tb token create static [OPTIONS] NAME\n\n"
254
+ dynamic_options_help = usage
255
+
256
+ # Description
257
+ dynamic_options_help += " Create a static token that will live forever.\n\n"
258
+
259
+ # Options
260
+ dynamic_options_help += "Options:\n"
261
+ dynamic_options_help += f" --scope [{','.join(valid_scopes)}] Scope for the token [Required]\n"
262
+ dynamic_options_help += " --resource TEXT Resource you want to associate the scope with\n"
263
+ dynamic_options_help += " --filter TEXT SQL condition used to filter the values when calling with this token (eg. --filter=value > 0) \n"
264
+ dynamic_options_help += " -h, --help Show this message and exit.\n"
265
+
266
+ return dynamic_options_help
267
+
268
+
269
+ @create.command(
270
+ name="static", context_settings=dict(ignore_unknown_options=True, allow_extra_args=True), cls=DynamicOptionsCommand
271
+ )
272
+ @click.argument("name")
273
+ @click.pass_context
274
+ @coro
275
+ async def create_static_token(ctx, name: str):
276
+ """Create a static token."""
277
+ obj: Dict[str, Any] = ctx.ensure_object(dict)
278
+ client: TinyB = obj["client"]
279
+
280
+ args = ctx.args
281
+ scopes: List[Dict[str, str]] = []
282
+ current_scope = None
283
+
284
+ # We parse the arguments to get the scopes, resources and filters
285
+ # The arguments should be in the format --scope <scope> --resource <resource> --filter <filter>
286
+ i = 0
287
+ while i < len(args):
288
+ if args[i] == "--scope":
289
+ if current_scope:
290
+ scopes.append(current_scope)
291
+ current_scope = {}
292
+ current_scope = {"scope": args[i + 1]}
293
+ i += 2
294
+ elif args[i] == "--resource":
295
+ if current_scope is None:
296
+ raise click.BadParameter("Resource must follow a scope")
297
+ if "resource" in current_scope:
298
+ raise click.BadParameter(
299
+ "Resource already defined for this scope. The format is --scope <scope> --resource <resource> --filter <filter>"
300
+ )
301
+ current_scope["resource"] = args[i + 1]
302
+ i += 2
303
+ elif args[i] == "--filter":
304
+ if current_scope is None:
305
+ raise click.BadParameter("Filter must follow a scope")
306
+ if "filter" in current_scope:
307
+ raise click.BadParameter(
308
+ "Filter already defined for this scope. The format is --scope <scope> --resource <resource> --filter <filter>"
309
+ )
310
+ current_scope["filter"] = args[i + 1]
311
+ i += 2
312
+ else:
313
+ raise click.BadParameter(f"Unknown parameter {args[i]}")
314
+
315
+ if current_scope:
316
+ scopes.append(current_scope)
317
+
318
+ # Parse the scopes like `SCOPE:RESOURCE:FILTER` or `SCOPE:RESOURCE` or `SCOPE` as that's what the API expsects
319
+ scoped_parsed: List[str] = []
320
+ for scope in scopes:
321
+ if scope.get("resource") and scope.get("filter"):
322
+ scoped_parsed.append(f"{scope.get('scope')}:{scope.get('resource')}:{scope.get('filter')}")
323
+ elif scope.get("resource"):
324
+ scoped_parsed.append(f"{scope.get('scope')}:{scope.get('resource')}")
325
+ elif "scope" in scope:
326
+ scoped_parsed.append(scope.get("scope", ""))
327
+ else:
328
+ raise CLITokenException("Unknown error")
329
+
330
+ try:
331
+ await client.create_token(name, scoped_parsed, origin_code=None)
332
+ except Exception as e:
333
+ raise CLITokenException(FeedbackManager.error_exception(error=e))
334
+
335
+ click.echo("The token has been generated successfully.")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tinybird-cli
3
- Version: 4.0.1.dev0
3
+ Version: 4.0.1.dev2
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/cli/introduction.html
6
6
  Author: Tinybird
@@ -51,6 +51,23 @@ The Tinybird command-line tool allows you to use all the Tinybird functionality
51
51
  Changelog
52
52
  ----------
53
53
 
54
+
55
+ 4.0.1.dev2
56
+ ************
57
+
58
+ - `Added` `tb datasource exchange datasource_a datasource_b` to exchange to data sources
59
+
60
+ 4.0.1.dev1
61
+ ************
62
+
63
+ - `Added` command `tb token create` to be able to create JWT and permanent tokens
64
+
65
+ 4.0.1.dev0
66
+ ************
67
+
68
+ - `Fixed` Pin `tinybird-cli>=4,<5` in `requirements.txt` on `tb init --git`
69
+
70
+
54
71
  4.0.0
55
72
  ************
56
73
 
@@ -1,12 +1,12 @@
1
- tinybird/__cli__.py,sha256=GTpedov6iss74oPDg-THSysiSzh1YppfWjTuJ1KIcjk,254
1
+ tinybird/__cli__.py,sha256=RHmuIG_ohD-7EKHghLDWhgxfpgo7Ftyzx8_ovcJkqSA,254
2
2
  tinybird/check_pypi.py,sha256=_4NkharLyR_ELrAdit-ftqIWvOf7jZNPt3i76frlo9g,975
3
- tinybird/client.py,sha256=S7nKDUoq7AnAunZuikqrs9yjC3j_rseWeyGVwXHCiTI,46918
3
+ tinybird/client.py,sha256=uZSo3fGvvlpKzg8-vkE7oCRdOmy7KaEWJ2mIRZX7EYc,47549
4
4
  tinybird/config.py,sha256=E0jDwbFD1zhdijNhtF8fg6mqIyKbZ8xpNPP_3n2PFpE,2003
5
5
  tinybird/connectors.py,sha256=lkpVSUmSuViEZBa4QjTK7YmPHUop0a5UFoTrSmlVq6k,15244
6
- tinybird/context.py,sha256=3-k0xmNNWvo7xrBXzYKB661y7k75S9572yPcqZtKCEs,1127
7
- tinybird/datafile.py,sha256=Lw_rW3A_qrIVj7mdcwL8sNf2PuF6ZpY4f0Wlh-eof0Q,210873
6
+ tinybird/context.py,sha256=NiWhCFJeBEtvwCMbu60OdIBRRd6V9qY9aqYaHmeumbo,1057
7
+ tinybird/datafile.py,sha256=cd-ugs09RvLGKurPAN8hbKvYbITSnZzb0GI77gcjk1I,210877
8
8
  tinybird/datatypes.py,sha256=adYOQBTyfeBGVINIlaRex_81gTQQuqF2M9VTQpzq1H0,7060
9
- tinybird/feedback_manager.py,sha256=AQRLTyBb2cTfpSfhzJvtBFh5Fi7fZug9mhaXvhV09fc,60657
9
+ tinybird/feedback_manager.py,sha256=Hnv8ZYyCnGivjjL4RrzxiQxSoswFql4behEVkk_OuBo,61138
10
10
  tinybird/git_settings.py,sha256=vu8sWb3TAXeM8Tqy27aR0el8MnPm7kqQzTRV76xB0ro,4707
11
11
  tinybird/sql.py,sha256=hUnpfmkRkdyZV2ylIYflzxnwyv5Es7fLEv6cBc39OJA,41589
12
12
  tinybird/sql_template.py,sha256=WqVD-nM7lZUoELxSLi-6iiwILk7A9PRWdqjR3iNWC7k,81800
@@ -19,25 +19,25 @@ tinybird/ch_utils/constants.py,sha256=QtaTrpYsmxnlogsC57YEnkrbw22t9hx6ueCvl6qD9f
19
19
  tinybird/ch_utils/engine.py,sha256=KVxZdmNkUCYXaw74aZgpUd7n1E5AhLynsGnLx8pi7UM,39699
20
20
  tinybird/tb_cli_modules/auth.py,sha256=0CWMrOHxWQBxiyj6guUY7RBGUhSVZ2KbTKl1i9vV9xk,8601
21
21
  tinybird/tb_cli_modules/branch.py,sha256=x3nTWyCMgVTBD5ZayDP86jmrWvVphPmf75Pr8baU_QA,38296
22
- tinybird/tb_cli_modules/cicd.py,sha256=EJoZNzDzOQuhUEAPstfm7rG6EH7I_xGBVzEqxwtGLiE,13910
22
+ tinybird/tb_cli_modules/cicd.py,sha256=HYOINvm1eD37P-oF_BZ6v-A-KFoySRkkMjMGAuSfrqc,13910
23
23
  tinybird/tb_cli_modules/cli.py,sha256=7ce9J4VuaKiGRhFrKZB-MxwS4uVtczI4cGFSDV1hwIE,64576
24
- tinybird/tb_cli_modules/common.py,sha256=Zr_-6RCAnUt1ZoC1sZt4Zb_0AtxgCp_rafOh_WZ5-bY,78296
24
+ tinybird/tb_cli_modules/common.py,sha256=uQdsRot2nE4_a2S9Btd-T03uJCM2vhF2QEuB8FYIHqY,78632
25
25
  tinybird/tb_cli_modules/config.py,sha256=6NTgIdwf0X132A1j6G_YrdPep87ymZ9b5pABabKLzh4,11484
26
26
  tinybird/tb_cli_modules/connection.py,sha256=Pf-p9tr72vkWUUj5jgMVb4wFum5zCpFKAg697uK-qf0,32368
27
- tinybird/tb_cli_modules/datasource.py,sha256=ZNGEDT0X7EPusG29gZwVOqLMQvdt01X2lcVoGlORgx0,31968
27
+ tinybird/tb_cli_modules/datasource.py,sha256=NXECxbQUOe7L2BBhC5ZHxVefhQBP32fviXIfMBE6eRQ,32189
28
28
  tinybird/tb_cli_modules/exceptions.py,sha256=pmucP4kTF4irIt7dXiG-FcnI-o3mvDusPmch1L8RCWk,3367
29
29
  tinybird/tb_cli_modules/job.py,sha256=AG69LPb9MbobA1awwJFZJvxqarDKfRlsBjw2V1zvYqc,2964
30
30
  tinybird/tb_cli_modules/pipe.py,sha256=qA6G-O1d9zVzV7ZTGmSlsCa3MGM2JdG8JXOj3TKdeqM,26132
31
31
  tinybird/tb_cli_modules/regions.py,sha256=YHi2BmeSaFRGvBKexJczwmm93TDxkr0RNTDp2tzIcn0,2228
32
32
  tinybird/tb_cli_modules/telemetry.py,sha256=YgHU-osP034xN9LZC4iaakkdfKJCWqPYDGPZFrrZfPI,10490
33
33
  tinybird/tb_cli_modules/test.py,sha256=NzFWa3EqPrpNvwBggUQYI9d0LqWcCKpSpkD22cvqhOQ,4223
34
- tinybird/tb_cli_modules/token.py,sha256=ZGUUX8x6Qk9214L4wSmFw5FsP5ht_iuIrKDBkpaT8Bc,4383
34
+ tinybird/tb_cli_modules/token.py,sha256=zmgfJezGXCEJVwSpKZoeCam3c2bPz1ZRfEpWaurguEA,12345
35
35
  tinybird/tb_cli_modules/workspace.py,sha256=DTIQ1-H5Kazz1CtRwqMeYAtyQ_BQtaLXfcHnI9Ujj-c,11064
36
36
  tinybird/tb_cli_modules/workspace_members.py,sha256=3HzY43218fb35ZM81mtkwDQe7gAAf1zEFY6kIEhGvgE,8268
37
37
  tinybird/tb_cli_modules/tinyunit/tinyunit.py,sha256=0dYYmZMMJVubxSPls2e_a-fqtUYvgLfu2B0xwLfkbHw,11667
38
38
  tinybird/tb_cli_modules/tinyunit/tinyunit_lib.py,sha256=j92za8QbXrv4eIPjKBZPn9ghR-nYQ2wZZ88MeXyMWXE,1868
39
- tinybird_cli-4.0.1.dev0.dist-info/METADATA,sha256=r6MgyUIl-aueaVoTg3PIofDB5eydHgS4uTS6XTe3rR0,72245
40
- tinybird_cli-4.0.1.dev0.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
41
- tinybird_cli-4.0.1.dev0.dist-info/entry_points.txt,sha256=PKPKuPmA4IfJYnCFHHUiw-aAWZuBomFvwCklv1OyCjE,43
42
- tinybird_cli-4.0.1.dev0.dist-info/top_level.txt,sha256=pgw6AzERHBcW3YTi2PW4arjxLkulk2msOz_SomfOEuc,45
43
- tinybird_cli-4.0.1.dev0.dist-info/RECORD,,
39
+ tinybird_cli-4.0.1.dev2.dist-info/METADATA,sha256=mh_nIFAIJJe920uprsM5FtmGIHTIPPI3S4jOfCDVhrU,72572
40
+ tinybird_cli-4.0.1.dev2.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
41
+ tinybird_cli-4.0.1.dev2.dist-info/entry_points.txt,sha256=PKPKuPmA4IfJYnCFHHUiw-aAWZuBomFvwCklv1OyCjE,43
42
+ tinybird_cli-4.0.1.dev2.dist-info/top_level.txt,sha256=pgw6AzERHBcW3YTi2PW4arjxLkulk2msOz_SomfOEuc,45
43
+ tinybird_cli-4.0.1.dev2.dist-info/RECORD,,