snowflake-cli-labs 3.0.0rc2__py3-none-any.whl → 3.0.0rc4__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 (88) hide show
  1. snowflake/cli/__about__.py +1 -1
  2. snowflake/cli/_app/commands_registration/builtin_plugins.py +2 -0
  3. snowflake/cli/_app/secret.py +9 -0
  4. snowflake/cli/_app/snow_connector.py +39 -27
  5. snowflake/cli/_app/telemetry.py +28 -0
  6. snowflake/cli/_plugins/connection/commands.py +9 -4
  7. snowflake/cli/_plugins/git/manager.py +53 -7
  8. snowflake/cli/_plugins/helpers/commands.py +61 -0
  9. snowflake/cli/{api/project/schemas/snowpark/__init__.py → _plugins/helpers/plugin_spec.py} +17 -0
  10. snowflake/cli/_plugins/nativeapp/artifacts.py +10 -9
  11. snowflake/cli/_plugins/nativeapp/bundle_context.py +1 -1
  12. snowflake/cli/_plugins/nativeapp/codegen/artifact_processor.py +1 -1
  13. snowflake/cli/_plugins/nativeapp/codegen/compiler.py +6 -1
  14. snowflake/cli/_plugins/nativeapp/codegen/setup/native_app_setup_processor.py +1 -1
  15. snowflake/cli/_plugins/nativeapp/codegen/snowpark/extension_function_utils.py +1 -1
  16. snowflake/cli/_plugins/nativeapp/codegen/snowpark/models.py +1 -1
  17. snowflake/cli/_plugins/nativeapp/codegen/snowpark/python_processor.py +5 -1
  18. snowflake/cli/_plugins/nativeapp/codegen/templates/templates_processor.py +4 -1
  19. snowflake/cli/_plugins/nativeapp/commands.py +87 -96
  20. snowflake/cli/_plugins/nativeapp/entities/__init__.py +0 -0
  21. snowflake/cli/{api/entities/application_entity.py → _plugins/nativeapp/entities/application.py} +264 -83
  22. snowflake/cli/_plugins/nativeapp/entities/application_package.py +1392 -0
  23. snowflake/cli/_plugins/nativeapp/exceptions.py +0 -9
  24. snowflake/cli/_plugins/nativeapp/manager.py +69 -185
  25. snowflake/cli/_plugins/nativeapp/policy.py +3 -0
  26. snowflake/cli/_plugins/nativeapp/project_model.py +2 -2
  27. snowflake/cli/_plugins/nativeapp/run_processor.py +17 -20
  28. snowflake/cli/_plugins/nativeapp/same_account_install_method.py +0 -4
  29. snowflake/cli/_plugins/nativeapp/teardown_processor.py +4 -6
  30. snowflake/cli/_plugins/nativeapp/v2_conversions/v2_to_v1_decorator.py +122 -88
  31. snowflake/cli/_plugins/nativeapp/version/commands.py +7 -39
  32. snowflake/cli/_plugins/nativeapp/version/version_processor.py +46 -312
  33. snowflake/cli/_plugins/object/manager.py +36 -15
  34. snowflake/cli/_plugins/snowpark/commands.py +4 -4
  35. snowflake/cli/_plugins/snowpark/common.py +4 -4
  36. snowflake/cli/{api/entities → _plugins/snowpark}/snowpark_entity.py +2 -2
  37. snowflake/cli/{api/project/schemas/entities/snowpark_entity.py → _plugins/snowpark/snowpark_entity_model.py} +3 -6
  38. snowflake/cli/_plugins/snowpark/snowpark_project_paths.py +1 -1
  39. snowflake/cli/_plugins/stage/manager.py +9 -4
  40. snowflake/cli/_plugins/streamlit/commands.py +15 -3
  41. snowflake/cli/_plugins/streamlit/manager.py +12 -4
  42. snowflake/cli/{api/entities → _plugins/streamlit}/streamlit_entity.py +2 -2
  43. snowflake/cli/{api/project/schemas/entities → _plugins/streamlit}/streamlit_entity_model.py +5 -12
  44. snowflake/cli/_plugins/workspace/commands.py +116 -36
  45. snowflake/cli/_plugins/workspace/plugin_spec.py +1 -1
  46. snowflake/cli/api/cli_global_context.py +7 -0
  47. snowflake/cli/api/commands/decorators.py +14 -0
  48. snowflake/cli/api/commands/flags.py +18 -0
  49. snowflake/cli/api/commands/snow_typer.py +1 -1
  50. snowflake/cli/api/config.py +25 -6
  51. snowflake/cli/api/connections.py +3 -1
  52. snowflake/cli/api/entities/common.py +4 -0
  53. snowflake/cli/api/entities/utils.py +3 -14
  54. snowflake/cli/api/errno.py +1 -0
  55. snowflake/cli/api/identifiers.py +4 -3
  56. snowflake/cli/api/metrics.py +92 -0
  57. snowflake/cli/api/project/definition_conversion.py +61 -18
  58. snowflake/cli/api/project/schemas/entities/common.py +17 -4
  59. snowflake/cli/api/project/schemas/entities/entities.py +11 -10
  60. snowflake/cli/api/project/schemas/project_definition.py +5 -7
  61. snowflake/cli/api/project/schemas/v1/__init__.py +0 -0
  62. snowflake/cli/api/project/schemas/{identifier_model.py → v1/identifier_model.py} +0 -7
  63. snowflake/cli/api/project/schemas/v1/native_app/__init__.py +0 -0
  64. snowflake/cli/api/project/schemas/{native_app → v1/native_app}/native_app.py +4 -4
  65. snowflake/cli/api/project/schemas/v1/snowpark/__init__.py +0 -0
  66. snowflake/cli/api/project/schemas/{snowpark → v1/snowpark}/callable.py +2 -2
  67. snowflake/cli/api/project/schemas/{snowpark → v1/snowpark}/snowpark.py +2 -2
  68. snowflake/cli/api/project/schemas/v1/streamlit/__init__.py +0 -0
  69. snowflake/cli/api/project/schemas/{streamlit → v1/streamlit}/streamlit.py +2 -1
  70. snowflake/cli/api/rendering/sql_templates.py +6 -0
  71. snowflake/cli/api/rest_api.py +11 -5
  72. snowflake/cli/api/sql_execution.py +6 -15
  73. snowflake/cli/api/utils/definition_rendering.py +24 -4
  74. {snowflake_cli_labs-3.0.0rc2.dist-info → snowflake_cli_labs-3.0.0rc4.dist-info}/METADATA +9 -7
  75. {snowflake_cli_labs-3.0.0rc2.dist-info → snowflake_cli_labs-3.0.0rc4.dist-info}/RECORD +83 -79
  76. snowflake/cli/_plugins/nativeapp/init.py +0 -345
  77. snowflake/cli/api/entities/application_package_entity.py +0 -658
  78. snowflake/cli/api/project/schemas/entities/application_entity_model.py +0 -56
  79. snowflake/cli/api/project/schemas/entities/application_package_entity_model.py +0 -94
  80. snowflake/cli/api/project/schemas/streamlit/__init__.py +0 -13
  81. /snowflake/cli/{api/project/schemas/native_app → _plugins/helpers}/__init__.py +0 -0
  82. /snowflake/cli/api/project/schemas/{native_app → v1/native_app}/application.py +0 -0
  83. /snowflake/cli/api/project/schemas/{native_app → v1/native_app}/package.py +0 -0
  84. /snowflake/cli/api/project/schemas/{native_app → v1/native_app}/path_mapping.py +0 -0
  85. /snowflake/cli/api/project/schemas/{snowpark → v1/snowpark}/argument.py +0 -0
  86. {snowflake_cli_labs-3.0.0rc2.dist-info → snowflake_cli_labs-3.0.0rc4.dist-info}/WHEEL +0 -0
  87. {snowflake_cli_labs-3.0.0rc2.dist-info → snowflake_cli_labs-3.0.0rc4.dist-info}/entry_points.txt +0 -0
  88. {snowflake_cli_labs-3.0.0rc2.dist-info → snowflake_cli_labs-3.0.0rc4.dist-info}/licenses/LICENSE +0 -0
