Skip to content

Commit 2c2b975

Browse files
Add configuration to project execute command (#2296)
* Add configuration to project execute command * example project with multiple configurations * add integration test * fix syntax * update snapshots * fix unit tests
1 parent d6c12ac commit 2c2b975

File tree

9 files changed

+132
-18
lines changed

9 files changed

+132
-18
lines changed

src/snowflake/cli/_plugins/project/commands.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,12 @@
5858
variables_flag = variables_option(
5959
'Variables for the execution context; for example: `-D "<key>=<value>"`.'
6060
)
61+
configuration_flag = typer.Option(
62+
None,
63+
"--configuration",
64+
help="Configuration of the project to use. If not specified default configuration is used.",
65+
show_default=False,
66+
)
6167
from_option = OverrideableOption(
6268
None,
6369
"--from",
@@ -83,13 +89,17 @@ def execute(
8389
identifier: FQN = project_identifier,
8490
version: Optional[str] = version_flag,
8591
variables: Optional[List[str]] = variables_flag,
92+
configuration: Optional[str] = configuration_flag,
8693
**options,
8794
):
8895
"""
8996
Executes a project.
9097
"""
9198
result = ProjectManager().execute(
92-
project_name=identifier, version=version, variables=variables
99+
project_name=identifier,
100+
configuration=configuration,
101+
version=version,
102+
variables=variables,
93103
)
94104
return SingleQueryResult(result)
95105

@@ -99,13 +109,18 @@ def dry_run(
99109
identifier: FQN = project_identifier,
100110
version: Optional[str] = version_flag,
101111
variables: Optional[List[str]] = variables_flag,
112+
configuration: Optional[str] = configuration_flag,
102113
**options,
103114
):
104115
"""
105116
Validates a project.
106117
"""
107118
result = ProjectManager().execute(
108-
project_name=identifier, version=version, dry_run=True, variables=variables
119+
project_name=identifier,
120+
configuration=configuration,
121+
version=version,
122+
dry_run=True,
123+
variables=variables,
109124
)
110125
return SingleQueryResult(result)
111126

src/snowflake/cli/_plugins/project/manager.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,20 @@ class ProjectManager(SqlExecutionMixin):
3131
def execute(
3232
self,
3333
project_name: FQN,
34+
configuration: str | None = None,
3435
version: str | None = None,
3536
variables: List[str] | None = None,
3637
dry_run: bool = False,
3738
):
3839
query = f"EXECUTE PROJECT {project_name.sql_identifier}"
40+
if configuration or variables:
41+
query += f" USING"
42+
if configuration:
43+
query += f" CONFIGURATION {configuration}"
3944
if variables:
4045
query += StageManager.parse_execute_variables(
4146
parse_key_value_variables(variables)
42-
)
47+
).removeprefix(" using")
4348
if version:
4449
query += f" WITH VERSION {version}"
4550
if dry_run:

tests/__snapshots__/test_help_messages.ambr

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7873,11 +7873,13 @@
78737873
| [required] |
78747874
+------------------------------------------------------------------------------+
78757875
+- Options --------------------------------------------------------------------+
7876-
| --version TEXT Version of the project to use. If not specified |
7877-
| default version is used |
7878-
| --variable -D TEXT Variables for the execution context; for example: |
7879-
| -D "<key>=<value>". |
7880-
| --help -h Show this message and exit. |
7876+
| --version TEXT Version of the project to use. If not |
7877+
| specified default version is used |
7878+
| --variable -D TEXT Variables for the execution context; for |
7879+
| example: -D "<key>=<value>". |
7880+
| --configuration TEXT Configuration of the project to use. If not |
7881+
| specified default configuration is used. |
7882+
| --help -h Show this message and exit. |
78817883
+------------------------------------------------------------------------------+
78827884
+- Connection configuration ---------------------------------------------------+
78837885
| --connection,--environment -c TEXT Name of the connection, as |
@@ -8000,11 +8002,13 @@
80008002
| [required] |
80018003
+------------------------------------------------------------------------------+
80028004
+- Options --------------------------------------------------------------------+
8003-
| --version TEXT Version of the project to use. If not specified |
8004-
| default version is used |
8005-
| --variable -D TEXT Variables for the execution context; for example: |
8006-
| -D "<key>=<value>". |
8007-
| --help -h Show this message and exit. |
8005+
| --version TEXT Version of the project to use. If not |
8006+
| specified default version is used |
8007+
| --variable -D TEXT Variables for the execution context; for |
8008+
| example: -D "<key>=<value>". |
8009+
| --configuration TEXT Configuration of the project to use. If not |
8010+
| specified default configuration is used. |
8011+
| --help -h Show this message and exit. |
80088012
+------------------------------------------------------------------------------+
80098013
+- Connection configuration ---------------------------------------------------+
80108014
| --connection,--environment -c TEXT Name of the connection, as |

tests/dcm_project/test_commands.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ def test_execute_project(mock_pm, runner, project_directory):
140140
project_name=FQN.from_string("fooBar"),
141141
version=None,
142142
variables=None,
143+
configuration=None,
143144
)
144145

145146

