snowflake-cli-labs 3.0.0rc1__py3-none-any.whl → 3.0.0rc2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- snowflake/cli/__about__.py +1 -1
- snowflake/cli/_app/cli_app.py +10 -1
- snowflake/cli/_app/snow_connector.py +76 -29
- snowflake/cli/_app/telemetry.py +8 -4
- snowflake/cli/_app/version_check.py +74 -0
- snowflake/cli/_plugins/git/commands.py +55 -14
- snowflake/cli/_plugins/nativeapp/codegen/snowpark/python_processor.py +2 -5
- snowflake/cli/_plugins/nativeapp/codegen/templates/templates_processor.py +49 -31
- snowflake/cli/_plugins/nativeapp/manager.py +46 -87
- snowflake/cli/_plugins/nativeapp/run_processor.py +56 -260
- snowflake/cli/_plugins/nativeapp/same_account_install_method.py +74 -0
- snowflake/cli/_plugins/nativeapp/teardown_processor.py +9 -152
- snowflake/cli/_plugins/nativeapp/v2_conversions/v2_to_v1_decorator.py +91 -17
- snowflake/cli/_plugins/snowpark/commands.py +1 -1
- snowflake/cli/_plugins/snowpark/models.py +2 -1
- snowflake/cli/_plugins/streamlit/commands.py +1 -1
- snowflake/cli/_plugins/streamlit/manager.py +9 -0
- snowflake/cli/_plugins/workspace/action_context.py +2 -1
- snowflake/cli/_plugins/workspace/commands.py +48 -16
- snowflake/cli/_plugins/workspace/manager.py +1 -0
- snowflake/cli/api/cli_global_context.py +136 -313
- snowflake/cli/api/commands/flags.py +76 -91
- snowflake/cli/api/commands/snow_typer.py +6 -4
- snowflake/cli/api/config.py +1 -1
- snowflake/cli/api/connections.py +214 -0
- snowflake/cli/api/console/abc.py +4 -2
- snowflake/cli/api/entities/application_entity.py +687 -2
- snowflake/cli/api/entities/application_package_entity.py +151 -46
- snowflake/cli/api/entities/common.py +1 -0
- snowflake/cli/api/entities/utils.py +41 -17
- snowflake/cli/api/identifiers.py +3 -0
- snowflake/cli/api/project/definition.py +11 -0
- snowflake/cli/api/project/definition_conversion.py +171 -13
- snowflake/cli/api/project/schemas/entities/common.py +0 -12
- snowflake/cli/api/project/schemas/identifier_model.py +2 -2
- snowflake/cli/api/project/schemas/project_definition.py +101 -39
- snowflake/cli/api/rendering/project_definition_templates.py +4 -0
- snowflake/cli/api/rendering/sql_templates.py +7 -0
- snowflake/cli/api/utils/definition_rendering.py +3 -1
- {snowflake_cli_labs-3.0.0rc1.dist-info → snowflake_cli_labs-3.0.0rc2.dist-info}/METADATA +6 -6
- {snowflake_cli_labs-3.0.0rc1.dist-info → snowflake_cli_labs-3.0.0rc2.dist-info}/RECORD +44 -42
- snowflake/cli/api/commands/typer_pre_execute.py +0 -26
- {snowflake_cli_labs-3.0.0rc1.dist-info → snowflake_cli_labs-3.0.0rc2.dist-info}/WHEEL +0 -0
- {snowflake_cli_labs-3.0.0rc1.dist-info → snowflake_cli_labs-3.0.0rc2.dist-info}/entry_points.txt +0 -0
- {snowflake_cli_labs-3.0.0rc1.dist-info → snowflake_cli_labs-3.0.0rc2.dist-info}/licenses/LICENSE +0 -0
|
@@ -15,179 +15,36 @@
|
|
|
15
15
|
from __future__ import annotations
|
|
16
16
|
|
|
17
17
|
from pathlib import Path
|
|
18
|
-
from textwrap import dedent
|
|
19
18
|
from typing import Dict, Optional
|
|
20
19
|
|
|
21
|
-
import typer
|
|
22
|
-
from snowflake.cli._plugins.nativeapp.constants import (
|
|
23
|
-
ALLOWED_SPECIAL_COMMENTS,
|
|
24
|
-
COMMENT_COL,
|
|
25
|
-
OWNER_COL,
|
|
26
|
-
)
|
|
27
20
|
from snowflake.cli._plugins.nativeapp.manager import (
|
|
28
21
|
NativeAppCommandProcessor,
|
|
29
22
|
NativeAppManager,
|
|
30
23
|
)
|
|
31
|
-
from snowflake.cli._plugins.nativeapp.utils import (
|
|
32
|
-
needs_confirmation,
|
|
33
|
-
)
|
|
34
24
|
from snowflake.cli.api.console import cli_console as cc
|
|
25
|
+
from snowflake.cli.api.entities.application_entity import (
|
|
26
|
+
ApplicationEntity,
|
|
27
|
+
)
|
|
35
28
|
from snowflake.cli.api.entities.application_package_entity import (
|
|
36
29
|
ApplicationPackageEntity,
|
|
37
30
|
)
|
|
38
|
-
from snowflake.cli.api.entities.utils import (
|
|
39
|
-
drop_generic_object,
|
|
40
|
-
ensure_correct_owner,
|
|
41
|
-
)
|
|
42
|
-
from snowflake.cli.api.errno import APPLICATION_NO_LONGER_AVAILABLE
|
|
43
|
-
from snowflake.connector import ProgrammingError
|
|
44
31
|
|
|
45
32
|
|
|
46
33
|
class NativeAppTeardownProcessor(NativeAppManager, NativeAppCommandProcessor):
|
|
47
34
|
def __init__(self, project_definition: Dict, project_root: Path):
|
|
48
35
|
super().__init__(project_definition, project_root)
|
|
49
36
|
|
|
50
|
-
def drop_generic_object(
|
|
51
|
-
self, object_type: str, object_name: str, role: str, cascade: bool = False
|
|
52
|
-
):
|
|
53
|
-
return drop_generic_object(
|
|
54
|
-
console=cc,
|
|
55
|
-
object_type=object_type,
|
|
56
|
-
object_name=object_name,
|
|
57
|
-
role=role,
|
|
58
|
-
cascade=cascade,
|
|
59
|
-
)
|
|
60
|
-
|
|
61
37
|
def drop_application(
|
|
62
38
|
self, auto_yes: bool, interactive: bool = False, cascade: Optional[bool] = None
|
|
63
39
|
):
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
# 1. If existing application is not found, exit gracefully
|
|
71
|
-
show_obj_row = self.get_existing_app_info()
|
|
72
|
-
if show_obj_row is None:
|
|
73
|
-
cc.warning(
|
|
74
|
-
f"Role {self.app_role} does not own any application object with the name {self.app_name}, or the application object does not exist."
|
|
75
|
-
)
|
|
76
|
-
return
|
|
77
|
-
|
|
78
|
-
# 2. Check for the right owner
|
|
79
|
-
ensure_correct_owner(
|
|
80
|
-
row=show_obj_row, role=self.app_role, obj_name=self.app_name
|
|
81
|
-
)
|
|
82
|
-
|
|
83
|
-
# 3. Check if created by the Snowflake CLI
|
|
84
|
-
row_comment = show_obj_row[COMMENT_COL]
|
|
85
|
-
if row_comment not in ALLOWED_SPECIAL_COMMENTS and needs_confirmation(
|
|
86
|
-
needs_confirm, auto_yes
|
|
87
|
-
):
|
|
88
|
-
should_drop_object = typer.confirm(
|
|
89
|
-
dedent(
|
|
90
|
-
f"""\
|
|
91
|
-
Application object {self.app_name} was not created by Snowflake CLI.
|
|
92
|
-
Application object details:
|
|
93
|
-
Name: {self.app_name}
|
|
94
|
-
Created on: {show_obj_row["created_on"]}
|
|
95
|
-
Source: {show_obj_row["source"]}
|
|
96
|
-
Owner: {show_obj_row[OWNER_COL]}
|
|
97
|
-
Comment: {show_obj_row[COMMENT_COL]}
|
|
98
|
-
Version: {show_obj_row["version"]}
|
|
99
|
-
Patch: {show_obj_row["patch"]}
|
|
100
|
-
Are you sure you want to drop it?
|
|
101
|
-
"""
|
|
102
|
-
)
|
|
103
|
-
)
|
|
104
|
-
if not should_drop_object:
|
|
105
|
-
cc.message(f"Did not drop application object {self.app_name}.")
|
|
106
|
-
# The user desires to keep the app, therefore we can't proceed since it would
|
|
107
|
-
# leave behind an orphan app when we get to dropping the package
|
|
108
|
-
raise typer.Abort()
|
|
109
|
-
|
|
110
|
-
# 4. Check for application objects owned by the application
|
|
111
|
-
# This query will fail if the application package has already been dropped, so handle this case gracefully
|
|
112
|
-
has_objects_to_drop = False
|
|
113
|
-
message_prefix = ""
|
|
114
|
-
cascade_true_message = ""
|
|
115
|
-
cascade_false_message = ""
|
|
116
|
-
interactive_prompt = ""
|
|
117
|
-
non_interactive_abort = ""
|
|
118
|
-
try:
|
|
119
|
-
if application_objects := self.get_objects_owned_by_application():
|
|
120
|
-
has_objects_to_drop = True
|
|
121
|
-
message_prefix = (
|
|
122
|
-
f"The following objects are owned by application {self.app_name}"
|
|
123
|
-
)
|
|
124
|
-
cascade_true_message = f"{message_prefix} and will be dropped:"
|
|
125
|
-
cascade_false_message = f"{message_prefix} and will NOT be dropped:"
|
|
126
|
-
interactive_prompt = "Would you like to drop these objects in addition to the application? [y/n/ABORT]"
|
|
127
|
-
non_interactive_abort = "Re-run teardown again with --cascade or --no-cascade to specify whether these objects should be dropped along with the application"
|
|
128
|
-
except ProgrammingError as e:
|
|
129
|
-
if e.errno != APPLICATION_NO_LONGER_AVAILABLE:
|
|
130
|
-
raise
|
|
131
|
-
application_objects = []
|
|
132
|
-
message_prefix = f"Could not determine which objects are owned by application {self.app_name}"
|
|
133
|
-
has_objects_to_drop = True # potentially, but we don't know what they are
|
|
134
|
-
cascade_true_message = (
|
|
135
|
-
f"{message_prefix}, an unknown number of objects will be dropped."
|
|
136
|
-
)
|
|
137
|
-
cascade_false_message = f"{message_prefix}, they will NOT be dropped."
|
|
138
|
-
interactive_prompt = f"Would you like to drop an unknown set of objects in addition to the application? [y/n/ABORT]"
|
|
139
|
-
non_interactive_abort = f"Re-run teardown again with --cascade or --no-cascade to specify whether any objects should be dropped along with the application."
|
|
140
|
-
|
|
141
|
-
if has_objects_to_drop:
|
|
142
|
-
if cascade is True:
|
|
143
|
-
# If the user explicitly passed the --cascade flag
|
|
144
|
-
cc.message(cascade_true_message)
|
|
145
|
-
with cc.indented():
|
|
146
|
-
for obj in application_objects:
|
|
147
|
-
cc.message(self._application_object_to_str(obj))
|
|
148
|
-
elif cascade is False:
|
|
149
|
-
# If the user explicitly passed the --no-cascade flag
|
|
150
|
-
cc.message(cascade_false_message)
|
|
151
|
-
with cc.indented():
|
|
152
|
-
for obj in application_objects:
|
|
153
|
-
cc.message(self._application_object_to_str(obj))
|
|
154
|
-
elif interactive:
|
|
155
|
-
# If the user didn't pass any cascade flag and the session is interactive
|
|
156
|
-
cc.message(message_prefix)
|
|
157
|
-
with cc.indented():
|
|
158
|
-
for obj in application_objects:
|
|
159
|
-
cc.message(self._application_object_to_str(obj))
|
|
160
|
-
user_response = typer.prompt(
|
|
161
|
-
interactive_prompt,
|
|
162
|
-
show_default=False,
|
|
163
|
-
default="ABORT",
|
|
164
|
-
).lower()
|
|
165
|
-
if user_response in ["y", "yes"]:
|
|
166
|
-
cascade = True
|
|
167
|
-
elif user_response in ["n", "no"]:
|
|
168
|
-
cascade = False
|
|
169
|
-
else:
|
|
170
|
-
raise typer.Abort()
|
|
171
|
-
else:
|
|
172
|
-
# Else abort since we don't know what to do and can't ask the user
|
|
173
|
-
cc.message(message_prefix)
|
|
174
|
-
with cc.indented():
|
|
175
|
-
for obj in application_objects:
|
|
176
|
-
cc.message(self._application_object_to_str(obj))
|
|
177
|
-
cc.message(non_interactive_abort)
|
|
178
|
-
raise typer.Abort()
|
|
179
|
-
elif cascade is None:
|
|
180
|
-
# If there's nothing to drop, set cascade to an explicit False value
|
|
181
|
-
cascade = False
|
|
182
|
-
|
|
183
|
-
# 5. All validations have passed, drop object
|
|
184
|
-
self.drop_generic_object(
|
|
185
|
-
object_type="application",
|
|
186
|
-
object_name=self.app_name,
|
|
187
|
-
role=self.app_role,
|
|
40
|
+
return ApplicationEntity.drop(
|
|
41
|
+
console=cc,
|
|
42
|
+
app_name=self.app_name,
|
|
43
|
+
app_role=self.app_role,
|
|
44
|
+
auto_yes=auto_yes,
|
|
45
|
+
interactive=interactive,
|
|
188
46
|
cascade=cascade,
|
|
189
47
|
)
|
|
190
|
-
return # The application object was successfully dropped, therefore exit gracefully
|
|
191
48
|
|
|
192
49
|
def drop_package(self, auto_yes: bool):
|
|
193
50
|
return ApplicationPackageEntity.drop(
|
|
@@ -14,14 +14,17 @@
|
|
|
14
14
|
|
|
15
15
|
from __future__ import annotations
|
|
16
16
|
|
|
17
|
+
import inspect
|
|
17
18
|
from functools import wraps
|
|
18
19
|
from typing import Any, Dict, Optional, Union
|
|
19
20
|
|
|
21
|
+
import typer
|
|
20
22
|
from click import ClickException
|
|
21
23
|
from snowflake.cli.api.cli_global_context import (
|
|
22
24
|
get_cli_context,
|
|
23
25
|
get_cli_context_manager,
|
|
24
26
|
)
|
|
27
|
+
from snowflake.cli.api.commands.decorators import _options_decorator_factory
|
|
25
28
|
from snowflake.cli.api.project.schemas.entities.application_entity_model import (
|
|
26
29
|
ApplicationEntityModel,
|
|
27
30
|
)
|
|
@@ -48,28 +51,75 @@ def _convert_v2_artifact_to_v1_dict(
|
|
|
48
51
|
return v2_artifact
|
|
49
52
|
|
|
50
53
|
|
|
51
|
-
def _pdf_v2_to_v1(
|
|
54
|
+
def _pdf_v2_to_v1(
|
|
55
|
+
v2_definition: DefinitionV20,
|
|
56
|
+
package_entity_id: str = "",
|
|
57
|
+
app_entity_id: str = "",
|
|
58
|
+
) -> DefinitionV11:
|
|
52
59
|
pdfv1: Dict[str, Any] = {"definition_version": "1.1", "native_app": {}}
|
|
53
60
|
|
|
54
61
|
app_package_definition: Optional[ApplicationPackageEntityModel] = None
|
|
55
62
|
app_definition: Optional[ApplicationEntityModel] = None
|
|
56
63
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
# Enumerate all application package and application entities in the project definition
|
|
65
|
+
packages: dict[
|
|
66
|
+
str, ApplicationPackageEntityModel
|
|
67
|
+
] = v2_definition.get_entities_by_type(ApplicationPackageEntityModel.get_type())
|
|
68
|
+
apps: dict[str, ApplicationEntityModel] = v2_definition.get_entities_by_type(
|
|
69
|
+
ApplicationEntityModel.get_type()
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
# Determine the application entity to convert, there can be zero or one
|
|
73
|
+
if app_entity_id:
|
|
74
|
+
# If the user specified an app entity ID, use that one directly
|
|
75
|
+
app_definition = apps.get(app_entity_id)
|
|
76
|
+
elif len(apps) == 1:
|
|
77
|
+
# Otherwise, if there is only one app entity, fall back to that one
|
|
78
|
+
app_definition = next(iter(apps.values()))
|
|
79
|
+
elif len(apps) > 1:
|
|
80
|
+
# If there are multiple app entities, the user must specify which one to use
|
|
81
|
+
raise ClickException(
|
|
82
|
+
"More than one application entity exists in the project definition file, "
|
|
83
|
+
"specify --app-entity-id to choose which one to operate on."
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
# Infer or verify the package if we have an app entity to convert
|
|
87
|
+
if app_definition:
|
|
88
|
+
target_package = app_definition.from_.target
|
|
89
|
+
if package_entity_id:
|
|
90
|
+
# If the user specified a package entity ID,
|
|
91
|
+
# check that the app entity targets the user-specified package entity
|
|
92
|
+
if target_package != package_entity_id:
|
|
66
93
|
raise ClickException(
|
|
67
|
-
"
|
|
94
|
+
f"The application entity {app_definition.entity_id} does not "
|
|
95
|
+
f"target the application package entity {package_entity_id}. Either"
|
|
96
|
+
f"use --package-entity-id {target_package} to target the correct package entity, "
|
|
97
|
+
f"or omit the --package-entity-id flag to automatically use the package entity "
|
|
98
|
+
f"that the application entity targets."
|
|
68
99
|
)
|
|
69
|
-
|
|
100
|
+
elif target_package in packages:
|
|
101
|
+
# If the user didn't target a specific package entity, use the one the app entity targets
|
|
102
|
+
package_entity_id = target_package
|
|
103
|
+
|
|
104
|
+
# Determine the package entity to convert, there must be one
|
|
105
|
+
if package_entity_id:
|
|
106
|
+
# If the user specified a package entity ID (or we inferred one from the app entity), use that one directly
|
|
107
|
+
app_package_definition = packages.get(package_entity_id)
|
|
108
|
+
elif len(packages) == 1:
|
|
109
|
+
# Otherwise, if there is only one package entity, fall back to that one
|
|
110
|
+
app_package_definition = next(iter(packages.values()))
|
|
111
|
+
elif len(packages) > 1:
|
|
112
|
+
# If there are multiple package entities, the user must specify which one to use
|
|
113
|
+
raise ClickException(
|
|
114
|
+
"More than one application package entity exists in the project definition file, "
|
|
115
|
+
"specify --package-entity-id to choose which one to operate on."
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
# If we don't have a package entity to convert, error out since it's not optional
|
|
70
119
|
if not app_package_definition:
|
|
120
|
+
with_id = f'with ID "{package_entity_id}" ' if package_entity_id else ""
|
|
71
121
|
raise ClickException(
|
|
72
|
-
"Could not find an application package entity in the project definition file."
|
|
122
|
+
f"Could not find an application package entity {with_id}in the project definition file."
|
|
73
123
|
)
|
|
74
124
|
|
|
75
125
|
# NativeApp
|
|
@@ -141,14 +191,38 @@ def nativeapp_definition_v2_to_v1(func):
|
|
|
141
191
|
|
|
142
192
|
@wraps(func)
|
|
143
193
|
def wrapper(*args, **kwargs):
|
|
144
|
-
original_pdf: DefinitionV20 = get_cli_context().project_definition
|
|
194
|
+
original_pdf: Optional[DefinitionV20] = get_cli_context().project_definition
|
|
145
195
|
if not original_pdf:
|
|
146
196
|
raise ValueError(
|
|
147
197
|
"Project definition could not be found. The nativeapp_definition_v2_to_v1 command decorator assumes with_project_definition() was called before it."
|
|
148
198
|
)
|
|
149
199
|
if original_pdf.definition_version == "2":
|
|
150
|
-
|
|
151
|
-
|
|
200
|
+
package_entity_id = kwargs.get("package_entity_id", "")
|
|
201
|
+
app_entity_id = kwargs.get("app_entity_id", "")
|
|
202
|
+
pdfv1 = _pdf_v2_to_v1(original_pdf, package_entity_id, app_entity_id)
|
|
203
|
+
get_cli_context_manager().override_project_definition = pdfv1
|
|
152
204
|
return func(*args, **kwargs)
|
|
153
205
|
|
|
154
|
-
return
|
|
206
|
+
return _options_decorator_factory(
|
|
207
|
+
wrapper,
|
|
208
|
+
additional_options=[
|
|
209
|
+
inspect.Parameter(
|
|
210
|
+
"package_entity_id",
|
|
211
|
+
inspect.Parameter.KEYWORD_ONLY,
|
|
212
|
+
annotation=Optional[str],
|
|
213
|
+
default=typer.Option(
|
|
214
|
+
default="",
|
|
215
|
+
help="The ID of the package entity on which to operate when definition_version is 2 or higher.",
|
|
216
|
+
),
|
|
217
|
+
),
|
|
218
|
+
inspect.Parameter(
|
|
219
|
+
"app_entity_id",
|
|
220
|
+
inspect.Parameter.KEYWORD_ONLY,
|
|
221
|
+
annotation=Optional[str],
|
|
222
|
+
default=typer.Option(
|
|
223
|
+
default="",
|
|
224
|
+
help="The ID of the application entity on which to operate when definition_version is 2 or higher.",
|
|
225
|
+
),
|
|
226
|
+
),
|
|
227
|
+
],
|
|
228
|
+
)
|
|
@@ -446,5 +446,5 @@ def describe(
|
|
|
446
446
|
def _get_v2_project_definition(cli_context) -> ProjectDefinitionV2:
|
|
447
447
|
pd = cli_context.project_definition
|
|
448
448
|
if not pd.meets_version_requirement("2"):
|
|
449
|
-
pd = convert_project_definition_to_v2(pd)
|
|
449
|
+
pd = convert_project_definition_to_v2(cli_context.project_root, pd)
|
|
450
450
|
return pd
|
|
@@ -121,13 +121,14 @@ class WheelMetadata:
|
|
|
121
121
|
if line.startswith(dep_keyword)
|
|
122
122
|
]
|
|
123
123
|
name = cls._get_name_from_wheel_filename(wheel_path.name)
|
|
124
|
+
|
|
124
125
|
return cls(name=name, wheel_path=wheel_path, dependencies=dependencies)
|
|
125
126
|
|
|
126
127
|
@staticmethod
|
|
127
128
|
def _get_name_from_wheel_filename(wheel_filename: str) -> str:
|
|
128
129
|
# wheel filename is in format {name}-{version}[-{extra info}]
|
|
129
130
|
# https://peps.python.org/pep-0491/#file-name-convention
|
|
130
|
-
return wheel_filename.split("-")[0]
|
|
131
|
+
return wheel_filename.split("-")[0].lower()
|
|
131
132
|
|
|
132
133
|
@staticmethod
|
|
133
134
|
def to_wheel_name_format(package_name: str) -> str:
|
|
@@ -138,7 +138,7 @@ def streamlit_deploy(
|
|
|
138
138
|
raise NoProjectDefinitionError(
|
|
139
139
|
project_type="streamlit", project_root=cli_context.project_root
|
|
140
140
|
)
|
|
141
|
-
pd = convert_project_definition_to_v2(pd)
|
|
141
|
+
pd = convert_project_definition_to_v2(cli_context.project_root, pd)
|
|
142
142
|
|
|
143
143
|
streamlits: Dict[str, StreamlitEntityModel] = pd.get_entities_by_type(
|
|
144
144
|
entity_type="streamlit"
|
|
@@ -18,11 +18,13 @@ import logging
|
|
|
18
18
|
from pathlib import Path
|
|
19
19
|
from typing import List, Optional
|
|
20
20
|
|
|
21
|
+
from click import ClickException
|
|
21
22
|
from snowflake.cli._plugins.connection.util import (
|
|
22
23
|
MissingConnectionAccountError,
|
|
23
24
|
MissingConnectionRegionError,
|
|
24
25
|
make_snowsight_url,
|
|
25
26
|
)
|
|
27
|
+
from snowflake.cli._plugins.object.manager import ObjectManager
|
|
26
28
|
from snowflake.cli._plugins.stage.manager import StageManager
|
|
27
29
|
from snowflake.cli.api.commands.experimental_behaviour import (
|
|
28
30
|
experimental_behaviour_enabled,
|
|
@@ -112,6 +114,13 @@ class StreamlitManager(SqlExecutionMixin):
|
|
|
112
114
|
|
|
113
115
|
def deploy(self, streamlit: StreamlitEntityModel, replace: bool = False):
|
|
114
116
|
streamlit_id = streamlit.fqn.using_connection(self._conn)
|
|
117
|
+
if (
|
|
118
|
+
ObjectManager().object_exists(object_type="streamlit", fqn=streamlit_id)
|
|
119
|
+
and not replace
|
|
120
|
+
):
|
|
121
|
+
raise ClickException(
|
|
122
|
+
f"Streamlit {streamlit.fqn} already exist. If you want to replace it use --replace flag."
|
|
123
|
+
)
|
|
115
124
|
|
|
116
125
|
# for backwards compatibility - quoted stage path might be case-sensitive
|
|
117
126
|
# https://docs.snowflake.com/en/sql-reference/identifiers-syntax#double-quoted-identifiers
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
2
|
from pathlib import Path
|
|
3
|
-
from typing import Optional
|
|
3
|
+
from typing import Callable, Optional
|
|
4
4
|
|
|
5
5
|
from snowflake.cli.api.console.abc import AbstractConsole
|
|
6
6
|
|
|
@@ -15,3 +15,4 @@ class ActionContext:
|
|
|
15
15
|
project_root: Path
|
|
16
16
|
default_role: str
|
|
17
17
|
default_warehouse: Optional[str]
|
|
18
|
+
get_entity: Callable
|
|
@@ -24,6 +24,7 @@ import yaml
|
|
|
24
24
|
from snowflake.cli._plugins.nativeapp.artifacts import BundleMap
|
|
25
25
|
from snowflake.cli._plugins.nativeapp.common_flags import (
|
|
26
26
|
ForceOption,
|
|
27
|
+
InteractiveOption,
|
|
27
28
|
ValidateOption,
|
|
28
29
|
)
|
|
29
30
|
from snowflake.cli._plugins.workspace.manager import WorkspaceManager
|
|
@@ -53,13 +54,14 @@ def migrate(
|
|
|
53
54
|
),
|
|
54
55
|
**options,
|
|
55
56
|
):
|
|
56
|
-
"""Migrates the Snowpark and
|
|
57
|
-
|
|
57
|
+
"""Migrates the Snowpark, Streamlit, and Native App project definition files from V1 to V2."""
|
|
58
|
+
manager = DefinitionManager()
|
|
59
|
+
pd = manager.unrendered_project_definition
|
|
58
60
|
|
|
59
61
|
if pd.meets_version_requirement("2"):
|
|
60
62
|
return MessageResult("Project definition is already at version 2.")
|
|
61
63
|
|
|
62
|
-
pd_v2 = convert_project_definition_to_v2(pd, accept_templates)
|
|
64
|
+
pd_v2 = convert_project_definition_to_v2(manager.project_root, pd, accept_templates)
|
|
63
65
|
|
|
64
66
|
SecurePath("snowflake.yml").rename("snowflake_V1.yml")
|
|
65
67
|
with open("snowflake.yml", "w") as file:
|
|
@@ -72,16 +74,6 @@ def migrate(
|
|
|
72
74
|
return MessageResult("Project definition migrated to version 2.")
|
|
73
75
|
|
|
74
76
|
|
|
75
|
-
@ws.command(requires_connection=True, hidden=True)
|
|
76
|
-
@with_project_definition()
|
|
77
|
-
def validate(
|
|
78
|
-
**options,
|
|
79
|
-
):
|
|
80
|
-
"""Validates the project definition file."""
|
|
81
|
-
# If we get to this point, @with_project_definition() has already validated the PDF schema
|
|
82
|
-
return MessageResult("Project definition is valid.")
|
|
83
|
-
|
|
84
|
-
|
|
85
77
|
@ws.command(requires_connection=True, hidden=True)
|
|
86
78
|
@with_project_definition()
|
|
87
79
|
def bundle(
|
|
@@ -104,7 +96,7 @@ def bundle(
|
|
|
104
96
|
return MessageResult(f"Bundle generated at {bundle_map.deploy_root()}")
|
|
105
97
|
|
|
106
98
|
|
|
107
|
-
@ws.command(requires_connection=True)
|
|
99
|
+
@ws.command(requires_connection=True, hidden=True)
|
|
108
100
|
@with_project_definition()
|
|
109
101
|
def deploy(
|
|
110
102
|
entity_id: str = typer.Option(
|
|
@@ -132,6 +124,15 @@ def deploy(
|
|
|
132
124
|
unspecified, the command syncs all local changes to the stage."""
|
|
133
125
|
).strip(),
|
|
134
126
|
),
|
|
127
|
+
from_release_directive: Optional[bool] = typer.Option(
|
|
128
|
+
False,
|
|
129
|
+
"--from-release-directive",
|
|
130
|
+
help=f"""Creates or upgrades an application object to the version and patch specified by the release directive applicable to your Snowflake account.
|
|
131
|
+
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.""",
|
|
132
|
+
is_flag=True,
|
|
133
|
+
),
|
|
134
|
+
interactive: bool = InteractiveOption,
|
|
135
|
+
force: Optional[bool] = ForceOption,
|
|
135
136
|
validate: bool = ValidateOption,
|
|
136
137
|
**options,
|
|
137
138
|
):
|
|
@@ -163,24 +164,32 @@ def deploy(
|
|
|
163
164
|
recursive=recursive,
|
|
164
165
|
paths=paths,
|
|
165
166
|
validate=validate,
|
|
167
|
+
from_release_directive=from_release_directive,
|
|
168
|
+
interactive=interactive,
|
|
169
|
+
force=force,
|
|
166
170
|
)
|
|
167
171
|
return MessageResult("Deployed successfully.")
|
|
168
172
|
|
|
169
173
|
|
|
170
|
-
@ws.command(requires_connection=True)
|
|
174
|
+
@ws.command(requires_connection=True, hidden=True)
|
|
171
175
|
@with_project_definition()
|
|
172
176
|
def drop(
|
|
173
177
|
entity_id: str = typer.Option(
|
|
174
178
|
help=f"""The ID of the entity you want to drop.""",
|
|
175
179
|
),
|
|
176
180
|
# TODO The following options should be generated automatically, depending on the specified entity type
|
|
181
|
+
interactive: bool = InteractiveOption,
|
|
177
182
|
force: Optional[bool] = ForceOption,
|
|
183
|
+
cascade: Optional[bool] = typer.Option(
|
|
184
|
+
None,
|
|
185
|
+
help=f"""Whether to drop all application objects owned by the application within the account. Default: false.""",
|
|
186
|
+
show_default=False,
|
|
187
|
+
),
|
|
178
188
|
**options,
|
|
179
189
|
):
|
|
180
190
|
"""
|
|
181
191
|
Drops the specified entity.
|
|
182
192
|
"""
|
|
183
|
-
|
|
184
193
|
cli_context = get_cli_context()
|
|
185
194
|
ws = WorkspaceManager(
|
|
186
195
|
project_definition=cli_context.project_definition,
|
|
@@ -191,4 +200,27 @@ def drop(
|
|
|
191
200
|
entity_id,
|
|
192
201
|
EntityActions.DROP,
|
|
193
202
|
force_drop=force,
|
|
203
|
+
interactive=interactive,
|
|
204
|
+
cascade=cascade,
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
@ws.command(requires_connection=True, hidden=True)
|
|
209
|
+
@with_project_definition()
|
|
210
|
+
def validate(
|
|
211
|
+
entity_id: str = typer.Option(
|
|
212
|
+
help=f"""The ID of the entity you want to validate.""",
|
|
213
|
+
),
|
|
214
|
+
**options,
|
|
215
|
+
):
|
|
216
|
+
"""Validates the specified entity."""
|
|
217
|
+
cli_context = get_cli_context()
|
|
218
|
+
ws = WorkspaceManager(
|
|
219
|
+
project_definition=cli_context.project_definition,
|
|
220
|
+
project_root=cli_context.project_root,
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
ws.perform_action(
|
|
224
|
+
entity_id,
|
|
225
|
+
EntityActions.VALIDATE,
|
|
194
226
|
)
|