snowflake-cli-labs 2.8.0rc0__py3-none-any.whl → 3.0.0rc0__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.
Files changed (220) hide show
  1. snowflake/cli/__about__.py +1 -1
  2. snowflake/cli/{app → _app}/__main__.py +1 -1
  3. snowflake/cli/{app → _app}/cli_app.py +12 -12
  4. snowflake/cli/{app → _app}/commands_registration/builtin_plugins.py +13 -19
  5. snowflake/cli/{app → _app}/commands_registration/command_plugins_loader.py +9 -9
  6. snowflake/cli/{app → _app}/commands_registration/commands_registration_with_callbacks.py +4 -4
  7. snowflake/cli/{app → _app}/commands_registration/exception_logging.py +2 -2
  8. snowflake/cli/{app → _app}/commands_registration/typer_registration.py +2 -2
  9. snowflake/cli/{app → _app}/dev/docs/commands_docs_generator.py +30 -12
  10. snowflake/cli/{app → _app}/dev/docs/generator.py +3 -3
  11. snowflake/cli/{app → _app}/dev/docs/project_definition_docs_generator.py +4 -4
  12. snowflake/cli/{app → _app}/dev/docs/templates/usage.rst.jinja2 +14 -4
  13. snowflake/cli/{app → _app}/main_typer.py +2 -2
  14. snowflake/cli/{app → _app}/printing.py +2 -2
  15. snowflake/cli/{app → _app}/snow_connector.py +6 -6
  16. snowflake/cli/{app → _app}/telemetry.py +4 -5
  17. snowflake/cli/{plugins → _plugins}/connection/commands.py +22 -5
  18. snowflake/cli/_plugins/connection/plugin_spec.py +30 -0
  19. snowflake/cli/{plugins → _plugins}/connection/util.py +16 -0
  20. snowflake/cli/{plugins → _plugins}/cortex/commands.py +54 -49
  21. snowflake/cli/{plugins → _plugins}/cortex/constants.py +1 -1
  22. snowflake/cli/{plugins → _plugins}/cortex/manager.py +5 -5
  23. snowflake/cli/{plugins → _plugins}/cortex/plugin_spec.py +1 -1
  24. snowflake/cli/{plugins → _plugins}/git/commands.py +32 -20
  25. snowflake/cli/{plugins → _plugins}/git/manager.py +6 -5
  26. snowflake/cli/{plugins → _plugins}/git/plugin_spec.py +1 -1
  27. snowflake/cli/{plugins → _plugins}/init/commands.py +10 -6
  28. snowflake/cli/{plugins → _plugins}/init/plugin_spec.py +1 -1
  29. snowflake/cli/{plugins → _plugins}/nativeapp/artifacts.py +14 -0
  30. snowflake/cli/_plugins/nativeapp/bundle_context.py +31 -0
  31. snowflake/cli/{plugins → _plugins}/nativeapp/codegen/artifact_processor.py +3 -3
  32. snowflake/cli/{plugins → _plugins}/nativeapp/codegen/compiler.py +16 -18
  33. snowflake/cli/{plugins → _plugins}/nativeapp/codegen/setup/native_app_setup_processor.py +24 -28
  34. snowflake/cli/{plugins → _plugins}/nativeapp/codegen/snowpark/extension_function_utils.py +4 -4
  35. snowflake/cli/{plugins → _plugins}/nativeapp/codegen/snowpark/python_processor.py +20 -24
  36. snowflake/cli/{plugins → _plugins}/nativeapp/commands.py +171 -42
  37. snowflake/cli/{plugins → _plugins}/nativeapp/common_flags.py +1 -1
  38. snowflake/cli/{plugins → _plugins}/nativeapp/init.py +1 -1
  39. snowflake/cli/_plugins/nativeapp/manager.py +601 -0
  40. snowflake/cli/{plugins/connection → _plugins/nativeapp}/plugin_spec.py +1 -1
  41. snowflake/cli/{plugins → _plugins}/nativeapp/project_model.py +34 -11
  42. snowflake/cli/{plugins → _plugins}/nativeapp/run_processor.py +25 -23
  43. snowflake/cli/{plugins → _plugins}/nativeapp/teardown_processor.py +8 -8
  44. snowflake/cli/{plugins → _plugins}/nativeapp/v2_conversions/v2_to_v1_decorator.py +47 -28
  45. snowflake/cli/{plugins → _plugins}/nativeapp/version/commands.py +15 -12
  46. snowflake/cli/{plugins → _plugins}/nativeapp/version/version_processor.py +22 -20
  47. snowflake/cli/{plugins → _plugins}/notebook/commands.py +8 -6
  48. snowflake/cli/{plugins → _plugins}/notebook/manager.py +14 -14
  49. snowflake/cli/{plugins → _plugins}/notebook/plugin_spec.py +1 -1
  50. snowflake/cli/{plugins → _plugins}/notebook/types.py +0 -1
  51. snowflake/cli/{plugins → _plugins}/object/command_aliases.py +6 -5
  52. snowflake/cli/{plugins → _plugins}/object/commands.py +16 -10
  53. snowflake/cli/{plugins → _plugins}/object/manager.py +7 -6
  54. snowflake/cli/{plugins → _plugins}/object/plugin_spec.py +1 -1
  55. snowflake/cli/_plugins/snowpark/commands.py +510 -0
  56. snowflake/cli/_plugins/snowpark/common.py +252 -0
  57. snowflake/cli/{plugins → _plugins}/snowpark/models.py +0 -7
  58. snowflake/cli/{plugins → _plugins}/snowpark/package/anaconda_packages.py +1 -1
  59. snowflake/cli/{plugins → _plugins}/snowpark/package/commands.py +13 -74
  60. snowflake/cli/{plugins → _plugins}/snowpark/package/manager.py +4 -3
  61. snowflake/cli/{plugins → _plugins}/snowpark/package_utils.py +5 -5
  62. snowflake/cli/{plugins/nativeapp → _plugins/snowpark}/plugin_spec.py +1 -1
  63. snowflake/cli/_plugins/snowpark/snowpark_project_paths.py +109 -0
  64. snowflake/cli/{plugins → _plugins}/snowpark/snowpark_shared.py +0 -36
  65. snowflake/cli/{plugins → _plugins}/snowpark/zipper.py +16 -8
  66. snowflake/cli/{plugins → _plugins}/spcs/__init__.py +5 -7
  67. snowflake/cli/{plugins → _plugins}/spcs/compute_pool/commands.py +29 -28
  68. snowflake/cli/{plugins → _plugins}/spcs/compute_pool/manager.py +3 -3
  69. snowflake/cli/{plugins → _plugins}/spcs/image_registry/commands.py +3 -3
  70. snowflake/cli/{plugins → _plugins}/spcs/image_repository/commands.py +25 -19
  71. snowflake/cli/{plugins → _plugins}/spcs/image_repository/manager.py +1 -1
  72. snowflake/cli/{plugins → _plugins}/spcs/plugin_spec.py +1 -1
  73. snowflake/cli/{plugins → _plugins}/spcs/services/commands.py +66 -32
  74. snowflake/cli/{plugins → _plugins}/spcs/services/manager.py +43 -5
  75. snowflake/cli/{plugins → _plugins}/sql/commands.py +19 -15
  76. snowflake/cli/{plugins → _plugins}/sql/manager.py +1 -1
  77. snowflake/cli/{plugins → _plugins}/sql/plugin_spec.py +1 -1
  78. snowflake/cli/{plugins → _plugins}/stage/commands.py +20 -17
  79. snowflake/cli/{plugins → _plugins}/stage/diff.py +1 -47
  80. snowflake/cli/{plugins → _plugins}/stage/manager.py +8 -6
  81. snowflake/cli/{plugins → _plugins}/stage/plugin_spec.py +1 -1
  82. snowflake/cli/_plugins/stage/utils.py +54 -0
  83. snowflake/cli/_plugins/streamlit/commands.py +242 -0
  84. snowflake/cli/{plugins → _plugins}/streamlit/manager.py +47 -70
  85. snowflake/cli/_plugins/streamlit/plugin_spec.py +30 -0
  86. snowflake/cli/_plugins/workspace/action_context.py +11 -0
  87. snowflake/cli/_plugins/workspace/commands.py +113 -0
  88. snowflake/cli/_plugins/workspace/manager.py +57 -0
  89. snowflake/cli/{plugins → _plugins}/workspace/plugin_spec.py +1 -1
  90. snowflake/cli/api/cli_global_context.py +34 -7
  91. snowflake/cli/api/commands/common.py +25 -0
  92. snowflake/cli/api/commands/decorators.py +4 -3
  93. snowflake/cli/api/commands/experimental_behaviour.py +2 -3
  94. snowflake/cli/api/commands/flags.py +73 -174
  95. snowflake/cli/api/commands/overrideable_parameter.py +143 -0
  96. snowflake/cli/api/commands/snow_typer.py +5 -4
  97. snowflake/cli/api/commands/typer_pre_execute.py +3 -3
  98. snowflake/cli/api/commands/utils.py +18 -0
  99. snowflake/cli/api/config.py +1 -1
  100. snowflake/cli/api/console/abc.py +5 -2
  101. snowflake/cli/api/entities/application_entity.py +12 -0
  102. snowflake/cli/api/entities/application_package_entity.py +260 -0
  103. snowflake/cli/api/entities/common.py +47 -0
  104. snowflake/cli/api/entities/snowpark_entity.py +29 -0
  105. snowflake/cli/api/entities/streamlit_entity.py +12 -0
  106. snowflake/cli/api/entities/utils.py +321 -0
  107. snowflake/cli/api/exceptions.py +19 -3
  108. snowflake/cli/api/feature_flags.py +2 -1
  109. snowflake/cli/api/identifiers.py +41 -9
  110. snowflake/cli/api/project/definition.py +13 -5
  111. snowflake/cli/api/project/definition_manager.py +12 -1
  112. snowflake/cli/api/project/errors.py +16 -1
  113. snowflake/cli/api/project/project_verification.py +3 -3
  114. snowflake/cli/api/project/schemas/entities/{application_entity.py → application_entity_model.py} +21 -9
  115. snowflake/cli/api/project/schemas/entities/{application_package_entity.py → application_package_entity_model.py} +26 -15
  116. snowflake/cli/api/project/schemas/entities/common.py +80 -6
  117. snowflake/cli/api/project/schemas/entities/entities.py +38 -8
  118. snowflake/cli/api/project/schemas/entities/snowpark_entity.py +176 -0
  119. snowflake/cli/api/project/schemas/entities/streamlit_entity_model.py +73 -0
  120. snowflake/cli/api/project/schemas/identifier_model.py +10 -1
  121. snowflake/cli/api/project/schemas/native_app/application.py +8 -9
  122. snowflake/cli/api/project/schemas/native_app/package.py +7 -1
  123. snowflake/cli/api/project/schemas/project_definition.py +97 -23
  124. snowflake/cli/api/project/schemas/updatable_model.py +11 -3
  125. snowflake/cli/api/project/util.py +23 -6
  126. snowflake/cli/api/rendering/jinja.py +28 -8
  127. snowflake/cli/api/rendering/sql_templates.py +41 -12
  128. snowflake/cli/api/secure_path.py +3 -0
  129. snowflake/cli/api/sql_execution.py +35 -19
  130. snowflake/cli/api/utils/definition_rendering.py +14 -2
  131. {snowflake_cli_labs-2.8.0rc0.dist-info → snowflake_cli_labs-3.0.0rc0.dist-info}/METADATA +12 -12
  132. snowflake_cli_labs-3.0.0rc0.dist-info/RECORD +234 -0
  133. snowflake_cli_labs-3.0.0rc0.dist-info/entry_points.txt +2 -0
  134. snowflake/cli/api/commands/project_initialisation.py +0 -65
  135. snowflake/cli/app/build_and_push.sh +0 -8
  136. snowflake/cli/plugins/nativeapp/manager.py +0 -819
  137. snowflake/cli/plugins/object_stage_deprecated/__init__.py +0 -15
  138. snowflake/cli/plugins/object_stage_deprecated/commands.py +0 -122
  139. snowflake/cli/plugins/object_stage_deprecated/plugin_spec.py +0 -32
  140. snowflake/cli/plugins/snowpark/commands.py +0 -548
  141. snowflake/cli/plugins/snowpark/common.py +0 -307
  142. snowflake/cli/plugins/snowpark/manager.py +0 -109
  143. snowflake/cli/plugins/snowpark/plugin_spec.py +0 -30
  144. snowflake/cli/plugins/snowpark/snowpark_package_paths.py +0 -65
  145. snowflake/cli/plugins/spcs/jobs/commands.py +0 -78
  146. snowflake/cli/plugins/spcs/jobs/manager.py +0 -53
  147. snowflake/cli/plugins/streamlit/commands.py +0 -186
  148. snowflake/cli/plugins/streamlit/plugin_spec.py +0 -30
  149. snowflake/cli/plugins/workspace/commands.py +0 -35
  150. snowflake/cli/templates/default_snowpark/.gitignore +0 -4
  151. snowflake/cli/templates/default_snowpark/app/__init__.py +0 -0
  152. snowflake/cli/templates/default_snowpark/app/common.py +0 -2
  153. snowflake/cli/templates/default_snowpark/app/functions.py +0 -15
  154. snowflake/cli/templates/default_snowpark/app/procedures.py +0 -22
  155. snowflake/cli/templates/default_snowpark/requirements.txt +0 -1
  156. snowflake/cli/templates/default_snowpark/snowflake.yml +0 -23
  157. snowflake/cli/templates/default_streamlit/.gitignore +0 -4
  158. snowflake/cli/templates/default_streamlit/common/hello.py +0 -2
  159. snowflake/cli/templates/default_streamlit/environment.yml +0 -6
  160. snowflake/cli/templates/default_streamlit/pages/my_page.py +0 -3
  161. snowflake/cli/templates/default_streamlit/snowflake.yml +0 -10
  162. snowflake/cli/templates/default_streamlit/streamlit_app.py +0 -4
  163. snowflake_cli_labs-2.8.0rc0.dist-info/RECORD +0 -240
  164. snowflake_cli_labs-2.8.0rc0.dist-info/entry_points.txt +0 -2
  165. /snowflake/cli/{app → _app}/__init__.py +0 -0
  166. /snowflake/cli/{app → _app}/api_impl/__init__.py +0 -0
  167. /snowflake/cli/{app → _app}/api_impl/plugin/__init__.py +0 -0
  168. /snowflake/cli/{app → _app}/api_impl/plugin/plugin_config_provider_impl.py +0 -0
  169. /snowflake/cli/{app → _app}/commands_registration/__init__.py +0 -0
  170. /snowflake/cli/{app → _app}/commands_registration/threadsafe.py +0 -0
  171. /snowflake/cli/{app → _app}/constants.py +0 -0
  172. /snowflake/cli/{app → _app}/dev/__init__.py +0 -0
  173. /snowflake/cli/{app → _app}/dev/commands_structure.py +0 -0
  174. /snowflake/cli/{app → _app}/dev/docs/__init__.py +0 -0
  175. /snowflake/cli/{app → _app}/dev/docs/project_definition_generate_json_schema.py +0 -0
  176. /snowflake/cli/{app → _app}/dev/docs/template_utils.py +0 -0
  177. /snowflake/cli/{app → _app}/dev/docs/templates/definition_description.rst.jinja2 +0 -0
  178. /snowflake/cli/{app → _app}/dev/docs/templates/overview.rst.jinja2 +0 -0
  179. /snowflake/cli/{app → _app}/dev/pycharm_remote_debug.py +0 -0
  180. /snowflake/cli/{app → _app}/loggers.py +0 -0
  181. /snowflake/cli/{plugins → _plugins}/__init__.py +0 -0
  182. /snowflake/cli/{plugins → _plugins}/connection/__init__.py +0 -0
  183. /snowflake/cli/{plugins → _plugins}/cortex/__init__.py +0 -0
  184. /snowflake/cli/{plugins → _plugins}/cortex/types.py +0 -0
  185. /snowflake/cli/{plugins → _plugins}/git/__init__.py +0 -0
  186. /snowflake/cli/{plugins → _plugins}/init/__init__.py +0 -0
  187. /snowflake/cli/{plugins → _plugins}/nativeapp/__init__.py +0 -0
  188. /snowflake/cli/{plugins → _plugins}/nativeapp/codegen/__init__.py +0 -0
  189. /snowflake/cli/{plugins → _plugins}/nativeapp/codegen/sandbox.py +0 -0
  190. /snowflake/cli/{plugins → _plugins}/nativeapp/codegen/setup/setup_driver.py.source +0 -0
  191. /snowflake/cli/{plugins → _plugins}/nativeapp/codegen/snowpark/callback_source.py.jinja +0 -0
  192. /snowflake/cli/{plugins → _plugins}/nativeapp/codegen/snowpark/models.py +0 -0
  193. /snowflake/cli/{plugins → _plugins}/nativeapp/constants.py +0 -0
  194. /snowflake/cli/{plugins → _plugins}/nativeapp/exceptions.py +0 -0
  195. /snowflake/cli/{plugins → _plugins}/nativeapp/feature_flags.py +0 -0
  196. /snowflake/cli/{plugins → _plugins}/nativeapp/policy.py +0 -0
  197. /snowflake/cli/{plugins → _plugins}/nativeapp/utils.py +0 -0
  198. /snowflake/cli/{plugins → _plugins}/nativeapp/version/__init__.py +0 -0
  199. /snowflake/cli/{plugins → _plugins}/notebook/__init__.py +0 -0
  200. /snowflake/cli/{plugins → _plugins}/notebook/exceptions.py +0 -0
  201. /snowflake/cli/{plugins → _plugins}/object/__init__.py +0 -0
  202. /snowflake/cli/{plugins → _plugins}/object/common.py +0 -0
  203. /snowflake/cli/{plugins → _plugins}/snowpark/__init__.py +0 -0
  204. /snowflake/cli/{plugins → _plugins}/snowpark/package/__init__.py +0 -0
  205. /snowflake/cli/{plugins → _plugins}/snowpark/package/utils.py +0 -0
  206. /snowflake/cli/{plugins → _plugins}/spcs/common.py +0 -0
  207. /snowflake/cli/{plugins → _plugins}/spcs/compute_pool/__init__.py +0 -0
  208. /snowflake/cli/{plugins → _plugins}/spcs/image_registry/__init__.py +0 -0
  209. /snowflake/cli/{plugins → _plugins}/spcs/image_registry/manager.py +0 -0
  210. /snowflake/cli/{plugins → _plugins}/spcs/image_repository/__init__.py +0 -0
  211. /snowflake/cli/{plugins/spcs/jobs → _plugins/spcs/services}/__init__.py +0 -0
  212. /snowflake/cli/{plugins/spcs/services → _plugins/sql}/__init__.py +0 -0
  213. /snowflake/cli/{plugins → _plugins}/sql/snowsql_templating.py +0 -0
  214. /snowflake/cli/{plugins/sql → _plugins/stage}/__init__.py +0 -0
  215. /snowflake/cli/{plugins → _plugins}/stage/md5.py +0 -0
  216. /snowflake/cli/{plugins/stage → _plugins/streamlit}/__init__.py +0 -0
  217. /snowflake/cli/{plugins/streamlit → _plugins/workspace}/__init__.py +0 -0
  218. /snowflake/cli/{plugins/workspace → api/project/schemas/entities}/__init__.py +0 -0
  219. {snowflake_cli_labs-2.8.0rc0.dist-info → snowflake_cli_labs-3.0.0rc0.dist-info}/WHEEL +0 -0
  220. {snowflake_cli_labs-2.8.0rc0.dist-info → snowflake_cli_labs-3.0.0rc0.dist-info}/licenses/LICENSE +0 -0
