From 557dbc96b20ec7674efc8a9c230d32ababcee4f2 Mon Sep 17 00:00:00 2001 From: Andreas Boesl Date: Mon, 25 Nov 2024 11:56:49 +0100 Subject: [PATCH] CMK_19926_vue_default Change-Id: I269ecc136f600c8536e5ab591c85361c0977199b --- cmk/gui/form_specs/vue/form_spec_visitor.py | 28 +++++++---- cmk/gui/htmllib/html.py | 28 ----------- cmk/gui/plugins/config/base.py | 4 +- cmk/gui/wato/_check_mk_configuration.py | 8 +-- cmk/gui/wato/pages/_simple_modes.py | 22 ++++---- cmk/gui/wato/pages/rulesets.py | 50 +++++++++++++------ .../cmk-frontend-vue/src/form/FormApp.vue | 6 +-- .../components/forms/FormLegacyValueSpec.vue | 7 +-- packages/cmk-frontend-vue/src/main.ts | 2 +- tests/unit/cmk/gui/test_gui_config.py | 2 +- tests/unit/cmk/gui/watolib/test_watolib.py | 2 +- 11 files changed, 79 insertions(+), 80 deletions(-) diff --git a/cmk/gui/form_specs/vue/form_spec_visitor.py b/cmk/gui/form_specs/vue/form_spec_visitor.py index 45cbe3847bd..c21baffb48d 100644 --- a/cmk/gui/form_specs/vue/form_spec_visitor.py +++ b/cmk/gui/form_specs/vue/form_spec_visitor.py @@ -8,7 +8,7 @@ from collections.abc import Sequence from dataclasses import asdict, dataclass from enum import Enum -from typing import Any, Literal, TypeVar +from typing import Any, TypeVar from cmk.ccc.exceptions import MKGeneralException @@ -146,13 +146,25 @@ T = TypeVar("T") +class DisplayMode(Enum): + EDIT = "edit" + READONLY = "readonly" + BOTH = "both" + + +class RenderMode(Enum): + BACKEND = "backend" + FRONTEND = "frontend" + BACKEND_AND_FRONTEND = "backend_and_frontend" + + @dataclass(kw_only=True) class VueAppConfig: id: str spec: shared_type_defs.FormSpec data: Any validation: Any - render_mode: Literal["edit", "readonly", "both"] + display_mode: str def register_form_specs(): @@ -264,19 +276,13 @@ def get_vue_value(field_id: str, fallback_value: Any) -> Any: return fallback_value -class RenderMode(Enum): - EDIT = "edit" - READONLY = "readonly" - BOTH = "both" - - def render_form_spec( form_spec: FormSpec[T], field_id: str, value: Any, origin: DataOrigin, do_validate: bool, - display_mode: RenderMode = RenderMode.EDIT, + display_mode: DisplayMode = DisplayMode.EDIT, ) -> None: """Renders the valuespec via vue within a div""" vue_app_config = serialize_data_for_frontend( @@ -319,7 +325,7 @@ def serialize_data_for_frontend( origin: DataOrigin, do_validate: bool, value: Any = DEFAULT_VALUE, - render_mode: RenderMode = RenderMode.EDIT, + display_mode: DisplayMode = DisplayMode.EDIT, ) -> VueAppConfig: """Serializes backend value to vue app compatible config.""" visitor = get_visitor(form_spec, VisitorOptions(data_origin=origin)) @@ -334,5 +340,5 @@ def serialize_data_for_frontend( spec=vue_component, data=vue_value, validation=validation, - render_mode=render_mode.value, + display_mode=display_mode.value, ) diff --git a/cmk/gui/htmllib/html.py b/cmk/gui/htmllib/html.py index 7a9c2170609..980a3e2d2fd 100644 --- a/cmk/gui/htmllib/html.py +++ b/cmk/gui/htmllib/html.py @@ -12,7 +12,6 @@ import time import typing from collections.abc import Callable, Iterable, Mapping, Sequence -from enum import Enum from functools import lru_cache from pathlib import Path from typing import Any, Literal, overload @@ -20,7 +19,6 @@ from flask import current_app, session import cmk.ccc.version as cmk_version -from cmk.ccc.exceptions import MKGeneralException import cmk.utils.paths @@ -28,7 +26,6 @@ from cmk.gui.config import active_config from cmk.gui.ctx_stack import request_local_attr from cmk.gui.exceptions import MKUserError -from cmk.gui.hooks import request_memoize from cmk.gui.http import Request from cmk.gui.i18n import _ from cmk.gui.logged_in import user @@ -65,37 +62,12 @@ from .type_defs import RequireConfirmation -class ExperimentalRenderMode(Enum): - BACKEND = "backend" - FRONTEND = "frontend" - BACKEND_AND_FRONTEND = "backend_and_frontend" - - class _Manifest(typing.NamedTuple): main: str main_stylesheets: list[str] stage1: str -@request_memoize() -def get_render_mode() -> ExperimentalRenderMode: - # Settings via url overwrite config based settings - if (rendering_mode := html.request.var("experimental_render_mode", None)) is None: - rendering_mode = active_config.experimental_features.get( - "render_mode", ExperimentalRenderMode.BACKEND.value - ) - - match rendering_mode: - case ExperimentalRenderMode.BACKEND.value: - return ExperimentalRenderMode.BACKEND - case ExperimentalRenderMode.FRONTEND.value: - return ExperimentalRenderMode.FRONTEND - case ExperimentalRenderMode.BACKEND_AND_FRONTEND.value: - return ExperimentalRenderMode.BACKEND_AND_FRONTEND - case _: - raise MKGeneralException(_("Unknown rendering mode %s") % rendering_mode) - - def inject_js_profiling_code(): return active_config.inject_js_profiling_code or html.request.has_var( "inject_js_profiling_code" diff --git a/cmk/gui/plugins/config/base.py b/cmk/gui/plugins/config/base.py index ebbed333e1b..e1f42a775e7 100644 --- a/cmk/gui/plugins/config/base.py +++ b/cmk/gui/plugins/config/base.py @@ -651,6 +651,6 @@ class CREConfig: inject_js_profiling_code: bool = False load_frontend_vue: Literal["static_files", "inject"] = "static_files" - # Experimental feature flags - experimental_features: dict[str, Any] = field(default_factory=dict) + # Vue experimental feature settings + vue_experimental_features: dict[str, Any] = field(default_factory=dict) automation_helper_active: bool = False diff --git a/cmk/gui/wato/_check_mk_configuration.py b/cmk/gui/wato/_check_mk_configuration.py index a5b1b870348..55e1db07ce8 100644 --- a/cmk/gui/wato/_check_mk_configuration.py +++ b/cmk/gui/wato/_check_mk_configuration.py @@ -841,17 +841,17 @@ def domain(self) -> ABCConfigDomain: return ConfigDomainGUI() def ident(self) -> str: - return "experimental_features" + return "vue_experimental_features" def valuespec(self) -> ValueSpec: return Dictionary( - title=_("Experimental rendering"), + title=_("Vue experimental features"), help=_("These settings only affect features that are currently under development."), elements=[ ( - "render_mode", + "rule_render_mode", DropdownChoice( - title=_("Form specs"), + title=_("Rule rendering mode"), help=_( "Enable experimental rendering modes for form specs. Keep in mind that" "some form specs are always rendered in the frontend, regardless " diff --git a/cmk/gui/wato/pages/_simple_modes.py b/cmk/gui/wato/pages/_simple_modes.py index 48194d50b11..d38926dd955 100644 --- a/cmk/gui/wato/pages/_simple_modes.py +++ b/cmk/gui/wato/pages/_simple_modes.py @@ -28,10 +28,14 @@ from cmk.gui.exceptions import MKUserError from cmk.gui.form_specs.generators.setup_site_choice import create_setup_site_choice from cmk.gui.form_specs.private import Catalog, CommentTextArea, LegacyValueSpec -from cmk.gui.form_specs.vue.form_spec_visitor import parse_data_from_frontend, render_form_spec +from cmk.gui.form_specs.vue.form_spec_visitor import ( + parse_data_from_frontend, + render_form_spec, + RenderMode, +) from cmk.gui.form_specs.vue.visitors import DataOrigin, DEFAULT_VALUE from cmk.gui.form_specs.vue.visitors.catalog import Dict2CatalogConverter, Headers -from cmk.gui.htmllib.html import ExperimentalRenderMode, html +from cmk.gui.htmllib.html import html from cmk.gui.http import request from cmk.gui.i18n import _ from cmk.gui.page_menu import ( @@ -642,10 +646,10 @@ def _vs_optional_keys(self) -> list[str]: def _update_entry_from_vars(self) -> None: render_mode, form_spec = self._get_render_mode() match render_mode: - case ExperimentalRenderMode.FRONTEND | ExperimentalRenderMode.BACKEND_AND_FRONTEND: + case RenderMode.FRONTEND | RenderMode.BACKEND_AND_FRONTEND: assert form_spec is not None self._update_entry_from_vars_form_spec(form_spec) - case ExperimentalRenderMode.BACKEND: + case RenderMode.BACKEND: self._update_entry_from_vars_valuespec() def _update_entry_from_vars_valuespec(self) -> None: @@ -753,24 +757,24 @@ def page(self, form_name: str = "edit") -> None: def _page_form_quick_setup_warning(self) -> None: pass - def _get_render_mode(self) -> tuple[ExperimentalRenderMode, FormSpec | None]: + def _get_render_mode(self) -> tuple[RenderMode, FormSpec | None]: # No longer depends on global switch, but on the global switch # but on existence of the fs_individual_elements implementation try: self._fs_individual_elements() except NotImplementedError: - return ExperimentalRenderMode.BACKEND, None + return RenderMode.BACKEND, None # Frontend rendering is done via the Catalog class. There is no valuespec fallback - return ExperimentalRenderMode.FRONTEND, self.catalog() + return RenderMode.FRONTEND, self.catalog() def _page_form_render_entry(self) -> None: render_mode, form_spec = self._get_render_mode() match render_mode: - case ExperimentalRenderMode.BACKEND: + case RenderMode.BACKEND: self._page_form_render_entry_valuespec() - case ExperimentalRenderMode.FRONTEND: + case RenderMode.FRONTEND: assert form_spec is not None # This prevents sending the form when pressing enter in an input field html.form_has_submit_button = True diff --git a/cmk/gui/wato/pages/rulesets.py b/cmk/gui/wato/pages/rulesets.py index cde59e5cd9b..2fee5a17e2b 100644 --- a/cmk/gui/wato/pages/rulesets.py +++ b/cmk/gui/wato/pages/rulesets.py @@ -16,6 +16,8 @@ from livestatus import SiteId +from cmk.ccc.exceptions import MKGeneralException + from cmk.utils.global_ident_type import is_locked_by_quick_setup from cmk.utils.hostaddress import HostName from cmk.utils.labels import LabelGroups @@ -46,14 +48,16 @@ from cmk.gui.exceptions import HTTPRedirect, MKAuthException, MKUserError from cmk.gui.form_specs.private.definitions import LegacyValueSpec from cmk.gui.form_specs.vue.form_spec_visitor import ( + DisplayMode, parse_data_from_frontend, render_form_spec, RenderMode, ) from cmk.gui.form_specs.vue.visitors import DataOrigin from cmk.gui.hooks import call as call_hooks +from cmk.gui.hooks import request_memoize from cmk.gui.htmllib.generator import HTMLWriter -from cmk.gui.htmllib.html import ExperimentalRenderMode, get_render_mode, html +from cmk.gui.htmllib.html import html from cmk.gui.http import mandatory_parameter, request from cmk.gui.i18n import _ from cmk.gui.logged_in import user @@ -1506,17 +1510,17 @@ def _show_rule_frontend(form_spec: FormSpec) -> None: value, DataOrigin.DISK, True, - display_mode=RenderMode.READONLY, + display_mode=DisplayMode.READONLY, ) render_mode, form_spec = _get_render_mode(self._rulespec) match render_mode: - case ExperimentalRenderMode.BACKEND: + case RenderMode.BACKEND: _show_rule_backend() - case ExperimentalRenderMode.FRONTEND: + case RenderMode.FRONTEND: assert form_spec is not None _show_rule_frontend(form_spec) - case ExperimentalRenderMode.FRONTEND | ExperimentalRenderMode.BACKEND_AND_FRONTEND: + case RenderMode.FRONTEND | RenderMode.BACKEND_AND_FRONTEND: assert form_spec is not None _show_rule_frontend(form_spec) # html.write_html("
") @@ -1878,9 +1882,9 @@ def render_hidden_if_locked(vs: ValueSpec, varprefix: str, value: object, locked html.close_div() -def _get_render_mode(rulespec: Rulespec) -> tuple[ExperimentalRenderMode, FormSpec | None]: - configured_mode = get_render_mode() - if configured_mode == ExperimentalRenderMode.BACKEND: +def _get_render_mode(rulespec: Rulespec) -> tuple[RenderMode, FormSpec | None]: + configured_mode = _get_rule_render_mode() + if configured_mode == RenderMode.BACKEND: return configured_mode, None try: @@ -2126,13 +2130,13 @@ def _update_rule_from_vars(self) -> HTTPRedirect | None: render_mode, registered_form_spec = _get_render_mode(self._ruleset.rulespec) self._do_validate_on_render = True match render_mode: - case ExperimentalRenderMode.FRONTEND | ExperimentalRenderMode.BACKEND_AND_FRONTEND: + case RenderMode.FRONTEND | RenderMode.BACKEND_AND_FRONTEND: assert registered_form_spec is not None value = parse_data_from_frontend( registered_form_spec, self._vue_field_id(), ) - case ExperimentalRenderMode.BACKEND: + case RenderMode.BACKEND: value = self._ruleset.valuespec().from_html_vars("ve") self._ruleset.valuespec().validate_value(value, "ve") @@ -2241,11 +2245,10 @@ def _page_form(self) -> None: try: render_mode, registered_form_spec = _get_render_mode(self._ruleset.rulespec) match render_mode: - case ExperimentalRenderMode.BACKEND: + case RenderMode.BACKEND: valuespec.validate_datatype(self._rule.value, "ve") valuespec.render_input("ve", self._rule.value) - case ExperimentalRenderMode.FRONTEND: - forms.section("Current setting as VUE") + case RenderMode.FRONTEND: assert registered_form_spec is not None value, origin = self._get_rule_value_and_origin() render_form_spec( @@ -2255,7 +2258,7 @@ def _page_form(self) -> None: origin, self._should_validate_on_render(), ) - case ExperimentalRenderMode.BACKEND_AND_FRONTEND: + case RenderMode.BACKEND_AND_FRONTEND: forms.section("Current setting as VUE") assert registered_form_spec is not None value, origin = self._get_rule_value_and_origin() @@ -3242,3 +3245,22 @@ def _page_menu_dropdowns(self) -> Iterable[PageMenuDropdown]: ), ], ) + + +@request_memoize() +def _get_rule_render_mode() -> RenderMode: + # Settings via url overwrite config based settings + if (rendering_mode := html.request.var("rule_render_mode", None)) is None: + rendering_mode = active_config.vue_experimental_features.get( + "rule_render_mode", RenderMode.FRONTEND.value + ) + + match rendering_mode: + case RenderMode.BACKEND.value: + return RenderMode.BACKEND + case RenderMode.FRONTEND.value: + return RenderMode.FRONTEND + case RenderMode.BACKEND_AND_FRONTEND.value: + return RenderMode.BACKEND_AND_FRONTEND + case _: + raise MKGeneralException(_("Unknown rendering mode %s") % rendering_mode) diff --git a/packages/cmk-frontend-vue/src/form/FormApp.vue b/packages/cmk-frontend-vue/src/form/FormApp.vue index 94991d948b7..fde2bdd7c3b 100644 --- a/packages/cmk-frontend-vue/src/form/FormApp.vue +++ b/packages/cmk-frontend-vue/src/form/FormApp.vue @@ -18,7 +18,7 @@ const props = defineProps<{ spec: FormSpec data: unknown backendValidation: ValidationMessages - renderMode: 'edit' | 'readonly' | 'both' + displayMode: 'edit' | 'readonly' | 'both' }>() const dataRef = ref() @@ -30,7 +30,7 @@ immediateWatch( ) immediateWatch( - () => props.renderMode, + () => props.displayMode, (newValue) => { activeMode.value = newValue } @@ -83,7 +83,7 @@ const { ErrorBoundary } = useErrorBoundary() diff --git a/packages/cmk-frontend-vue/src/form/components/forms/FormLegacyValueSpec.vue b/packages/cmk-frontend-vue/src/form/components/forms/FormLegacyValueSpec.vue index 5433d5db848..1f4453897a6 100644 --- a/packages/cmk-frontend-vue/src/form/components/forms/FormLegacyValueSpec.vue +++ b/packages/cmk-frontend-vue/src/form/components/forms/FormLegacyValueSpec.vue @@ -83,12 +83,7 @@ function collectData() { diff --git a/packages/cmk-frontend-vue/src/main.ts b/packages/cmk-frontend-vue/src/main.ts index e9b3caae544..5c9bb63fff6 100644 --- a/packages/cmk-frontend-vue/src/main.ts +++ b/packages/cmk-frontend-vue/src/main.ts @@ -44,7 +44,7 @@ function setupVue() { // eslint-disable-next-line vue/no-deprecated-data-object-declaration, vue/no-shared-component-data data: appData.data, backendValidation: appData.validation, - renderMode: appData.render_mode + displayMode: appData.display_mode }) break } diff --git a/tests/unit/cmk/gui/test_gui_config.py b/tests/unit/cmk/gui/test_gui_config.py index f739ddc7ade..e69802a4dc4 100644 --- a/tests/unit/cmk/gui/test_gui_config.py +++ b/tests/unit/cmk/gui/test_gui_config.py @@ -151,7 +151,7 @@ def test_default_config_from_plugins() -> None: "enable_deprecated_automation_user_authentication", "enable_community_translations", "default_temperature_unit", - "experimental_features", + "vue_experimental_features", "inject_js_profiling_code", "load_frontend_vue", "automation_helper_active", diff --git a/tests/unit/cmk/gui/watolib/test_watolib.py b/tests/unit/cmk/gui/watolib/test_watolib.py index 63647531032..b5dbe9f7b58 100644 --- a/tests/unit/cmk/gui/watolib/test_watolib.py +++ b/tests/unit/cmk/gui/watolib/test_watolib.py @@ -215,7 +215,7 @@ def test_registered_configvars() -> None: "enable_community_translations", "default_language", "default_temperature_unit", - "experimental_features", + "vue_experimental_features", "inject_js_profiling_code", "load_frontend_vue", "automation_helper_active",