@@ -1,658 +0,0 @@
1
- import json
2
- from contextlib import contextmanager
3
- from pathlib import Path
4
- from textwrap import dedent
5
- from typing import Callable, List, Optional
6
-
7
- import typer
8
- from click import ClickException
9
- from snowflake.cli._plugins.nativeapp.artifacts import build_bundle
10
- from snowflake.cli._plugins.nativeapp.bundle_context import BundleContext
11
- from snowflake.cli._plugins.nativeapp.codegen.compiler import NativeAppCompiler
12
- from snowflake.cli._plugins.nativeapp.constants import (
13
- ALLOWED_SPECIAL_COMMENTS,
14
- COMMENT_COL,
15
- EXTERNAL_DISTRIBUTION,
16
- INTERNAL_DISTRIBUTION,
17
- NAME_COL,
18
- OWNER_COL,
19
- SPECIAL_COMMENT,
20
- )
21
- from snowflake.cli._plugins.nativeapp.exceptions import (
22
- ApplicationPackageAlreadyExistsError,
23
- ApplicationPackageDoesNotExistError,
24
- CouldNotDropApplicationPackageWithVersions,
25
- SetupScriptFailedValidation,
26
- )
27
- from snowflake.cli._plugins.nativeapp.utils import (
28
- needs_confirmation,
29
- )
30
- from snowflake.cli._plugins.stage.diff import DiffResult
31
- from snowflake.cli._plugins.stage.manager import StageManager
32
- from snowflake.cli._plugins.workspace.action_context import ActionContext
33
- from snowflake.cli.api.console.abc import AbstractConsole
34
- from snowflake.cli.api.entities.common import EntityBase, get_sql_executor
35
- from snowflake.cli.api.entities.utils import (
36
- drop_generic_object,
37
- ensure_correct_owner,
38
- execute_post_deploy_hooks,
39
- generic_sql_error_handler,
40
- render_script_templates,
41
- sync_deploy_root_with_stage,
42
- validation_item_to_str,
43
- )
44
- from snowflake.cli.api.errno import (
45
- DOES_NOT_EXIST_OR_NOT_AUTHORIZED,
46
- )
47
- from snowflake.cli.api.exceptions import SnowflakeSQLExecutionError
48
- from snowflake.cli.api.project.schemas.entities.application_package_entity_model import (
49
- ApplicationPackageEntityModel,
50
- )
51
- from snowflake.cli.api.project.schemas.entities.common import PostDeployHook
52
- from snowflake.cli.api.project.schemas.native_app.path_mapping import PathMapping
53
- from snowflake.cli.api.project.util import extract_schema
54
- from snowflake.cli.api.rendering.jinja import (
55
- get_basic_jinja_env,
56
- )
57
- from snowflake.connector import ProgrammingError
58
- from snowflake.connector.cursor import DictCursor
59
-
60
-
61
- class ApplicationPackageEntity(EntityBase[ApplicationPackageEntityModel]):
62
- """
63
- A Native App application package.
64
- """
65
-
66
- def action_bundle(self, ctx: ActionContext, *args, **kwargs):
67
- model = self._entity_model
68
- return self.bundle(
69
- project_root=ctx.project_root,
70
- deploy_root=Path(model.deploy_root),
71
- bundle_root=Path(model.bundle_root),
72
- generated_root=Path(model.generated_root),
73
- package_name=model.identifier,
74
- artifacts=model.artifacts,
75
- )
76
-
77
- def action_deploy(
78
- self,
79
- ctx: ActionContext,
80
- prune: bool,
81
- recursive: bool,
82
- paths: List[Path],
83
- validate: bool,
84
- stage_fqn: Optional[str] = None,
85
- *args,
86
- **kwargs,
87
- ):
88
- model = self._entity_model
89
- package_name = model.fqn.identifier
90
- return self.deploy(
91
- console=ctx.console,
92
- project_root=ctx.project_root,
93
- deploy_root=Path(model.deploy_root),
94
- bundle_root=Path(model.bundle_root),
95
- generated_root=Path(model.generated_root),
96
- artifacts=model.artifacts,
97
- package_name=package_name,
98
- package_role=(model.meta and model.meta.role) or ctx.default_role,
99
- package_distribution=model.distribution,
100
- prune=prune,
101
- recursive=recursive,
102
- paths=paths,
103
- print_diff=True,
104
- validate=validate,
105
- stage_fqn=stage_fqn or f"{package_name}.{model.stage}",
106
- package_warehouse=(
107
- (model.meta and model.meta.warehouse) or ctx.default_warehouse
108
- ),
109
- post_deploy_hooks=model.meta and model.meta.post_deploy,
110
- package_scripts=[], # Package scripts are not supported in PDFv2
111
- )
112
-
113
- def action_drop(self, ctx: ActionContext, force_drop: bool, *args, **kwargs):
114
- model = self._entity_model
115
- package_name = model.fqn.identifier
116
- if model.meta and model.meta.role:
117
- package_role = model.meta.role
118
- else:
119
- package_role = ctx.default_role
120
-
121
- self.drop(
122
- console=ctx.console,
123
- package_name=package_name,
124
- package_role=package_role,
125
- force_drop=force_drop,
126
- )
127
-
128
- def action_validate(self, ctx: ActionContext, *args, **kwargs):
129
- model = self._entity_model
130
- package_name = model.fqn.identifier
131
- stage_fqn = f"{package_name}.{model.stage}"
132
- if model.meta and model.meta.role:
133
- package_role = model.meta.role
134
- else:
135
- package_role = ctx.default_role
136
-
137
- def deploy_to_scratch_stage_fn():
138
- self.action_deploy(
139
- ctx=ctx,
140
- prune=True,
141
- recursive=True,
142
- paths=[],
143
- validate=False,
144
- stage_fqn=model.scratch_stage,
145
- )
146
-
147
- self.validate_setup_script(
148
- console=ctx.console,
149
- package_name=package_name,
150
- package_role=package_role,
151
- stage_fqn=stage_fqn,
152
- use_scratch_stage=True,
153
- scratch_stage_fqn=model.scratch_stage,
154
- deploy_to_scratch_stage_fn=deploy_to_scratch_stage_fn,
155
- )
156
- ctx.console.message("Setup script is valid")
157
-
158
- @staticmethod
159
- def bundle(
160
- project_root: Path,
161
- deploy_root: Path,
162
- bundle_root: Path,
163
- generated_root: Path,
164
- artifacts: list[PathMapping],
165
- package_name: str,
166
- ):
167
- bundle_map = build_bundle(project_root, deploy_root, artifacts)
168
- bundle_context = BundleContext(
169
- package_name=package_name,
170
- artifacts=artifacts,
171
- project_root=project_root,
172
- bundle_root=bundle_root,
173
- deploy_root=deploy_root,
174
- generated_root=generated_root,
175
- )
176
- compiler = NativeAppCompiler(bundle_context)
177
- compiler.compile_artifacts()
178
- return bundle_map
179
-
180
- @classmethod
181
- def deploy(
182
- cls,
183
- console: AbstractConsole,
184
- project_root: Path,
185
- deploy_root: Path,
186
- bundle_root: Path,
187
- generated_root: Path,
188
- artifacts: list[PathMapping],
189
- package_name: str,
190
- package_role: str,
191
- package_distribution: str,
192
- package_warehouse: str | None,
193
- prune: bool,
194
- recursive: bool,
195
- paths: List[Path],
196
- print_diff: bool,
197
- validate: bool,
198
- stage_fqn: str,
199
- post_deploy_hooks: list[PostDeployHook] | None,
200
- package_scripts: List[str],
201
- ) -> DiffResult:
202
- # 1. Create a bundle
203
- bundle_map = cls.bundle(
204
- project_root=project_root,
205
- deploy_root=deploy_root,
206
- bundle_root=bundle_root,
207
- generated_root=generated_root,
208
- artifacts=artifacts,
209
- package_name=package_name,
210
- )
211
-
212
- # 2. Create an empty application package, if none exists
213
- cls.create_app_package(
214
- console=console,
215
- package_name=package_name,
216
- package_role=package_role,
217
- package_distribution=package_distribution,
218
- )
219
-
220
- with get_sql_executor().use_role(package_role):
221
- if package_scripts:
222
- cls.apply_package_scripts(
223
- console=console,
224
- package_scripts=package_scripts,
225
- package_warehouse=package_warehouse,
226
- project_root=project_root,
227
- package_role=package_role,
228
- package_name=package_name,
229
- )
230
-
231
- # 3. Upload files from deploy root local folder to the above stage
232
- stage_schema = extract_schema(stage_fqn)
233
- diff = sync_deploy_root_with_stage(
234
- console=console,
235
- deploy_root=deploy_root,
236
- package_name=package_name,
237
- stage_schema=stage_schema,
238
- bundle_map=bundle_map,
239
- role=package_role,
240
- prune=prune,
241
- recursive=recursive,
242
- stage_fqn=stage_fqn,
243
- local_paths_to_sync=paths,
244
- print_diff=print_diff,
245
- )
246
-
247
- if post_deploy_hooks:
248
- cls.execute_post_deploy_hooks(
249
- console=console,
250
- project_root=project_root,
251
- post_deploy_hooks=post_deploy_hooks,
252
- package_name=package_name,
253
- package_warehouse=package_warehouse,
254
- )
255
-
256
- if validate:
257
- cls.validate_setup_script(
258
- console=console,
259
- package_name=package_name,
260
- package_role=package_role,
261
- stage_fqn=stage_fqn,
262
- use_scratch_stage=False,
263
- scratch_stage_fqn="",
264
- deploy_to_scratch_stage_fn=lambda *args: None,
265
- )
266
-
267
- return diff
268
-
269
- @staticmethod
270
- def get_existing_app_pkg_info(
271
- package_name: str,
272
- package_role: str,
273
- ) -> Optional[dict]:
274
- """
275
- Check for an existing application package by the same name as in project definition, in account.
276
- It executes a 'show application packages like' query and returns the result as single row, if one exists.
277
- """
278
- sql_executor = get_sql_executor()
279
- with sql_executor.use_role(package_role):
280
- return sql_executor.show_specific_object(
281
- "application packages", package_name, name_col=NAME_COL
282
- )
283
-
284
- @staticmethod
285
- def get_app_pkg_distribution_in_snowflake(
286
- package_name: str,
287
- package_role: str,
288
- ) -> str:
289
- """
290
- Returns the 'distribution' attribute of a 'describe application package' SQL query, in lowercase.
291
- """
292
- sql_executor = get_sql_executor()
293
- with sql_executor.use_role(package_role):
294
- try:
295
- desc_cursor = sql_executor.execute_query(
296
- f"describe application package {package_name}"
297
- )
298
- except ProgrammingError as err:
299
- generic_sql_error_handler(err)
300
-
301
- if desc_cursor.rowcount is None or desc_cursor.rowcount == 0:
302
- raise SnowflakeSQLExecutionError()
303
- else:
304
- for row in desc_cursor:
305
- if row[0].lower() == "distribution":
306
- return row[1].lower()
307
- raise ProgrammingError(
308
- msg=dedent(
309
- f"""\
310
- Could not find the 'distribution' attribute for application package {package_name} in the output of SQL query:
311
- 'describe application package {package_name}'
312
- """
313
- )
314
- )
315
-
316
- @classmethod
317
- def verify_project_distribution(
318
- cls,
319
- console: AbstractConsole,
320
- package_name: str,
321
- package_role: str,
322
- package_distribution: str,
323
- expected_distribution: Optional[str] = None,
324
- ) -> bool:
325
- """
326
- Returns true if the 'distribution' attribute of an existing application package in snowflake
327
- is the same as the the attribute specified in project definition file.
328
- """
329
- actual_distribution = (
330
- expected_distribution
331
- if expected_distribution
332
- else cls.get_app_pkg_distribution_in_snowflake(
333
- package_name=package_name,
334
- package_role=package_role,
335
- )
336
- )
337
- project_def_distribution = package_distribution.lower()
338
- if actual_distribution != project_def_distribution:
339
- console.warning(
340
- dedent(
341
- f"""\
342
- Application package {package_name} in your Snowflake account has distribution property {actual_distribution},
343
- which does not match the value specified in project definition file: {project_def_distribution}.
344
- """
345
- )
346
- )
347
- return False
348
- return True
349
-
350
- @staticmethod
351
- @contextmanager
352
- def use_package_warehouse(
353
- package_warehouse: Optional[str],
354
- ):
355
- if package_warehouse:
356
- with get_sql_executor().use_warehouse(package_warehouse):
357
- yield
358
- else:
359
- raise ClickException(
360
- dedent(
361
- f"""\
362
- Application package warehouse cannot be empty.
363
- Please provide a value for it in your connection information or your project definition file.
364
- """
365
- )
366
- )
367
-
368
- @classmethod
369
- def apply_package_scripts(
370
- cls,
371
- console: AbstractConsole,
372
- package_scripts: List[str],
373
- package_warehouse: Optional[str],
374
- project_root: Path,
375
- package_role: str,
376
- package_name: str,
377
- ) -> None:
378
- """
379
- Assuming the application package exists and we are using the correct role,
380
- applies all package scripts in-order to the application package.
381
- """
382
-
383
- if package_scripts:
384
- console.warning(
385
- "WARNING: native_app.package.scripts is deprecated. Please migrate to using native_app.package.post_deploy."
386
- )
387
-
388
- queued_queries = render_script_templates(
389
- project_root,
390
- dict(package_name=package_name),
391
- package_scripts,
392
- get_basic_jinja_env(),
393
- )
394
-
395
- # once we're sure all the templates expanded correctly, execute all of them
396
- with cls.use_package_warehouse(
397
- package_warehouse=package_warehouse,
398
- ):
399
- try:
400
- for i, queries in enumerate(queued_queries):
401
- console.step(f"Applying package script: {package_scripts[i]}")
402
- get_sql_executor().execute_queries(queries)
403
- except ProgrammingError as err:
404
- generic_sql_error_handler(
405
- err, role=package_role, warehouse=package_warehouse
406
- )
407
-
408
- @classmethod
409
- def create_app_package(
410
- cls,
411
- console: AbstractConsole,
412
- package_name: str,
413
- package_role: str,
414
- package_distribution: str,
415
- ) -> None:
416
- """
417
- Creates the application package with our up-to-date stage if none exists.
418
- """
419
-
420
- # 1. Check for existing existing application package
421
- show_obj_row = cls.get_existing_app_pkg_info(
422
- package_name=package_name,
423
- package_role=package_role,
424
- )
425
-
426
- if show_obj_row:
427
- # 1. Check for the right owner role
428
- ensure_correct_owner(
429
- row=show_obj_row, role=package_role, obj_name=package_name
430
- )
431
-
432
- # 2. Check distribution of the existing application package
433
- actual_distribution = cls.get_app_pkg_distribution_in_snowflake(
434
- package_name=package_name,
435
- package_role=package_role,
436
- )
437
- if not cls.verify_project_distribution(
438
- console=console,
439
- package_name=package_name,
440
- package_role=package_role,
441
- package_distribution=package_distribution,
442
- expected_distribution=actual_distribution,
443
- ):
444
- console.warning(
445
- f"Continuing to execute `snow app run` on application package {package_name} with distribution '{actual_distribution}'."
446
- )
447
-
448
- # 3. If actual_distribution is external, skip comment check
449
- if actual_distribution == INTERNAL_DISTRIBUTION:
450
- row_comment = show_obj_row[COMMENT_COL]
451
-
452
- if row_comment not in ALLOWED_SPECIAL_COMMENTS:
453
- raise ApplicationPackageAlreadyExistsError(package_name)
454
-
455
- return
456
-
457
- # If no application package pre-exists, create an application package, with the specified distribution in the project definition file.
458
- sql_executor = get_sql_executor()
459
- with sql_executor.use_role(package_role):
460
- console.step(f"Creating new application package {package_name} in account.")
461
- sql_executor.execute_query(
462
- dedent(
463
- f"""\
464
- create application package {package_name}
465
- comment = {SPECIAL_COMMENT}
466
- distribution = {package_distribution}
467
- """
468
- )
469
- )
470
-
471
- @classmethod
472
- def execute_post_deploy_hooks(
473
- cls,
474
- console: AbstractConsole,
475
- project_root: Path,
476
- post_deploy_hooks: Optional[List[PostDeployHook]],
477
- package_name: str,
478
- package_warehouse: Optional[str],
479
- ):
480
- with cls.use_package_warehouse(package_warehouse):
481
- execute_post_deploy_hooks(
482
- console=console,
483
- project_root=project_root,
484
- post_deploy_hooks=post_deploy_hooks,
485
- deployed_object_type="application package",
486
- database_name=package_name,
487
- )
488
-
489
- @classmethod
490
- def validate_setup_script(
491
- cls,
492
- console: AbstractConsole,
493
- package_name: str,
494
- package_role: str,
495
- stage_fqn: str,
496
- use_scratch_stage: bool,
497
- scratch_stage_fqn: str,
498
- deploy_to_scratch_stage_fn: Callable,
499
- ):
500
- """Validates Native App setup script SQL."""
501
- with console.phase(f"Validating Snowflake Native App setup script."):
502
- validation_result = cls.get_validation_result(
503
- console=console,
504
- package_name=package_name,
505
- package_role=package_role,
506
- stage_fqn=stage_fqn,
507
- use_scratch_stage=use_scratch_stage,
508
- scratch_stage_fqn=scratch_stage_fqn,
509
- deploy_to_scratch_stage_fn=deploy_to_scratch_stage_fn,
510
- )
511
-
512
- # First print warnings, regardless of the outcome of validation
513
- for warning in validation_result.get("warnings", []):
514
- console.warning(validation_item_to_str(warning))
515
-
516
- # Then print errors
517
- for error in validation_result.get("errors", []):
518
- # Print them as warnings for now since we're going to be
519
- # revamping CLI output soon
520
- console.warning(validation_item_to_str(error))
521
-
522
- # Then raise an exception if validation failed
523
- if validation_result["status"] == "FAIL":
524
- raise SetupScriptFailedValidation()
525
-
526
- @staticmethod
527
- def get_validation_result(
528
- console: AbstractConsole,
529
- package_name: str,
530
- package_role: str,
531
- stage_fqn: str,
532
- use_scratch_stage: bool,
533
- scratch_stage_fqn: str,
534
- deploy_to_scratch_stage_fn: Callable,
535
- ):
536
- """Call system$validate_native_app_setup() to validate deployed Native App setup script."""
537
- if use_scratch_stage:
538
- stage_fqn = scratch_stage_fqn
539
- deploy_to_scratch_stage_fn()
540
- prefixed_stage_fqn = StageManager.get_standard_stage_prefix(stage_fqn)
541
- sql_executor = get_sql_executor()
542
- try:
543
- cursor = sql_executor.execute_query(
544
- f"call system$validate_native_app_setup('{prefixed_stage_fqn}')"
545
- )
546
- except ProgrammingError as err:
547
- if err.errno == DOES_NOT_EXIST_OR_NOT_AUTHORIZED:
548
- raise ApplicationPackageDoesNotExistError(package_name)
549
- generic_sql_error_handler(err)
550
- else:
551
- if not cursor.rowcount:
552
- raise SnowflakeSQLExecutionError()
553
- return json.loads(cursor.fetchone()[0])
554
- finally:
555
- if use_scratch_stage:
556
- console.step(f"Dropping stage {scratch_stage_fqn}.")
557
- with sql_executor.use_role(package_role):
558
- sql_executor.execute_query(
559
- f"drop stage if exists {scratch_stage_fqn}"
560
- )
561
-
562
- @classmethod
563
- def drop(
564
- cls,
565
- console: AbstractConsole,
566
- package_name: str,
567
- package_role: str,
568
- force_drop: bool,
569
- ):
570
- sql_executor = get_sql_executor()
571
- needs_confirm = True
572
-
573
- # 1. If existing application package is not found, exit gracefully
574
- show_obj_row = cls.get_existing_app_pkg_info(
575
- package_name=package_name,
576
- package_role=package_role,
577
- )
578
- if show_obj_row is None:
579
- console.warning(
580
- f"Role {package_role} does not own any application package with the name {package_name}, or the application package does not exist."
581
- )
582
- return
583
-
584
- # 2. Check for the right owner
585
- ensure_correct_owner(row=show_obj_row, role=package_role, obj_name=package_name)
586
-
587
- with sql_executor.use_role(package_role):
588
- # 3. Check for versions in the application package
589
- show_versions_query = f"show versions in application package {package_name}"
590
- show_versions_cursor = sql_executor.execute_query(
591
- show_versions_query, cursor_class=DictCursor
592
- )
593
- if show_versions_cursor.rowcount is None:
594
- raise SnowflakeSQLExecutionError(show_versions_query)
595
-
596
- if show_versions_cursor.rowcount > 0:
597
- # allow dropping a package with versions when --force is set
598
- if not force_drop:
599
- raise CouldNotDropApplicationPackageWithVersions(
600
- "Drop versions first, or use --force to override."
601
- )
602
-
603
- # 4. Check distribution of the existing application package
604
- actual_distribution = cls.get_app_pkg_distribution_in_snowflake(
605
- package_name=package_name,
606
- package_role=package_role,
607
- )
608
- if not cls.verify_project_distribution(
609
- console=console,
610
- package_name=package_name,
611
- package_role=package_role,
612
- package_distribution=actual_distribution,
613
- ):
614
- console.warning(
615
- f"Dropping application package {package_name} with distribution '{actual_distribution}'."
616
- )
617
-
618
- # 5. If distribution is internal, check if created by the Snowflake CLI
619
- row_comment = show_obj_row[COMMENT_COL]
620
- if actual_distribution == INTERNAL_DISTRIBUTION:
621
- if row_comment in ALLOWED_SPECIAL_COMMENTS:
622
- needs_confirm = False
623
- else:
624
- if needs_confirmation(needs_confirm, force_drop):
625
- console.warning(
626
- f"Application package {package_name} was not created by Snowflake CLI."
627
- )
628
- else:
629
- if needs_confirmation(needs_confirm, force_drop):
630
- console.warning(
631
- f"Application package {package_name} in your Snowflake account has distribution property '{EXTERNAL_DISTRIBUTION}' and could be associated with one or more of your listings on Snowflake Marketplace."
632
- )
633
-
634
- if needs_confirmation(needs_confirm, force_drop):
635
- should_drop_object = typer.confirm(
636
- dedent(
637
- f"""\
638
- Application package details:
639
- Name: {package_name}
640
- Created on: {show_obj_row["created_on"]}
641
- Distribution: {actual_distribution}
642
- Owner: {show_obj_row[OWNER_COL]}
643
- Comment: {show_obj_row[COMMENT_COL]}
644
- Are you sure you want to drop it?
645
- """
646
- )
647
- )
648
- if not should_drop_object:
649
- console.message(f"Did not drop application package {package_name}.")
650
- return # The user desires to keep the application package, therefore exit gracefully
651
-
652
- # All validations have passed, drop object
653
- drop_generic_object(
654
- console=console,
655
- object_type="application package",
656
- object_name=package_name,
657
- role=package_role,
658
- )