Skip to content

Commit

Permalink
CMK_19926_vue_default
Browse files Browse the repository at this point in the history
Change-Id: I269ecc136f600c8536e5ab591c85361c0977199b
  • Loading branch information
schnetzzz committed Jan 14, 2025
1 parent c365c0d commit 557dbc9
Show file tree
Hide file tree
Showing 11 changed files with 79 additions and 80 deletions.
28 changes: 17 additions & 11 deletions cmk/gui/form_specs/vue/form_spec_visitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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():
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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))
Expand All @@ -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,
)
28 changes: 0 additions & 28 deletions cmk/gui/htmllib/html.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,20 @@
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

from flask import current_app, session

import cmk.ccc.version as cmk_version
from cmk.ccc.exceptions import MKGeneralException

import cmk.utils.paths

from cmk.gui import log, utils
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
Expand Down Expand Up @@ -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"
Expand Down
4 changes: 2 additions & 2 deletions cmk/gui/plugins/config/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
8 changes: 4 additions & 4 deletions cmk/gui/wato/_check_mk_configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 "
Expand Down
22 changes: 13 additions & 9 deletions cmk/gui/wato/pages/_simple_modes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down
50 changes: 36 additions & 14 deletions cmk/gui/wato/pages/rulesets.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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("<hr>")
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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")

Expand Down Expand Up @@ -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(
Expand All @@ -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()
Expand Down Expand Up @@ -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)
6 changes: 3 additions & 3 deletions packages/cmk-frontend-vue/src/form/FormApp.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const props = defineProps<{
spec: FormSpec
data: unknown
backendValidation: ValidationMessages
renderMode: 'edit' | 'readonly' | 'both'
displayMode: 'edit' | 'readonly' | 'both'
}>()

const dataRef = ref()
Expand All @@ -30,7 +30,7 @@ immediateWatch(
)

immediateWatch(
() => props.renderMode,
() => props.displayMode,
(newValue) => {
activeMode.value = newValue
}
Expand Down Expand Up @@ -83,7 +83,7 @@ const { ErrorBoundary } = useErrorBoundary()
<td>
<FormEdit
v-model:data="dataRef"
:v-if="renderMode === 'edit' || renderMode === 'both'"
:v-if="displayMode === 'edit' || displayMode === 'both'"
:backend-validation="backendValidation"
:spec="spec"
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,7 @@ function collectData() {

<template>
<!-- eslint-disable vue/no-v-html -->
<form
ref="legacyDOM"
style="background: #595959"
class="legacy_valuespec"
v-html="inputHtml"
></form>
<form ref="legacyDOM" class="legacy_valuespec" v-html="inputHtml"></form>
<!--eslint-enable-->
<FormValidation :validation="validation"></FormValidation>
</template>
2 changes: 1 addition & 1 deletion packages/cmk-frontend-vue/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
Loading

0 comments on commit 557dbc9

Please sign in to comment.