Skip to content

Commit 5899eca

Browse files
authored
Merge pull request #553 from cs7-shrey/revert_gdrive_connection_method
Revert: Google drive connection method to config token based approach
2 parents 0a6c69d + f52648f commit 5899eca

13 files changed

Lines changed: 490 additions & 306 deletions

File tree

datashuttle/configs/config_class.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ def get_base_folder(
250250
def get_rclone_config_name(
251251
self, connection_method: Optional[str] = None
252252
) -> str:
253-
"""Generate the rclone configuration name for the project.
253+
"""Generate the rclone configuration name for the central project.
254254
255255
These configs are created by datashuttle but managed and stored by rclone.
256256
"""

datashuttle/datashuttle_class.py

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -845,7 +845,8 @@ def setup_google_drive_connection(self) -> None:
845845
secret if `gdrive_client_id` is set in the configs.
846846
847847
Next, the user will be asked if their machine has access to a browser.
848-
If not, they will be prompted to input their service account file path.
848+
If not, they will be prompted to input a config_token after running an
849+
rclone command displayed to the user on a machine with access to a browser.
849850
850851
Next, with the provided credentials, the final setup will be done. This
851852
opens up a browser if the user confirmed access to a browser.
@@ -855,23 +856,25 @@ def setup_google_drive_connection(self) -> None:
855856
local_vars=locals(),
856857
)
857858

858-
browser_available = gdrive.ask_user_for_browser(log=True)
859-
860-
service_account_filepath = None
861-
gdrive_client_secret = None
862-
863-
if browser_available and self.cfg["gdrive_client_id"]:
859+
if self.cfg["gdrive_client_id"]:
864860
gdrive_client_secret = gdrive.get_client_secret()
861+
else:
862+
gdrive_client_secret = None
865863

866-
elif not browser_available:
867-
service_account_filepath = (
868-
gdrive.prompt_and_get_service_account_filepath(
869-
log=True,
870-
)
864+
browser_available = gdrive.ask_user_for_browser(log=True)
865+
866+
if not browser_available:
867+
config_token = gdrive.prompt_and_get_config_token(
868+
self.cfg,
869+
gdrive_client_secret,
870+
self.cfg.get_rclone_config_name("gdrive"),
871+
log=True,
871872
)
873+
else:
874+
config_token = None
872875

873876
process = self._setup_rclone_gdrive_config(
874-
gdrive_client_secret, service_account_filepath
877+
gdrive_client_secret, config_token
875878
)
876879

877880
rclone.await_call_rclone_with_popen_raise_on_fail(process, log=True)
@@ -1527,13 +1530,13 @@ def _setup_rclone_central_local_filesystem_config(self) -> None:
15271530
def _setup_rclone_gdrive_config(
15281531
self,
15291532
gdrive_client_secret: str | None,
1530-
service_account_filepath: str | None,
1533+
config_token: str | None,
15311534
) -> subprocess.Popen:
15321535
return rclone.setup_rclone_config_for_gdrive(
15331536
self.cfg,
15341537
self.cfg.get_rclone_config_name("gdrive"),
15351538
gdrive_client_secret,
1536-
service_account_filepath,
1539+
config_token,
15371540
)
15381541

15391542
def _setup_rclone_aws_config(

datashuttle/tui/interface.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
from datashuttle import DataShuttle
1515
from datashuttle.configs import load_configs
16-
from datashuttle.utils import aws, rclone, ssh, utils
16+
from datashuttle.utils import aws, gdrive, rclone, ssh, utils
1717

1818

1919
class Interface:
@@ -511,7 +511,7 @@ def setup_key_pair_and_rclone_config(
511511
def setup_google_drive_connection(
512512
self,
513513
gdrive_client_secret: Optional[str] = None,
514-
service_account_filepath: Optional[str] = None,
514+
config_token: Optional[str] = None,
515515
) -> InterfaceOutput:
516516
"""Try to set up and validate connection to Google Drive.
517517
@@ -526,7 +526,7 @@ def setup_google_drive_connection(
526526
"""
527527
try:
528528
process = self.project._setup_rclone_gdrive_config(
529-
gdrive_client_secret, service_account_filepath
529+
gdrive_client_secret, config_token
530530
)
531531
self.google_drive_rclone_setup_process = process
532532
self.gdrive_setup_process_killed = False
@@ -539,6 +539,21 @@ def setup_google_drive_connection(
539539
except BaseException as e:
540540
return False, str(e)
541541

542+
def get_rclone_message_for_gdrive_without_browser(
543+
self, gdrive_client_secret: Optional[str] = None
544+
) -> InterfaceOutput:
545+
"""Get the rclone message for Google Drive setup without a browser."""
546+
try:
547+
output = gdrive.preliminary_for_setup_without_browser(
548+
self.project.cfg,
549+
gdrive_client_secret,
550+
self.project.cfg.get_rclone_config_name("gdrive"),
551+
log=False,
552+
)
553+
return True, output
554+
except BaseException as e:
555+
return False, str(e)
556+
542557
def terminate_google_drive_setup(self) -> None:
543558
"""Terminate rclone setup for google drive by killing the rclone process."""
544559
assert self.google_drive_rclone_setup_process is not None

datashuttle/tui/screens/setup_gdrive.py

Lines changed: 55 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ class SetupGdriveScreen(ModalScreen):
2525
2626
If the config contains a "gdrive_client_id", the user is prompted
2727
to enter a client secret. If the user has access to a browser, a Google Drive
28-
authentication page will open. Otherwise, the user is asked to enter path to a
29-
service account file.
28+
authentication page will open. Otherwise, the user is asked to run an rclone command
29+
and input a config token.
3030
"""
3131

3232
def __init__(self, interface: Interface) -> None:
@@ -37,6 +37,7 @@ def __init__(self, interface: Interface) -> None:
3737
self.stage: int = 0
3838
self.setup_worker: Worker | None = None
3939
self.is_browser_available: bool = True
40+
self.gdrive_client_secret: Optional[str] = None
4041

4142
# For handling credential inputs
4243
self.input_box: Input = Input(
@@ -75,9 +76,9 @@ def on_button_pressed(self, event: Button.Pressed) -> None:
7576
and proceeds to a browser authentication.
7677
7778
3) "no" button : A "no" answer to the availability of browser question. On click,
78-
prompts the user to enter path to their service account file.
79+
prompts the user to enter a config token by running an rclone command.
7980
80-
4) "enter" button : To enter the client secret or service account file path.
81+
4) "enter" button : To enter the client secret or config token.
8182
8283
5) "finish" button : To finish the setup.
8384
@@ -94,39 +95,44 @@ def on_button_pressed(self, event: Button.Pressed) -> None:
9495
self.dismiss()
9596