@@ -242,7 +242,7 @@ def _init_from_template(
242
242
  try:
243
243
  with SecurePath.temporary_directory() as temp_path:
244
244
  from git import rmtree as git_rmtree
245
- from snowflake.cli.plugins.nativeapp.utils import shallow_git_clone
245
+ from snowflake.cli._plugins.nativeapp.utils import shallow_git_clone
246
246
 
247
247
  shallow_git_clone(git_url, temp_path.path)
248
248
 
@@ -0,0 +1,601 @@
1
+ # Copyright (c) 2024 Snowflake Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from __future__ import annotations
16
+
17
+ import json
18
+ import time
19
+ from abc import ABC, abstractmethod
20
+ from contextlib import contextmanager
21
+ from datetime import datetime
22
+ from functools import cached_property
23
+ from pathlib import Path
24
+ from textwrap import dedent
25
+ from typing import Generator, List, Optional, TypedDict
26
+
27
+ from click import ClickException
28
+ from snowflake.cli._plugins.connection.util import make_snowsight_url
29
+ from snowflake.cli._plugins.nativeapp.artifacts import (
30
+ BundleMap,
31
+ build_bundle,
32
+ )
33
+ from snowflake.cli._plugins.nativeapp.codegen.compiler import (
34
+ NativeAppCompiler,
35
+ )
36
+ from snowflake.cli._plugins.nativeapp.constants import (
37
+ NAME_COL,
38
+ )
39
+ from snowflake.cli._plugins.nativeapp.exceptions import (
40
+ ApplicationPackageDoesNotExistError,
41
+ NoEventTableForAccount,
42
+ SetupScriptFailedValidation,
43
+ )
44
+ from snowflake.cli._plugins.nativeapp.project_model import (
45
+ NativeAppProjectModel,
46
+ )
47
+ from snowflake.cli._plugins.stage.diff import (
48
+ DiffResult,
49
+ )
50
+ from snowflake.cli._plugins.stage.manager import StageManager
51
+ from snowflake.cli.api.console import cli_console as cc
52
+ from snowflake.cli.api.entities.application_package_entity import (
53
+ ApplicationPackageEntity,
54
+ )
55
+ from snowflake.cli.api.entities.utils import (
56
+ execute_post_deploy_hooks,
57
+ generic_sql_error_handler,
58
+ sync_deploy_root_with_stage,
59
+ )
60
+ from snowflake.cli.api.errno import (
61
+ DOES_NOT_EXIST_OR_NOT_AUTHORIZED,
62
+ )
63
+ from snowflake.cli.api.exceptions import SnowflakeSQLExecutionError
64
+ from snowflake.cli.api.project.schemas.entities.common import PostDeployHook
65
+ from snowflake.cli.api.project.schemas.native_app.native_app import NativeApp
66
+ from snowflake.cli.api.project.schemas.native_app.path_mapping import PathMapping
67
+ from snowflake.cli.api.project.util import (
68
+ identifier_for_url,
69
+ unquote_identifier,
70
+ )
71
+ from snowflake.cli.api.sql_execution import SqlExecutionMixin
72
+ from snowflake.connector import DictCursor, ProgrammingError
73
+
74
+ ApplicationOwnedObject = TypedDict("ApplicationOwnedObject", {"name": str, "type": str})
75
+
76
+
77
+ class NativeAppCommandProcessor(ABC):
78
+ @abstractmethod
79
+ def process(self, *args, **kwargs):
80
+ pass
81
+
82
+
83
+ class NativeAppManager(SqlExecutionMixin):
84
+ """
85
+ Base class with frequently used functionality already implemented and ready to be used by related subclasses.
86
+ """
87
+
88
+ def __init__(self, project_definition: NativeApp, project_root: Path):
89
+ super().__init__()
90
+ self._na_project = NativeAppProjectModel(
91
+ project_definition=project_definition,
92
+ project_root=project_root,
93
+ )
94
+
95
+ @property
96
+ def na_project(self) -> NativeAppProjectModel:
97
+ return self._na_project
98
+
99
+ @property
100
+ def project_root(self) -> Path:
101
+ return self.na_project.project_root
102
+
103
+ @property
104
+ def definition(self) -> NativeApp:
105
+ return self.na_project.definition
106
+
107
+ @property
108
+ def artifacts(self) -> List[PathMapping]:
109
+ return self.na_project.artifacts
110
+
111
+ @property
112
+ def bundle_root(self) -> Path:
113
+ return self.na_project.bundle_root
114
+
115
+ @property
116
+ def deploy_root(self) -> Path:
117
+ return self.na_project.deploy_root
118
+
119
+ @property
120
+ def generated_root(self) -> Path:
121
+ return self.na_project.generated_root
122
+
123
+ @property
124
+ def package_scripts(self) -> List[str]:
125
+ return self.na_project.package_scripts
126
+
127
+ @property
128
+ def stage_fqn(self) -> str:
129
+ return self.na_project.stage_fqn
130
+
131
+ @property
132
+ def scratch_stage_fqn(self) -> str:
133
+ return self.na_project.scratch_stage_fqn
134
+
135
+ @property
136
+ def stage_schema(self) -> Optional[str]:
137
+ return self.na_project.stage_schema
138
+
139
+ @property
140
+ def package_warehouse(self) -> Optional[str]:
141
+ return self.na_project.package_warehouse
142
+
143
+ def use_package_warehouse(self):
144
+ return ApplicationPackageEntity.use_package_warehouse(
145
+ self.package_warehouse,
146
+ )
147
+
148
+ @property
149
+ def application_warehouse(self) -> Optional[str]:
150
+ return self.na_project.application_warehouse
151
+
152
+ @contextmanager
153
+ def use_application_warehouse(self):
154
+ if self.application_warehouse:
155
+ with self.use_warehouse(self.application_warehouse):
156
+ yield
157
+ else:
158
+ raise ClickException(
159
+ dedent(
160
+ f"""\
161
+ Application warehouse cannot be empty.
162
+ Please provide a value for it in your connection information or your project definition file.
163
+ """
164
+ )
165
+ )
166
+
167
+ @property
168
+ def project_identifier(self) -> str:
169
+ return self.na_project.project_identifier
170
+
171
+ @property
172
+ def package_name(self) -> str:
173
+ return self.na_project.package_name
174
+
175
+ @property
176
+ def package_role(self) -> str:
177
+ return self.na_project.package_role
178
+
179
+ @property
180
+ def package_distribution(self) -> str:
181
+ return self.na_project.package_distribution
182
+
183
+ @property
184
+ def app_name(self) -> str:
185
+ return self.na_project.app_name
186
+
187
+ @property
188
+ def app_role(self) -> str:
189
+ return self.na_project.app_role
190
+
191
+ @property
192
+ def app_post_deploy_hooks(self) -> Optional[List[PostDeployHook]]:
193
+ return self.na_project.app_post_deploy_hooks
194
+
195
+ @property
196
+ def package_post_deploy_hooks(self) -> Optional[List[PostDeployHook]]:
197
+ return self.na_project.package_post_deploy_hooks
198
+
199
+ @property
200
+ def debug_mode(self) -> bool:
201
+ return self.na_project.debug_mode
202
+
203
+ @cached_property
204
+ def get_app_pkg_distribution_in_snowflake(self) -> str:
205
+ return ApplicationPackageEntity.get_app_pkg_distribution_in_snowflake(
206
+ self.package_name, self.package_role
207
+ )
208
+
209
+ @cached_property
210
+ def account_event_table(self) -> str:
211
+ query = "show parameters like 'event_table' in account"
212
+ results = self._execute_query(query, cursor_class=DictCursor)
213
+ return next((r["value"] for r in results if r["key"] == "EVENT_TABLE"), "")
214
+
215
+ def verify_project_distribution(
216
+ self, expected_distribution: Optional[str] = None
217
+ ) -> bool:
218
+ return ApplicationPackageEntity.verify_project_distribution(
219
+ console=cc,
220
+ package_name=self.package_name,
221
+ package_role=self.package_role,
222
+ package_distribution=self.package_distribution,
223
+ expected_distribution=expected_distribution,
224
+ )
225
+
226
+ def build_bundle(self) -> BundleMap:
227
+ """
228
+ Populates the local deploy root from artifact sources.
229
+ """
230
+ bundle_map = build_bundle(self.project_root, self.deploy_root, self.artifacts)
231
+ compiler = NativeAppCompiler(self.na_project.get_bundle_context())
232
+ compiler.compile_artifacts()
233
+ return bundle_map
234
+
235
+ def sync_deploy_root_with_stage(
236
+ self,
237
+ bundle_map: BundleMap,
238
+ role: str,
239
+ prune: bool,
240
+ recursive: bool,
241
+ stage_fqn: str,
242
+ local_paths_to_sync: List[Path] | None = None,
243
+ print_diff: bool = True,
244
+ ) -> DiffResult:
245
+ return sync_deploy_root_with_stage(
246
+ console=cc,
247
+ deploy_root=self.deploy_root,
248
+ package_name=self.package_name,
249
+ stage_schema=self.stage_schema,
250
+ bundle_map=bundle_map,
251
+ role=role,
252
+ prune=prune,
253
+ recursive=recursive,
254
+ stage_fqn=stage_fqn,
255
+ local_paths_to_sync=local_paths_to_sync,
256
+ print_diff=print_diff,
257
+ )
258
+
259
+ def get_existing_app_info(self) -> Optional[dict]:
260
+ """
261
+ Check for an existing application object by the same name as in project definition, in account.
262
+ It executes a 'show applications like' query and returns the result as single row, if one exists.
263
+ """
264
+ with self.use_role(self.app_role):
265
+ return self.show_specific_object(
266
+ "applications", self.app_name, name_col=NAME_COL
267
+ )
268
+
269
+ def get_existing_app_pkg_info(self) -> Optional[dict]:
270
+ return ApplicationPackageEntity.get_existing_app_pkg_info(
271
+ package_name=self.package_name,
272
+ package_role=self.package_role,
273
+ )
274
+
275
+ def get_objects_owned_by_application(self) -> List[ApplicationOwnedObject]:
276
+ """
277
+ Returns all application objects owned by this application.
278
+ """
279
+ with self.use_role(self.app_role):
280
+ results = self._execute_query(
281
+ f"show objects owned by application {self.app_name}"
282
+ ).fetchall()
283
+ return [{"name": row[1], "type": row[2]} for row in results]
284
+
285
+ def _application_objects_to_str(
286
+ self, application_objects: list[ApplicationOwnedObject]
287
+ ) -> str:
288
+ """
289
+ Returns a list in an "(Object Type) Object Name" format. Database-level and schema-level object names are fully qualified:
290
+ (COMPUTE_POOL) POOL_NAME
291
+ (DATABASE) DB_NAME
292
+ (SCHEMA) DB_NAME.PUBLIC
293
+ ...
294
+ """
295
+ return "\n".join(
296
+ [self._application_object_to_str(obj) for obj in application_objects]
297
+ )
298
+
299
+ def _application_object_to_str(self, obj: ApplicationOwnedObject) -> str:
300
+ return f"({obj['type']}) {obj['name']}"
301
+
302
+ def get_snowsight_url(self) -> str:
303
+ """Returns the URL that can be used to visit this app via Snowsight."""
304
+ name = identifier_for_url(self.app_name)
305
+ with self.use_application_warehouse():
306
+ return make_snowsight_url(self._conn, f"/#/apps/application/{name}")
307
+
308
+ def create_app_package(self) -> None:
309
+ return ApplicationPackageEntity.create_app_package(
310
+ console=cc,
311
+ package_name=self.package_name,
312
+ package_role=self.package_role,
313
+ package_distribution=self.package_distribution,
314
+ )
315
+
316
+ def _apply_package_scripts(self) -> None:
317
+ return ApplicationPackageEntity.apply_package_scripts(
318
+ console=cc,
319
+ package_scripts=self.package_scripts,
320
+ package_warehouse=self.package_warehouse,
321
+ project_root=self.project_root,
322
+ package_role=self.package_role,
323
+ package_name=self.package_name,
324
+ )
325
+
326
+ def execute_package_post_deploy_hooks(self) -> None:
327
+ execute_post_deploy_hooks(
328
+ console=cc,
329
+ project_root=self.project_root,
330
+ post_deploy_hooks=self.package_post_deploy_hooks,
331
+ deployed_object_type="application package",
332
+ database_name=self.package_name,
333
+ )
334
+
335
+ def execute_app_post_deploy_hooks(self) -> None:
336
+ execute_post_deploy_hooks(
337
+ console=cc,
338
+ project_root=self.project_root,
339
+ post_deploy_hooks=self.app_post_deploy_hooks,
340
+ deployed_object_type="application",
341
+ database_name=self.app_name,
342
+ )
343
+
344
+ def deploy(
345
+ self,
346
+ bundle_map: BundleMap,
347
+ prune: bool,
348
+ recursive: bool,
349
+ stage_fqn: Optional[str] = None,
350
+ local_paths_to_sync: List[Path] | None = None,
351
+ validate: bool = True,
352
+ print_diff: bool = True,
353
+ ) -> DiffResult:
354
+ """app deploy process"""
355
+
356
+ # 1. Create an empty application package, if none exists
357
+ self.create_app_package()
358
+
359
+ with self.use_role(self.package_role):
360
+ # 2. now that the application package exists, create shared data
361
+ self._apply_package_scripts()
362
+
363
+ # 3. Upload files from deploy root local folder to the above stage
364
+ stage_fqn = stage_fqn or self.stage_fqn
365
+ diff = self.sync_deploy_root_with_stage(
366
+ bundle_map=bundle_map,
367
+ role=self.package_role,
368
+ prune=prune,
369
+ recursive=recursive,
370
+ stage_fqn=stage_fqn,
371
+ local_paths_to_sync=local_paths_to_sync,
372
+ print_diff=print_diff,
373
+ )
374
+
375
+ # 4. Execute post-deploy hooks
376
+ with self.use_package_warehouse():
377
+ self.execute_package_post_deploy_hooks()
378
+
379
+ if validate:
380
+ self.validate(use_scratch_stage=False)
381
+
382
+ return diff
383
+
384
+ def validate(self, use_scratch_stage: bool = False):
385
+ """Validates Native App setup script SQL."""
386
+ with cc.phase(f"Validating Snowflake Native App setup script."):
387
+ validation_result = self.get_validation_result(use_scratch_stage)
388
+
389
+ # First print warnings, regardless of the outcome of validation
390
+ for warning in validation_result.get("warnings", []):
391
+ cc.warning(_validation_item_to_str(warning))
392
+
393
+ # Then print errors
394
+ for error in validation_result.get("errors", []):
395
+ # Print them as warnings for now since we're going to be
396
+ # revamping CLI output soon
397
+ cc.warning(_validation_item_to_str(error))
398
+
399
+ # Then raise an exception if validation failed
400
+ if validation_result["status"] == "FAIL":
401
+ raise SetupScriptFailedValidation()
402
+
403
+ def get_validation_result(self, use_scratch_stage: bool):
404
+ """Call system$validate_native_app_setup() to validate deployed Native App setup script."""
405
+ stage_fqn = self.stage_fqn
406
+ if use_scratch_stage:
407
+ stage_fqn = self.scratch_stage_fqn
408
+ bundle_map = self.build_bundle()
409
+ self.deploy(
410
+ bundle_map=bundle_map,
411
+ prune=True,
412
+ recursive=True,
413
+ stage_fqn=stage_fqn,
414
+ validate=False,
415
+ print_diff=False,
416
+ )
417
+ prefixed_stage_fqn = StageManager.get_standard_stage_prefix(stage_fqn)
418
+ try:
419
+ cursor = self._execute_query(
420
+ f"call system$validate_native_app_setup('{prefixed_stage_fqn}')"
421
+ )
422
+ except ProgrammingError as err:
423
+ if err.errno == DOES_NOT_EXIST_OR_NOT_AUTHORIZED:
424
+ raise ApplicationPackageDoesNotExistError(self.package_name)
425
+ generic_sql_error_handler(err)
426
+ else:
427
+ if not cursor.rowcount:
428
+ raise SnowflakeSQLExecutionError()
429
+ return json.loads(cursor.fetchone()[0])
430
+ finally:
431
+ if use_scratch_stage:
432
+ cc.step(f"Dropping stage {self.scratch_stage_fqn}.")
433
+ with self.use_role(self.package_role):
434
+ self._execute_query(
435
+ f"drop stage if exists {self.scratch_stage_fqn}"
436
+ )
437
+
438
+ def get_events( # type: ignore [return]
439
+ self,
440
+ since: str | datetime | None = None,
441
+ until: str | datetime | None = None,
442
+ record_types: list[str] | None = None,
443
+ scopes: list[str] | None = None,
444
+ consumer_org: str = "",
445
+ consumer_account: str = "",
446
+ consumer_app_hash: str = "",
447
+ first: int = -1,
448
+ last: int = -1,
449
+ ) -> list[dict]:
450
+ record_types = record_types or []
451
+ scopes = scopes or []
452
+
453
+ if first >= 0 and last >= 0:
454
+ raise ValueError("first and last cannot be used together")
455
+
456
+ if not self.account_event_table:
457
+ raise NoEventTableForAccount()
458
+
459
+ # resource_attributes uses the unquoted/uppercase app and package name
460
+ app_name = unquote_identifier(self.app_name)
461
+ package_name = unquote_identifier(self.package_name)
462
+ org_name = unquote_identifier(consumer_org)
463
+ account_name = unquote_identifier(consumer_account)
464
+
465
+ # Filter on record attributes
466
+ if consumer_org and consumer_account:
467
+ # Look for events shared from a consumer account
468
+ app_clause = (
469
+ f"resource_attributes:\"snow.application.package.name\" = '{package_name}' "
470
+ f"and resource_attributes:\"snow.application.consumer.organization\" = '{org_name}' "
471
+ f"and resource_attributes:\"snow.application.consumer.name\" = '{account_name}'"
472
+ )
473
+ if consumer_app_hash:
474
+ # If the user has specified a hash of a specific app installation
475
+ # in the consumer account, filter events to that installation only
476
+ app_clause += f" and resource_attributes:\"snow.database.hash\" = '{consumer_app_hash.lower()}'"
477
+ else:
478
+ # Otherwise look for events from an app installed in the same account as the package
479
+ app_clause = f"resource_attributes:\"snow.database.name\" = '{app_name}'"
480
+
481
+ # Filter on event time
482
+ if isinstance(since, datetime):
483
+ since_clause = f"and timestamp >= '{since}'"
484
+ elif isinstance(since, str) and since:
485
+ since_clause = f"and timestamp >= sysdate() - interval '{since}'"
486
+ else:
487
+ since_clause = ""
488
+ if isinstance(until, datetime):
489
+ until_clause = f"and timestamp <= '{until}'"
490
+ elif isinstance(until, str) and until:
491
+ until_clause = f"and timestamp <= sysdate() - interval '{until}'"
492
+ else:
493
+ until_clause = ""
494
+
495
+ # Filter on event type (log, span, span_event)
496
+ type_in_values = ",".join(f"'{v}'" for v in record_types)
497
+ types_clause = (
498
+ f"and record_type in ({type_in_values})" if type_in_values else ""
499
+ )
500
+
501
+ # Filter on event scope (e.g. the logger name)
502
+ scope_in_values = ",".join(f"'{v}'" for v in scopes)
503
+ scopes_clause = (
504
+ f"and scope:name in ({scope_in_values})" if scope_in_values else ""
505
+ )
506
+
507
+ # Limit event count
508
+ first_clause = f"limit {first}" if first >= 0 else ""
509
+ last_clause = f"limit {last}" if last >= 0 else ""
510
+
511
+ query = dedent(
512
+ f"""\
513
+ select * from (
514
+ select timestamp, value::varchar value
515
+ from {self.account_event_table}
516
+ where ({app_clause})
517
+ {since_clause}
518
+ {until_clause}
519
+ {types_clause}
520
+ {scopes_clause}
521
+ order by timestamp desc
522
+ {last_clause}
523
+ ) order by timestamp asc
524
+ {first_clause}
525
+ """
526
+ )
527
+ try:
528
+ return self._execute_query(query, cursor_class=DictCursor).fetchall()
529
+ except ProgrammingError as err:
530
+ generic_sql_error_handler(err)
531
+
532
+ def stream_events(
533
+ self,
534
+ interval_seconds: int,
535
+ since: str | datetime | None = None,
536
+ record_types: list[str] | None = None,
537
+ scopes: list[str] | None = None,
538
+ consumer_org: str = "",
539
+ consumer_account: str = "",
540
+ consumer_app_hash: str = "",
541
+ last: int = -1,
542
+ ) -> Generator[dict, None, None]:
543
+ try:
544
+ events = self.get_events(
545
+ since=since,
546
+ record_types=record_types,
547
+ scopes=scopes,
548
+ consumer_org=consumer_org,
549
+ consumer_account=consumer_account,
550
+ consumer_app_hash=consumer_app_hash,
551
+ last=last,
552
+ )
553
+ yield from events # Yield the initial batch of events
554
+ last_event_time = events[-1]["TIMESTAMP"] if events else None
555
+
556
+ while True: # Then infinite poll for new events
557
+ time.sleep(interval_seconds)
558
+ previous_events = events
559
+ events = self.get_events(
560
+ since=last_event_time,
561
+ record_types=record_types,
562
+ scopes=scopes,
563
+ consumer_org=consumer_org,
564
+ consumer_account=consumer_account,
565
+ consumer_app_hash=consumer_app_hash,
566
+ )
567
+ if not events:
568
+ continue
569
+
570
+ yield from _new_events_only(previous_events, events)
571
+ last_event_time = events[-1]["TIMESTAMP"]
572
+ except KeyboardInterrupt:
573
+ return
574
+
575
+
576
+ def _new_events_only(previous_events: list[dict], new_events: list[dict]) -> list[dict]:
577
+ # The timestamp that overlaps between both sets of events
578
+ overlap_time = new_events[0]["TIMESTAMP"]
579
+
580
+ # Remove all the events from the new result set
581
+ # if they were already printed. We iterate and remove
582
+ # instead of filtering in order to handle duplicates
583
+ # (i.e. if an event is present 3 times in new_events
584
+ # but only once in previous_events, it should still
585
+ # appear twice in new_events at the end
586
+ new_events = new_events.copy()
587
+ for event in reversed(previous_events):
588
+ if event["TIMESTAMP"] < overlap_time:
589
+ break
590
+ # No need to handle ValueError here since we know
591
+ # that events that pass the above if check will
592
+ # either be in both lists or in new_events only
593
+ new_events.remove(event)
594
+ return new_events
595
+
596
+
597
+ def _validation_item_to_str(item: dict[str, str | int]):
598
+ s = item["message"]
599
+ if item["errorCode"]:
600
+ s = f"{s} (error code {item['errorCode']})"
601
+ return s
@@ -12,13 +12,13 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ from snowflake.cli._plugins.nativeapp import commands
15
16
  from snowflake.cli.api.plugins.command import (
16
17
  SNOWCLI_ROOT_COMMAND_PATH,
17
18
  CommandSpec,
18
19
  CommandType,
19
20
  plugin_hook_impl,
20
21
  )
21
- from snowflake.cli.plugins.connection import commands
22
22
 
23
23
 
24
24
  @plugin_hook_impl