This is an automated email from the ASF dual-hosted git repository.

gongchao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hertzbeat.git


The following commit(s) were added to refs/heads/master by this push:
     new 01f5cb1fd6 [feat] Add Grafana to follow the system theme (#3920)
01f5cb1fd6 is described below

commit 01f5cb1fd6e4d3b7a4d615cedfa36232e82d05e0
Author: Duansg <[email protected]>
AuthorDate: Sat Dec 20 19:44:53 2025 +0800

    [feat] Add Grafana to follow the system theme (#3920)
    
    Co-authored-by: lynx009 <[email protected]>
---
 .../hertzbeat/common/constants/ThemeEnum.java      | 50 +++++++++++++++++
 .../common/entity/grafana/GrafanaDashboard.java    |  2 +-
 hertzbeat-grafana/pom.xml                          |  4 +-
 .../hertzbeat/grafana/common/GrafanaConstants.java |  4 +-
 .../grafana/service/DashboardService.java          | 65 +++++++++++++++++-----
 .../grafana/service/DashboardServiceTest.java      | 55 ++++++++++++++----
 6 files changed, 153 insertions(+), 27 deletions(-)

diff --git 
a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/constants/ThemeEnum.java
 
b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/constants/ThemeEnum.java
new file mode 100644
index 0000000000..2138ea2fa4
--- /dev/null
+++ 
b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/constants/ThemeEnum.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hertzbeat.common.constants;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.ToString;
+
+/**
+ * Theme Enum
+ */
+@Getter
+@AllArgsConstructor
+@ToString
+public enum ThemeEnum {
+
+    COMPACT("compact"), DEFAULT("default"), LIGHT("light"), DARK("dark");
+
+    private final String value;
+
+    /**
+     * Convert theme to standard theme
+     *
+     * @param theme the input theme
+     * @return the converted theme
+     */
+    public static String convert(String theme) {
+        if ((theme == null || theme.isEmpty())
+            || ThemeEnum.COMPACT.getValue().equals(theme)
+            || ThemeEnum.DEFAULT.getValue().equals(theme)) {
+            return ThemeEnum.LIGHT.getValue();
+        }
+        return theme;
+    }
+}
diff --git 
a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/entity/grafana/GrafanaDashboard.java
 
b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/entity/grafana/GrafanaDashboard.java
index 00d1cc8103..2602a4ddf9 100644
--- 
a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/entity/grafana/GrafanaDashboard.java
+++ 
b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/entity/grafana/GrafanaDashboard.java
@@ -34,7 +34,7 @@ import lombok.NoArgsConstructor;
 @Entity
 @Table(name = "hzb_grafana_dashboard")
 @Data
-@Builder
+@Builder(toBuilder = true)
 @AllArgsConstructor
 @NoArgsConstructor
 @Schema(description = "Grafana dashboard entity")
diff --git a/hertzbeat-grafana/pom.xml b/hertzbeat-grafana/pom.xml
index 8e5afd8b46..1e20a2fe33 100644
--- a/hertzbeat-grafana/pom.xml
+++ b/hertzbeat-grafana/pom.xml
@@ -32,10 +32,10 @@
     </properties>
 
     <dependencies>
-        <!-- hertzbeat common -->
+        <!-- hertzbeat base -->
         <dependency>
             <groupId>org.apache.hertzbeat</groupId>
-            <artifactId>hertzbeat-common</artifactId>
+            <artifactId>hertzbeat-base</artifactId>
         </dependency>
         <!-- hertzbeat warehouse -->
         <dependency>
diff --git 
a/hertzbeat-grafana/src/main/java/org/apache/hertzbeat/grafana/common/GrafanaConstants.java
 
b/hertzbeat-grafana/src/main/java/org/apache/hertzbeat/grafana/common/GrafanaConstants.java
index b30f699e47..e2dc9d25ce 100644
--- 
a/hertzbeat-grafana/src/main/java/org/apache/hertzbeat/grafana/common/GrafanaConstants.java
+++ 
b/hertzbeat-grafana/src/main/java/org/apache/hertzbeat/grafana/common/GrafanaConstants.java
@@ -33,6 +33,8 @@ public interface GrafanaConstants {
 
     String INSTANCE = "&var-instance=";
 
+    String THEME = "&theme=";
+
     String CREATE_DASHBOARD_API = "/api/dashboards/db";
 
     String DELETE_DASHBOARD_API = "/api/dashboards/uid/%s";
@@ -92,4 +94,4 @@ public interface GrafanaConstants {
     static String generateUseDatasource(String datasourceName) {
         return "&var-ds=" + datasourceName;
     }
-}
\ No newline at end of file
+}
diff --git 
a/hertzbeat-grafana/src/main/java/org/apache/hertzbeat/grafana/service/DashboardService.java
 
b/hertzbeat-grafana/src/main/java/org/apache/hertzbeat/grafana/service/DashboardService.java
index 7b46485853..293f82f529 100644
--- 
a/hertzbeat-grafana/src/main/java/org/apache/hertzbeat/grafana/service/DashboardService.java
+++ 
b/hertzbeat-grafana/src/main/java/org/apache/hertzbeat/grafana/service/DashboardService.java
@@ -17,17 +17,15 @@
 
 package org.apache.hertzbeat.grafana.service;
 
-import static 
org.apache.hertzbeat.grafana.common.GrafanaConstants.CREATE_DASHBOARD_API;
-import static 
org.apache.hertzbeat.grafana.common.GrafanaConstants.DELETE_DASHBOARD_API;
-import static org.apache.hertzbeat.grafana.common.GrafanaConstants.INSTANCE;
-import static org.apache.hertzbeat.grafana.common.GrafanaConstants.KIOSK;
-import static org.apache.hertzbeat.grafana.common.GrafanaConstants.REFRESH;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.hertzbeat.base.dao.GeneralConfigDao;
+import org.apache.hertzbeat.common.constants.GeneralConfigTypeEnum;
+import org.apache.hertzbeat.common.constants.ThemeEnum;
 import org.apache.hertzbeat.common.entity.grafana.GrafanaDashboard;
+import org.apache.hertzbeat.common.entity.manager.GeneralConfig;
 import org.apache.hertzbeat.common.util.JsonUtil;
 import org.apache.hertzbeat.grafana.common.GrafanaConstants;
 import org.apache.hertzbeat.grafana.config.GrafanaProperties;
@@ -42,7 +40,17 @@ import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.web.client.HttpClientErrorException;
 import org.springframework.web.client.RestTemplate;
-import lombok.extern.slf4j.Slf4j;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import static 
org.apache.hertzbeat.grafana.common.GrafanaConstants.CREATE_DASHBOARD_API;
+import static 
org.apache.hertzbeat.grafana.common.GrafanaConstants.DELETE_DASHBOARD_API;
+import static org.apache.hertzbeat.grafana.common.GrafanaConstants.INSTANCE;
+import static org.apache.hertzbeat.grafana.common.GrafanaConstants.KIOSK;
+import static org.apache.hertzbeat.grafana.common.GrafanaConstants.REFRESH;
 
 /**
  * Service for managing Grafana dashboards.
@@ -66,6 +74,11 @@ public class DashboardService {
     @Autowired
     private DatasourceService datasourceService;
 
+    @Autowired
+    private GeneralConfigDao generalConfigDao;
+
+    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+
     /**
      * Creates or updates a dashboard in Grafana.
      * The "id" field will be removed from the dashboard JSON before sending
@@ -225,7 +238,16 @@ public class DashboardService {
      * @return GrafanaDashboard object
      */
     public GrafanaDashboard getDashboardByMonitorId(Long monitorId) {
-        return dashboardDao.findByMonitorId(monitorId);
+        GrafanaDashboard dashboard = dashboardDao.findByMonitorId(monitorId);
+        if (null != dashboard
+            && StringUtils.isNotBlank(dashboard.getUrl())
+            && !dashboard.getUrl().contains(GrafanaConstants.THEME)) {
+            String theme = loadThemeConfig();
+            return dashboard.toBuilder()
+                    .url(dashboard.getUrl() + GrafanaConstants.THEME + theme)
+                    .build();
+        }
+        return dashboard;
     }
 
     /**
@@ -248,4 +270,21 @@ public class DashboardService {
             log.warn("No Grafana dashboard record found for monitorId {} to 
disable.", monitorId);
         }
     }
-}
\ No newline at end of file
+
+    private String loadThemeConfig() {
+        try {
+            GeneralConfig config = 
generalConfigDao.findByType(GeneralConfigTypeEnum.system.name());
+            if (config != null && config.getContent() != null) {
+                JsonNode root = OBJECT_MAPPER.readTree(config.getContent());
+                JsonNode node = root.get("theme");
+                if (node != null && !node.isNull()) {
+                    return ThemeEnum.convert(node.asText());
+                }
+                return ThemeEnum.LIGHT.getValue();
+            }
+        } catch (Exception e) {
+            log.error("Failed to load database theme configuration", e);
+        }
+        return ThemeEnum.LIGHT.getValue();
+    }
+}
diff --git 
a/hertzbeat-grafana/src/test/java/org/apache/hertzbeat/grafana/service/DashboardServiceTest.java
 
b/hertzbeat-grafana/src/test/java/org/apache/hertzbeat/grafana/service/DashboardServiceTest.java
index 69af4249e3..292f1d7c46 100644
--- 
a/hertzbeat-grafana/src/test/java/org/apache/hertzbeat/grafana/service/DashboardServiceTest.java
+++ 
b/hertzbeat-grafana/src/test/java/org/apache/hertzbeat/grafana/service/DashboardServiceTest.java
@@ -17,6 +17,7 @@
 
 package org.apache.hertzbeat.grafana.service;
 
+import org.apache.hertzbeat.base.dao.GeneralConfigDao;
 import org.apache.hertzbeat.common.entity.grafana.GrafanaDashboard;
 import org.apache.hertzbeat.grafana.config.GrafanaProperties;
 import org.apache.hertzbeat.grafana.dao.DashboardDao;
@@ -34,10 +35,12 @@ import org.springframework.web.client.RestTemplate;
 
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.lenient;
 
 /**
  * Test case for {@link DashboardService}
@@ -60,6 +63,9 @@ public class DashboardServiceTest {
     @Mock
     private DatasourceService datasourceService;
 
+    @Mock
+    private GeneralConfigDao generalConfigDao;
+
     @InjectMocks
     private DashboardService dashboardService;
 
@@ -73,25 +79,27 @@ public class DashboardServiceTest {
             + "  \"version\": 3"
             + "}";
 
+
+
     @BeforeEach
     void setUp() {
-        
when(datasourceService.getCurrentDatasourceName()).thenReturn("hertzbeat-vm-localhost-8428");
-        when(grafanaProperties.enabled()).thenReturn(Boolean.TRUE);
-        when(grafanaProperties.getPrefix()).thenReturn("");
-        when(grafanaProperties.getUrl()).thenReturn("http://127.0.0.1:3000";);
-        
when(grafanaProperties.exposeUrl()).thenReturn("http://127.0.0.1:3000";);
-        when(serviceAccountService.getToken()).thenReturn("test-token");
+        
lenient().when(datasourceService.getCurrentDatasourceName()).thenReturn("hertzbeat-vm-localhost-8428");
+        lenient().when(grafanaProperties.enabled()).thenReturn(Boolean.TRUE);
+        lenient().when(grafanaProperties.getPrefix()).thenReturn("");
+        
lenient().when(grafanaProperties.getUrl()).thenReturn("http://127.0.0.1:3000";);
+        
lenient().when(grafanaProperties.exposeUrl()).thenReturn("http://127.0.0.1:3000";);
+        
lenient().when(serviceAccountService.getToken()).thenReturn("test-token");
     }
 
 
     @Test
     void testCreateOrUpdateDashboard() {
         ResponseEntity<String> responseEntity = new 
ResponseEntity<>(GRAFANA_API_RESULT, HttpStatus.OK);
-        
+
         when(restTemplate.postForEntity(
                 eq("http://127.0.0.1:3000/api/dashboards/db";), 
any(HttpEntity.class), eq(String.class)
         )).thenReturn(responseEntity);
-        
+
         ArgumentCaptor<GrafanaDashboard> dashboardCaptor = 
ArgumentCaptor.forClass(GrafanaDashboard.class);
         dashboardService.createOrUpdateDashboard("{\"id\":11}", 1L);
         verify(dashboardDao).save(dashboardCaptor.capture());
@@ -109,7 +117,7 @@ public class DashboardServiceTest {
         assertTrue(savedDashboard.getUrl().contains("refresh=15s"), "URL 
should contain refresh parameter");
         assertTrue(savedDashboard.getUrl().contains("var-instance=1"), "URL 
should contain instance parameter");
     }
-    
+
     @Test
     void testCreateOrUpdateDashboardWithTrailingSlash() {
         
when(grafanaProperties.exposeUrl()).thenReturn("http://127.0.0.1:3000/";);
@@ -138,4 +146,31 @@ public class DashboardServiceTest {
         assertTrue(savedDashboard.getUrl().contains("var-instance=1"), "URL 
should contain instance parameter");
     }
 
-}
\ No newline at end of file
+
+    @Test
+    void testGetDashboardByMonitorId() {
+        Long monitorId = 1L;
+        String originalUrl = "http://localhost:3000/d/123/my-dashboard?kiosk";;
+        GrafanaDashboard originalDashboard = GrafanaDashboard.builder()
+                .monitorId(monitorId)
+                .url(originalUrl)
+                .build();
+
+        
when(dashboardDao.findByMonitorId(eq(monitorId))).thenReturn(originalDashboard);
+
+        String themeConfigJson = "{\"theme\":\"light\"}";
+        org.apache.hertzbeat.common.entity.manager.GeneralConfig runConfig = 
new org.apache.hertzbeat.common.entity.manager.GeneralConfig();
+        runConfig.setContent(themeConfigJson);
+
+        
when(generalConfigDao.findByType(eq(org.apache.hertzbeat.common.constants.GeneralConfigTypeEnum.system.name())))
+                .thenReturn(runConfig);
+
+        GrafanaDashboard resultDashboard = 
dashboardService.getDashboardByMonitorId(monitorId);
+
+        assertNotNull(resultDashboard);
+        assertTrue(resultDashboard.getUrl().contains("&theme=light"));
+        assertTrue(originalDashboard.getUrl().endsWith("?kiosk"));
+        assertFalse(originalDashboard.getUrl().contains("&theme="));
+    }
+
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to