diff --git a/piperider_cli/cli.py b/piperider_cli/cli.py index 70450d52f..e714692f1 100644 --- a/piperider_cli/cli.py +++ b/piperider_cli/cli.py @@ -9,7 +9,7 @@ from rich.console import Console from piperider_cli import __version__, event, sentry_dns, sentry_env -from piperider_cli.cli_utils import DbtUtil +from piperider_cli.cli_utils import DbtUtil, verify_upload_related_options from piperider_cli.cli_utils.cloud import CloudConnectorHelper from piperider_cli.configuration import FileSystem, is_piperider_workspace_exist from piperider_cli.error import DbtProjectNotFoundError @@ -63,6 +63,11 @@ def dbt_select_option_builder(): help='Disable auto detection of dbt projects.'), ] +feature_flags = [ + click.option('--enable-quick-look-share', envvar='PIPERIDER_ENABLE_QUICK_LOOK_SHARE', + is_flag=True, default=False, hidden=True, help='Enable share to Quick Look.') +] + def add_options(options): def _add_options(func): @@ -230,6 +235,7 @@ def diagnose(**kwargs): help='If set, use the given directory as the source for JSON files to compare with this project.') ]) @add_options(dbt_related_options) +@add_options(feature_flags) @add_options(debug_option) def run(**kwargs): """ @@ -287,6 +293,7 @@ def generate_report(**kwargs): help='Specify the project name to upload.') @click.option('--share', default=False, is_flag=True, help='Enable public share of the report to PipeRider Cloud.') @click.option('--open', is_flag=True, help='Opens the generated report in the system\'s default browser') +@add_options(feature_flags) @add_options(debug_option) def compare_reports(**kwargs): 'Compare two existing reports selected in interactive mode or by option.' @@ -298,21 +305,14 @@ def compare_reports(**kwargs): tables_from = kwargs.get('tables_from') summary_file = kwargs.get('summary_file') open_report = kwargs.get('open') - force_upload = kwargs.get('upload') - enable_share = kwargs.get('share') project_name = kwargs.get('project') - if enable_share or CloudConnectorHelper.is_auto_upload(): - force_upload = True - - if force_upload and not CloudConnectorHelper.is_login(): - console = Console() - console.print('[bold yellow]Warning: [/bold yellow]Reports will be uploaded as a temporary quick look.') + enable_upload, enable_share = verify_upload_related_options(**kwargs) from piperider_cli.compare_report import CompareReport CompareReport.exec(a=a, b=b, last=last, datasource=datasource, report_dir=kwargs.get('report_dir'), output=kwargs.get('output'), summary_file=summary_file, - tables_from=tables_from, force_upload=force_upload, enable_share=enable_share, + tables_from=tables_from, force_upload=enable_upload, enable_share=enable_share, open_report=open_report, project_name=project_name, debug=kwargs.get('debug', False), show_progress=True) @@ -449,6 +449,7 @@ def cloud_compare_reports(**kwargs): show_default=True), ]) @add_options(dbt_related_options) +@add_options(feature_flags) @add_options(debug_option) def compare_with_recipe(ref, **kwargs): """ diff --git a/piperider_cli/cli_utils/__init__.py b/piperider_cli/cli_utils/__init__.py index dbe1e1ec9..291143fbe 100644 --- a/piperider_cli/cli_utils/__init__.py +++ b/piperider_cli/cli_utils/__init__.py @@ -1,6 +1,10 @@ import io from typing import Union +from rich.console import Console + +from piperider_cli.cli_utils.cloud import CloudConnectorHelper + class DbtUtil: @@ -31,3 +35,33 @@ def load_credential_from_dbt_profile(dbt_profile, profile_name, target_name): import piperider_cli.dbtutil as u return u.load_credential_from_dbt_profile(dbt_profile=dbt_profile, profile_name=profile_name, target_name=target_name) + + +def verify_upload_related_options(**kwargs): + console = Console() + upload = kwargs.get('upload', False) + upload_with_share = kwargs.get('share', False) + enable_quick_look_share = kwargs.get('enable_quick_look_share', False) + enable_auto_upload = CloudConnectorHelper.is_auto_upload() + is_cloud_login = CloudConnectorHelper.is_login() + + if is_cloud_login is True: + if enable_auto_upload is True: + console.print('[[bold green]Enable Auto Upload[/bold green]]') + upload = True + if upload_with_share is True: + upload = True + return upload, upload_with_share + else: + if upload is False and upload_with_share is True and enable_quick_look_share is True: + # Upload to Cloud Quick Look without login + console.print( + '[[bold green]Enable Quick Look Share[/bold green]] ' + 'Reports will be uploaded as a temporary quick look.') + return True, True + if upload is True or upload_with_share is True: + console.print( + '[[bold yellow]Warning[/bold yellow]] ' + 'The report won\'t be uploaded due to not logged in.') + + return False, False diff --git a/piperider_cli/cli_utils/compare_with_recipe.py b/piperider_cli/cli_utils/compare_with_recipe.py index 11c5222b2..261c016e8 100644 --- a/piperider_cli/cli_utils/compare_with_recipe.py +++ b/piperider_cli/cli_utils/compare_with_recipe.py @@ -1,5 +1,8 @@ import time + from rich.console import Console + +from piperider_cli.cli_utils import verify_upload_related_options from piperider_cli.event import CompareEventPayload, log_event @@ -32,9 +35,8 @@ def compare_with_recipe(ref, **kwargs): """ from piperider_cli.cli_utils import DbtUtil - from piperider_cli.cli_utils.cloud import CloudConnectorHelper from piperider_cli.configuration import FileSystem, is_piperider_workspace_exist - from piperider_cli.error import DbtProjectNotFoundError, RecipeConfigException + from piperider_cli.error import DbtProjectNotFoundError from piperider_cli.initializer import Initializer from piperider_cli.recipes import RecipeConfiguration, configure_recipe_execution_flags, is_recipe_dry_run @@ -42,8 +44,6 @@ def compare_with_recipe(ref, **kwargs): recipe = kwargs.get('recipe') summary_file = kwargs.get('summary_file') - force_upload = kwargs.get('upload') - enable_share = kwargs.get('share') open_report = kwargs.get('open') project_name = kwargs.get('project') debug = kwargs.get('debug', False) @@ -66,14 +66,7 @@ def compare_with_recipe(ref, **kwargs): # reconfigure recipe global flags configure_recipe_execution_flags(dry_run=kwargs.get('dry_run'), interactive=kwargs.get('interactive')) - if enable_share: - force_upload = True - - if force_upload is True and CloudConnectorHelper.is_login() is False: - raise RecipeConfigException( - message='Please login to PipeRider Cloud first.', - hint='Run "piperider cloud login" to login to PipeRider Cloud.' - ) + enable_upload, enable_share = verify_upload_related_options(**kwargs) # Search dbt project config files dbt_project_dir = kwargs.get('dbt_project_dir') @@ -116,7 +109,7 @@ def compare_with_recipe(ref, **kwargs): CompareReport.exec(a=base, b=target, last=last, datasource=None, output=kwargs.get('output'), tables_from="all", summary_file=summary_file, - force_upload=force_upload, + force_upload=enable_upload, enable_share=enable_share, open_report=open_report, project_name=project_name, diff --git a/piperider_cli/cli_utils/run_cmd.py b/piperider_cli/cli_utils/run_cmd.py index 8abbf8c4d..0fe60bb3e 100644 --- a/piperider_cli/cli_utils/run_cmd.py +++ b/piperider_cli/cli_utils/run_cmd.py @@ -4,6 +4,7 @@ from rich.console import Console +from piperider_cli.cli_utils import verify_upload_related_options from piperider_cli.event import log_event @@ -30,15 +31,15 @@ def run(**kwargs): table = kwargs.get('table') output = kwargs.get('output') open_report = kwargs.get('open') - enable_share = kwargs.get('share') skip_report = kwargs.get('skip_report') dbt_target_path = kwargs.get('dbt_target_path') dbt_list = kwargs.get('dbt_list') - force_upload = kwargs.get('upload') project_name = kwargs.get('project') select = kwargs.get('select') state = kwargs.get('state') + enable_upload, enable_share = verify_upload_related_options(**kwargs) + if project_name is not None: os.environ.get('PIPERIDER_API_PROJECT') @@ -90,14 +91,9 @@ def run(**kwargs): skip_datasource_connection=kwargs.get('skip_datasource'), event_payload=event_payload) if ret in (0, EC_WARN_NO_PROFILED_MODULES): - if enable_share: - force_upload = True - - auto_upload = CloudConnectorHelper.is_auto_upload() - is_cloud_view = (force_upload or auto_upload) if not skip_report: - GenerateReport.exec(None, kwargs.get('report_dir'), output, open_report, is_cloud_view) + GenerateReport.exec(None, kwargs.get('report_dir'), output, open_report, open_in_cloud=enable_upload) if ret == EC_WARN_NO_PROFILED_MODULES: # No module was profiled @@ -107,14 +103,11 @@ def run(**kwargs): ret = 0 event_payload.step = 'upload' - if CloudConnectorHelper.is_login() and is_cloud_view: + if enable_upload: ret = CloudConnectorHelper.upload_latest_report(report_dir=kwargs.get('report_dir'), debug=kwargs.get('debug'), open_report=open_report, enable_share=enable_share, project_name=project_name) - elif not CloudConnectorHelper.is_login() and is_cloud_view: - console = Console() - console.print('[bold yellow]Warning: [/bold yellow]The report is not uploaded due to not logged in.') if ret != 0: reason = 'error' diff --git a/tests/test_cli.py b/tests/test_cli.py index ef61a4f81..a11d509f8 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -4,6 +4,7 @@ from click.testing import CliRunner from piperider_cli.cli import init, version, diagnose, run +from piperider_cli.cli_utils import verify_upload_related_options class TestPipeRiderCli(TestCase): @@ -25,15 +26,17 @@ def test_diagnose_command(self, *args): result = self.cli_runner.invoke(diagnose, ["--dbt-project-dir", self.unit_test_dir]) assert result.exit_code == 0 - @mock.patch('piperider_cli.runner.Runner.exec', return_value=0) + @mock.patch('piperider_cli.runner.Runner.exec', return_value=3) @mock.patch('piperider_cli.generate_report.GenerateReport.exec', return_value=0) - @mock.patch('piperider_cli.cloud_connector.CloudConnector.is_login', return_value=False) - @mock.patch('piperider_cli.cloud_connector.CloudConnector.upload_latest_report', return_value=0) + @mock.patch('piperider_cli.cli_utils.cloud.CloudConnectorHelper.is_login', return_value=True) + @mock.patch('piperider_cli.cli_utils.cloud.CloudConnectorHelper.upload_latest_report', return_value=0) + @mock.patch('piperider_cli.runner.RunEventPayload') def test_run_command(self, *args): # Manually config dbt project and profiles dir result = self.cli_runner.invoke(run, [ "--dbt-project-dir", self.unit_test_dir, "--dbt-profiles-dir", self.unit_test_dir, + "--select", "test", ]) assert result.exit_code == 0 @@ -52,3 +55,89 @@ def test_run_command(self, *args): "--dbt-list", ]) assert result.exit_code == 1 + + # Upload the latest report + result = self.cli_runner.invoke(run, [ + "--dbt-project-dir", self.unit_test_dir, + "--upload", + ]) + assert result.exit_code == 0 + + mock_runner_exec = args[-1] + mock_runner_exec.return_value = 666 + mock_run_event_payload = args[0] + result = self.cli_runner.invoke(run, [ + "--dbt-project-dir", self.unit_test_dir, + ]) + assert result.exit_code == 0 + assert mock_run_event_payload.return_value.reason == 'error' + + @mock.patch('piperider_cli.cloud_connector.CloudConnector.is_login', return_value=False) + @mock.patch('piperider_cli.cloud_connector.CloudConnector.is_auto_upload', return_value=False) + def test_verify_upload_related_options_without_login(self, *args): + enable_upload, enable_share = verify_upload_related_options(**{ + 'upload': False, + 'share': False, + }) + assert enable_upload is False and enable_share is False + + enable_upload, enable_share = verify_upload_related_options(**{ + 'upload': False, + 'share': True, + }) + assert enable_upload is False and enable_share is False + + enable_upload, enable_share = verify_upload_related_options(**{ + 'upload': True, + 'share': False, + }) + assert enable_upload is False and enable_share is False + + enable_upload, enable_share = verify_upload_related_options(**{ + 'upload': True, + 'share': True, + }) + assert enable_upload is False and enable_share is False + + enable_upload, enable_share = verify_upload_related_options(**{ + 'upload': False, + 'share': True, + 'enable_quick_look_share': True, + }) + assert enable_upload is True and enable_share is True + + @mock.patch('piperider_cli.cloud_connector.CloudConnector.is_login', return_value=True) + @mock.patch('piperider_cli.cloud_connector.CloudConnector.is_auto_upload', return_value=False) + def test_verify_upload_related_options_with_login(self, *args): + mock_is_auto_upload = args[0] + + enable_upload, enable_share = verify_upload_related_options(**{ + 'upload': False, + 'share': False, + }) + assert enable_upload is False and enable_share is False + + enable_upload, enable_share = verify_upload_related_options(**{ + 'upload': False, + 'share': True, + }) + assert enable_upload is True and enable_share is True + + enable_upload, enable_share = verify_upload_related_options(**{ + 'upload': True, + 'share': False, + }) + assert enable_upload is True and enable_share is False + + mock_is_auto_upload.return_value = True + enable_upload, enable_share = verify_upload_related_options(**{ + 'upload': False, + 'share': False, + }) + assert enable_upload is True and enable_share is False + + enable_upload, enable_share = verify_upload_related_options(**{ + 'upload': False, + 'share': True, + }) + assert enable_upload is True and enable_share is True