Skip to content

Commit

Permalink
Merge pull request #198 from alneberg/non_interactive_new
Browse files Browse the repository at this point in the history
No Prompt and more
  • Loading branch information
i-oden committed Nov 23, 2021
2 parents 7257ed7 + 44b2928 commit 3036a97
Show file tree
Hide file tree
Showing 16 changed files with 210 additions and 135 deletions.
219 changes: 134 additions & 85 deletions dds_cli/__main__.py

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions dds_cli/account_adder.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@
class AccountAdder(dds_cli.base.DDSBaseClass):
"""Admin class for adding users, etc."""

def __init__(self, username: str, method: str = "add"):
def __init__(self, username: str, method: str = "add", no_prompt: bool = False):
"""Initialize, incl. user authentication."""
# Initiate DDSBaseClass to authenticate user
super().__init__(username=username, method=method)
super().__init__(username=username, method=method, no_prompt=no_prompt)

# Only method "add" can use the AccountAdder class
if self.method != "add":
Expand Down
28 changes: 17 additions & 11 deletions dds_cli/session.py → dds_cli/auth.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
"""Data Delivery System Session Manager."""
"""Data Delivery System saved authentication token manager."""
import logging

# Installed
import requests
import simplejson

# Own modules
from dds_cli import base
from dds_cli import user
Expand All @@ -21,24 +17,34 @@
###############################################################################


class Session(base.DDSBaseClass):
"""Session manager class."""
class Auth(base.DDSBaseClass):
"""Authentication manager class."""

def __init__(
self,
username: str,
check: bool = False,
authenticate: bool = True,
):
"""Handle actions regarding session management in DDS."""
# Initiate DDSBaseClass to authenticate user
super().__init__(
username=username,
authenticate=not check,
authenticate=authenticate,
method_check=False,
force_renew_token=True, # Only used if authenticate is True
)

def check(self):
token_file = user.TokenFile()
token_file.check_token_file_permissions()
token_file.token_report()
if token_file.file_exists():
token_file.check_token_file_permissions()
token_file.token_report()
else:
LOG.error(f"[red]No saved authentication token found![/red]")

def logout(self):
token_file = user.TokenFile()
if token_file.file_exists():
token_file.delete_token()
else:
LOG.info(f"[green]Already logged out![/green]")
11 changes: 8 additions & 3 deletions dds_cli/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,14 @@ def __init__(
authenticate: bool = True,
method_check: bool = True,
force_renew_token: bool = False,
no_prompt: bool = False,
):
"""Initialize Base class for authenticating the user and preparing for DDS action."""
self.username = username
self.project = project
self.method_check = method_check
self.method = method
self.no_prompt = no_prompt

