diff --git a/tabcmd/commands/auth/login_command.py b/tabcmd/commands/auth/login_command.py index 647fee3f..d042f724 100644 --- a/tabcmd/commands/auth/login_command.py +++ b/tabcmd/commands/auth/login_command.py @@ -22,4 +22,4 @@ def run_command(args): logger = log(__class__.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() - session.create_session(args) + session.create_session(args, logger) diff --git a/tabcmd/commands/auth/session.py b/tabcmd/commands/auth/session.py index e6583f87..2e7107ef 100644 --- a/tabcmd/commands/auth/session.py +++ b/tabcmd/commands/auth/session.py @@ -48,8 +48,8 @@ def __init__(self): self.timeout = None self.logging_level = "info" - self._read_from_json() self.logger = log(__name__, self.logging_level) # instantiate here mostly for tests + self._read_from_json() self.tableau_server = None # this one is an object that doesn't get persisted in the file # called before we connect to the server @@ -206,7 +206,7 @@ def _create_new_connection(self) -> TSC.Server: return self.tableau_server def _read_existing_state(self): - if self._check_json(): + if self._json_exists(): self._read_from_json() def _print_server_info(self): @@ -265,13 +265,13 @@ def _get_saved_credentials(self): return credentials # external entry point: - def create_session(self, args): + def create_session(self, args, logger): signed_in_object = None # pull out cached info from json, then overwrite with new args if available self._read_existing_state() self._update_session_data(args) self.logging_level = args.logging_level or self.logging_level - self.logger = self.logger or log(__class__.__name__, self.logging_level) + self.logger = logger or log(__class__.__name__, self.logging_level) credentials = None if args.password: @@ -344,51 +344,70 @@ def _clear_data(self): self.timeout = None # json file functions ---------------------------------------------------- + # These should be moved into a separate class def _get_file_path(self): home_path = os.path.expanduser("~") file_path = os.path.join(home_path, "tableau_auth.json") return file_path def _read_from_json(self): - if not self._check_json(): + if not self._json_exists(): return file_path = self._get_file_path() - data = {} - with open(str(file_path), "r") as file_contents: - data = json.load(file_contents) + content = None try: - for auth in data["tableau_auth"]: - self.auth_token = auth["auth_token"] - self.server_url = auth["server"] - self.site_name = auth["site_name"] - self.site_id = auth["site_id"] - self.username = auth["username"] - self.user_id = auth["user_id"] - self.token_name = auth["personal_access_token_name"] - self.token_value = auth["personal_access_token"] - self.last_login_using = auth["last_login_using"] - self.password_file = auth["password_file"] - self.no_prompt = auth["no_prompt"] - self.no_certcheck = auth["no_certcheck"] - self.certificate = auth["certificate"] - self.no_proxy = auth["no_proxy"] - self.proxy = auth["proxy"] - self.timeout = auth["timeout"] - except KeyError as e: - self.logger.debug(_("sessionoptions.errors.bad_password_file"), e) - self._remove_json() - except Exception as any_error: - self.logger.info(_("session.new_session")) - self._remove_json() + with open(str(file_path), "r") as file_contents: + data = json.load(file_contents) + content = data["tableau_auth"] + except json.JSONDecodeError as e: + self._wipe_bad_json(e, "Error reading data from session file") + except IOError as e: + self._wipe_bad_json(e, "Error reading session file") + except AttributeError as e: + self._wipe_bad_json(e, "Error parsing session details from file") + except Exception as e: + self._wipe_bad_json(e, "Unexpected error reading session details from file") + + try: + auth = content[0] + self.auth_token = auth["auth_token"] + self.server_url = auth["server"] + self.site_name = auth["site_name"] + self.site_id = auth["site_id"] + self.username = auth["username"] + self.user_id = auth["user_id"] + self.token_name = auth["personal_access_token_name"] + self.token_value = auth["personal_access_token"] + self.last_login_using = auth["last_login_using"] + self.password_file = auth["password_file"] + self.no_prompt = auth["no_prompt"] + self.no_certcheck = auth["no_certcheck"] + self.certificate = auth["certificate"] + self.no_proxy = auth["no_proxy"] + self.proxy = auth["proxy"] + self.timeout = auth["timeout"] + except AttributeError as e: + self._wipe_bad_json(e, "Unrecognized attribute in session file") + except Exception as e: + self._wipe_bad_json(e, "Failed to load session file") - def _check_json(self): + def _wipe_bad_json(self, e, message): + self.logger.debug(message + ": " + e.__str__()) + self.logger.info(_("session.new_session")) + self._remove_json() + + def _json_exists(self): + # todo: make this location configurable home_path = os.path.expanduser("~") file_path = os.path.join(home_path, "tableau_auth.json") return os.path.exists(file_path) def _save_session_to_json(self): - data = self._serialize_for_save() - self._save_file(data) + try: + data = self._serialize_for_save() + self._save_file(data) + except Exception as e: + self._wipe_bad_json(e, "Failed to save session file") def _save_file(self, data): file_path = self._get_file_path() @@ -420,7 +439,15 @@ def _serialize_for_save(self): return data def _remove_json(self): - file_path = self._get_file_path() - self._save_file({}) - if os.path.exists(file_path): - os.remove(file_path) + file_path = "" + try: + if not self._json_exists(): + return + file_path = self._get_file_path() + self._save_file({}) + if os.path.exists(file_path): + os.remove(file_path) + except Exception as e: + message = "Error clearing session data from {}: check and remove manually".format(file_path) + self.logger.error(message) + self.logger.error(e) diff --git a/tabcmd/commands/constants.py b/tabcmd/commands/constants.py index c37cb259..c491a135 100644 --- a/tabcmd/commands/constants.py +++ b/tabcmd/commands/constants.py @@ -1,8 +1,6 @@ import inspect import sys -from tableauserverclient import ServerResponseError - from tabcmd.execution.localize import _ @@ -25,20 +23,19 @@ def is_expired_session(error): @staticmethod def is_resource_conflict(error): if hasattr(error, "code"): - return error.code == Constants.source_already_exists + return error.code.startswith(Constants.resource_conflict_general) @staticmethod def is_login_error(error): if hasattr(error, "code"): return error.code == Constants.login_error - @staticmethod - def is_server_response_error(error): - return isinstance(error, ServerResponseError) - # https://gist.github.com/FredLoney/5454553 @staticmethod def log_stack(logger): + if not logger: + print("logger not available: cannot show stack") + return try: """The log header message formatter.""" HEADER_FMT = "Printing Call Stack at %s::%s" @@ -49,11 +46,10 @@ def log_stack(logger): file, line, func = here[1:4] start = 0 n_lines = 5 - logger.trace(HEADER_FMT % (file, func)) - - for frame in stack[start + 2 : n_lines]: + logger.debug(HEADER_FMT % (file, func)) + for frame in stack[start + 1 : n_lines]: file, line, func = frame[1:4] - logger.trace(STACK_FMT % (file, line, func)) + logger.debug(STACK_FMT % (file, line, func)) except Exception as e: logger.info("Error printing stack trace:", e) @@ -66,9 +62,9 @@ def exit_with_error(logger, message=None, exception=None): if exception: if message: logger.debug("Error message: " + message) - Errors.check_common_error_codes_and_explain(logger, exception) + Errors.check_common_error_codes_and_explain(logger, exception) except Exception as exc: - print(sys.stderr, "Error during log call from exception - {}".format(exc)) + print(sys.stderr, "Error during log call from exception - {} {}".format(exc.__class__, message)) try: logger.info("Exiting...") except Exception: @@ -77,16 +73,15 @@ def exit_with_error(logger, message=None, exception=None): @staticmethod def check_common_error_codes_and_explain(logger, exception): - if Errors.is_server_response_error(exception): - logger.error(_("publish.errors.unexpected_server_response").format(exception)) - if Errors.is_expired_session(exception): - logger.error(_("session.errors.session_expired")) - # TODO: add session as an argument to this method - # and add the full command line as a field in Session? - # "session.session_expired_login")) - # session.renew_session - return - if exception.code == Constants.source_not_found: - logger.error(_("publish.errors.server_resource_not_found"), exception) + # most errors contain as much info in the message as we can get from the code + # identify any that we can add useful detail for and include them here + if Errors.is_expired_session(exception): + # catch this one so we can attempt to refresh the session before telling them it failed + logger.error(_("session.errors.session_expired")) + # TODO: add session as an argument to this method + # and add the full command line as a field in Session? + # "session.session_expired_login")) + # session.renew_session() + return else: logger.error(exception) diff --git a/tabcmd/commands/datasources_and_workbooks/delete_command.py b/tabcmd/commands/datasources_and_workbooks/delete_command.py index e28f5f45..9ac1a9e3 100644 --- a/tabcmd/commands/datasources_and_workbooks/delete_command.py +++ b/tabcmd/commands/datasources_and_workbooks/delete_command.py @@ -32,7 +32,7 @@ def run_command(args): logger = log(__class__.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() - server = session.create_session(args) + server = session.create_session(args, logger) content_type: str = "" if args.workbook: content_type = "workbook" diff --git a/tabcmd/commands/datasources_and_workbooks/export_command.py b/tabcmd/commands/datasources_and_workbooks/export_command.py index 6775181a..4dad864b 100644 --- a/tabcmd/commands/datasources_and_workbooks/export_command.py +++ b/tabcmd/commands/datasources_and_workbooks/export_command.py @@ -73,7 +73,7 @@ def run_command(args): logger = log(__class__.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() - server = session.create_session(args) + server = session.create_session(args, logger) view_content_url, wb_content_url = ExportCommand.parse_export_url_to_workbook_and_view(logger, args.url) logger.debug([view_content_url, wb_content_url]) if not view_content_url and not wb_content_url: diff --git a/tabcmd/commands/datasources_and_workbooks/get_url_command.py b/tabcmd/commands/datasources_and_workbooks/get_url_command.py index 83fb158a..440f8c8a 100644 --- a/tabcmd/commands/datasources_and_workbooks/get_url_command.py +++ b/tabcmd/commands/datasources_and_workbooks/get_url_command.py @@ -41,7 +41,7 @@ def run_command(args): logger = log(__class__.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() - server = session.create_session(args) + server = session.create_session(args, logger) if " " in args.url: Errors.exit_with_error(logger, _("export.errors.white_space_workbook_view")) diff --git a/tabcmd/commands/datasources_and_workbooks/publish_command.py b/tabcmd/commands/datasources_and_workbooks/publish_command.py index c4866bbd..ce04fa31 100644 --- a/tabcmd/commands/datasources_and_workbooks/publish_command.py +++ b/tabcmd/commands/datasources_and_workbooks/publish_command.py @@ -36,7 +36,7 @@ def run_command(args): logger = log(__class__.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() - server = session.create_session(args) + server = session.create_session(args, logger) if args.project_name: try: diff --git a/tabcmd/commands/datasources_and_workbooks/runschedule_command.py b/tabcmd/commands/datasources_and_workbooks/runschedule_command.py index 9661135b..9634c4e2 100644 --- a/tabcmd/commands/datasources_and_workbooks/runschedule_command.py +++ b/tabcmd/commands/datasources_and_workbooks/runschedule_command.py @@ -23,7 +23,7 @@ def run_command(args): logger = log(__class__.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() - server = session.create_session(args) + server = session.create_session(args, logger) schedule = DatasourcesAndWorkbooks.get_items_by_name(logger, server.schedules, args.schedule)[0] logger.info(_("runschedule.status")) Errors.exit_with_error(logger, "Not yet implemented") diff --git a/tabcmd/commands/extracts/create_extracts_command.py b/tabcmd/commands/extracts/create_extracts_command.py index f23be293..4dcf2ba0 100644 --- a/tabcmd/commands/extracts/create_extracts_command.py +++ b/tabcmd/commands/extracts/create_extracts_command.py @@ -31,7 +31,7 @@ def run_command(args): logger = log(__class__.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() - server = session.create_session(args) + server = session.create_session(args, logger) creation_call = None try: logger.debug( diff --git a/tabcmd/commands/extracts/decrypt_extracts_command.py b/tabcmd/commands/extracts/decrypt_extracts_command.py index a43d8fa6..cb47d7fa 100644 --- a/tabcmd/commands/extracts/decrypt_extracts_command.py +++ b/tabcmd/commands/extracts/decrypt_extracts_command.py @@ -24,7 +24,7 @@ def run_command(args): logger = log(__class__.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() - server = session.create_session(args) + server = session.create_session(args, logger) site_item = Server.get_site_for_command_or_throw(logger, server, args.site_name) try: logger.info(_("decryptextracts.status").format(args.site_name)) diff --git a/tabcmd/commands/extracts/delete_extracts_command.py b/tabcmd/commands/extracts/delete_extracts_command.py index 62ba3bb0..20eaf5b8 100644 --- a/tabcmd/commands/extracts/delete_extracts_command.py +++ b/tabcmd/commands/extracts/delete_extracts_command.py @@ -31,7 +31,7 @@ def run_command(args): logger = log(__class__.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() - server = session.create_session(args) + server = session.create_session(args, logger) try: if args.datasource: logger.info(_("deleteextracts.for.datasource").format(args.datasource)) diff --git a/tabcmd/commands/extracts/encrypt_extracts_command.py b/tabcmd/commands/extracts/encrypt_extracts_command.py index 1b697fd7..0454d0ea 100644 --- a/tabcmd/commands/extracts/encrypt_extracts_command.py +++ b/tabcmd/commands/extracts/encrypt_extracts_command.py @@ -26,7 +26,7 @@ def run_command(args): logger = log(__class__.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() - server = session.create_session(args) + server = session.create_session(args, logger) site_item = Server.get_site_for_command_or_throw(logger, server, args.site_name) try: logger.info(_("encryptextracts.status").format(site_item.name)) diff --git a/tabcmd/commands/extracts/reencrypt_extracts_command.py b/tabcmd/commands/extracts/reencrypt_extracts_command.py index 4f7b4ef5..e8eb363a 100644 --- a/tabcmd/commands/extracts/reencrypt_extracts_command.py +++ b/tabcmd/commands/extracts/reencrypt_extracts_command.py @@ -26,7 +26,7 @@ def run_command(args): logger = log(__class__.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() - server = session.create_session(args) + server = session.create_session(args, logger) site_item = Server.get_site_for_command_or_throw(logger, server, args.site_name) try: logger.info(_("reencryptextracts.status").format(site_item.name)) diff --git a/tabcmd/commands/extracts/refresh_extracts_command.py b/tabcmd/commands/extracts/refresh_extracts_command.py index e18020a3..73a467ff 100644 --- a/tabcmd/commands/extracts/refresh_extracts_command.py +++ b/tabcmd/commands/extracts/refresh_extracts_command.py @@ -33,7 +33,7 @@ def run_command(args): logger = log(__class__.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() - server = session.create_session(args) + server = session.create_session(args, logger) refresh_action = "refresh" if args.addcalculations or args.removecalculations: diff --git a/tabcmd/commands/group/create_group_command.py b/tabcmd/commands/group/create_group_command.py index 8aeddb84..357fccb5 100644 --- a/tabcmd/commands/group/create_group_command.py +++ b/tabcmd/commands/group/create_group_command.py @@ -25,7 +25,7 @@ def run_command(args): logger = log(__class__.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() - server = session.create_session(args) + server = session.create_session(args, logger) try: logger.info(_("creategroup.status").format(args.name)) new_group = TSC.GroupItem(args.name) diff --git a/tabcmd/commands/group/delete_group_command.py b/tabcmd/commands/group/delete_group_command.py index accf62e4..71af42a5 100644 --- a/tabcmd/commands/group/delete_group_command.py +++ b/tabcmd/commands/group/delete_group_command.py @@ -25,7 +25,7 @@ def run_command(args): logger = log(__class__.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() - server = session.create_session(args) + server = session.create_session(args, logger) try: logger.info(_("tabcmd.find.group").format(args.name)) group_id = Server.find_group(logger, server, args.name).id diff --git a/tabcmd/commands/project/create_project_command.py b/tabcmd/commands/project/create_project_command.py index 03a7477d..85853edd 100644 --- a/tabcmd/commands/project/create_project_command.py +++ b/tabcmd/commands/project/create_project_command.py @@ -30,7 +30,7 @@ def run_command(args): logger = log(__class__.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() - server = session.create_session(args) + server = session.create_session(args, logger) parent_id = None readable_name = args.project_name if args.parent_project_path: diff --git a/tabcmd/commands/project/delete_project_command.py b/tabcmd/commands/project/delete_project_command.py index 74b72395..b107406c 100644 --- a/tabcmd/commands/project/delete_project_command.py +++ b/tabcmd/commands/project/delete_project_command.py @@ -27,7 +27,7 @@ def run_command(args): logger = log(__class__.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() - server = session.create_session(args) + server = session.create_session(args, logger) if args.parent_project_path: logger.debug("parent path: {}".format(args.parent_project_path)) diff --git a/tabcmd/commands/project/publish_samples_command.py b/tabcmd/commands/project/publish_samples_command.py index 6d8a54c4..046e4491 100644 --- a/tabcmd/commands/project/publish_samples_command.py +++ b/tabcmd/commands/project/publish_samples_command.py @@ -31,7 +31,7 @@ def run_command(args): logger = log(__class__.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() - server = session.create_session(args) + server = session.create_session(args, logger) try: project = PublishSamplesCommand.get_project_by_name_and_parent_path( logger, server, args.project_name, args.parent_project_path diff --git a/tabcmd/commands/site/create_site_command.py b/tabcmd/commands/site/create_site_command.py index 84208188..f684ba1f 100644 --- a/tabcmd/commands/site/create_site_command.py +++ b/tabcmd/commands/site/create_site_command.py @@ -27,7 +27,7 @@ def run_command(args): logger = log(__class__.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() - server = session.create_session(args) + server = session.create_session(args, logger) admin_mode = "ContentAndUsers" # default: allow site admins to manage users if not args.site_admin_user_management: admin_mode = "ContentOnly" diff --git a/tabcmd/commands/site/delete_site_command.py b/tabcmd/commands/site/delete_site_command.py index 56256c8e..f1892730 100644 --- a/tabcmd/commands/site/delete_site_command.py +++ b/tabcmd/commands/site/delete_site_command.py @@ -25,7 +25,7 @@ def run_command(args): logger = log(__class__.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() - server = session.create_session(args) + server = session.create_session(args, logger) target_site: TSC.SiteItem = Server.get_site_by_name(logger, server, args.site_name_to_delete) target_site_id = target_site.id logger.debug(strings[3].format(target_site_id, server.site_id)) diff --git a/tabcmd/commands/site/edit_site_command.py b/tabcmd/commands/site/edit_site_command.py index 2d7e3db4..ab6451ed 100644 --- a/tabcmd/commands/site/edit_site_command.py +++ b/tabcmd/commands/site/edit_site_command.py @@ -30,7 +30,7 @@ def run_command(args): logger = log(__class__.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() - server = session.create_session(args) + server = session.create_session(args, logger) site_item = Server.get_site_for_command_or_throw(logger, server, args.site_name) if args.url: diff --git a/tabcmd/commands/site/list_command.py b/tabcmd/commands/site/list_command.py index a77eab75..da6b9f76 100644 --- a/tabcmd/commands/site/list_command.py +++ b/tabcmd/commands/site/list_command.py @@ -3,7 +3,6 @@ from tabcmd.commands.auth.session import Session from tabcmd.commands.constants import Errors from tabcmd.commands.server import Server -from tabcmd.execution.global_options import * from tabcmd.execution.localize import _ from tabcmd.execution.logger_config import log @@ -13,34 +12,48 @@ class ListCommand(Server): Command to return a list of content the user can access """ + # strings to move to string files + local_strings = { + "tabcmd_content_listing": "===== Listing {0} content for user {1}...", + "tabcmd_listing_label_name": "NAME:", + "tabcmd_listing_label_id": "ID:", + } + name: str = "list" description: str = "List content items of a specified type" @staticmethod def define_args(list_parser): args_group = list_parser.add_argument_group(title=ListCommand.name) - args_group.add_argument("content", choices=["projects", "workbooks", "datasources"], help="View content") + args_group.add_argument( + "content", choices=["projects", "workbooks", "datasources", "flows"], help="View content" + ) + args_group.add_argument("-d", "--details", action="store_true", help="Show object details") @staticmethod def run_command(args): logger = log(__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() - server = session.create_session(args) + server = session.create_session(args, logger) content_type = args.content try: + logger.info(ListCommand.local_strings.tabcmd_content_listing.format(content_type, session.username)) + if content_type == "projects": items = server.projects.all() elif content_type == "workbooks": items = server.workbooks.all() elif content_type == "datasources": items = server.datasources.all() + elif content_type == "flows": + items = server.flows.all() - logger.info("===== Listing {0} content for user {1}...".format(content_type, session.username)) for item in items: - logger.info("NAME:".rjust(10), item.name) - logger.info("ID:".rjust(10), item.id) + logger.info(ListCommand.local_strings.tabcmd_listing_label_name.rjust(10), item.name) + if args.details: + logger.info(item) except Exception as e: Errors.exit_with_error(logger, e) diff --git a/tabcmd/commands/site/list_sites_command.py b/tabcmd/commands/site/list_sites_command.py index 66ad24c7..e077072e 100644 --- a/tabcmd/commands/site/list_sites_command.py +++ b/tabcmd/commands/site/list_sites_command.py @@ -26,7 +26,7 @@ def run_command(args): logger = log(__class__.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() - server = session.create_session(args) + server = session.create_session(args, logger) try: sites, pagination = server.sites.get() logger.info(_("listsites.status").format(session.username)) diff --git a/tabcmd/commands/user/add_users_command.py b/tabcmd/commands/user/add_users_command.py index 801f0253..1fc79b46 100644 --- a/tabcmd/commands/user/add_users_command.py +++ b/tabcmd/commands/user/add_users_command.py @@ -25,7 +25,7 @@ def run_command(args): logger = log(__class__.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() - server = session.create_session(args) + server = session.create_session(args, logger) logger.info(_("tabcmd.add.users.to_site").format(args.users.name, args.name)) diff --git a/tabcmd/commands/user/create_site_users.py b/tabcmd/commands/user/create_site_users.py index 7a01af21..ce515fec 100644 --- a/tabcmd/commands/user/create_site_users.py +++ b/tabcmd/commands/user/create_site_users.py @@ -31,7 +31,7 @@ def run_command(args): logger = log(__class__.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() - server = session.create_session(args) + server = session.create_session(args, logger) number_of_users_listed = 0 number_of_users_added = 0 number_of_errors = 0 diff --git a/tabcmd/commands/user/create_users_command.py b/tabcmd/commands/user/create_users_command.py index 71468f39..1c3ab4fa 100644 --- a/tabcmd/commands/user/create_users_command.py +++ b/tabcmd/commands/user/create_users_command.py @@ -31,7 +31,7 @@ def run_command(args): logger = log(__class__.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() - server = session.create_session(args) + server = session.create_session(args, logger) number_of_users_listed = 0 number_of_users_added = 0 number_of_errors = 0 diff --git a/tabcmd/commands/user/delete_site_users_command.py b/tabcmd/commands/user/delete_site_users_command.py index 42f088fd..731ed7bf 100644 --- a/tabcmd/commands/user/delete_site_users_command.py +++ b/tabcmd/commands/user/delete_site_users_command.py @@ -28,7 +28,7 @@ def run_command(args): logger = log(__class__.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() - server = session.create_session(args) + server = session.create_session(args, logger) logger.info(_("deleteusers.status").format(args.filename.name)) diff --git a/tabcmd/commands/user/remove_users_command.py b/tabcmd/commands/user/remove_users_command.py index 722ce3d5..38f0f720 100644 --- a/tabcmd/commands/user/remove_users_command.py +++ b/tabcmd/commands/user/remove_users_command.py @@ -25,7 +25,7 @@ def run_command(args): logger = log(__class__.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() - server = session.create_session(args) + server = session.create_session(args, logger) logger.info(_("tabcmd.delete.users.from_server").format(args.users.name, args.name)) diff --git a/tabcmd/execution/tabcmd_controller.py b/tabcmd/execution/tabcmd_controller.py index 1f7f0fdc..3798b30c 100644 --- a/tabcmd/execution/tabcmd_controller.py +++ b/tabcmd/execution/tabcmd_controller.py @@ -23,29 +23,27 @@ def run(parser, user_input=None): sys.exit(0) user_input = user_input or sys.argv[1:] namespace = parser.parse_args(user_input) - if namespace.logging_level and namespace.logging_level != logging.INFO: + if hasattr("namespace", "logging_level") and namespace.logging_level != logging.INFO: print("logging:", namespace.logging_level) logger = log(__name__, namespace.logging_level or logging.INFO) - if namespace.password or namespace.token_value: - logger.trace(namespace.func) + if (hasattr("namespace", "password") or hasattr("namespace", "token_value")) and hasattr("namespace", "func"): + # don't print whole namespace because it has secrets + logger.debug(namespace.func) else: - logger.trace(namespace) + logger.debug(namespace) if namespace.language: set_client_locale(namespace.language, logger) - try: - command_name = namespace.func - except AttributeError as aer: - # if no command was given, argparse will just not create the attribute - parser.print_help() - sys.exit(2) try: # if a subcommand was identified, call the function assigned to it # this is the functional equivalent of the call by reflection in the previous structure # https://stackoverflow.com/questions/49038616/argparse-subparsers-with-functions namespace.func.run_command(namespace) except Exception as e: + # todo: use log_stack here for better presentation logger.exception(e) + # if no command was given, argparse will just not create the attribute + sys.exit(2) return namespace diff --git a/tests/commands/test_run_commands.py b/tests/commands/test_run_commands.py index 774878b6..43004ba5 100644 --- a/tests/commands/test_run_commands.py +++ b/tests/commands/test_run_commands.py @@ -76,7 +76,8 @@ def _set_up_session(mock_session, mock_server): def test_login(self, mock_session, mock_server): RunCommandsTest._set_up_session(mock_session, mock_server) login_command.LoginCommand.run_command(mock_args) - mock_session.assert_called_with(mock_args) + + mock_session.assert_called_with(mock_args, ANY) @patch("tabcmd.commands.auth.session.Session.end_session_and_clear_data") def test_logout(self, mock_end_session, mock_create_session, mock_server): diff --git a/tests/commands/test_session.py b/tests/commands/test_session.py index fba90fd8..5f4c8297 100644 --- a/tests/commands/test_session.py +++ b/tests/commands/test_session.py @@ -68,9 +68,9 @@ def _set_mocks_for_json_file_exists(mock_path, does_it_exist=True): return mock_path -def _set_mocks_for_creds_file(mock_file): - mock_file.readlines.return_value = "dummypassword" - return mock_file +def _set_mock_file_content(mock_load, expected_content): + mock_load.return_value = expected_content + return mock_load @mock.patch("json.dump") @@ -96,6 +96,25 @@ def test_save_session_to_json(self, mock_open, mock_path, mock_load, mock_dump): test_session._save_session_to_json() assert mock_dump.was_called() + def clear_session(self, mock_open, mock_path, mock_load, mock_dump): + _set_mocks_for_json_file_exists(mock_path) + test_session = Session() + test_session.username = "USN" + test_session.server = "SRVR" + test_session._clear_data() + assert test_session.username is None + assert test_session.server is None + + def test_json_not_present(self, mock_open, mock_path, mock_load, mock_dump): + _set_mocks_for_json_file_exists(mock_path, False) + assert mock_open.was_not_called() + + def test_json_invalid(self, mock_open, mock_path, mock_load, mock_dump): + _set_mocks_for_json_file_exists(mock_path) + _set_mock_file_content(mock_load, "just a string") + test_session = Session() + assert test_session.username is None + @mock.patch("getpass.getpass") class BuildCredentialsTests(unittest.TestCase): @@ -212,7 +231,7 @@ def test_create_session_first_time_no_args( mock_tsc().users.get_by_id.return_value = None new_session = Session() with self.assertRaises(SystemExit): - auth = new_session.create_session(test_args) + auth = new_session.create_session(test_args, None) @mock.patch("tableauserverclient.Server") def test_create_session_first_time_with_token_arg( @@ -224,7 +243,7 @@ def test_create_session_first_time_with_token_arg( test_args.token_name = "tn" test_args.token_value = "foo" new_session = Session() - auth = new_session.create_session(test_args) + auth = new_session.create_session(test_args, None) assert auth is not None, auth assert auth.auth_token is not None, auth.auth_token assert auth.auth_token.name is not None, auth.auth_token @@ -241,7 +260,7 @@ def test_create_session_first_time_with_password_arg( test_args.password = "pppp" new_session = Session() - auth = new_session.create_session(test_args) + auth = new_session.create_session(test_args, None) assert auth is not None, auth assert auth.auth_token is not None, auth.auth_token assert new_session.username == "uuuu", new_session @@ -259,7 +278,7 @@ def test_create_session_first_time_with_password_file_as_password( test_args.password_file = "filename" with mock.patch("builtins.open", mock.mock_open(read_data="my_password")): new_session = Session() - auth = new_session.create_session(test_args) + auth = new_session.create_session(test_args, None) assert auth is not None, auth assert auth.auth_token is not None, auth.auth_token @@ -277,7 +296,7 @@ def test_create_session_first_time_with_password_file_as_token( test_args.password_file = "filename" with mock.patch("builtins.open", mock.mock_open(read_data="my_token")): new_session = Session() - auth = new_session.create_session(test_args) + auth = new_session.create_session(test_args, None) assert auth is not None, auth assert auth.auth_token is not None, auth.auth_token @@ -309,7 +328,7 @@ def test_create_session_with_active_session_saved( test_args.no_prompt = False new_session = Session() - auth = new_session.create_session(test_args) + auth = new_session.create_session(test_args, None) assert auth is not None, auth assert auth.auth_token is not None, auth.auth_token assert mock_tsc.has_been_called() @@ -326,7 +345,7 @@ def test_create_session_with_saved_expired_username_session( mock_pass.getpass.return_value = "success" test_args.password = "eqweqwe" new_session = Session() - auth = new_session.create_session(test_args) + auth = new_session.create_session(test_args, None) assert mock_pass.has_been_called() assert auth is not None, auth assert auth.auth_token is not None, auth.auth_token @@ -379,7 +398,7 @@ def test_connection_times_out(self): test_args.server = "https://nothere.com" with self.assertRaises(SystemExit): - new_session.create_session(test_args) + new_session.create_session(test_args, None) # should test connection doesn't time out? diff --git a/tests/e2e/tests_integration.py b/tests/e2e/tests_integration.py index e18c38b6..f63588b1 100644 --- a/tests/e2e/tests_integration.py +++ b/tests/e2e/tests_integration.py @@ -65,7 +65,7 @@ def test_log_in(): no_cookie=False, ) test_session = Session() - server = test_session.create_session(args) + server = test_session.create_session(args, logger) assert test_session.auth_token is not None assert test_session.site_id is not None assert test_session.user_id is not None @@ -96,7 +96,7 @@ def test_reuse_session(self): no_cookie=False, ) test_session = Session() - test_session.create_session(args) + test_session.create_session(args, logger) assert test_session.auth_token is not None assert test_session.site_id is not None assert test_session.user_id is not None