tinybird 0.0.1.dev12__py3-none-any.whl → 0.0.1.dev14__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/tb/cli.py CHANGED
@@ -14,6 +14,7 @@ import tinybird.tb.modules.create
14
14
  import tinybird.tb.modules.datasource
15
15
  import tinybird.tb.modules.fmt
16
16
  import tinybird.tb.modules.job
17
+ import tinybird.tb.modules.login
17
18
  import tinybird.tb.modules.mock
18
19
  import tinybird.tb.modules.pipe
19
20
  import tinybird.tb.modules.tag
@@ -565,24 +565,17 @@ async def push(
565
565
  @click.option(
566
566
  "--folder", default=None, type=click.Path(exists=True, file_okay=False), help="Folder where files will be placed"
567
567
  )
568
- @click.option(
569
- "--auto/--no-auto",
570
- is_flag=True,
571
- default=True,
572
- help="Saves datafiles automatically into their default directories (/datasources or /pipes). Default is True",
573
- )
574
- @click.option("--match", default=None, help="Retrieve any resourcing matching the pattern. eg --match _test")
575
568
  @click.option("-f", "--force", is_flag=True, default=False, help="Override existing files")
576
569
  @click.option("--fmt", is_flag=True, default=False, help="Format files before saving")
577
570
  @click.pass_context
578
571
  @coro
579
- async def pull(ctx: Context, folder: str, auto: bool, match: Optional[str], force: bool, fmt: bool) -> None:
572
+ async def pull(ctx: Context, folder: str, force: bool, fmt: bool) -> None:
580
573
  """Retrieve latest versions for project files from Tinybird."""
581
574
 
582
575
  client = ctx.ensure_object(dict)["client"]
583
576
  folder = folder if folder else getcwd()
584
577
 
585
- return await folder_pull(client, folder, auto, match, force, fmt=fmt)
578
+ return await folder_pull(client, folder, force, fmt=fmt)
586
579
 
587
580
 
588
581
  @cli.command()
@@ -121,12 +121,12 @@ async def diff_command(
121
121
  if filenames:
122
122
  if len(filenames) == 1:
123
123
  filenames = [filenames[0], *get_project_filenames(filenames[0])]
124
- await folder_pull(client, target_dir, False, None, True, verbose=False)
124
+ await folder_pull(client, target_dir, True, verbose=False)
125
125
  else:
126
126
  filenames = get_project_filenames(".")
127
127
  if verbose:
128
128
  click.echo("Saving remote resources in .diff_tmp folder.\n")
129
- await folder_pull(client, target_dir, False, None, True, verbose=verbose, progress_bar=progress_bar)
129
+ await folder_pull(client, target_dir, True, verbose=verbose, progress_bar=progress_bar)
130
130
 
131
131
  remote_datasources: List[Dict[str, Any]] = await client.datasources()
132
132
  remote_pipes: List[Dict[str, Any]] = await client.pipes()
@@ -1,7 +1,7 @@
1
1
  import re
2
2
  from asyncio import Semaphore, gather
3
3
  from pathlib import Path
4
- from typing import Any, Dict, List, Optional
4
+ from typing import Any, Dict, List, Optional, Tuple
5
5
 
6
6
  import aiofiles
7
7
  import click
@@ -16,38 +16,39 @@ from tinybird.tb.modules.datafile.format_pipe import format_pipe
16
16
  async def folder_pull(
17
17
  client: TinyB,
18
18
  folder: str,
19
- auto: bool,
20
- match: Optional[str],
21
19
  force: bool,
22
20
  verbose: bool = True,
23
21
  progress_bar: bool = False,
24
22
  fmt: bool = False,
25
23
  ):
26
- pattern = re.compile(match) if match else None
27
-
28
- def _get_latest_versions(resources: List[str]):
24
+ def _get_latest_versions(resources: List[Tuple[str, str]]):
29
25
  versions: Dict[str, Any] = {}
30
26
 
31
- for x in resources:
27
+ for x, resource_type in resources:
32
28
  t = get_name_version(x)
33
29
  t["original_name"] = x
34
30
  if t["version"] is None:
35
31
  t["version"] = -1
36
32
  name = t["name"]
33
+ t["type"] = resource_type
37
34
 
38
35
  if name not in versions or name == x or versions[name]["version"] < t["version"]:
39
36
  versions[name] = t
40
37
  return versions
41
38
 
42
- def get_file_folder(extension: str):
43
- if not auto:
44
- return None
39
+ def get_file_folder(extension: str, resource_type: Optional[str]):
45
40
  if extension == "datasource":
46
41
  return "datasources"
42
+ if resource_type == "endpoint":
43
+ return "endpoints"
44
+ if resource_type == "sink":
45
+ return "sinks"
46
+ if resource_type == "copy":
47
+ return "copies"
48
+ if resource_type == "materialized":
49
+ return "materializations"
47
50
  if extension == "pipe":
48
51
  return "pipes"
49
- if extension == "token":
50
- return "tokens"
51
52
  return None
52
53
 
53
54
  async def write_files(
@@ -61,11 +62,6 @@ async def folder_pull(
61
62
  async def write_resource(k: Dict[str, Any]):
62
63
  name = f"{k['name']}.{extension}"
63
64
  try:
64
- if pattern and not pattern.search(name):
65
- if verbose:
66
- click.echo(FeedbackManager.info_skipping_resource(resource=name))
67
- return
68
-
69
65
  resource = await getattr(client, get_resource_function)(k["original_name"])
70
66
  resource_to_write = resource
71
67
 
@@ -76,11 +72,11 @@ async def folder_pull(
76
72
  resource_to_write = await format_pipe(name, content=resource)
77
73
 
78
74
  dest_folder = folder
79
- if "." in k["name"] and extension != "token":
75
+ if "." in k["name"]:
80
76
  dest_folder = Path(folder) / "vendor" / k["name"].split(".", 1)[0]
81
77
  name = f"{k['name'].split('.', 1)[1]}.{extension}"
82
78
 
83
- file_folder = get_file_folder(extension)
79
+ file_folder = get_file_folder(extension, k["type"])
84
80
  f = Path(dest_folder) / file_folder if file_folder is not None else Path(dest_folder)
85
81
 
86
82
  if not f.exists():
@@ -125,11 +121,11 @@ async def folder_pull(
125
121
 
126
122
  try:
127
123
  datasources = await client.datasources()
128
- remote_datasources = sorted([x["name"] for x in datasources])
124
+ remote_datasources = sorted([(x["name"], x.get("type", "csv")) for x in datasources], key=lambda x: x[0])
129
125
  datasources_versions = _get_latest_versions(remote_datasources)
130
126
 
131
127
  pipes = await client.pipes()
132
- remote_pipes = sorted([x["name"] for x in pipes])
128
+ remote_pipes = sorted([(pipe["name"], pipe.get("type", "default")) for pipe in pipes], key=lambda x: x[0])
133
129
  pipes_versions = _get_latest_versions(remote_pipes)
134
130
 
135
131
  resources = list(datasources_versions.keys()) + list(pipes_versions.keys())
@@ -0,0 +1,169 @@
1
+ import http.server
2
+ import socketserver
3
+ import threading
4
+ import time
5
+ import urllib.parse
6
+ import webbrowser
7
+ from urllib.parse import urlencode
8
+
9
+ import click
10
+ import requests
11
+
12
+ from tinybird.feedback_manager import FeedbackManager
13
+ from tinybird.tb.modules.cli import CLIConfig, cli
14
+
15
+
16
+ class AuthHandler(http.server.SimpleHTTPRequestHandler):
17
+ def do_GET(self):
18
+ # The access_token is in the URL fragment, which is not sent to the server
19
+ # We'll send a small HTML page that extracts the token and sends it back to the server
20
+ self.send_response(200)
21
+ self.send_header("Content-type", "text/html")
22
+ self.end_headers()
23
+ self.wfile.write(b"""
24
+ <html>
25
+ <head>
26
+ <style>
27
+ body {
28
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
29
+ background: #f5f5f5;
30
+ display: flex;
31
+ align-items: center;
32
+ justify-content: center;
33
+ height: 100vh;
34
+ margin: 0;
35
+ }
36
+ .message {
37
+ background: white;
38
+ padding: 2rem 3rem;
39
+ border-radius: 8px;
40
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
41
+ text-align: center;
42
+ display: flex;
43
+ flex-direction: column;
44
+ align-items: center;
45
+ }
46
+ h1 {
47
+ color: #25283D;
48
+ font-size: 1.2rem;
49
+ margin: 0;
50
+ font-weight: 500;
51
+ }
52
+ </style>
53
+ </head>
54
+ <body>
55
+ <div class="message">
56
+ <h1>Authenticating...</h1>
57
+ </div>
58
+ <script>
59
+ var hash = window.location.hash.substr(1);
60
+ var access_token = new URLSearchParams(hash).get('access_token');
61
+ window.history.pushState({}, '', '/');
62
+ fetch('/?token=' + access_token, {method: 'POST'})
63
+ .then(() => {
64
+ document.querySelector('.message h1').textContent = 'Authentication successful! You can close this window.';
65
+ });
66
+ </script>
67
+ </body>
68
+ </html>
69
+ """)
70
+
71
+ def do_POST(self):
72
+ parsed_path = urllib.parse.urlparse(self.path)
73
+ query_params = urllib.parse.parse_qs(parsed_path.query)
74
+
75
+ if "token" in query_params:
76
+ token = query_params["token"][0]
77
+ self.server.auth_callback(token)
78
+ self.send_response(200)
79
+ self.end_headers()
80
+ else:
81
+ self.send_error(400, "Missing 'token' parameter")
82
+
83
+ self.server.shutdown()
84
+
85
+ def log_message(self, format, *args):
86
+ # Suppress log messages
87
+ return
88
+
89
+
90
+ AUTH_SERVER_PORT = 49160
91
+
92
+
93
+ class AuthServer(socketserver.TCPServer):
94
+ allow_reuse_address = True
95
+
96
+ def __init__(self, server_address, RequestHandlerClass, auth_callback):
97
+ self.auth_callback = auth_callback
98
+ super().__init__(server_address, RequestHandlerClass)
99
+
100
+
101
+ def start_server(auth_callback):
102
+ with AuthServer(("", AUTH_SERVER_PORT), AuthHandler, auth_callback) as httpd:
103
+ httpd.timeout = 30
104
+ start_time = time.time()
105
+ while time.time() - start_time < 60: # Run for a maximum of 60 seconds
106
+ httpd.handle_request()
107
+
108
+
109
+ @cli.command()
110
+ @click.option(
111
+ "--host",
112
+ help="Set custom host if it's different than https://api.tinybird.co. Check https://www.tinybird.co/docs/api-reference/overview#regions-and-endpoints for the available list of regions",
113
+ )
114
+ @click.option(
115
+ "--workspace",
116
+ help="Set the workspace to authenticate to. If not set, the default workspace will be used.",
117
+ )
118
+ def login(host: str, workspace: str):
119
+ """Authenticate via browser."""
120
+ auth_event = threading.Event()
121
+ auth_code = [None] # Using a list to store the code, as it's mutable
122
+ config = CLIConfig.get_project_config()
123
+ host = host or "https://api.tinybird.co"
124
+
125
+ def auth_callback(code):
126
+ auth_code[0] = code
127
+ auth_event.set()
128
+
129
+ click.echo("Opening browser for authentication...")
130
+
131
+ # Start the local server in a separate thread
132
+ server_thread = threading.Thread(target=start_server, args=(auth_callback,))
133
+ server_thread.daemon = True
134
+ server_thread.start()
135
+
136
+ # Open the browser to the auth page
137
+ client_id = "T6excMo8IKguvUw4vFNYfqlt9pe6msCU"
138
+ callback_url = f"http://localhost:{AUTH_SERVER_PORT}"
139
+ params = {
140
+ "client_id": client_id,
141
+ "redirect_uri": callback_url,
142
+ "response_type": "token",
143
+ "scope": "openid profile email",
144
+ }
145
+ auth_url = f"https://auth.tinybird.co/authorize?{urlencode(params)}"
146
+ webbrowser.open(auth_url)
147
+
148
+ # Wait for the authentication to complete or timeout
149
+ if auth_event.wait(timeout=60): # Wait for up to 60 seconds
150
+ params = {}
151
+ if workspace:
152
+ params["workspace_id"] = workspace
153
+ response = requests.get(
154
+ f"https://api.tinybird.co/v0/user/tokens?{urlencode(params)}",
155
+ headers={"Authorization": f"Bearer {auth_code[0]}"},
156
+ )
157
+ data = response.json()
158
+ cli_config = CLIConfig.get_project_config()
159
+ workspace_token = data["workspace_token"]
160
+ user_token = data["user_token"]
161
+ cli_config.set_token(workspace_token)
162
+ cli_config.set_token_for_host(workspace_token, host)
163
+ cli_config.set_user_token(user_token)
164
+ config.set_host(host)
165
+
166
+ cli_config.persist_to_file()
167
+ click.echo(FeedbackManager.success(message="✓ Authentication successful!"))
168
+ else:
169
+ click.echo(FeedbackManager.error(message="Authentication failed or timed out."))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tinybird
3
- Version: 0.0.1.dev12
3
+ Version: 0.0.1.dev14
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/cli/introduction.html
6
6
  Author: Tinybird
@@ -15,12 +15,12 @@ tinybird/syncasync.py,sha256=fAvq0qkRgqXqXMKwbY2iJNYqLT_r6mDsh1MRpGKrdRU,27763
15
15
  tinybird/tornado_template.py,sha256=o2HguxrL1Evnt8o3IvrsI8Zm6JtRQ3zhLJKf1XyR3SQ,41965
16
16
  tinybird/ch_utils/constants.py,sha256=aYvg2C_WxYWsnqPdZB1ZFoIr8ZY-XjUXYyHKE9Ansj0,3890
17
17
  tinybird/ch_utils/engine.py,sha256=OXkBhlzGjZotjD0vaT-rFIbSGV4tpiHxE8qO_ip0SyQ,40454
18
- tinybird/tb/cli.py,sha256=6Lu3wsCNepAxjJCWy4c6RhVPArBtm8TlUcSxX--TsBo,783
18
+ tinybird/tb/cli.py,sha256=-ritrHugOxQCW7qtV1hSIW8xEHKWDILawEwlcP9AVyI,816
19
19
  tinybird/tb/modules/auth.py,sha256=hynZ-Temot8YBsySUWKSFzZlYadtFPxG3o6lCSu1n6E,9018
20
20
  tinybird/tb/modules/branch.py,sha256=R1tTUBGyI0p_dt2IAWbuyNOvemhjCIPwYxEmOxL3zOg,38468
21
21
  tinybird/tb/modules/build.py,sha256=_kiloHo4cGdyQyp7TgCmXGHtmPP0cCL1gq1m-YcwNHQ,11181
22
22
  tinybird/tb/modules/cicd.py,sha256=KCFfywFfvGRh24GZwqrhICiTK_arHelPs_X4EB-pXIw,7331
23
- tinybird/tb/modules/cli.py,sha256=c-XNRu-idb2Hz43IT9ejd-QjsZy-xPQ3rnrdVIz0wxM,56568
23
+ tinybird/tb/modules/cli.py,sha256=zXFzafgt88K5CZ5mgkeSU-R3kmA6riPklYtUbTrZ6RA,56219
24
24
  tinybird/tb/modules/common.py,sha256=Vubc2AIR8BfEupnT5e1Y8OYGEyvNoIcjo8th-SaUflw,80111
25
25
  tinybird/tb/modules/config.py,sha256=ppWvACHrSLkb5hOoQLYNby2w8jR76-8Kx2NBCst7ntQ,11760
26
26
  tinybird/tb/modules/connection.py,sha256=ZSqBGoRiJedjHKEyB_fr1ybucOHtaad8d7uqGa2Q92M,28668
@@ -31,6 +31,7 @@ tinybird/tb/modules/fmt.py,sha256=UszEQO15fdzQ49QEj7Unhu68IKwSuKPsOrKhk2p2TAg,35
31
31
  tinybird/tb/modules/job.py,sha256=eoBVyA24lYIPonU88Jn7FF9hBKz1kScy9_w_oWreuc4,2952
32
32
  tinybird/tb/modules/llm.py,sha256=TvJJ9BlKISAb1SVI-pnHp_PcHcxGfTyjxOE_qAz90Ck,2441
33
33
  tinybird/tb/modules/local.py,sha256=sImiZwUMsvJRGBVZovOGBqxXo0SBWYwpZ7b8zVG_QNc,6943
34
+ tinybird/tb/modules/login.py,sha256=rxPjVtaW0r8hgx2wRISKPSfprSUXVzn6XE_YAxF9oz0,5850
34
35
  tinybird/tb/modules/mock.py,sha256=RohmEhNfudVryn2pJrI4fASE74inovNxzN0ew85Y830,2747
35
36
  tinybird/tb/modules/pipe.py,sha256=9wnfKbp2FkmLiJgVk3qbra76ktwsUTXghu6j9cCEahQ,31058
36
37
  tinybird/tb/modules/prompts.py,sha256=g0cBW2ePzuftib02wV82VIcAZd59buAAusnirAbzqVE,8662
@@ -47,7 +48,7 @@ tinybird/tb/modules/datafile/build_common.py,sha256=74547h5ja4C66DAwDMabj75FA_BU
47
48
  tinybird/tb/modules/datafile/build_datasource.py,sha256=fquzEGwk9NL_0K5YYG86Xtvgn4J5YHtRUoKJxbQGO0s,17344
48
49
  tinybird/tb/modules/datafile/build_pipe.py,sha256=wxqvVY3vIlG2_IAX8__mevhxqGkOxQ4-YyoWE6v2OxE,27465
49
50
  tinybird/tb/modules/datafile/common.py,sha256=q0XPpNE-l011Um3TXh3BmkSkUlYP5Ydkn24jXLq1I9Y,34239
50
- tinybird/tb/modules/datafile/diff.py,sha256=ZaTPGjRFJWokhaad_rMSxfYT92PA96s4WhhvlZubgyA,6769
51
+ tinybird/tb/modules/datafile/diff.py,sha256=-iaP7GvAzZtZSa8jPgVpOFlTRutxgxRBLBcGL1_RFr4,6743
51
52
  tinybird/tb/modules/datafile/exceptions.py,sha256=8rw2umdZjtby85QbuRKFO5ETz_eRHwUY5l7eHsy1wnI,556
52
53
  tinybird/tb/modules/datafile/fixture.py,sha256=YHlL4tojmPwm343Y8KO6r7d5Bhsk7U3lKP-oLMeBMsY,1771
53
54
  tinybird/tb/modules/datafile/format_common.py,sha256=zNWDXvwSKC9_T5e9R92LLj9ekDflVWwsllhGQilZsnY,2184
@@ -56,7 +57,7 @@ tinybird/tb/modules/datafile/format_pipe.py,sha256=R5tnlEccLn3KX6ehtC_H2sGQNrthu
56
57
  tinybird/tb/modules/datafile/parse_datasource.py,sha256=9pp0fJ2Da-7pznaHq5OV7NvzswK0RuXOPHU4kXNRMnA,1111
57
58
  tinybird/tb/modules/datafile/parse_pipe.py,sha256=STgA12LOLUnnb_cvVvZeEE4ka-nfk0jsNzxJhWj94cY,2599
58
59
  tinybird/tb/modules/datafile/pipe_checker.py,sha256=cp80Bru41GlyMRvyERpdJNXns2MjmtIAWFnBLF4cPXs,24667
59
- tinybird/tb/modules/datafile/pull.py,sha256=MleQG9GTo9_ZoMKrneps-QzI4dr6n5IPosHWz2sTz20,5894
60
+ tinybird/tb/modules/datafile/pull.py,sha256=wBXBAZIruIyCRQZvfYxMc7h1q35NlKF-hFIF-bUm4iY,5956
60
61
  tinybird/tb/modules/tinyunit/tinyunit.py,sha256=IkjRCvb8HnNEE84rtl0I1b9gQVpE_zCE8MvFFet51sg,11716
61
62
  tinybird/tb/modules/tinyunit/tinyunit_lib.py,sha256=hGh1ZaXC1af7rKnX7222urkj0QJMhMWclqMy59dOqwE,1922
62
63
  tinybird/tb_cli_modules/cicd.py,sha256=0lMkb6CVOFZl5HOwgY8mK4T4mgI7O8335UngLXtCc-c,13851
@@ -65,8 +66,8 @@ tinybird/tb_cli_modules/config.py,sha256=6NTgIdwf0X132A1j6G_YrdPep87ymZ9b5pABabK
65
66
  tinybird/tb_cli_modules/exceptions.py,sha256=pmucP4kTF4irIt7dXiG-FcnI-o3mvDusPmch1L8RCWk,3367
66
67
  tinybird/tb_cli_modules/regions.py,sha256=QjsL5H6Kg-qr0aYVLrvb1STeJ5Sx_sjvbOYO0LrEGMk,166
67
68
  tinybird/tb_cli_modules/telemetry.py,sha256=iEGnMuCuNhvF6ln__j6X9MSTwL_0Hm-GgFHHHvhfknk,10466
68
- tinybird-0.0.1.dev12.dist-info/METADATA,sha256=ZwbPVXa3y_heMMrs0TuUbc94QtmsymDy_--2yL6kyS0,2405
69
- tinybird-0.0.1.dev12.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
70
- tinybird-0.0.1.dev12.dist-info/entry_points.txt,sha256=LwdHU6TfKx4Qs7BqqtaczEZbImgU7Abe9Lp920zb_fo,43
71
- tinybird-0.0.1.dev12.dist-info/top_level.txt,sha256=pgw6AzERHBcW3YTi2PW4arjxLkulk2msOz_SomfOEuc,45
72
- tinybird-0.0.1.dev12.dist-info/RECORD,,
69
+ tinybird-0.0.1.dev14.dist-info/METADATA,sha256=ZuulDALTiwqGmBBXo45rpSNQnzPtV9JId3zHN2q2Si4,2405
70
+ tinybird-0.0.1.dev14.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
71
+ tinybird-0.0.1.dev14.dist-info/entry_points.txt,sha256=LwdHU6TfKx4Qs7BqqtaczEZbImgU7Abe9Lp920zb_fo,43
72
+ tinybird-0.0.1.dev14.dist-info/top_level.txt,sha256=pgw6AzERHBcW3YTi2PW4arjxLkulk2msOz_SomfOEuc,45
73
+ tinybird-0.0.1.dev14.dist-info/RECORD,,