From 8e53182c9ff80e260a7d38b06c6b67ef6378275c Mon Sep 17 00:00:00 2001 From: Francois Campbell Date: Thu, 6 Jun 2024 14:43:27 -0400 Subject: [PATCH] [SNOW1456079] Change patch to Optional[int] (#1169) Changes `snow app version create --patch` to require an integer for the patch number since that's what the backend expects (passing in a string results in a SQL compilation error). --- RELEASE-NOTES.md | 1 + .../cli/plugins/nativeapp/commands.py | 2 +- .../cli/plugins/nativeapp/run_processor.py | 4 +- .../cli/plugins/nativeapp/version/commands.py | 2 +- .../nativeapp/version/version_processor.py | 4 +- tests/__snapshots__/test_help_messages.ambr | 225 +++++++++--------- tests_integration/nativeapp/test_version.py | 91 +++++++ 7 files changed, 214 insertions(+), 115 deletions(-) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index ae21cdb3b3..89a6616b5e 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -5,6 +5,7 @@ ## New additions * Added `snow app bundle` command that prepares a local folder in the project directory with artifacts to be uploaded to a stage as part of creating a Snowflake Native App. +* Changed `snow app version create --patch` to require an integer patch number, aligning with what Snowflake expects * Added `snow notebook` commands: * `snow notebook execute` enabling head-less execution of a notebook. * `snow notebook create` proving an option to create a Snowflake Notebook from a file on stage. diff --git a/src/snowflake/cli/plugins/nativeapp/commands.py b/src/snowflake/cli/plugins/nativeapp/commands.py index 3c6684ec31..eb8f012616 100644 --- a/src/snowflake/cli/plugins/nativeapp/commands.py +++ b/src/snowflake/cli/plugins/nativeapp/commands.py @@ -146,7 +146,7 @@ def app_run( help=f"""The version defined in an existing application package from which you want to create an application object. The application object and application package names are determined from the project definition file.""", ), - patch: Optional[str] = typer.Option( + patch: Optional[int] = typer.Option( None, "--patch", help=f"""The patch number under the given `--version` defined in an existing application package that should be used to create an application object. diff --git a/src/snowflake/cli/plugins/nativeapp/run_processor.py b/src/snowflake/cli/plugins/nativeapp/run_processor.py index 554771d4f9..2979694830 100644 --- a/src/snowflake/cli/plugins/nativeapp/run_processor.py +++ b/src/snowflake/cli/plugins/nativeapp/run_processor.py @@ -195,7 +195,7 @@ def upgrade_app( policy: PolicyBase, is_interactive: bool, version: Optional[str] = None, - patch: Optional[str] = None, + patch: Optional[int] = None, ): patch_clause = f"patch {patch}" if patch else "" @@ -280,7 +280,7 @@ def process( self, policy: PolicyBase, version: Optional[str] = None, - patch: Optional[str] = None, + patch: Optional[int] = None, from_release_directive: bool = False, is_interactive: bool = False, *args, diff --git a/src/snowflake/cli/plugins/nativeapp/version/commands.py b/src/snowflake/cli/plugins/nativeapp/version/commands.py index 1f8492f2ed..61be5f372a 100644 --- a/src/snowflake/cli/plugins/nativeapp/version/commands.py +++ b/src/snowflake/cli/plugins/nativeapp/version/commands.py @@ -38,7 +38,7 @@ def create( None, help=f"""Version to define in your application package. If the version already exists, an auto-incremented patch is added to the version instead. Defaults to the version specified in the `manifest.yml` file.""", ), - patch: Optional[str] = typer.Option( + patch: Optional[int] = typer.Option( None, "--patch", help=f"""The patch number you want to create for an existing version. diff --git a/src/snowflake/cli/plugins/nativeapp/version/version_processor.py b/src/snowflake/cli/plugins/nativeapp/version/version_processor.py index dbd075a17b..67cd253efc 100644 --- a/src/snowflake/cli/plugins/nativeapp/version/version_processor.py +++ b/src/snowflake/cli/plugins/nativeapp/version/version_processor.py @@ -117,7 +117,7 @@ def add_new_version(self, version: str) -> None: f"Version {version} created for application package {self.package_name}." ) - def add_new_patch_to_version(self, version: str, patch: Optional[str] = None): + def add_new_patch_to_version(self, version: str, patch: Optional[int] = None): """ Add a new patch, optionally a custom one, to an existing version in an application package. """ @@ -149,7 +149,7 @@ def add_new_patch_to_version(self, version: str, patch: Optional[str] = None): def process( self, version: Optional[str], - patch: Optional[str], + patch: Optional[int], policy: PolicyBase, git_policy: PolicyBase, is_interactive: bool, diff --git a/tests/__snapshots__/test_help_messages.ambr b/tests/__snapshots__/test_help_messages.ambr index 4ea4cf1ec9..2f61039895 100644 --- a/tests/__snapshots__/test_help_messages.ambr +++ b/tests/__snapshots__/test_help_messages.ambr @@ -320,76 +320,79 @@ application package. ╭─ Options ────────────────────────────────────────────────────────────────────╮ - │ --version TEXT The version defined in │ - │ an existing │ - │ application package │ - │ from which you want to │ - │ create an application │ - │ object. The │ - │ application object and │ - │ application package │ - │ names are determined │ - │ from the project │ - │ definition file. │ - │ [default: None] │ - │ --patch TEXT The patch number under │ - │ the given `--version` │ - │ defined in an existing │ - │ application package │ - │ that should be used to │ - │ create an application │ - │ object. The │ - │ application object and │ - │ application package │ - │ names are determined │ - │ from the project │ - │ definition file. │ - │ [default: None] │ - │ --from-release-direct… Creates or upgrades an │ - │ application object to │ - │ the version and patch │ - │ specified by the │ - │ release directive │ - │ applicable to your │ - │ Snowflake account. The │ - │ command fails if no │ - │ release directive │ - │ exists for your │ - │ Snowflake account for │ - │ a given application │ - │ package, which is │ - │ determined from the │ - │ project definition │ - │ file. Default: unset. │ - │ --interactive --no-interactive When enabled, this │ - │ option displays │ - │ prompts even if the │ - │ standard input and │ - │ output are not │ - │ terminal devices. │ - │ Defaults to True in an │ - │ interactive shell │ - │ environment, and False │ - │ otherwise. │ - │ --force When enabled, this │ - │ option causes the │ - │ command to implicitly │ - │ approve any prompts │ - │ that arise. You should │ - │ enable this option if │ - │ interactive mode is │ - │ not specified and if │ - │ you want perform │ - │ potentially │ - │ destructive actions. │ - │ Defaults to unset. │ - │ --project -p TEXT Path where the │ - │ Snowflake Native App │ - │ project resides. │ - │ Defaults to current │ - │ working directory. │ - │ --help -h Show this message and │ - │ exit. │ + │ --version TEXT The version defined │ + │ in an existing │ + │ application package │ + │ from which you want │ + │ to create an │ + │ application object. │ + │ The application │ + │ object and │ + │ application package │ + │ names are determined │ + │ from the project │ + │ definition file. │ + │ [default: None] │ + │ --patch INTEGER The patch number │ + │ under the given │ + │ `--version` defined │ + │ in an existing │ + │ application package │ + │ that should be used │ + │ to create an │ + │ application object. │ + │ The application │ + │ object and │ + │ application package │ + │ names are determined │ + │ from the project │ + │ definition file. │ + │ [default: None] │ + │ --from-release-dire… Creates or upgrades │ + │ an application object │ + │ to the version and │ + │ patch specified by │ + │ the release directive │ + │ applicable to your │ + │ Snowflake account. │ + │ The command fails if │ + │ no release directive │ + │ exists for your │ + │ Snowflake account for │ + │ a given application │ + │ package, which is │ + │ determined from the │ + │ project definition │ + │ file. Default: unset. │ + │ --interactive --no-interactive When enabled, this │ + │ option displays │ + │ prompts even if the │ + │ standard input and │ + │ output are not │ + │ terminal devices. │ + │ Defaults to True in │ + │ an interactive shell │ + │ environment, and │ + │ False otherwise. │ + │ --force When enabled, this │ + │ option causes the │ + │ command to implicitly │ + │ approve any prompts │ + │ that arise. You │ + │ should enable this │ + │ option if interactive │ + │ mode is not specified │ + │ and if you want │ + │ perform potentially │ + │ destructive actions. │ + │ Defaults to unset. │ + │ --project -p TEXT Path where the │ + │ Snowflake Native App │ + │ project resides. │ + │ Defaults to current │ + │ working directory. │ + │ --help -h Show this message and │ + │ exit. │ ╰──────────────────────────────────────────────────────────────────────────────╯ ╭─ Connection configuration ───────────────────────────────────────────────────╮ │ --connection,--environment -c TEXT Name of the connection, as defined │ @@ -551,45 +554,49 @@ │ [default: None] │ ╰──────────────────────────────────────────────────────────────────────────────╯ ╭─ Options ────────────────────────────────────────────────────────────────────╮ - │ --patch TEXT The patch number you want to │ - │ create for an existing │ - │ version. Defaults to │ - │ undefined if it is not set, │ - │ which means the Snowflake │ - │ CLI either uses the patch │ - │ specified in the │ - │ `manifest.yml` file or │ - │ automatically generates a │ - │ new patch number. │ - │ [default: None] │ - │ --skip-git-check When enabled, the Snowflake │ - │ CLI skips checking if your │ - │ project has any untracked or │ - │ stages files in git. │ - │ Default: unset. │ - │ --interactive --no-interactive When enabled, this option │ - │ displays prompts even if the │ - │ standard input and output │ - │ are not terminal devices. │ - │ Defaults to True in an │ - │ interactive shell │ - │ environment, and False │ - │ otherwise. │ - │ --force When enabled, this option │ - │ causes the command to │ - │ implicitly approve any │ - │ prompts that arise. You │ - │ should enable this option if │ - │ interactive mode is not │ - │ specified and if you want │ - │ perform potentially │ - │ destructive actions. │ - │ Defaults to unset. │ - │ --project -p TEXT Path where the Snowflake │ - │ Native App project resides. │ - │ Defaults to current working │ - │ directory. │ - │ --help -h Show this message and exit. │ + │ --patch INTEGER The patch number you want │ + │ to create for an existing │ + │ version. Defaults to │ + │ undefined if it is not │ + │ set, which means the │ + │ Snowflake CLI either uses │ + │ the patch specified in │ + │ the `manifest.yml` file │ + │ or automatically │ + │ generates a new patch │ + │ number. │ + │ [default: None] │ + │ --skip-git-check When enabled, the │ + │ Snowflake CLI skips │ + │ checking if your project │ + │ has any untracked or │ + │ stages files in git. │ + │ Default: unset. │ + │ --interactive --no-interactive When enabled, this option │ + │ displays prompts even if │ + │ the standard input and │ + │ output are not terminal │ + │ devices. Defaults to True │ + │ in an interactive shell │ + │ environment, and False │ + │ otherwise. │ + │ --force When enabled, this option │ + │ causes the command to │ + │ implicitly approve any │ + │ prompts that arise. You │ + │ should enable this option │ + │ if interactive mode is │ + │ not specified and if you │ + │ want perform potentially │ + │ destructive actions. │ + │ Defaults to unset. │ + │ --project -p TEXT Path where the Snowflake │ + │ Native App project │ + │ resides. Defaults to │ + │ current working │ + │ directory. │ + │ --help -h Show this message and │ + │ exit. │ ╰──────────────────────────────────────────────────────────────────────────────╯ ╭─ Connection configuration ───────────────────────────────────────────────────╮ │ --connection,--environment -c TEXT Name of the connection, as defined │ diff --git a/tests_integration/nativeapp/test_version.py b/tests_integration/nativeapp/test_version.py index 0a6e2a3abc..e96f0395e3 100644 --- a/tests_integration/nativeapp/test_version.py +++ b/tests_integration/nativeapp/test_version.py @@ -216,3 +216,94 @@ def test_nativeapp_version_create_3_patches( env=TEST_ENV, ) assert result.exit_code == 0 + + +@pytest.mark.integration +@pytest.mark.parametrize("project_definition_files", ["integration"], indirect=True) +def test_nativeapp_version_create_patch_is_integer( + runner, + snowflake_session, + project_definition_files: List[Path], +): + project_name = "integration" + project_dir = project_definition_files[0].parent + with pushd(project_dir): + try: + package_name = f"{project_name}_pkg_{USER_NAME}".upper() + + # create initial version + result = runner.invoke_with_connection_json( + ["app", "version", "create", "v1", "--force", "--skip-git-check"], + env=TEST_ENV, + ) + assert result.exit_code == 0 + + # create non-integer patch + result = runner.invoke_with_connection_json( + [ + "app", + "version", + "create", + "v1", + "--force", + "--skip-git-check", + "--patch", + "foo", + ], + env=TEST_ENV, + ) + assert result.exit_code == 2 + assert ( + "Invalid value for '--patch': 'foo' is not a valid integer." + in result.output + ) + + # create integer patch + result = runner.invoke_with_connection_json( + [ + "app", + "version", + "create", + "v1", + "--force", + "--skip-git-check", + "--patch", + "1", + ], + env=TEST_ENV, + ) + assert result.exit_code == 0 + + # drop the version + result_drop = runner.invoke_with_connection_json( + ["app", "version", "drop", "v1", "--force"], + env=TEST_ENV, + ) + assert result_drop.exit_code == 0 + + # ensure there are no versions now + actual = runner.invoke_with_connection_json( + ["app", "version", "list"], env=TEST_ENV + ) + assert len(actual.json) == 0 + + # make sure we always delete the package + result = runner.invoke_with_connection_json( + ["app", "teardown"], + env=TEST_ENV, + ) + assert result.exit_code == 0 + + expect = snowflake_session.execute_string( + f"show application packages like '{package_name}'" + ) + assert not_contains_row_with( + row_from_snowflake_session(expect), {"name": package_name} + ) + finally: + # teardown is idempotent, so we can execute it again with no ill effects + result = runner.invoke_with_connection_json( + ["app", "teardown", "--force"], + env=TEST_ENV, + ) + assert result.exit_code == 0