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.
- snowflake/cli/__about__.py +1 -1
- snowflake/cli/{app → _app}/__main__.py +1 -1
- snowflake/cli/{app → _app}/cli_app.py +12 -12
- snowflake/cli/{app → _app}/commands_registration/builtin_plugins.py +13 -19
- snowflake/cli/{app → _app}/commands_registration/command_plugins_loader.py +9 -9
- snowflake/cli/{app → _app}/commands_registration/commands_registration_with_callbacks.py +4 -4
- snowflake/cli/{app → _app}/commands_registration/exception_logging.py +2 -2
- snowflake/cli/{app → _app}/commands_registration/typer_registration.py +2 -2
- snowflake/cli/{app → _app}/dev/docs/commands_docs_generator.py +30 -12
- snowflake/cli/{app → _app}/dev/docs/generator.py +3 -3
- snowflake/cli/{app → _app}/dev/docs/project_definition_docs_generator.py +4 -4
- snowflake/cli/{app → _app}/dev/docs/templates/usage.rst.jinja2 +14 -4
- snowflake/cli/{app → _app}/main_typer.py +2 -2
- snowflake/cli/{app → _app}/printing.py +2 -2
- snowflake/cli/{app → _app}/snow_connector.py +6 -6
- snowflake/cli/{app → _app}/telemetry.py +4 -5
- snowflake/cli/{plugins → _plugins}/connection/commands.py +22 -5
- snowflake/cli/_plugins/connection/plugin_spec.py +30 -0
- snowflake/cli/{plugins → _plugins}/connection/util.py +16 -0
- snowflake/cli/{plugins → _plugins}/cortex/commands.py +54 -49
- snowflake/cli/{plugins → _plugins}/cortex/constants.py +1 -1
- snowflake/cli/{plugins → _plugins}/cortex/manager.py +5 -5
- snowflake/cli/{plugins → _plugins}/cortex/plugin_spec.py +1 -1
- snowflake/cli/{plugins → _plugins}/git/commands.py +32 -20
- snowflake/cli/{plugins → _plugins}/git/manager.py +6 -5
- snowflake/cli/{plugins → _plugins}/git/plugin_spec.py +1 -1
- snowflake/cli/{plugins → _plugins}/init/commands.py +10 -6
- snowflake/cli/{plugins → _plugins}/init/plugin_spec.py +1 -1
- snowflake/cli/{plugins → _plugins}/nativeapp/artifacts.py +14 -0
- snowflake/cli/_plugins/nativeapp/bundle_context.py +31 -0
- snowflake/cli/{plugins → _plugins}/nativeapp/codegen/artifact_processor.py +3 -3
- snowflake/cli/{plugins → _plugins}/nativeapp/codegen/compiler.py +16 -18
- snowflake/cli/{plugins → _plugins}/nativeapp/codegen/setup/native_app_setup_processor.py +24 -28
- snowflake/cli/{plugins → _plugins}/nativeapp/codegen/snowpark/extension_function_utils.py +4 -4
- snowflake/cli/{plugins → _plugins}/nativeapp/codegen/snowpark/python_processor.py +20 -24
- snowflake/cli/{plugins → _plugins}/nativeapp/commands.py +171 -42
- snowflake/cli/{plugins → _plugins}/nativeapp/common_flags.py +1 -1
- snowflake/cli/{plugins → _plugins}/nativeapp/init.py +1 -1
- snowflake/cli/_plugins/nativeapp/manager.py +601 -0
- snowflake/cli/{plugins/connection → _plugins/nativeapp}/plugin_spec.py +1 -1
- snowflake/cli/{plugins → _plugins}/nativeapp/project_model.py +34 -11
- snowflake/cli/{plugins → _plugins}/nativeapp/run_processor.py +25 -23
- snowflake/cli/{plugins → _plugins}/nativeapp/teardown_processor.py +8 -8
- snowflake/cli/{plugins → _plugins}/nativeapp/v2_conversions/v2_to_v1_decorator.py +47 -28
- snowflake/cli/{plugins → _plugins}/nativeapp/version/commands.py +15 -12
- snowflake/cli/{plugins → _plugins}/nativeapp/version/version_processor.py +22 -20
- snowflake/cli/{plugins → _plugins}/notebook/commands.py +8 -6
- snowflake/cli/{plugins → _plugins}/notebook/manager.py +14 -14
- snowflake/cli/{plugins → _plugins}/notebook/plugin_spec.py +1 -1
- snowflake/cli/{plugins → _plugins}/notebook/types.py +0 -1
- snowflake/cli/{plugins → _plugins}/object/command_aliases.py +6 -5
- snowflake/cli/{plugins → _plugins}/object/commands.py +16 -10
- snowflake/cli/{plugins → _plugins}/object/manager.py +7 -6
- snowflake/cli/{plugins → _plugins}/object/plugin_spec.py +1 -1
- snowflake/cli/_plugins/snowpark/commands.py +510 -0
- snowflake/cli/_plugins/snowpark/common.py +252 -0
- snowflake/cli/{plugins → _plugins}/snowpark/models.py +0 -7
- snowflake/cli/{plugins → _plugins}/snowpark/package/anaconda_packages.py +1 -1
- snowflake/cli/{plugins → _plugins}/snowpark/package/commands.py +13 -74
- snowflake/cli/{plugins → _plugins}/snowpark/package/manager.py +4 -3
- snowflake/cli/{plugins → _plugins}/snowpark/package_utils.py +5 -5
- snowflake/cli/{plugins/nativeapp → _plugins/snowpark}/plugin_spec.py +1 -1
- snowflake/cli/_plugins/snowpark/snowpark_project_paths.py +109 -0
- snowflake/cli/{plugins → _plugins}/snowpark/snowpark_shared.py +0 -36
- snowflake/cli/{plugins → _plugins}/snowpark/zipper.py +16 -8
- snowflake/cli/{plugins → _plugins}/spcs/__init__.py +5 -7
- snowflake/cli/{plugins → _plugins}/spcs/compute_pool/commands.py +29 -28
- snowflake/cli/{plugins → _plugins}/spcs/compute_pool/manager.py +3 -3
- snowflake/cli/{plugins → _plugins}/spcs/image_registry/commands.py +3 -3
- snowflake/cli/{plugins → _plugins}/spcs/image_repository/commands.py +25 -19
- snowflake/cli/{plugins → _plugins}/spcs/image_repository/manager.py +1 -1
- snowflake/cli/{plugins → _plugins}/spcs/plugin_spec.py +1 -1
- snowflake/cli/{plugins → _plugins}/spcs/services/commands.py +66 -32
- snowflake/cli/{plugins → _plugins}/spcs/services/manager.py +43 -5
- snowflake/cli/{plugins → _plugins}/sql/commands.py +19 -15
- snowflake/cli/{plugins → _plugins}/sql/manager.py +1 -1
- snowflake/cli/{plugins → _plugins}/sql/plugin_spec.py +1 -1
- snowflake/cli/{plugins → _plugins}/stage/commands.py +20 -17
- snowflake/cli/{plugins → _plugins}/stage/diff.py +1 -47
- snowflake/cli/{plugins → _plugins}/stage/manager.py +8 -6
- snowflake/cli/{plugins → _plugins}/stage/plugin_spec.py +1 -1
- snowflake/cli/_plugins/stage/utils.py +54 -0
- snowflake/cli/_plugins/streamlit/commands.py +242 -0
- snowflake/cli/{plugins → _plugins}/streamlit/manager.py +47 -70
- snowflake/cli/_plugins/streamlit/plugin_spec.py +30 -0
- snowflake/cli/_plugins/workspace/action_context.py +11 -0
- snowflake/cli/_plugins/workspace/commands.py +113 -0
- snowflake/cli/_plugins/workspace/manager.py +57 -0
- snowflake/cli/{plugins → _plugins}/workspace/plugin_spec.py +1 -1
- snowflake/cli/api/cli_global_context.py +34 -7
- snowflake/cli/api/commands/common.py +25 -0
- snowflake/cli/api/commands/decorators.py +4 -3
- snowflake/cli/api/commands/experimental_behaviour.py +2 -3
- snowflake/cli/api/commands/flags.py +73 -174
- snowflake/cli/api/commands/overrideable_parameter.py +143 -0
- snowflake/cli/api/commands/snow_typer.py +5 -4
- snowflake/cli/api/commands/typer_pre_execute.py +3 -3
- snowflake/cli/api/commands/utils.py +18 -0
- snowflake/cli/api/config.py +1 -1
- snowflake/cli/api/console/abc.py +5 -2
- snowflake/cli/api/entities/application_entity.py +12 -0
- snowflake/cli/api/entities/application_package_entity.py +260 -0
- snowflake/cli/api/entities/common.py +47 -0
- snowflake/cli/api/entities/snowpark_entity.py +29 -0
- snowflake/cli/api/entities/streamlit_entity.py +12 -0
- snowflake/cli/api/entities/utils.py +321 -0
- snowflake/cli/api/exceptions.py +19 -3
- snowflake/cli/api/feature_flags.py +2 -1
- snowflake/cli/api/identifiers.py +41 -9
- snowflake/cli/api/project/definition.py +13 -5
- snowflake/cli/api/project/definition_manager.py +12 -1
- snowflake/cli/api/project/errors.py +16 -1
- snowflake/cli/api/project/project_verification.py +3 -3
- snowflake/cli/api/project/schemas/entities/{application_entity.py → application_entity_model.py} +21 -9
- snowflake/cli/api/project/schemas/entities/{application_package_entity.py → application_package_entity_model.py} +26 -15
- snowflake/cli/api/project/schemas/entities/common.py +80 -6
- snowflake/cli/api/project/schemas/entities/entities.py +38 -8
- snowflake/cli/api/project/schemas/entities/snowpark_entity.py +176 -0
- snowflake/cli/api/project/schemas/entities/streamlit_entity_model.py +73 -0
- snowflake/cli/api/project/schemas/identifier_model.py +10 -1
- snowflake/cli/api/project/schemas/native_app/application.py +8 -9
- snowflake/cli/api/project/schemas/native_app/package.py +7 -1
- snowflake/cli/api/project/schemas/project_definition.py +97 -23
- snowflake/cli/api/project/schemas/updatable_model.py +11 -3
- snowflake/cli/api/project/util.py +23 -6
- snowflake/cli/api/rendering/jinja.py +28 -8
- snowflake/cli/api/rendering/sql_templates.py +41 -12
- snowflake/cli/api/secure_path.py +3 -0
- snowflake/cli/api/sql_execution.py +35 -19
- snowflake/cli/api/utils/definition_rendering.py +14 -2
- {snowflake_cli_labs-2.8.0rc0.dist-info → snowflake_cli_labs-3.0.0rc0.dist-info}/METADATA +12 -12
- snowflake_cli_labs-3.0.0rc0.dist-info/RECORD +234 -0
- snowflake_cli_labs-3.0.0rc0.dist-info/entry_points.txt +2 -0
- snowflake/cli/api/commands/project_initialisation.py +0 -65
- snowflake/cli/app/build_and_push.sh +0 -8
- snowflake/cli/plugins/nativeapp/manager.py +0 -819
- snowflake/cli/plugins/object_stage_deprecated/__init__.py +0 -15
- snowflake/cli/plugins/object_stage_deprecated/commands.py +0 -122
- snowflake/cli/plugins/object_stage_deprecated/plugin_spec.py +0 -32
- snowflake/cli/plugins/snowpark/commands.py +0 -548
- snowflake/cli/plugins/snowpark/common.py +0 -307
- snowflake/cli/plugins/snowpark/manager.py +0 -109
- snowflake/cli/plugins/snowpark/plugin_spec.py +0 -30
- snowflake/cli/plugins/snowpark/snowpark_package_paths.py +0 -65
- snowflake/cli/plugins/spcs/jobs/commands.py +0 -78
- snowflake/cli/plugins/spcs/jobs/manager.py +0 -53
- snowflake/cli/plugins/streamlit/commands.py +0 -186
- snowflake/cli/plugins/streamlit/plugin_spec.py +0 -30
- snowflake/cli/plugins/workspace/commands.py +0 -35
- snowflake/cli/templates/default_snowpark/.gitignore +0 -4
- snowflake/cli/templates/default_snowpark/app/__init__.py +0 -0
- snowflake/cli/templates/default_snowpark/app/common.py +0 -2
- snowflake/cli/templates/default_snowpark/app/functions.py +0 -15
- snowflake/cli/templates/default_snowpark/app/procedures.py +0 -22
- snowflake/cli/templates/default_snowpark/requirements.txt +0 -1
- snowflake/cli/templates/default_snowpark/snowflake.yml +0 -23
- snowflake/cli/templates/default_streamlit/.gitignore +0 -4
- snowflake/cli/templates/default_streamlit/common/hello.py +0 -2
- snowflake/cli/templates/default_streamlit/environment.yml +0 -6
- snowflake/cli/templates/default_streamlit/pages/my_page.py +0 -3
- snowflake/cli/templates/default_streamlit/snowflake.yml +0 -10
- snowflake/cli/templates/default_streamlit/streamlit_app.py +0 -4
- snowflake_cli_labs-2.8.0rc0.dist-info/RECORD +0 -240
- snowflake_cli_labs-2.8.0rc0.dist-info/entry_points.txt +0 -2
- /snowflake/cli/{app → _app}/__init__.py +0 -0
- /snowflake/cli/{app → _app}/api_impl/__init__.py +0 -0
- /snowflake/cli/{app → _app}/api_impl/plugin/__init__.py +0 -0
- /snowflake/cli/{app → _app}/api_impl/plugin/plugin_config_provider_impl.py +0 -0
- /snowflake/cli/{app → _app}/commands_registration/__init__.py +0 -0
- /snowflake/cli/{app → _app}/commands_registration/threadsafe.py +0 -0
- /snowflake/cli/{app → _app}/constants.py +0 -0
- /snowflake/cli/{app → _app}/dev/__init__.py +0 -0
- /snowflake/cli/{app → _app}/dev/commands_structure.py +0 -0
- /snowflake/cli/{app → _app}/dev/docs/__init__.py +0 -0
- /snowflake/cli/{app → _app}/dev/docs/project_definition_generate_json_schema.py +0 -0
- /snowflake/cli/{app → _app}/dev/docs/template_utils.py +0 -0
- /snowflake/cli/{app → _app}/dev/docs/templates/definition_description.rst.jinja2 +0 -0
- /snowflake/cli/{app → _app}/dev/docs/templates/overview.rst.jinja2 +0 -0
- /snowflake/cli/{app → _app}/dev/pycharm_remote_debug.py +0 -0
- /snowflake/cli/{app → _app}/loggers.py +0 -0
- /snowflake/cli/{plugins → _plugins}/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/connection/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/cortex/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/cortex/types.py +0 -0
- /snowflake/cli/{plugins → _plugins}/git/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/init/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/nativeapp/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/nativeapp/codegen/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/nativeapp/codegen/sandbox.py +0 -0
- /snowflake/cli/{plugins → _plugins}/nativeapp/codegen/setup/setup_driver.py.source +0 -0
- /snowflake/cli/{plugins → _plugins}/nativeapp/codegen/snowpark/callback_source.py.jinja +0 -0
- /snowflake/cli/{plugins → _plugins}/nativeapp/codegen/snowpark/models.py +0 -0
- /snowflake/cli/{plugins → _plugins}/nativeapp/constants.py +0 -0
- /snowflake/cli/{plugins → _plugins}/nativeapp/exceptions.py +0 -0
- /snowflake/cli/{plugins → _plugins}/nativeapp/feature_flags.py +0 -0
- /snowflake/cli/{plugins → _plugins}/nativeapp/policy.py +0 -0
- /snowflake/cli/{plugins → _plugins}/nativeapp/utils.py +0 -0
- /snowflake/cli/{plugins → _plugins}/nativeapp/version/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/notebook/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/notebook/exceptions.py +0 -0
- /snowflake/cli/{plugins → _plugins}/object/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/object/common.py +0 -0
- /snowflake/cli/{plugins → _plugins}/snowpark/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/snowpark/package/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/snowpark/package/utils.py +0 -0
- /snowflake/cli/{plugins → _plugins}/spcs/common.py +0 -0
- /snowflake/cli/{plugins → _plugins}/spcs/compute_pool/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/spcs/image_registry/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/spcs/image_registry/manager.py +0 -0
- /snowflake/cli/{plugins → _plugins}/spcs/image_repository/__init__.py +0 -0
- /snowflake/cli/{plugins/spcs/jobs → _plugins/spcs/services}/__init__.py +0 -0
- /snowflake/cli/{plugins/spcs/services → _plugins/sql}/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/sql/snowsql_templating.py +0 -0
- /snowflake/cli/{plugins/sql → _plugins/stage}/__init__.py +0 -0
- /snowflake/cli/{plugins → _plugins}/stage/md5.py +0 -0
- /snowflake/cli/{plugins/stage → _plugins/streamlit}/__init__.py +0 -0
- /snowflake/cli/{plugins/streamlit → _plugins/workspace}/__init__.py +0 -0
- /snowflake/cli/{plugins/workspace → api/project/schemas/entities}/__init__.py +0 -0
- {snowflake_cli_labs-2.8.0rc0.dist-info → snowflake_cli_labs-3.0.0rc0.dist-info}/WHEEL +0 -0
- {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.
|
|
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
|