@@ -154,13 +155,39 @@ def test_execute_project_with_variables(mock_pm, runner, project_directory):
154155
project_name=FQN.from_string("fooBar"),
155156
version="v1",
156157
variables=["key=value"],
158+
configuration=None,
159+
)
160+
161+
162+
@mock.patch(ProjectManager)
163+
def test_execute_project_with_configuration(mock_pm, runner, project_directory):
164+
result = runner.invoke(
165+
["project", "execute", "fooBar", "--configuration", "some_configuration"]
166+
)
167+
assert result.exit_code == 0, result.output
168+
169+
mock_pm().execute.assert_called_once_with(
170+
project_name=FQN.from_string("fooBar"),
171+
configuration="some_configuration",
172+
version=None,
173+
variables=None,
157174
)
158175

159176

160177
@mock.patch(ProjectManager)
161178
def test_validate_project(mock_pm, runner, project_directory):
162179
result = runner.invoke(
163-
["project", "dry-run", "fooBar", "--version", "v1", "-D", "key=value"]
180+
[
181+
"project",
182+
"dry-run",
183+
"fooBar",
184+
"--version",
185+
"v1",
186+
"-D",
187+
"key=value",
188+
"--configuration",
189+
"some_configuration",
190+
]
164191
)
165192
assert result.exit_code == 0, result.output
166193

@@ -169,6 +196,7 @@ def test_validate_project(mock_pm, runner, project_directory):
169196
version="v1",
170197
dry_run=True,
171198
variables=["key=value"],
199+
configuration="some_configuration",
172200
)
173201

174202

tests/dcm_project/test_manager.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,15 @@ def test_create(mock_sync_artifacts, mock_execute_query, initialize_version):
103103
def test_execute_project(mock_execute_query):
104104
mgr = ProjectManager()
105105
mgr.execute(
106-
project_name=TEST_PROJECT, version="v42", variables=["key=value", "aaa=bbb"]
106+
project_name=TEST_PROJECT,
107+
version="v42",
108+
variables=["key=value", "aaa=bbb"],
109+
configuration="some_configuration",
107110
)
108111

109112
mock_execute_query.assert_called_once_with(
110-
query="EXECUTE PROJECT IDENTIFIER('my_project') using (key=>value, aaa=>bbb) WITH VERSION v42"
113+
query="EXECUTE PROJECT IDENTIFIER('my_project') USING CONFIGURATION some_configuration"
114+
" (key=>value, aaa=>bbb) WITH VERSION v42"
111115
)
112116

113117

@@ -125,10 +129,16 @@ def test_execute_project_with_default_version(mock_execute_query, project_direct
125129
@mock.patch(execute_queries)
126130
def test_validate_project(mock_execute_query, project_directory):
127131
mgr = ProjectManager()
128-
mgr.execute(project_name=TEST_PROJECT, version="v42", dry_run=True)
132+
mgr.execute(
133+
project_name=TEST_PROJECT,
134+
version="v42",
135+
dry_run=True,
136+
configuration="some_configuration",
137+
)
129138

130139
mock_execute_query.assert_called_once_with(
131-
query="EXECUTE PROJECT IDENTIFIER('my_project') WITH VERSION v42 DRY_RUN=TRUE"
140+
query="EXECUTE PROJECT IDENTIFIER('my_project') USING CONFIGURATION some_configuration"
141+
" WITH VERSION v42 DRY_RUN=TRUE"
132142
)
133143

134144

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
define table identifier('{{ db }}.public.snowcli_test_table_{{ table_name_suffix }}') (fooBar string);
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
manifest_version: 1
2+
include_definitions:
3+
- file_a.sql
4+
configurations:
5+
dev:
6+
table_name_suffix: "dev"
7+
test:
8+
table_name_suffix: "test"
9+
prod:
10+
table_name_suffix: "prod"
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
definition_version: 2
2+
entities:
3+
my_project:
4+
type: project
5+
stage: "my_project_stage"
6+
identifier: "project_descriptive_name"
7+
artifacts:
8+
- file_a.sql

tests_integration/test_dcm_project.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,39 @@ def test_project_deploy(
9191
assert project["name"].lower() == project_name.lower()
9292

9393

94+
@pytest.mark.integration
95+
@pytest.mark.qa_only
96+
def test_execute_multiple_configurations(
97+
runner,
98+
test_database,
99+
project_directory,
100+
):
101+
project_name = "project_descriptive_name"
102+
entity_id = "my_project"
103+
with project_directory("dcm_project_multiple_configurations"):
104+
result = runner.invoke_with_connection(["project", "create", entity_id])
105+
assert result.exit_code == 0, result.output
106+
107+
for configuration in ["test", "dev", "prod"]:
108+
for command in ["dry-run", "execute"]:
109+
result = runner.invoke_with_connection_json(
110+
[
111+
"project",
112+
command,
113+
project_name,
114+
"--configuration",
115+
configuration,
116+
"-D",
117+
f"db='{test_database}'",
118+
]
119+
)
120+
assert result.exit_code == 0, result.output
121+
assert (
122+
f"SNOWCLI_TEST_TABLE_{configuration}".upper()
123+
in result.json["operations"]
124+
)
125+
126+
94127
@pytest.mark.integration
95128
@pytest.mark.qa_only
96129
def test_create_corner_cases(

0 commit comments

Comments
 (0)