mirror of
https://github.com/dromara/hertzbeat.git
synced 2026-09-17 09:40:58 +00:00
Compare commits
9
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd3d70e437 | ||
|
|
e3235e202c | ||
|
|
36084af96d | ||
|
|
d2dd8068d3 | ||
|
|
a52f6afa90 | ||
|
|
9cfc431f16 | ||
|
|
be2987e901 | ||
|
|
c0753f08ae | ||
|
|
5ef8db0c52 |
+8
@@ -121,6 +121,14 @@ public class MonitorsController {
|
||||
monitorService.export(ids, type, res);
|
||||
}
|
||||
|
||||
@GetMapping("/export/all")
|
||||
@Operation(summary = "export all monitor config", description = "export all monitor config")
|
||||
public void exportAll(
|
||||
@Parameter(description = "Export Type:JSON,EXCEL,YAML") @RequestParam(defaultValue = "JSON") String type,
|
||||
HttpServletResponse res) throws Exception {
|
||||
monitorService.exportAll(type, res);
|
||||
}
|
||||
|
||||
@PostMapping("/import")
|
||||
@Operation(summary = "import monitor config", description = "import monitor config")
|
||||
public ResponseEntity<Message<Void>> export(MultipartFile file) throws Exception {
|
||||
|
||||
+9
@@ -173,6 +173,15 @@ public interface MonitorService {
|
||||
*/
|
||||
void export(List<Long> ids, String type, HttpServletResponse res) throws Exception;
|
||||
|
||||
/**
|
||||
* Export All Monitoring Configuration
|
||||
*
|
||||
* @param type file type
|
||||
* @param res response
|
||||
* @throws Exception This exception will be thrown if the export fails
|
||||
*/
|
||||
void exportAll(String type, HttpServletResponse res) throws Exception;
|
||||
|
||||
/**
|
||||
* Import Monitoring Configuration
|
||||
*
|
||||
|
||||
+12
@@ -235,6 +235,18 @@ public class MonitorServiceImpl implements MonitorService {
|
||||
imExportService.exportConfig(res.getOutputStream(), ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exportAll(String type, HttpServletResponse res) throws Exception {
|
||||
// Get all monitor IDs from the database
|
||||
List<Long> allMonitorIds = monitorDao.findAll()
|
||||
.stream()
|
||||
.map(Monitor::getId)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// Use the existing export method to export all monitors
|
||||
export(allMonitorIds, type, res);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void importConfig(MultipartFile file) throws Exception {
|
||||
var fileName = FileUtil.getFileName(file);
|
||||
|
||||
+14
@@ -145,4 +145,18 @@ class MonitorsControllerTest {
|
||||
.andExpect(jsonPath("$.code").value("0"))
|
||||
.andExpect(jsonPath("$.msg").value("Import success"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void exportAll() throws Exception {
|
||||
String type = "JSON";
|
||||
|
||||
// Mock the behavior of monitorService.exportAll
|
||||
doNothing().when(monitorService).exportAll(Mockito.anyString(), Mockito.any());
|
||||
|
||||
// Perform the request and verify the response
|
||||
this.mockMvc.perform(MockMvcRequestBuilders.get("/api/monitors/export/all")
|
||||
.param("type", type))
|
||||
.andExpect(status().isOk())
|
||||
.andReturn();
|
||||
}
|
||||
}
|
||||
|
||||
+27
@@ -745,6 +745,33 @@ class MonitorServiceTest {
|
||||
when(monitorDao.findById(1L)).thenReturn(Optional.of(monitor));
|
||||
when(paramDao.findParamsByMonitorId(1L)).thenReturn(params);
|
||||
assertDoesNotThrow(() -> monitorService.copyMonitor(1L));
|
||||
}
|
||||
|
||||
@Test
|
||||
void exportAll() throws Exception {
|
||||
// Create some test monitors
|
||||
Monitor monitor1 = Monitor.builder().id(1L).name("test1").app("app1").build();
|
||||
Monitor monitor2 = Monitor.builder().id(2L).name("test2").app("app2").build();
|
||||
List<Monitor> allMonitors = List.of(monitor1, monitor2);
|
||||
|
||||
// Mock the behavior of monitorDao.findAll
|
||||
when(monitorDao.findAll()).thenReturn(allMonitors);
|
||||
|
||||
// Create a mock HttpServletResponse
|
||||
jakarta.servlet.http.HttpServletResponse mockResponse = org.mockito.Mockito.mock(jakarta.servlet.http.HttpServletResponse.class);
|
||||
|
||||
// Mock the ImExportService
|
||||
org.apache.hertzbeat.manager.service.ImExportService mockImExportService = org.mockito.Mockito.mock(org.apache.hertzbeat.manager.service.ImExportService.class);
|
||||
// Mock the getFileName method
|
||||
when(mockImExportService.getFileName()).thenReturn("test.json");
|
||||
// Set the field using reflection
|
||||
java.lang.reflect.Field field = MonitorServiceImpl.class.getDeclaredField("imExportServiceMap");
|
||||
field.setAccessible(true);
|
||||
java.util.Map<String, org.apache.hertzbeat.manager.service.ImExportService> imExportServiceMap = new java.util.HashMap<>();
|
||||
imExportServiceMap.put("JSON", mockImExportService);
|
||||
field.set(monitorService, imExportServiceMap);
|
||||
|
||||
// Test the exportAll method
|
||||
assertDoesNotThrow(() -> monitorService.exportAll("JSON", mockResponse));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,6 +77,12 @@
|
||||
{{ 'monitor.export' | i18n }}
|
||||
</button>
|
||||
</li>
|
||||
<li nz-menu-item>
|
||||
<button nz-button (click)="onExportAllMonitors()">
|
||||
<i nz-icon nzType="export" nzTheme="outline"></i>
|
||||
{{ 'monitor.export-all' | i18n }}
|
||||
</button>
|
||||
</li>
|
||||
<li nz-menu-item>
|
||||
<nz-upload nzAction="/monitors/import" [nzLimit]="1" [nzShowUploadList]="false" (nzChange)="onImportMonitors($event)">
|
||||
<button nz-button>
|
||||
@@ -287,7 +293,12 @@
|
||||
>
|
||||
<ng-container *nzModalContent>
|
||||
<div class="export-type-container">
|
||||
<div class="export-type-card" (click)="exportMonitors('JSON')" [class.loading]="exportJsonButtonLoading">
|
||||
<div
|
||||
class="export-type-card"
|
||||
(click)="exportMonitors('JSON')"
|
||||
[class.loading]="exportJsonButtonLoading"
|
||||
*ngIf="checkedMonitorIds.size > 0"
|
||||
>
|
||||
<div class="export-type-icon">
|
||||
<i nz-icon nzType="code" nzTheme="outline"></i>
|
||||
</div>
|
||||
@@ -296,7 +307,12 @@
|
||||
<p>{{ 'monitor.export.use-type' | i18n : { type: 'JSON' } }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="export-type-card" (click)="exportMonitors('EXCEL')" [class.loading]="exportExcelButtonLoading">
|
||||
<div
|
||||
class="export-type-card"
|
||||
(click)="exportMonitors('EXCEL')"
|
||||
[class.loading]="exportExcelButtonLoading"
|
||||
*ngIf="checkedMonitorIds.size > 0"
|
||||
>
|
||||
<div class="export-type-icon">
|
||||
<i nz-icon nzType="file-excel" nzTheme="outline"></i>
|
||||
</div>
|
||||
@@ -305,6 +321,34 @@
|
||||
<p>{{ 'monitor.export.use-type' | i18n : { type: 'EXCEL' } }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="export-type-card"
|
||||
(click)="exportAllMonitors('JSON')"
|
||||
[class.loading]="exportJsonButtonLoading"
|
||||
*ngIf="checkedMonitorIds.size === 0"
|
||||
>
|
||||
<div class="export-type-icon">
|
||||
<i nz-icon nzType="code" nzTheme="outline"></i>
|
||||
</div>
|
||||
<div class="export-type-info">
|
||||
<h3>JSON</h3>
|
||||
<p>{{ 'monitor.export-all.use-type' | i18n : { type: 'JSON' } }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="export-type-card"
|
||||
(click)="exportAllMonitors('EXCEL')"
|
||||
[class.loading]="exportExcelButtonLoading"
|
||||
*ngIf="checkedMonitorIds.size === 0"
|
||||
>
|
||||
<div class="export-type-icon">
|
||||
<i nz-icon nzType="file-excel" nzTheme="outline"></i>
|
||||
</div>
|
||||
<div class="export-type-info">
|
||||
<h3>EXCEL</h3>
|
||||
<p>{{ 'monitor.export-all.use-type' | i18n : { type: 'EXCEL' } }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</nz-modal>
|
||||
|
||||
@@ -264,6 +264,10 @@ export class MonitorListComponent implements OnInit, OnDestroy {
|
||||
this.isSwitchExportTypeModalVisible = true;
|
||||
}
|
||||
|
||||
onExportAllMonitors() {
|
||||
this.isSwitchExportTypeModalVisible = true;
|
||||
}
|
||||
|
||||
onImportMonitors(info: NzUploadChangeParam): void {
|
||||
console.log(info.type);
|
||||
if (info.type === 'start') {
|
||||
@@ -362,6 +366,46 @@ export class MonitorListComponent implements OnInit, OnDestroy {
|
||||
);
|
||||
}
|
||||
|
||||
exportAllMonitors(type: string) {
|
||||
switch (type) {
|
||||
case 'JSON':
|
||||
this.exportJsonButtonLoading = true;
|
||||
break;
|
||||
case 'EXCEL':
|
||||
this.exportExcelButtonLoading = true;
|
||||
break;
|
||||
}
|
||||
const exportAllMonitors$ = this.monitorSvc
|
||||
.exportAllMonitors(type)
|
||||
.pipe(
|
||||
finalize(() => {
|
||||
this.exportExcelButtonLoading = false;
|
||||
this.exportJsonButtonLoading = false;
|
||||
exportAllMonitors$.unsubscribe();
|
||||
})
|
||||
)
|
||||
.subscribe(
|
||||
response => {
|
||||
const message = response.body!;
|
||||
if (message.type == 'application/json') {
|
||||
this.notifySvc.error(this.i18nSvc.fanyi('common.notify.export-fail'), '');
|
||||
} else {
|
||||
const blob = new Blob([message], { type: response.headers.get('Content-Type')! });
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.download = response.headers.get('Content-Disposition')!.split(';')[1].split('filename=')[1];
|
||||
a.href = url;
|
||||
a.click();
|
||||
window.URL.revokeObjectURL(url);
|
||||
this.isSwitchExportTypeModalVisible = false;
|
||||
}
|
||||
},
|
||||
error => {
|
||||
this.notifySvc.error(this.i18nSvc.fanyi('common.notify.export-fail'), error.msg);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
onCancelManageMonitors() {
|
||||
if (this.checkedMonitorIds == null || this.checkedMonitorIds.size === 0) {
|
||||
this.notifySvc.warning(this.i18nSvc.fanyi('common.notify.no-select-cancel'), '');
|
||||
|
||||
@@ -30,6 +30,7 @@ const monitors_uri = '/monitors';
|
||||
const detect_monitor_uri = '/monitor/detect';
|
||||
const manage_monitors_uri = '/monitors/manage';
|
||||
const export_monitors_uri = '/monitors/export';
|
||||
const export_all_monitors_uri = '/monitors/export/all';
|
||||
const summary_uri = '/summary';
|
||||
const warehouse_storage_status_uri = '/warehouse/storage/status';
|
||||
const grafana_dashboard_uri = '/grafana/dashboard';
|
||||
@@ -74,6 +75,16 @@ export class MonitorService {
|
||||
});
|
||||
}
|
||||
|
||||
public exportAllMonitors(type: string): Observable<HttpResponse<Blob>> {
|
||||
let httpParams = new HttpParams();
|
||||
httpParams = httpParams.append('type', type);
|
||||
return this.http.get(export_all_monitors_uri, {
|
||||
params: httpParams,
|
||||
observe: 'response',
|
||||
responseType: 'blob'
|
||||
});
|
||||
}
|
||||
|
||||
public cancelManageMonitors(monitorIds: Set<number>): Observable<Message<any>> {
|
||||
let httpParams = new HttpParams();
|
||||
monitorIds.forEach(monitorId => {
|
||||
|
||||
@@ -70,11 +70,11 @@
|
||||
"alert.help.inhibit.link": "https://hertzbeat.apache.org/docs/help/alert_inhibit",
|
||||
"alert.help.integration": "Unified management of alerts from different third-party platforms, integrating and receiving alert messages from third-party monitoring and observability systems, and performing actions such as grouping, aggregation, inhibition, silencing, and notification distribution.",
|
||||
"alert.help.integration.link": "https://hertzbeat.apache.org",
|
||||
"alert.help.notice": "Notification is used to config the receiver of alarm message and receiving method. The alarm message will be sent to the receiver by specified way(support email, discord, webhook etc). <a href='https://hertzbeat.apache.org/zh-cn/docs/help/alert_webhook'>Click here to see configuration steps.</a>.<br>“<i>Notice Template</i>” is message content structure template. The built-in template is used by default or you can customize the template to customize the message notification structure.<br><span class='help_module_span'>Note⚠️: After configuring the “<i>Receiver</i>”, you also need to config the“<i>Notice Policy</i>”to specify which messages are sent to which receivers.</span><a href='https://hertzbeat.apache.org/docs/help/alert_email'> Click here to see potential issues</a>.",
|
||||
"alert.help.notice": "Notification is used to config the receiver of alarm message and receiving method. The alarm message will be sent to the receiver by specified way(support email, discord, webhook etc). <a href='https://hertzbeat.apache.org/zh-cn/docs/help/alert_webhook'>Click here to see configuration steps.</a>.<br>\"<i>Notice Template</i>\" is message content structure template. The built-in template is used by default or you can customize the template to customize the message notification structure.<br><span class='help_module_span'>Note⚠️: After configuring the \"<i>Receiver</i>\", you also need to config the\"<i>Notice Policy</i>\"to specify which messages are sent to which receivers.</span><a href='https://hertzbeat.apache.org/docs/help/alert_email'> Click here to see potential issues</a>.",
|
||||
"alert.help.notice.link": "https://hertzbeat.apache.org/docs/help/alert_email",
|
||||
"alert.help.setting": "Threshold Rules are used for metrics alarm threshold rule management. Click the \"<i>New Threshold</i>\" to configure the alarm threshold for monitoring metrics. Hertzbeat will trigger alarms based on the threshold and metrics data.<br>Note⚠️: The alarm message that has been triggered can be checked in [Alter Center], and you can also set the notification method and personnel in [Notification].",
|
||||
"alert.help.setting.link": "https://hertzbeat.apache.org/docs/help/alert_threshold",
|
||||
"alert.help.silence": "Alarm Silence management is used when you don’t want to be disturbed during system maintenance or on nights weekend. <br> Click \"<i>New Silence Strategy</i>\" and configure the time period to block messages so you would not get disturbed during breaks.",
|
||||
"alert.help.silence": "Alarm Silence management is used when you don't want to be disturbed during system maintenance or on nights weekend. <br> Click \"<i>New Silence Strategy</i>\" and configure the time period to block messages so you would not get disturbed during breaks.",
|
||||
"alert.help.silence.link": "https://hertzbeat.apache.org/docs",
|
||||
"alert.inhibit.delete": "Delete Inhibit Rule",
|
||||
"alert.inhibit.edit": "Edit Inhibit Rule",
|
||||
@@ -701,10 +701,12 @@
|
||||
"monitor.edit-monitor": "Edit Monitor",
|
||||
"monitor.edit.failed": "Update Monitor Failed",
|
||||
"monitor.edit.success": "Update Monitor Success",
|
||||
"monitor.enable": "Resume Monitor",
|
||||
"monitor.export": "Export Monitor",
|
||||
"monitor.export.switch-type": "Please select the export file format!",
|
||||
"monitor.export.use-type": "Export monitors in {{type}} file format",
|
||||
"monitor.enable": "Enable",
|
||||
"monitor.export": "Export Selected",
|
||||
"monitor.export-all": "Export All",
|
||||
"monitor.export.switch-type": "Please select the export file format",
|
||||
"monitor.export.use-type": "Export selected monitors in {{type}} format",
|
||||
"monitor.export-all.use-type": "Export all monitors in {{type}} format",
|
||||
"monitor.grafana.enabled.label": "Enable Grafana",
|
||||
"monitor.grafana.enabled.tip": "is enabled, the monitoring data will be displayed in Grafana",
|
||||
"monitor.grafana.upload.label": "Upload Grafana Template",
|
||||
@@ -912,5 +914,6 @@
|
||||
"ai.bot.greeting": "Hello! I am an AI assistant. How can I help you?",
|
||||
"ai.bot.input.placeholder": "Please enter a question...",
|
||||
"ai.bot.send": "Send",
|
||||
"ai.bot.connect-fail": "Sorry, there was an issue connecting to the AI assistant. Please try again later."
|
||||
"ai.bot.connect-fail": "Sorry, there was an issue connecting to the AI assistant. Please try again later.",
|
||||
"monitor.help": "Monitoring and management page, you can check the metric data and manage monitoring tasks here. The status of normal service is"
|
||||
}
|
||||
|
||||
@@ -702,9 +702,11 @@
|
||||
"monitor.edit.failed": "修改监控失败",
|
||||
"monitor.edit.success": "修改监控成功",
|
||||
"monitor.enable": "恢复监控",
|
||||
"monitor.export": "导出监控",
|
||||
"monitor.export": "导出所选",
|
||||
"monitor.export-all": "导出全部",
|
||||
"monitor.export.switch-type": "请选择导出文件格式!",
|
||||
"monitor.export.use-type": "以 {{type}} 文件格式导出监控",
|
||||
"monitor.export.use-type": "以 {{type}} 文件格式导出所选监控",
|
||||
"monitor.export-all.use-type": "以 {{type}} 文件格式导出全部监控",
|
||||
"monitor.grafana.enabled.label": "启用Grafana",
|
||||
"monitor.grafana.enabled.tip": "是否启用Grafana",
|
||||
"monitor.grafana.upload.label": "上传Grafana模板",
|
||||
|
||||
Reference in New Issue
Block a user