Skip to content

Commit

Permalink
Support project definition v2 in "app version" and "app run" (#1353)
Browse files Browse the repository at this point in the history
* run tests

* version tests

* add unit tests

* project name tests
  • Loading branch information
sfc-gh-gbloom authored Jul 22, 2024
1 parent b3e91ea commit 9cd6096
Show file tree
Hide file tree
Showing 32 changed files with 432 additions and 54 deletions.
1 change: 1 addition & 0 deletions src/snowflake/cli/plugins/nativeapp/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ def app_bundle(

@app.command("run", requires_connection=True)
@with_project_definition()
@nativeapp_definition_v2_to_v1
def app_run(
version: Optional[str] = typer.Option(
None,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

from functools import wraps
from pathlib import Path
from typing import Any, Dict, Optional, Union
from typing import Any, Dict, List, Optional, Union

from click import ClickException
from snowflake.cli.api.cli_global_context import cli_context, cli_context_manager
Expand All @@ -26,6 +26,10 @@
from snowflake.cli.api.project.schemas.entities.application_package_entity import (
ApplicationPackageEntity,
)
from snowflake.cli.api.project.schemas.native_app.application import (
ApplicationPostDeployHook,
SqlScriptHookType,
)
from snowflake.cli.api.project.schemas.native_app.path_mapping import PathMapping
from snowflake.cli.api.project.schemas.project_definition import (
DefinitionV11,
Expand All @@ -45,6 +49,14 @@ def _convert_v2_artifact_to_v1_dict(
return str(v2_artifact)


def _convert_v2_post_deploy_hook_to_v1_scripts(
v2_post_deploy_hook: ApplicationPostDeployHook,
) -> List[str]:
if isinstance(v2_post_deploy_hook, SqlScriptHookType):
return v2_post_deploy_hook.sql_script
raise ValueError(f"Unsupported post deploy hook type: {v2_post_deploy_hook}")


def _pdf_v2_to_v1(v2_definition: DefinitionV20) -> DefinitionV11:
pdfv1: Dict[str, Any] = {"definition_version": "1.1", "native_app": {}}

Expand All @@ -70,7 +82,10 @@ def _pdf_v2_to_v1(v2_definition: DefinitionV20) -> DefinitionV11:
)

# NativeApp
pdfv1["native_app"]["name"] = "Auto converted NativeApp project from V2"
if app_definition and app_definition.name:
pdfv1["native_app"]["name"] = app_definition.name
else:
pdfv1["native_app"]["name"] = app_package_definition.name.split("_pkg_")[0]
pdfv1["native_app"]["artifacts"] = [
_convert_v2_artifact_to_v1_dict(a) for a in app_package_definition.artifacts
]
Expand All @@ -82,13 +97,26 @@ def _pdf_v2_to_v1(v2_definition: DefinitionV20) -> DefinitionV11:
# Package
pdfv1["native_app"]["package"] = {}
pdfv1["native_app"]["package"]["name"] = app_package_definition.name
if app_package_definition.distribution:
pdfv1["native_app"]["package"][
"distribution"
] = app_package_definition.distribution
if app_package_definition.meta and app_package_definition.meta.post_deploy:
pdfv1["native_app"]["package"]["scripts"] = [
_convert_v2_post_deploy_hook_to_v1_scripts(s)
for s in app_package_definition.meta.post_deploy
]

# Application
if app_definition:
pdfv1["native_app"]["application"] = {}
pdfv1["native_app"]["application"]["name"] = app_definition.name
if app_definition.meta and app_definition.meta.role:
pdfv1["native_app"]["application"]["role"] = app_definition.meta.role
if app_definition.meta and app_definition.meta.post_deploy:
pdfv1["native_app"]["application"][
"post_deploy"
] = app_definition.meta.post_deploy

# Override the definition object in global context
return DefinitionV11(**pdfv1)
Expand Down
6 changes: 6 additions & 0 deletions src/snowflake/cli/plugins/nativeapp/version/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@
DenyAlwaysPolicy,
)
from snowflake.cli.plugins.nativeapp.run_processor import NativeAppRunProcessor
from snowflake.cli.plugins.nativeapp.v2_conversions.v2_to_v1_decorator import (
nativeapp_definition_v2_to_v1,
)
from snowflake.cli.plugins.nativeapp.version.version_processor import (
NativeAppVersionCreateProcessor,
NativeAppVersionDropProcessor,
Expand All @@ -48,6 +51,7 @@

@app.command(requires_connection=True)
@with_project_definition()
@nativeapp_definition_v2_to_v1
def create(
version: Optional[str] = typer.Argument(
None,
Expand Down Expand Up @@ -112,6 +116,7 @@ def create(

@app.command("list", requires_connection=True)
@with_project_definition()
@nativeapp_definition_v2_to_v1
def version_list(
**options,
) -> CommandResult:
Expand All @@ -131,6 +136,7 @@ def version_list(

@app.command(requires_connection=True)
@with_project_definition()
@nativeapp_definition_v2_to_v1
def drop(
version: Optional[str] = typer.Argument(
None,
Expand Down
112 changes: 104 additions & 8 deletions tests/nativeapp/test_v2_to_v1.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,33 +88,55 @@
"artifacts": [{"src": "app/*", "dest": "./"}],
"manifest": "",
"stage": "app.stage",
"bundle_root": "bundle_root",
"generated_root": "generated_root",
"deploy_root": "deploy_root",
"bundle_root": "bundle_root_path",
"generated_root": "generated_root_path",
"deploy_root": "deploy_root_path",
"distribution": "external",
"meta": {
"post_deploy": [
{"sql_script": "scripts/script1.sql"},
{"sql_script": "scripts/script2.sql"},
]
},
},
"app": {
"type": "application",
"name": "app_name",
"from": {"target": "pkg"},
"meta": {"role": "app_role"},
"meta": {
"role": "app_role",
"post_deploy": [
{"sql_script": "scripts/script3.sql"},
{"sql_script": "scripts/script4.sql"},
],
},
},
},
},
{
"definition_version": "1.1",
"native_app": {
"name": "Auto converted NativeApp project from V2",
"name": "app_name",
"artifacts": [{"src": "app/*", "dest": "./"}],
"source_stage": "app.stage",
"bundle_root": "bundle_root",
"generated_root": "generated_root",
"deploy_root": "deploy_root",
"bundle_root": "bundle_root_path",
"generated_root": "generated_root_path",
"deploy_root": "deploy_root_path",
"package": {
"name": "pkg_name",
"distribution": "external",
"scripts": [
"scripts/script1.sql",
"scripts/script2.sql",
],
},
"application": {
"name": "app_name",
"role": "app_role",
"post_deploy": [
{"sql_script": "scripts/script3.sql"},
{"sql_script": "scripts/script4.sql"},
],
},
},
},
Expand All @@ -141,6 +163,80 @@ def test_decorator_error_when_no_project_exists():
nativeapp_definition_v2_to_v1(lambda *args: None)()


@pytest.mark.parametrize(
"pdfv2_input, expected_project_name",
[
[
# Using application name as project name
{
"definition_version": "2",
"entities": {
"pkg": {
"type": "application package",
"name": "package_name",
"artifacts": [{"src": "app/*", "dest": "./"}],
"manifest": "",
"stage": "app.stage",
},
"app": {
"type": "application",
"name": "application_name",
"from": {"target": "pkg"},
"meta": {
"role": "app_role",
"post_deploy": [
{"sql_script": "scripts/script3.sql"},
{"sql_script": "scripts/script4.sql"},
],
},
},
},
},
"application_name",
],
[
# Using package name as project name
{
"definition_version": "2",
"entities": {
"pkg": {
"type": "application package",
"name": "package_name",
"artifacts": [{"src": "app/*", "dest": "./"}],
"manifest": "",
"stage": "app.stage",
},
},
},
"package_name",
],
[
# Using package name as project name, stripping _pkg_.*
{
"definition_version": "2",
"entities": {
"pkg": {
"type": "application package",
"name": "appname_pkg_username",
"artifacts": [{"src": "app/*", "dest": "./"}],
"manifest": "",
"stage": "app.stage",
},
},
},
"appname",
],
],
)
def test_project_name(pdfv2_input, expected_project_name):
with mock_config_key("enable_project_definition_v2", True):
pdfv2 = DefinitionV20(**pdfv2_input)
pdfv1 = _pdf_v2_to_v1(pdfv2)

# Assert that the expected dict is a subset of the actual dict
assert pdfv1.native_app.name == expected_project_name


@mock.patch(
"snowflake.cli.plugins.nativeapp.v2_conversions.v2_to_v1_decorator._pdf_v2_to_v1"
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# README

This is the v2 version of the "integration_external" project definition
16 changes: 16 additions & 0 deletions tests/test_data/projects/integration_external_v2/app/manifest.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# This is the v2 version of the "integration_external" project definition

manifest_version: 1

version:
name: dev
label: "Dev Version"
comment: "Default version used for development. Override for actual deployment."

artifacts:
setup_script: setup.sql
readme: README.md

configuration:
log_level: INFO
trace_level: ALWAYS
19 changes: 19 additions & 0 deletions tests/test_data/projects/integration_external_v2/app/setup.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
create application role app_public;
create or alter versioned schema core;

create or replace procedure core.echo(inp varchar)
returns varchar
language sql
immutable
as
$$
begin
return inp;
end;
$$;

grant usage on procedure core.echo(varchar) to application role app_public;

create or replace view core.shared_view as select * from my_shared_content.shared_table;

grant select on view core.shared_view to application role app_public;
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-- package script (1/2)

create schema if not exists {{ package_name }}.my_shared_content;
grant usage on schema {{ package_name }}.my_shared_content
to share in application package {{ package_name }};
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
-- package script (2/2)

create or replace table {{ package_name }}.my_shared_content.shared_table (
col1 number,
col2 varchar
);

insert into {{ package_name }}.my_shared_content.shared_table (col1, col2)
values (1, 'hello');

grant select on table {{ package_name }}.my_shared_content.shared_table
to share in application package {{ package_name }};
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# This is the v2 version of the "integration_external" project definition
entities:
pkg:
distribution: external
14 changes: 14 additions & 0 deletions tests/test_data/projects/integration_external_v2/snowflake.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# This is the v2 version of the "integration_external" project definition
definition_version: 2
entities:
pkg:
type: application package
name: integration_external_pkg_<% ctx.env.USER %>
artifacts:
- src: app/*
dest: ./
manifest: app/manifest.yml
meta:
post_deploy:
- sql_script: package/001-shared.sql
- sql_script: package/002-shared.sql
18 changes: 18 additions & 0 deletions tests/test_data/projects/integration_v2/app/manifest.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# This is a manifest.yml file, a required component of creating a native application.
# This file defines properties required by the application package, including the location of the setup script and version definitions.
# Refer to https://docs.snowflake.com/en/developer-guide/native-apps/creating-manifest for a detailed understanding of this file.

manifest_version: 1

version:
name: dev
label: "Dev Version"
comment: "Default version used for development. Override for actual deployment."

artifacts:
setup_script: setup.sql
readme: README.md

configuration:
log_level: INFO
trace_level: ALWAYS
19 changes: 19 additions & 0 deletions tests/test_data/projects/integration_v2/app/setup.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
create application role if not exists app_public;
create or alter versioned schema core;

create or replace procedure core.echo(inp varchar)
returns varchar
language sql
immutable
as
$$
begin
return inp;
end;
$$;

grant usage on procedure core.echo(varchar) to application role app_public;

create or replace view core.shared_view as select * from my_shared_content.shared_table;

grant select on view core.shared_view to application role app_public;
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-- package script (1/2)

create schema if not exists {{ package_name }}.my_shared_content;
grant usage on schema {{ package_name }}.my_shared_content
to share in application package {{ package_name }};
12 changes: 12 additions & 0 deletions tests/test_data/projects/integration_v2/package/002-shared.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
-- package script (2/2)

create or replace table {{ package_name }}.my_shared_content.shared_table (
col1 number,
col2 varchar
);

insert into {{ package_name }}.my_shared_content.shared_table (col1, col2)
values (1, 'hello');

grant select on table {{ package_name }}.my_shared_content.shared_table
to share in application package {{ package_name }};
Loading

0 comments on commit 9cd6096

Please sign in to comment.