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]