[feat] Add Grafana to follow the system theme (#3920)

Co-authored-by: lynx009 <2030509072@qq.com>
This commit is contained in:
Duansg
2025-12-20 19:44:53 +08:00
committed by GitHub
co-authored by lynx009
parent 472554bc55
commit 01f5cb1fd6
6 changed files with 153 additions and 27 deletions
+2 -2
View File
@@ -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>
@@ -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;
}
}
}
@@ -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);
}
}
}
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();
}
}
@@ -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");
}
}
@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="));
}
}