if self.method_check:
# Get attempted operation e.g. put/ls/rm/get
Expand All @@ -86,7 +88,11 @@ def __init__(

# Authenticate the user and get the token
if authenticate:
dds_user = user.User(username=username, force_renew_token=force_renew_token)
dds_user = user.User(
username=username,
force_renew_token=force_renew_token,
no_prompt=no_prompt,
)
self.token = dds_user.token_dict

LOG.debug(f"Method: {self.method}, Project: {self.project}")
Expand Down Expand Up @@ -115,7 +121,6 @@ def __exit__(self, exc_type, exc_value, tb, max_fileerrs: int = 40):
return True

# Private methods ############################### Private methods #

def __get_project_keys(self):
"""Get public and private project keys depending on method."""
# Project public key required for both put and get
Expand Down Expand Up @@ -261,7 +266,7 @@ def verify_bucket_exist(self):
with s3.S3Connector(project_id=self.project, token=self.token) as conn:

if None in [conn.safespring_project, conn.keys, conn.bucketname, conn.url]:
dds_cli.utils.console.print(f"\n:warning: {conn.message} :warning:\n")
dds_cli.utils.console.print(f"\n:warning-emoji: {conn.message} :warning-emoji:\n")
os._exit(1)

bucket_exists = conn.check_bucket_exists()
Expand Down
6 changes: 4 additions & 2 deletions dds_cli/data_getter.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ def __init__(
silent: bool = False,
verify_checksum: bool = False,
method: str = "get",
no_prompt: bool = False,
):
"""Handle actions regarding downloading data."""
# Initiate DDSBaseClass to authenticate user
Expand All @@ -63,6 +64,7 @@ def __init__(
project=project,
dds_directory=destination,
method=method,
no_prompt=no_prompt,
)

# Initiate DataGetter specific attributes
Expand Down Expand Up @@ -95,8 +97,8 @@ def __init__(

if self.filehandler.failed and self.break_on_fail:
dds_cli.utils.console.print(
"\n:warning: Some specified files were not found in the system "
"and '--break-on-fail' flag used. :warning:\n\n"
"\n:warning-emoji: Some specified files were not found in the system "
"and '--break-on-fail' flag used. :warning-emoji:\n\n"
f"Files not found: {self.filehandler.failed}\n"
)
os._exit(1)
Expand Down
3 changes: 2 additions & 1 deletion dds_cli/data_lister.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ def __init__(
project: str = None,
show_usage: bool = False,
tree: bool = False,
no_prompt: bool = False,
):
"""Handle actions regarding data listing in the cli."""
# Only method "ls" can use the DataLister class
Expand All @@ -55,7 +56,7 @@ def __init__(
)

# Initiate DDSBaseClass to authenticate user
super().__init__(username=username, project=project, method=method)
super().__init__(username=username, project=project, method=method, no_prompt=no_prompt)

self.show_usage = show_usage
self.tree = tree
Expand Down
5 changes: 4 additions & 1 deletion dds_cli/data_putter.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ def put(
overwrite,
num_threads,
silent,
no_prompt,
):
"""Handle upload of data."""
# Initialize delivery - check user access etc
Expand All @@ -63,6 +64,7 @@ def put(
break_on_fail=break_on_fail,
overwrite=overwrite,
silent=silent,
no_prompt=no_prompt,
) as putter:

# Progress object to keep track of progress tasks
Expand Down Expand Up @@ -178,10 +180,11 @@ def __init__(
source_path_file: pathlib.Path = None,
silent: bool = False,
method: str = "put",
no_prompt: bool = False,
):
"""Handle actions regarding upload of data."""
# Initiate DDSBaseClass to authenticate user
super().__init__(username=username, project=project, method=method)
super().__init__(username=username, project=project, method=method, no_prompt=no_prompt)

# Initiate DataPutter specific attributes
self.break_on_fail = break_on_fail
Expand Down
4 changes: 2 additions & 2 deletions dds_cli/data_remover.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@
class DataRemover(base.DDSBaseClass):
"""Data remover class."""

def __init__(self, project: str, username: str, method: str = "rm"):
def __init__(self, project: str, username: str, method: str = "rm", no_prompt: bool = False):
"""Handle actions regarding data deletion in the cli."""
# Initiate DDSBaseClass to authenticate user
super().__init__(username=username, project=project, method=method)
super().__init__(username=username, project=project, method=method, no_prompt=no_prompt)

self.failed_table = None

Expand Down
2 changes: 1 addition & 1 deletion dds_cli/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def __str__(self):
class TokenNotFoundError(AuthenticationError):
"""No token retrieved from REST API or from File."""

def __init__(self, message, sign=":warning:"):
def __init__(self, message, sign=":warning-emoji:"):
"""Reformat error message."""
super().__init__(message=message, sign=sign)

Expand Down
6 changes: 4 additions & 2 deletions dds_cli/file_handler_local.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ def __init__(self, user_input, temporary_destination, project):

# No data -- cannot proceed
if not self.data_list:
dds_cli.utils.console.print("\n:warning: No data specified. :warning:\n")
dds_cli.utils.console.print("\n:warning-emoji: No data specified. :warning-emoji:\n")
os._exit(1)

self.data, _ = self.__collect_file_info_local(all_paths=self.data_list)
Expand Down Expand Up @@ -250,7 +250,9 @@ def check_previous_upload(self, token):

# API failure
if "files" not in files_in_db:
dds_cli.utils.console.print("\n:warning: Files not returned from API. :warning:\n")
dds_cli.utils.console.print(
"\n:warning-emoji: Files not returned from API. :warning-emoji:\n"
)
os._exit(1)

LOG.debug("Previous upload check finished.")
Expand Down
8 changes: 4 additions & 4 deletions dds_cli/file_handler_remote.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def __init__(self, get_all, user_input, token, project, destination=pathlib.Path
self.data_list = list(set(self.data_list))

if not self.data_list and not get_all:
dds_cli.utils.console.print("\n:warning: No data specified. :warning:\n")
dds_cli.utils.console.print("\n:warning-emoji: No data specified. :warning-emoji:\n")
os._exit(1)

self.data = self.__collect_file_info_remote(all_paths=self.data_list, token=token)
Expand Down Expand Up @@ -101,15 +101,15 @@ def __collect_file_info_remote(self, all_paths, token):
# Folder info required if specific files requested
if all_paths and "folders" not in file_info:
dds_cli.utils.console.print(
"\n:warning: Error in response. "
"Not enough info returned despite ok request. :warning:\n"
"\n:warning-emoji: Error in response. "
"Not enough info returned despite ok request. :warning-emoji:\n"
)
os._exit(1)

# Files in response always required
if "files" not in file_info:
dds_cli.utils.console.print(
"\n:warning: No files in response despite ok request. :warning:\n"
"\n:warning-emoji: No files in response despite ok request. :warning-emoji:\n"
)
os._exit(1)

Expand Down
3 changes: 2 additions & 1 deletion dds_cli/project_creator.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,11 @@ def __init__(
self,
username: str,
method: str = "create",
no_prompt: bool = False,
):
"""Handle actions regarding project creation in the cli."""
# Initiate DDSBaseClass to authenticate user
super().__init__(username=username, method=method)
super().__init__(username=username, method=method, no_prompt=no_prompt)

# Only method "create" can use the ProjectCreator class
if self.method != "create":
Expand Down
31 changes: 24 additions & 7 deletions dds_cli/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

# Installed
import getpass
import rich

# Own modules
import dds_cli
Expand All @@ -37,9 +38,10 @@ class User:
when instantiating, an authentication token will be read from a file or
renewed from the DDS API if the saved token is not found or has expired."""

def __init__(self, username: str, force_renew_token: bool = False):
def __init__(self, username: str, force_renew_token: bool = False, no_prompt: bool = False):
self.username = username
self.force_renew_token = force_renew_token
self.no_prompt = no_prompt
self.token = None

# Fetch encrypted JWT token or authenticate against API
Expand Down Expand Up @@ -79,12 +81,17 @@ def __retrieve_token(self):
def __authenticate_user(self):
"""Authenticates the username and password via a call to the API."""

if self.username is None:
LOG.debug(f"Authenticating the user: {self.username} on the api")

if self.no_prompt:
raise exceptions.AuthenticationError(
message="Please supply username (--username) to be able to authenticate."
message=(
"Authentication not possible when running with --no-prompt. "
"Please run the `dds auth login` command and authenticate interactively."
)
)

LOG.debug(f"Authenticating the user: {self.username} on the api")
if self.username is None:
self.username = rich.prompt.Prompt.ask("DDS username")

password = getpass.getpass(prompt="DDS Password: ")

Expand Down Expand Up @@ -137,7 +144,7 @@ def read_token(self):
Returns None if no valid token can be found."""

if not self.token_file.is_file():
if not self.file_exists():
LOG.debug(f"Token file {self.token_file} does not exist.")
return None

Expand All @@ -156,6 +163,10 @@ def read_token(self):
LOG.debug("Token retrieved from file.")
return token

def file_exists(self):
"""Returns True if the token file exists."""
return self.token_file.is_file()

def save_token(self, token):
"""Saves the token to the token file."""

Expand All @@ -169,6 +180,12 @@ def save_token(self, token):

LOG.debug("New token saved to file.")

def delete_token(self):
"""Deletes the token file."""

if self.file_exists():
self.token_file.unlink()

def check_token_file_permissions(self):
"""Verify permissions for token file. Raises dds_cli.exceptions.DDSCLIException if
permissions are not properly set.
Expand Down Expand Up @@ -202,7 +219,7 @@ def token_expired(self):
LOG.debug(
"Token file is too old so token has likely expired. Now deleting it and fetching new token."
)
self.token_file.unlink()
self.delete_token()
return True
elif age > dds_cli.TOKEN_WARNING_AGE:
LOG.warning(
Expand Down
3 changes: 2 additions & 1 deletion dds_cli/utils.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import rich.console
import numbers

console = rich.console.Console(stderr=True)
console = rich.console.Console()
stderr_console = rich.console.Console(stderr=True)


def calculate_magnitude(projects, keys, iec_standard=False):
Expand Down
5 changes: 0 additions & 5 deletions demo/creds_get.json

This file was deleted.

7 changes: 0 additions & 7 deletions demo/creds_put.json

This file was deleted.

0 comments on commit 3036a97

Please sign in to comment.