Skip to content

Commit

Permalink
Add snow ws version list command (#1574)
Browse files Browse the repository at this point in the history
Adds `snow ws version list` command to be invoked on an app package entity. Behaves the same as `snow app version list`.
  • Loading branch information
sfc-gh-fcampbell authored Sep 17, 2024
1 parent 4164652 commit 800f8fd
Show file tree
Hide file tree
Showing 8 changed files with 356 additions and 255 deletions.
19 changes: 6 additions & 13 deletions src/snowflake/cli/_plugins/nativeapp/run_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,16 @@
from snowflake.cli.api.entities.application_entity import (
ApplicationEntity,
)
from snowflake.cli.api.entities.application_package_entity import (
ApplicationPackageEntity,
)
from snowflake.cli.api.entities.utils import (
generic_sql_error_handler,
)
from snowflake.cli.api.errno import (
APPLICATION_NO_LONGER_AVAILABLE,
APPLICATION_OWNS_EXTERNAL_OBJECTS,
)
from snowflake.cli.api.exceptions import SnowflakeSQLExecutionError
from snowflake.cli.api.project.schemas.native_app.native_app import NativeApp
from snowflake.connector import ProgrammingError
from snowflake.connector.cursor import SnowflakeCursor
Expand All @@ -49,18 +51,9 @@ def __init__(self, project_definition: NativeApp, project_root: Path):
super().__init__(project_definition, project_root)

def get_all_existing_versions(self) -> SnowflakeCursor:
"""
Get all existing versions, if defined, for an application package.
It executes a 'show versions in application package' query and returns all the results.
"""
with self.use_role(self.package_role):
show_obj_query = f"show versions in application package {self.package_name}"
show_obj_cursor = self._execute_query(show_obj_query)

if show_obj_cursor.rowcount is None:
raise SnowflakeSQLExecutionError(show_obj_query)

return show_obj_cursor
return ApplicationPackageEntity.version_list(
self.package_name, self.package_role
)

def get_existing_version_info(self, version: str) -> Optional[dict]:
return ApplicationEntity.get_existing_version_info(
Expand Down
34 changes: 31 additions & 3 deletions src/snowflake/cli/_plugins/workspace/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,17 @@
from snowflake.cli._plugins.workspace.manager import WorkspaceManager
from snowflake.cli.api.cli_global_context import get_cli_context
from snowflake.cli.api.commands.decorators import with_project_definition
from snowflake.cli.api.commands.snow_typer import SnowTyper
from snowflake.cli.api.commands.snow_typer import SnowTyperFactory
from snowflake.cli.api.entities.common import EntityActions
from snowflake.cli.api.exceptions import IncompatibleParametersError
from snowflake.cli.api.output.types import MessageResult
from snowflake.cli.api.output.types import MessageResult, QueryResult
from snowflake.cli.api.project.definition_conversion import (
convert_project_definition_to_v2,
)
from snowflake.cli.api.project.definition_manager import DefinitionManager
from snowflake.cli.api.secure_path import SecurePath

ws = SnowTyper(
ws = SnowTyperFactory(
name="ws",
help="Deploy and interact with snowflake.yml-based entities.",
)
Expand Down Expand Up @@ -224,3 +224,31 @@ def validate(
entity_id,
EntityActions.VALIDATE,
)


version = SnowTyperFactory(
name="version",
help="Manages versions for project entities.",
)
ws.add_typer(version)


@version.command(name="list", requires_connection=True, hidden=True)
@with_project_definition()
def version_list(
entity_id: str = typer.Option(
help="The ID of the entity you want to list versions for.",
),
**options,
):
"""Lists the versions of the specified entity."""
cli_context = get_cli_context()
ws = WorkspaceManager(
project_definition=cli_context.project_definition,
project_root=cli_context.project_root,
)
cursor = ws.perform_action(
entity_id,
EntityActions.VERSION_LIST,
)
return QueryResult(cursor)
2 changes: 1 addition & 1 deletion src/snowflake/cli/_plugins/workspace/plugin_spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,5 @@ def command_spec():
return CommandSpec(
parent_command_path=SNOWCLI_ROOT_COMMAND_PATH,
command_type=CommandType.COMMAND_GROUP,
typer_instance=commands.ws,
typer_instance=commands.ws.create_instance(),
)
27 changes: 26 additions & 1 deletion src/snowflake/cli/api/entities/application_package_entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
get_basic_jinja_env,
)
from snowflake.connector import ProgrammingError
from snowflake.connector.cursor import DictCursor
from snowflake.connector.cursor import DictCursor, SnowflakeCursor


class ApplicationPackageEntity(EntityBase[ApplicationPackageEntityModel]):
Expand Down Expand Up @@ -155,6 +155,15 @@ def deploy_to_scratch_stage_fn():
)
ctx.console.message("Setup script is valid")

def action_version_list(
self, ctx: ActionContext, *args, **kwargs
) -> SnowflakeCursor:
model = self._entity_model
return self.version_list(
package_name=model.fqn.identifier,
package_role=(model.meta and model.meta.role) or ctx.default_role,
)

@staticmethod
def bundle(
project_root: Path,
Expand Down Expand Up @@ -266,6 +275,22 @@ def deploy(

return diff

@staticmethod
def version_list(package_name: str, package_role: str) -> SnowflakeCursor:
"""
Get all existing versions, if defined, for an application package.
It executes a 'show versions in application package' query and returns all the results.
"""
sql_executor = get_sql_executor()
with sql_executor.use_role(package_role):
show_obj_query = f"show versions in application package {package_name}"
show_obj_cursor = sql_executor.execute_query(show_obj_query)

if show_obj_cursor.rowcount is None:
raise SnowflakeSQLExecutionError(show_obj_query)

return show_obj_cursor

@staticmethod
def get_existing_app_pkg_info(
package_name: str,
Expand Down
2 changes: 2 additions & 0 deletions src/snowflake/cli/api/entities/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ class EntityActions(str, Enum):
DROP = "action_drop"
VALIDATE = "action_validate"

VERSION_LIST = "action_version_list"


T = TypeVar("T")

Expand Down
23 changes: 23 additions & 0 deletions tests/workspace/test_application_package_entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,3 +163,26 @@ def test_deploy(
package_warehouse="wh",
)
assert mock_execute.mock_calls == expected


@mock.patch(SQL_EXECUTOR_EXECUTE)
def test_version_list(mock_execute, mock_cursor):
package_role = "package_role"
package_name = "test_pkg"
side_effects, expected = mock_execute_helper(
[
(
mock_cursor([{"CURRENT_ROLE()": "old_role"}], []),
mock.call("select current_role()", cursor_class=DictCursor),
),
(None, mock.call(f"use role {package_role}")),
(
mock_cursor([], []),
mock.call(f"show versions in application package {package_name}"),
),
(None, mock.call("use role old_role")),
]
)
mock_execute.side_effect = side_effects
ApplicationPackageEntity.version_list(package_name, package_role)
assert mock_execute.mock_calls == expected
62 changes: 44 additions & 18 deletions tests_integration/nativeapp/__snapshots__/test_version.ambr
Original file line number Diff line number Diff line change
@@ -1,52 +1,78 @@
# serializer version: 1
# name: test_nativeapp_version_create_package_no_magic_comment[integration]
# name: test_nativeapp_version_create_package_no_magic_comment[app version list-napp_init_v1]
list([
dict({
'comment': 'Default version used for development. Override for actual deployment.',
'comment': None,
'dropped_on': None,
'label': 'Dev Version',
'log_level': 'INFO',
'label': None,
'log_level': 'OFF',
'patch': 0,
'review_status': 'NOT_REVIEWED',
'state': 'READY',
'trace_level': 'ALWAYS',
'trace_level': 'OFF',
'version': 'V1',
}),
dict({
'comment': 'Default version used for development. Override for actual deployment.',
'comment': None,
'dropped_on': None,
'label': 'Dev Version',
'log_level': 'INFO',
'label': None,
'log_level': 'OFF',
'patch': 1,
'review_status': 'NOT_REVIEWED',
'state': 'READY',
'trace_level': 'ALWAYS',
'trace_level': 'OFF',
'version': 'V1',
}),
])
# ---
# name: test_nativeapp_version_create_package_no_magic_comment[integration_v2]
# name: test_nativeapp_version_create_package_no_magic_comment[app version list-napp_init_v2]
list([
dict({
'comment': 'Default version used for development. Override for actual deployment.',
'comment': None,
'dropped_on': None,
'label': 'Dev Version',
'log_level': 'INFO',
'label': None,
'log_level': 'OFF',
'patch': 0,
'review_status': 'NOT_REVIEWED',
'state': 'READY',
'trace_level': 'ALWAYS',
'trace_level': 'OFF',
'version': 'V1',
}),
dict({
'comment': 'Default version used for development. Override for actual deployment.',
'comment': None,
'dropped_on': None,
'label': 'Dev Version',
'log_level': 'INFO',
'label': None,
'log_level': 'OFF',
'patch': 1,
'review_status': 'NOT_REVIEWED',
'state': 'READY',
'trace_level': 'ALWAYS',
'trace_level': 'OFF',
'version': 'V1',
}),
])
# ---
# name: test_nativeapp_version_create_package_no_magic_comment[ws version list --entity-id=pkg-napp_init_v2]
list([
dict({
'comment': None,
'dropped_on': None,
'label': None,
'log_level': 'OFF',
'patch': 0,
'review_status': 'NOT_REVIEWED',
'state': 'READY',
'trace_level': 'OFF',
'version': 'V1',
}),
dict({
'comment': None,
'dropped_on': None,
'label': None,
'log_level': 'OFF',
'patch': 1,
'review_status': 'NOT_REVIEWED',
'state': 'READY',
'trace_level': 'OFF',
'version': 'V1',
}),
])
Expand Down
Loading

0 comments on commit 800f8fd

Please sign in to comment.