9697
elif event.button.id == "setup_gdrive_ok_button":
97-
self.ask_user_for_browser()
98-
99-
elif event.button.id == "setup_gdrive_yes_button":
100-
self.remove_yes_no_buttons()
98+
self.query_one("#setup_gdrive_ok_button").remove()
10199

102100
if self.interface.project.cfg["gdrive_client_id"]:
103101
self.ask_user_for_gdrive_client_secret()
104102
else:
105-
self.open_browser_and_setup_gdrive_connection()
103+
self.ask_user_for_browser()
104+
105+
elif event.button.id == "setup_gdrive_yes_button":
106+
self.remove_yes_no_buttons()
107+
self.open_browser_and_setup_gdrive_connection(
108+
self.gdrive_client_secret
109+
)
106110

107111
elif event.button.id == "setup_gdrive_no_button":
108112
self.is_browser_available = False
109113
self.remove_yes_no_buttons()
110-
self.prompt_user_for_service_account_filepath()
114+
self.prompt_user_for_config_token()
111115

112116
elif event.button.id == "setup_gdrive_enter_button":
113-
if self.is_browser_available:
114-
gdrive_client_secret = (
117+
if (
118+
self.interface.project.cfg["gdrive_client_id"]
119+
and self.stage == 0
120+
):
121+
self.gdrive_client_secret = (
115122
self.input_box.value.strip()
116123
if self.input_box.value.strip()
117124
else None
118125
)
119-
self.open_browser_and_setup_gdrive_connection(
120-
gdrive_client_secret
121-
)
126+
self.stage += 1
127+
self.ask_user_for_browser()
122128
else:
123-
service_account_filepath = (
129+
config_token = (
124130
self.input_box.value.strip()
125131
if self.input_box.value.strip()
126132
else None
127133
)
128-
self.setup_gdrive_connection_using_service_account(
129-
service_account_filepath
134+
self.setup_gdrive_connection_using_config_token(
135+
self.gdrive_client_secret, config_token
130136
)
131137

132138
def ask_user_for_browser(self) -> None:
@@ -137,7 +143,11 @@ def ask_user_for_browser(self) -> None:
137143
)
138144
self.update_message_box_message(message)
139145

140-
self.query_one("#setup_gdrive_ok_button").remove()
146+
if self.enter_button.is_mounted:
147+
self.enter_button.remove()
148+
149+
if self.input_box.is_mounted:
150+
self.input_box.visible = False
141151

142152
# Mount the Yes and No buttons
143153
yes_button = Button("Yes", id="setup_gdrive_yes_button")
@@ -184,34 +194,46 @@ def open_browser_and_setup_gdrive_connection(
184194
)
185195
)
186196

187-
def prompt_user_for_service_account_filepath(self) -> None:
188-
"""Set up widgets and prompt user for their service account file path for browser-less connection."""
189-
message = "Please enter your service account file path."
197+
def prompt_user_for_config_token(self) -> None:
198+
"""Prompt the user for the rclone config token for Google Drive setup."""
199+
success, message = (
200+
self.interface.get_rclone_message_for_gdrive_without_browser(
201+
self.gdrive_client_secret
202+
)
203+
)
190204

191-
self.update_message_box_message(message)
205+
if not success:
206+
self.display_failed(message)
207+
return
192208

209+
self.update_message_box_message(
210+
message + "\nPress shift+click to copy."
211+
)
212+
213+
self.enter_button = Button("Enter", id="setup_gdrive_enter_button")
193214
self.query_one("#setup_gdrive_buttons_horizontal").mount(
194215
self.enter_button, before="#setup_gdrive_cancel_button"
195216
)
196217
self.mount_input_box_before_buttons()
197218

198-
def setup_gdrive_connection_using_service_account(
199-
self, service_account_filepath: Optional[str] = None
219+
def setup_gdrive_connection_using_config_token(
220+
self, gdrive_client_secret: str | None, config_token: str | None
200221
) -> None:
201-
"""Set up the Google Drive connection using service account and show success message."""
222+
"""Set up the Google Drive connection using rclone config token."""
202223
message = "Setting up connection."
203224
self.update_message_box_message(message)
204225

205226
asyncio.create_task(
206227
self.setup_gdrive_connection_and_update_ui(
207-
service_account_filepath=service_account_filepath
228+
gdrive_client_secret=gdrive_client_secret,
229+
config_token=config_token,
208230
)
209231
)
210232

211233
async def setup_gdrive_connection_and_update_ui(
212234
self,
213235
gdrive_client_secret: Optional[str] = None,
214-
service_account_filepath: Optional[str] = None,
236+
config_token: Optional[str] = None,
215237
) -> None:
216238
"""Start the Google Drive connection setup in a separate thread.
217239
@@ -232,7 +254,7 @@ async def setup_gdrive_connection_and_update_ui(
232254
self.enter_button.disabled = True
233255

234256
worker = self.setup_gdrive_connection(
235-
gdrive_client_secret, service_account_filepath
257+
gdrive_client_secret, config_token
236258
)
237259
self.setup_worker = worker
238260
if worker.is_running:
@@ -250,7 +272,7 @@ async def setup_gdrive_connection_and_update_ui(
250272
def setup_gdrive_connection(
251273
self,
252274
gdrive_client_secret: Optional[str] = None,
253-
service_account_filepath: Optional[str] = None,
275+
config_token: Optional[str] = None,
254276
) -> Worker[InterfaceOutput]:
255277
"""Authenticate the Google Drive connection.
256278
@@ -260,7 +282,7 @@ def setup_gdrive_connection(
260282
with Google Drive.
261283
"""
262284
success, output = self.interface.setup_google_drive_connection(
263-
gdrive_client_secret, service_account_filepath
285+
gdrive_client_secret, config_token
264286
)
265287
return success, output
266288

@@ -302,6 +324,8 @@ def mount_input_box_before_buttons(
302324
self.query_one("#gdrive_setup_messagebox_message_container").mount(
303325
self.input_box, after="#gdrive_setup_messagebox_message"
304326
)
327+
self.input_box.visible = True
328+
self.input_box.value = ""
305329

306330
def remove_yes_no_buttons(self) -> None:
307331
"""Remove yes and no buttons."""

datashuttle/tui/shared/configs_content.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -530,7 +530,7 @@ def widget_configs_match_saved_configs(self):
530530
for key, value in cfg_kwargs.items():
531531
saved_val = self.interface.get_configs()[key]
532532
if key in ["central_path", "local_path"]:
533-
if value.name != project_name:
533+
if value is not None and value.name != project_name:
534534
value = value / project_name
535535
if saved_val != value:
536536
return False

0 commit comments

Comments
 (0)