mirror of
https://github.com/dromara/hertzbeat.git
synced 2026-09-17 18:19:02 +00:00
Compare commits
12
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a65e23b9b | ||
|
|
49da06219f | ||
|
|
8e426f391d | ||
|
|
e8bec5d890 | ||
|
|
045f2e165a | ||
|
|
d3bfd65686 | ||
|
|
8b82986064 | ||
|
|
7b129d5ead | ||
|
|
fb07107171 | ||
|
|
0dc79bc6c7 | ||
|
|
19c0b78e2a | ||
|
|
7528db346d |
@@ -73,7 +73,7 @@ jobs:
|
||||
|
||||
# upload application logs
|
||||
- name: Upload logs & API test reports
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: hz-logs-${{ github.run_id }}
|
||||
|
||||
@@ -39,7 +39,7 @@ jobs:
|
||||
run: npx docusaurus-prince-pdf -u https://hertzbeat.apache.org/docs --output docs-en.pdf
|
||||
|
||||
- name: Upload results
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: docs-cn-pdf
|
||||
path: docs-cn.pdf
|
||||
@@ -47,7 +47,7 @@ jobs:
|
||||
retention-days: 1
|
||||
|
||||
- name: Upload results
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: docs-en-pdf
|
||||
path: docs-en.pdf
|
||||
|
||||
@@ -29,11 +29,6 @@
|
||||
"type": 0,
|
||||
"paramValue": 1000
|
||||
},
|
||||
{
|
||||
"field": "ssl",
|
||||
"type": 1,
|
||||
"paramValue": false
|
||||
},
|
||||
{
|
||||
"field": "username",
|
||||
"type": 1
|
||||
|
||||
@@ -40,11 +40,6 @@
|
||||
<groupId>org.apache.hertzbeat</groupId>
|
||||
<artifactId>hertzbeat-plugin</artifactId>
|
||||
</dependency>
|
||||
<!-- warehouse -->
|
||||
<dependency>
|
||||
<groupId>org.apache.hertzbeat</groupId>
|
||||
<artifactId>hertzbeat-warehouse</artifactId>
|
||||
</dependency>
|
||||
<!-- spring -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
||||
+50
-132
@@ -17,172 +17,90 @@
|
||||
|
||||
package org.apache.hertzbeat.alert.calculate;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Objects;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce;
|
||||
import org.apache.hertzbeat.alert.service.DataSourceService;
|
||||
import org.apache.hertzbeat.alert.util.AlertTemplateUtil;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertDefine;
|
||||
import org.apache.hertzbeat.common.entity.alerter.SingleAlert;
|
||||
import org.apache.hertzbeat.common.util.JexlExpressionRunner;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Periodic Alert Calculator
|
||||
*/
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class PeriodicAlertCalculator {
|
||||
|
||||
private static final String VALUE = "__value__";
|
||||
private static final String TIMESTAMP = "__timestamp__";
|
||||
|
||||
private final DataSourceService dataSourceService;
|
||||
private final AlarmCommonReduce alarmCommonReduce;
|
||||
/**
|
||||
* The alarm in the process is triggered
|
||||
* key - labels fingerprint
|
||||
*/
|
||||
private final Map<String, SingleAlert> pendingAlertMap;
|
||||
/**
|
||||
* The not recover alert
|
||||
* key - labels fingerprint
|
||||
*/
|
||||
private final Map<String, SingleAlert> firingAlertMap;
|
||||
private final JexlExpressionRunner expressionRunner;
|
||||
private final Map<String, SingleAlert> notRecoveredAlertMap = new ConcurrentHashMap<>(16);
|
||||
|
||||
public PeriodicAlertCalculator(DataSourceService dataSourceService, AlarmCommonReduce alarmCommonReduce) {
|
||||
this.dataSourceService = dataSourceService;
|
||||
this.alarmCommonReduce = alarmCommonReduce;
|
||||
this.pendingAlertMap = new ConcurrentHashMap<>(8);
|
||||
this.firingAlertMap = new ConcurrentHashMap<>(8);
|
||||
}
|
||||
|
||||
public void calculate(AlertDefine rule) {
|
||||
public List<SingleAlert> calculate(AlertDefine rule) {
|
||||
if (!rule.isEnable() || StringUtils.isEmpty(rule.getExpr())) {
|
||||
log.error("Periodic rule {} is disabled or expression is empty", rule.getName());
|
||||
return;
|
||||
return Collections.emptyList();
|
||||
}
|
||||
long currentTimeMilli = System.currentTimeMillis();
|
||||
// todo: implement the following logic
|
||||
try {
|
||||
// for prometheus is instant promql query, for db is sql query
|
||||
// result: [{'value': 100, 'timestamp': 1343554, 'instance': 'node1'},{'value': 200, 'timestamp': 1343555, 'instance': 'node2'}]
|
||||
// the return result should be matched with threshold
|
||||
try {
|
||||
List<Map<String, Object>> results = dataSourceService.calculate(
|
||||
rule.getDatasource(),
|
||||
rule.getExpr()
|
||||
);
|
||||
// if no match the expr threshold, the results item map {'value': null} should be null and others field keep
|
||||
// if results has multi list, should trigger multi alert
|
||||
if (CollectionUtils.isEmpty(results)) {
|
||||
return;
|
||||
}
|
||||
for (Map<String, Object> result : results) {
|
||||
Map<String, String> fingerPrints = new HashMap<>(8);
|
||||
// here use the alert name as finger, not care the alert name may be changed
|
||||
fingerPrints.put(CommonConstants.LABEL_ALERT_NAME, rule.getName());
|
||||
fingerPrints.putAll(rule.getLabels());
|
||||
for (Map.Entry<String, Object> entry : result.entrySet()) {
|
||||
if (entry.getValue() != null && !VALUE.equals(entry.getKey())
|
||||
&& !TIMESTAMP.equals(entry.getKey())) {
|
||||
fingerPrints.put(entry.getKey(), entry.getValue().toString());
|
||||
}
|
||||
}
|
||||
if (result.get(VALUE) == null) {
|
||||
// recovery the alert
|
||||
handleRecoveredAlert(fingerPrints);
|
||||
continue;
|
||||
}
|
||||
Map<String, Object> fieldValueMap = new HashMap<>(8);
|
||||
fieldValueMap.putAll(rule.getLabels());
|
||||
fieldValueMap.put(CommonConstants.LABEL_ALERT_NAME, rule.getName());
|
||||
for (Map.Entry<String, Object> entry : result.entrySet()) {
|
||||
if (entry.getValue() != null) {
|
||||
fieldValueMap.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
afterThresholdRuleMatch(currentTimeMilli, fingerPrints, fieldValueMap, rule);
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
// ignore the query exception eg: no result, timeout, etc
|
||||
return;
|
||||
// Execute query
|
||||
List<Map<String, Object>> queryResults = dataSourceService.query(
|
||||
rule.getDatasource(),
|
||||
rule.getExpr()
|
||||
);
|
||||
|
||||
if (CollectionUtils.isEmpty(queryResults)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// Execute expression calculation on query results
|
||||
List<SingleAlert> newAlerts = queryResults.stream()
|
||||
.filter(result -> execAlertExpression(result, rule.getExpr()))
|
||||
.map(result -> buildAlert(rule, result))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// Handle recovery notification
|
||||
if (newAlerts.isEmpty()) {
|
||||
return handleAlertRecover(rule);
|
||||
}
|
||||
|
||||
return newAlerts;
|
||||
} catch (Exception e) {
|
||||
log.error("Calculate periodic rule {} failed: {}", rule.getName(), e.getMessage());
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
private void afterThresholdRuleMatch(long currentTimeMilli, Map<String, String> fingerPrints,
|
||||
Map<String, Object> fieldValueMap, AlertDefine define) {
|
||||
String fingerprint = calculateFingerprint(fingerPrints);
|
||||
SingleAlert existingAlert = pendingAlertMap.get(fingerprint);
|
||||
Map<String, String> labels = new HashMap<>(8);
|
||||
fieldValueMap.putAll(define.getLabels());
|
||||
labels.putAll(fingerPrints);
|
||||
int requiredTimes = define.getTimes() == null ? 1 : define.getTimes();
|
||||
if (existingAlert == null) {
|
||||
// First time triggering alert, create new alert and set to pending status
|
||||
SingleAlert newAlert = SingleAlert.builder()
|
||||
.labels(labels)
|
||||
// todo render var content in annotations
|
||||
.annotations(define.getAnnotations())
|
||||
.content(AlertTemplateUtil.render(define.getTemplate(), fieldValueMap))
|
||||
.status(CommonConstants.ALERT_STATUS_PENDING)
|
||||
.triggerTimes(1)
|
||||
.startAt(currentTimeMilli)
|
||||
.activeAt(currentTimeMilli)
|
||||
.build();
|
||||
|
||||
// If required trigger times is 1, set to firing status directly
|
||||
if (requiredTimes <= 1) {
|
||||
newAlert.setStatus(CommonConstants.ALERT_STATUS_FIRING);
|
||||
firingAlertMap.put(fingerprint, newAlert);
|
||||
alarmCommonReduce.reduceAndSendAlarm(newAlert.clone());
|
||||
} else {
|
||||
// Otherwise put into pending queue first
|
||||
pendingAlertMap.put(fingerprint, newAlert);
|
||||
}
|
||||
} else {
|
||||
// Update existing alert
|
||||
existingAlert.setTriggerTimes(existingAlert.getTriggerTimes() + 1);
|
||||
existingAlert.setActiveAt(currentTimeMilli);
|
||||
|
||||
// Check if required trigger times reached
|
||||
if (existingAlert.getStatus().equals(CommonConstants.ALERT_STATUS_PENDING) && existingAlert.getTriggerTimes() >= requiredTimes) {
|
||||
// Reached trigger times threshold, change to firing status
|
||||
pendingAlertMap.remove(fingerprint);
|
||||
existingAlert.setStatus(CommonConstants.ALERT_STATUS_FIRING);
|
||||
firingAlertMap.put(fingerprint, existingAlert);
|
||||
alarmCommonReduce.reduceAndSendAlarm(existingAlert.clone());
|
||||
}
|
||||
}
|
||||
private boolean execAlertExpression(Map<String, Object> result, String expr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void handleRecoveredAlert(Map<String, String> fingerprints) {
|
||||
String fingerprint = calculateFingerprint(fingerprints);
|
||||
SingleAlert firingAlert = firingAlertMap.remove(fingerprint);
|
||||
private SingleAlert buildAlert(AlertDefine rule, Map<String, Object> metrics) {
|
||||
return SingleAlert.builder()
|
||||
.labels(rule.getLabels())
|
||||
.annotations(rule.getAnnotations())
|
||||
.triggerTimes(1)
|
||||
.startAt(System.currentTimeMillis())
|
||||
.activeAt(System.currentTimeMillis())
|
||||
.build();
|
||||
}
|
||||
|
||||
private List<SingleAlert> handleAlertRecover(AlertDefine rule) {
|
||||
SingleAlert firingAlert = notRecoveredAlertMap.remove(rule.getId().toString());
|
||||
if (firingAlert != null) {
|
||||
// todo consider multi times to tig for resolved alert
|
||||
firingAlert.setTriggerTimes(1);
|
||||
firingAlert.setEndAt(System.currentTimeMillis());
|
||||
firingAlert.setStatus(CommonConstants.ALERT_STATUS_RESOLVED);
|
||||
alarmCommonReduce.reduceAndSendAlarm(firingAlert.clone());
|
||||
return Collections.singletonList(buildResolvedAlert(rule, firingAlert));
|
||||
}
|
||||
pendingAlertMap.remove(fingerprint);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private String calculateFingerprint(Map<String, String> fingerPrints) {
|
||||
List<String> keyList = fingerPrints.keySet().stream().filter(Objects::nonNull).sorted().toList();
|
||||
List<String> valueList = fingerPrints.values().stream().filter(Objects::nonNull).sorted().toList();
|
||||
return Arrays.hashCode(keyList.toArray(new String[0])) + "-"
|
||||
+ Arrays.hashCode(valueList.toArray(new String[0]));
|
||||
private SingleAlert buildResolvedAlert(AlertDefine rule, SingleAlert firingAlert) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
+4
-70
@@ -17,79 +17,13 @@
|
||||
|
||||
package org.apache.hertzbeat.alert.calculate;
|
||||
|
||||
import static org.apache.hertzbeat.common.constants.CommonConstants.ALERT_THRESHOLD_TYPE_PERIODIC;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.alert.dao.AlertDefineDao;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertDefine;
|
||||
|
||||
/**
|
||||
* Periodic Alert Rule Scheduler
|
||||
* period alert rule scheduler
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class PeriodicAlertRuleScheduler implements CommandLineRunner {
|
||||
|
||||
private final PeriodicAlertCalculator calculator;
|
||||
private final AlertDefineDao alertDefineDao;
|
||||
private final ScheduledExecutorService scheduledExecutor;
|
||||
private final Map<Long, ScheduledFuture<?>> scheduledFutures;
|
||||
|
||||
public PeriodicAlertRuleScheduler(PeriodicAlertCalculator calculator, AlertDefineDao alertDefineDao) {
|
||||
this.calculator = calculator;
|
||||
this.alertDefineDao = alertDefineDao;
|
||||
ThreadFactory threadFactory = new ThreadFactoryBuilder()
|
||||
.setUncaughtExceptionHandler((thread, throwable) -> {
|
||||
log.error("Scheduled periodic alert threshold has uncaughtException.");
|
||||
log.error(throwable.getMessage(), throwable);
|
||||
})
|
||||
.setDaemon(true)
|
||||
.setNameFormat("periodic-alert-threshold-worker-%d")
|
||||
.build();
|
||||
this.scheduledExecutor = Executors.newScheduledThreadPool(10, threadFactory);
|
||||
this.scheduledFutures = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
public void cancelSchedule(Long ruleId) {
|
||||
if (ruleId == null) {
|
||||
return;
|
||||
}
|
||||
ScheduledFuture<?> future = scheduledFutures.get(ruleId);
|
||||
if (future != null) {
|
||||
future.cancel(true);
|
||||
scheduledFutures.remove(ruleId);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateSchedule(AlertDefine rule) {
|
||||
if (rule == null || rule.getId() == null) {
|
||||
log.error("Alert rule is null or rule id is null.");
|
||||
return;
|
||||
}
|
||||
cancelSchedule(rule.getId());
|
||||
if (rule.getType().equals(ALERT_THRESHOLD_TYPE_PERIODIC)) {
|
||||
ScheduledFuture<?> future = scheduledExecutor.scheduleAtFixedRate(() -> {
|
||||
calculator.calculate(rule);
|
||||
}, 0, rule.getPeriod(), java.util.concurrent.TimeUnit.SECONDS);
|
||||
scheduledFutures.put(rule.getId(), future);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
log.info("Starting periodic alert rule scheduler...");
|
||||
List<AlertDefine> periodicRules = alertDefineDao.findAlertDefinesByTypeAndEnableTrue(ALERT_THRESHOLD_TYPE_PERIODIC);
|
||||
for (AlertDefine rule : periodicRules) {
|
||||
updateSchedule(rule);
|
||||
}
|
||||
}
|
||||
public class PeriodicAlertRuleScheduler {
|
||||
|
||||
// todo implement the following logic
|
||||
}
|
||||
|
||||
-63
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* 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.alert.config;
|
||||
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* SSE manager for alert
|
||||
*/
|
||||
@Component
|
||||
public class AlertSseManager {
|
||||
private final Map<Long, SseEmitter> emitters = new ConcurrentHashMap<>();
|
||||
|
||||
public SseEmitter createEmitter(Long clientId) {
|
||||
SseEmitter emitter = new SseEmitter(Long.MAX_VALUE);
|
||||
emitter.onCompletion(() -> removeEmitter(clientId));
|
||||
emitter.onTimeout(() -> removeEmitter(clientId));
|
||||
emitters.put(clientId, emitter);
|
||||
return emitter;
|
||||
}
|
||||
|
||||
@Async
|
||||
public void broadcast(String data) {
|
||||
emitters.forEach((clientId, emitter) -> {
|
||||
try {
|
||||
emitter.send(SseEmitter.event()
|
||||
.id(String.valueOf(System.currentTimeMillis()))
|
||||
.name("ALERT_EVENT")
|
||||
.data(data));
|
||||
} catch (IOException e) {
|
||||
emitter.complete();
|
||||
removeEmitter(clientId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void removeEmitter(Long clientId) {
|
||||
emitters.remove(clientId);
|
||||
}
|
||||
}
|
||||
+7
-7
@@ -38,9 +38,9 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* Alarm Inhibit management API
|
||||
* Alarm Silence management API
|
||||
*/
|
||||
@Tag(name = "Alert Inhibit API")
|
||||
@Tag(name = "Alert Silence API")
|
||||
@RestController
|
||||
@RequestMapping(path = "/api/alert/inhibit", produces = {APPLICATION_JSON_VALUE})
|
||||
public class AlertInhibitController {
|
||||
@@ -49,7 +49,7 @@ public class AlertInhibitController {
|
||||
private AlertInhibitService alertInhibitService;
|
||||
|
||||
@PostMapping
|
||||
@Operation(summary = "New Alarm Inhibit", description = "Added an alarm Inhibit")
|
||||
@Operation(summary = "New Alarm Silence", description = "Added an alarm Silence")
|
||||
public ResponseEntity<Message<Void>> addNewAlertInhibit(@Valid @RequestBody AlertInhibit alertInhibit) {
|
||||
alertInhibitService.validate(alertInhibit, false);
|
||||
alertInhibitService.addAlertInhibit(alertInhibit);
|
||||
@@ -57,7 +57,7 @@ public class AlertInhibitController {
|
||||
}
|
||||
|
||||
@PutMapping
|
||||
@Operation(summary = "Modifying an Alarm Inhibit", description = "Modify an existing alarm Inhibit")
|
||||
@Operation(summary = "Modifying an Alarm Silence", description = "Modify an existing alarm Silence")
|
||||
public ResponseEntity<Message<Void>> modifyAlertInhibit(@Valid @RequestBody AlertInhibit alertInhibit) {
|
||||
alertInhibitService.validate(alertInhibit, true);
|
||||
alertInhibitService.modifyAlertInhibit(alertInhibit);
|
||||
@@ -65,10 +65,10 @@ public class AlertInhibitController {
|
||||
}
|
||||
|
||||
@GetMapping(path = "/{id}")
|
||||
@Operation(summary = "Querying Alarm Inhibit",
|
||||
description = "You can obtain alarm Inhibit information based on the alarm Inhibit ID")
|
||||
@Operation(summary = "Querying Alarm Silence",
|
||||
description = "You can obtain alarm Silence information based on the alarm Silence ID")
|
||||
public ResponseEntity<Message<AlertInhibit>> getAlertInhibit(
|
||||
@Parameter(description = "Alarm Inhibit ID", example = "6565463543") @PathVariable("id") long id) {
|
||||
@Parameter(description = "Alarm Silence ID", example = "6565463543") @PathVariable("id") long id) {
|
||||
AlertInhibit alertInhibit = alertInhibitService.getAlertInhibit(id);
|
||||
|
||||
return Objects.isNull(alertInhibit)
|
||||
|
||||
+4
-4
@@ -36,9 +36,9 @@ import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* Inhibit the batch API for alarms
|
||||
* Silence the batch API for alarms
|
||||
*/
|
||||
@Tag(name = "Alert Inhibit Batch API")
|
||||
@Tag(name = "Alert Silence Batch API")
|
||||
@RestController
|
||||
@RequestMapping(path = "/api/alert/inhibits", produces = {APPLICATION_JSON_VALUE})
|
||||
public class AlertInhibitsController {
|
||||
@@ -50,7 +50,7 @@ public class AlertInhibitsController {
|
||||
@Operation(summary = "Query the alarm inhibit list",
|
||||
description = "You can obtain the list of alarm inhibit by querying filter items")
|
||||
public ResponseEntity<Message<Page<AlertInhibit>>> getAlertInhibits(
|
||||
@Parameter(description = "Alarm Inhibit ID", example = "6565463543") @RequestParam(required = false) List<Long> ids,
|
||||
@Parameter(description = "Alarm Silence ID", example = "6565463543") @RequestParam(required = false) List<Long> ids,
|
||||
@Parameter(description = "Search Name", example = "x") @RequestParam(required = false) String search,
|
||||
@Parameter(description = "Sort field, default id", example = "id") @RequestParam(defaultValue = "id") String sort,
|
||||
@Parameter(description = "Sort mode: asc: ascending, desc: descending", example = "desc") @RequestParam(defaultValue = "desc") String order,
|
||||
@@ -64,7 +64,7 @@ public class AlertInhibitsController {
|
||||
@Operation(summary = "Delete alarm inhibit in batches",
|
||||
description = "Delete alarm inhibit in batches based on the alarm inhibit ID list")
|
||||
public ResponseEntity<Message<Void>> deleteAlertDefines(
|
||||
@Parameter(description = "Alarm Inhibit IDs", example = "6565463543") @RequestParam(required = false) List<Long> ids
|
||||
@Parameter(description = "Alarm Silence IDs", example = "6565463543") @RequestParam(required = false) List<Long> ids
|
||||
) {
|
||||
if (ids != null && !ids.isEmpty()) {
|
||||
alertInhibitService.deleteAlertInhibits(new HashSet<>(ids));
|
||||
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* 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.alert.controller;
|
||||
|
||||
import static org.springframework.http.MediaType.TEXT_EVENT_STREAM_VALUE;
|
||||
import org.apache.hertzbeat.alert.config.AlertSseManager;
|
||||
import org.apache.hertzbeat.common.util.SnowFlakeIdGenerator;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||
|
||||
/**
|
||||
* SSE controller for alert
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping(path = "/api/alert/sse", produces = {TEXT_EVENT_STREAM_VALUE})
|
||||
public class AlertSseController {
|
||||
|
||||
private final AlertSseManager emitterManager;
|
||||
|
||||
public AlertSseController(AlertSseManager emitterManager) {
|
||||
this.emitterManager = emitterManager;
|
||||
}
|
||||
|
||||
@GetMapping(path = "/subscribe")
|
||||
public SseEmitter subscribe() {
|
||||
Long clientId = SnowFlakeIdGenerator.generateId();
|
||||
return emitterManager.createEmitter(clientId);
|
||||
}
|
||||
}
|
||||
+9
-31
@@ -31,7 +31,6 @@ import org.apache.hertzbeat.common.entity.alerter.NoticeRule;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeTemplate;
|
||||
import org.apache.hertzbeat.alert.service.NoticeConfigService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
@@ -83,18 +82,9 @@ public class NoticeConfigController {
|
||||
@GetMapping(path = "/receivers")
|
||||
@Operation(summary = "Get a list of message notification recipients based on query filter items",
|
||||
description = "Get a list of message notification recipients based on query filter items")
|
||||
public ResponseEntity<Message<Page<NoticeReceiver>>> getReceivers(
|
||||
@Parameter(description = "en: Recipient name,support fuzzy query", example = "tom") @RequestParam(required = false) final String name,
|
||||
@Parameter(description = "en: List current page", example = "0") @RequestParam(defaultValue = "0") final int pageIndex,
|
||||
@Parameter(description = "en: Number of list pages", example = "8") @RequestParam(defaultValue = "8") final int pageSize) {
|
||||
return ResponseEntity.ok(Message.success(noticeConfigService.getNoticeReceivers(name, pageIndex, pageSize)));
|
||||
}
|
||||
|
||||
@GetMapping(path = "/receivers/all")
|
||||
@Operation(summary = "Get a list of all message notification recipients",
|
||||
description = "Get a list of all message notification recipients")
|
||||
public ResponseEntity<Message<List<NoticeReceiver>>> getAllReceivers() {
|
||||
return ResponseEntity.ok(Message.success(noticeConfigService.getAllNoticeReceivers()));
|
||||
public ResponseEntity<Message<List<NoticeReceiver>>> getReceivers(
|
||||
@Parameter(description = "en: Recipient name,support fuzzy query", example = "tom") @RequestParam(required = false) final String name) {
|
||||
return ResponseEntity.ok(Message.success(noticeConfigService.getNoticeReceivers(name)));
|
||||
}
|
||||
|
||||
@GetMapping(path = "/receiver/{id}")
|
||||
@@ -139,11 +129,9 @@ public class NoticeConfigController {
|
||||
@GetMapping(path = "/rules")
|
||||
@Operation(summary = "Get a list of message notification policies based on query filter items",
|
||||
description = "Get a list of message notification policies based on query filter items")
|
||||
public ResponseEntity<Message<Page<NoticeRule>>> getRules(
|
||||
@Parameter(description = "en: Recipient name", example = "rule1") @RequestParam(required = false) final String name,
|
||||
@Parameter(description = "en: List current page", example = "0") @RequestParam(defaultValue = "0") final int pageIndex,
|
||||
@Parameter(description = "en: Number of list pages", example = "8") @RequestParam(defaultValue = "8") final int pageSize) {
|
||||
return ResponseEntity.ok(Message.success(noticeConfigService.getNoticeRules(name, pageIndex, pageSize)));
|
||||
public ResponseEntity<Message<List<NoticeRule>>> getRules(
|
||||
@Parameter(description = "en: Recipient name", example = "rule1") @RequestParam(required = false) final String name) {
|
||||
return ResponseEntity.ok(Message.success(noticeConfigService.getNoticeRules(name)));
|
||||
}
|
||||
|
||||
@GetMapping(path = "/rule/{id}")
|
||||
@@ -188,22 +176,12 @@ public class NoticeConfigController {
|
||||
@GetMapping(path = "/templates")
|
||||
@Operation(summary = "Get a list of message notification templates based on query filter items",
|
||||
description = "Get a list of message notification templates based on query filter items")
|
||||
public ResponseEntity<Message<Page<NoticeTemplate>>> getTemplates(
|
||||
@Parameter(description = "Template name,support fuzzy query", example = "rule1") @RequestParam(required = false) final String name,
|
||||
@Parameter(description = "Whether it is a preset template", example = "true") @RequestParam(defaultValue = "true") final boolean preset,
|
||||
@Parameter(description = "List current page", example = "0") @RequestParam(defaultValue = "0") final int pageIndex,
|
||||
@Parameter(description = "Number of list pages", example = "8") @RequestParam(defaultValue = "8") final int pageSize) {
|
||||
Page<NoticeTemplate> templatePage = noticeConfigService.getNoticeTemplates(name, preset, pageIndex, pageSize);
|
||||
public ResponseEntity<Message<List<NoticeTemplate>>> getTemplates(
|
||||
@Parameter(description = "Template name,support fuzzy query", example = "rule1") @RequestParam(required = false) final String name) {
|
||||
List<NoticeTemplate> templatePage = noticeConfigService.getNoticeTemplates(name);
|
||||
return ResponseEntity.ok(Message.success(templatePage));
|
||||
}
|
||||
|
||||
@GetMapping(path = "/templates/all")
|
||||
@Operation(summary = "Get a list of all message notification templates",
|
||||
description = "Get a list of all message notification templates")
|
||||
public ResponseEntity<Message<List<NoticeTemplate>>> getAllTemplates() {
|
||||
return ResponseEntity.ok(Message.success(noticeConfigService.getAllNoticeTemplates()));
|
||||
}
|
||||
|
||||
@GetMapping(path = "/template/{id}")
|
||||
@Operation(summary = "Get the notification template information based on the template ID",
|
||||
description = "Get the notification template information based on the template ID")
|
||||
|
||||
+16
-20
@@ -23,13 +23,11 @@ import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.alert.AlerterWorkerPool;
|
||||
import org.apache.hertzbeat.alert.config.AlertSseManager;
|
||||
import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeRule;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeTemplate;
|
||||
import org.apache.hertzbeat.alert.service.NoticeConfigService;
|
||||
import org.apache.hertzbeat.common.util.JsonUtil;
|
||||
import org.apache.hertzbeat.plugin.PostAlertPlugin;
|
||||
import org.apache.hertzbeat.plugin.Plugin;
|
||||
import org.apache.hertzbeat.plugin.runner.PluginRunner;
|
||||
@@ -47,18 +45,16 @@ public class AlertNoticeDispatch {
|
||||
private final AlertStoreHandler alertStoreHandler;
|
||||
private final Map<Byte, AlertNotifyHandler> alertNotifyHandlerMap;
|
||||
private final PluginRunner pluginRunner;
|
||||
private final AlertSseManager emitterManager;
|
||||
|
||||
public AlertNoticeDispatch(AlerterWorkerPool workerPool,
|
||||
NoticeConfigService noticeConfigService,
|
||||
AlertStoreHandler alertStoreHandler,
|
||||
List<AlertNotifyHandler> alertNotifyHandlerList, PluginRunner pluginRunner, AlertSseManager emitterManager) {
|
||||
List<AlertNotifyHandler> alertNotifyHandlerList, PluginRunner pluginRunner) {
|
||||
this.workerPool = workerPool;
|
||||
this.noticeConfigService = noticeConfigService;
|
||||
this.alertStoreHandler = alertStoreHandler;
|
||||
this.pluginRunner = pluginRunner;
|
||||
alertNotifyHandlerMap = Maps.newHashMapWithExpectedSize(alertNotifyHandlerList.size());
|
||||
this.emitterManager = emitterManager;
|
||||
alertNotifyHandlerList.forEach(r -> alertNotifyHandlerMap.put(r.type(), r));
|
||||
}
|
||||
|
||||
@@ -108,27 +104,27 @@ public class AlertNoticeDispatch {
|
||||
public void dispatchAlarm(GroupAlert groupAlert) {
|
||||
if (groupAlert != null) {
|
||||
// Determining alarm type storage
|
||||
GroupAlert storedGroupAlert = alertStoreHandler.store(groupAlert);
|
||||
alertStoreHandler.store(groupAlert);
|
||||
// Notice distribution
|
||||
sendNotify(storedGroupAlert);
|
||||
sendNotify(groupAlert);
|
||||
// Execute the plugin if enable (Compatible with old version plugins, will be removed in later versions)
|
||||
pluginRunner.pluginExecute(Plugin.class, plugin -> plugin.alert(storedGroupAlert));
|
||||
pluginRunner.pluginExecute(Plugin.class, plugin -> plugin.alert(groupAlert));
|
||||
// Execute the plugin if enable with params
|
||||
pluginRunner.pluginExecute(PostAlertPlugin.class, (afterAlertPlugin, pluginContext) -> afterAlertPlugin.execute(storedGroupAlert, pluginContext));
|
||||
// Send alert to the sse client
|
||||
emitterManager.broadcast(JsonUtil.toJson(storedGroupAlert));
|
||||
pluginRunner.pluginExecute(PostAlertPlugin.class, (afterAlertPlugin, pluginContext) -> afterAlertPlugin.execute(groupAlert, pluginContext));
|
||||
}
|
||||
}
|
||||
|
||||
private void sendNotify(GroupAlert alert) {
|
||||
matchNoticeRulesByAlert(alert).ifPresent(noticeRules -> noticeRules.forEach(rule -> workerPool.executeNotify(() -> rule.getReceiverId()
|
||||
.forEach(receiverId -> {
|
||||
try {
|
||||
sendNoticeMsg(getOneReceiverById(receiverId),
|
||||
getOneTemplateById(rule.getTemplateId()), alert);
|
||||
} catch (AlertNoticeException e) {
|
||||
log.warn("DispatchTask sendNoticeMsg error, message: {}", e.getMessage());
|
||||
}
|
||||
}))));
|
||||
matchNoticeRulesByAlert(alert).ifPresent(noticeRules -> noticeRules.forEach(rule -> {
|
||||
workerPool.executeNotify(() -> rule.getReceiverId()
|
||||
.forEach(receiverId -> {
|
||||
try {
|
||||
sendNoticeMsg(getOneReceiverById(receiverId),
|
||||
getOneTemplateById(rule.getTemplateId()), alert);
|
||||
} catch (AlertNoticeException e) {
|
||||
log.warn("DispatchTask sendNoticeMsg error, message: {}", e.getMessage());
|
||||
}
|
||||
}));
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
+1
-3
@@ -28,9 +28,7 @@ public interface AlertStoreHandler {
|
||||
* Persistent alarm records
|
||||
* It is necessary to associate and assign values
|
||||
* to the alert tag information tags while persisting.
|
||||
*
|
||||
* @param alert alarm information
|
||||
* @return groupAlert
|
||||
*/
|
||||
GroupAlert store(GroupAlert alert);
|
||||
void store(GroupAlert alert);
|
||||
}
|
||||
|
||||
+14
-26
@@ -17,7 +17,6 @@
|
||||
|
||||
package org.apache.hertzbeat.alert.notice.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -47,53 +46,44 @@ final class DbAlertStoreHandlerImpl implements AlertStoreHandler {
|
||||
private final SingleAlertDao singleAlertDao;
|
||||
|
||||
@Override
|
||||
public GroupAlert store(GroupAlert groupAlert) {
|
||||
public void store(GroupAlert groupAlert) {
|
||||
if (groupAlert == null || groupAlert.getAlerts() == null || groupAlert.getAlerts().isEmpty()) {
|
||||
log.error("The Group Alerts is empty, ignore store");
|
||||
return groupAlert;
|
||||
return;
|
||||
}
|
||||
// 1. Find existing alert group
|
||||
GroupAlert existGroupAlert = groupAlertDao.findByGroupKey(groupAlert.getGroupKey());
|
||||
|
||||
// 2. Process individual alerts
|
||||
Set<String> alertFingerprints = new HashSet<>(8);
|
||||
|
||||
List<SingleAlert> originalAlerts = groupAlert.getAlerts();
|
||||
List<SingleAlert> newAlerts = new ArrayList<>();
|
||||
|
||||
|
||||
for (SingleAlert singleAlert : originalAlerts) {
|
||||
groupAlert.getAlerts().forEach(singleAlert -> {
|
||||
SingleAlert existAlert = singleAlertDao.findByFingerprint(singleAlert.getFingerprint());
|
||||
|
||||
if (existAlert != null) {
|
||||
// Update the existing alert with the ID and creation time from the database
|
||||
// Update existing alert
|
||||
singleAlert.setId(existAlert.getId());
|
||||
singleAlert.setGmtCreate(existAlert.getGmtCreate());
|
||||
|
||||
|
||||
// Status transition logic
|
||||
if (CommonConstants.ALERT_STATUS_FIRING.equals(singleAlert.getStatus())) {
|
||||
// If the alert is firing and the existing alert is not resolved, update the start time and trigger times
|
||||
if (!CommonConstants.ALERT_STATUS_RESOLVED.equals(existAlert.getStatus())) {
|
||||
singleAlert.setStartAt(existAlert.getStartAt());
|
||||
int triggerTimes = Optional.ofNullable(existAlert.getTriggerTimes()).orElse(1)
|
||||
+ Optional.ofNullable(singleAlert.getTriggerTimes()).orElse(1);
|
||||
int triggerTimes = Optional.ofNullable(existAlert.getTriggerTimes()).orElse(1) + Optional.ofNullable(singleAlert.getTriggerTimes()).orElse(1);
|
||||
singleAlert.setTriggerTimes(triggerTimes);
|
||||
}
|
||||
}
|
||||
} else if (CommonConstants.ALERT_STATUS_RESOLVED.equals(singleAlert.getStatus())) {
|
||||
// If the alert is resolved, set the end time (if not already set) and copy other fields from the existing alert
|
||||
// Transition to resolved state
|
||||
if (singleAlert.getEndAt() == null) {
|
||||
singleAlert.setEndAt(System.currentTimeMillis());
|
||||
singleAlert.setEndAt(System.currentTimeMillis());
|
||||
}
|
||||
singleAlert.setStartAt(existAlert.getStartAt());
|
||||
singleAlert.setActiveAt(existAlert.getActiveAt());
|
||||
singleAlert.setTriggerTimes(existAlert.getTriggerTimes());
|
||||
}
|
||||
}
|
||||
SingleAlert savedSingleAlert = singleAlertDao.save(singleAlert);
|
||||
newAlerts.add(savedSingleAlert);
|
||||
alertFingerprints.add(savedSingleAlert.getFingerprint());
|
||||
}
|
||||
groupAlert.setAlerts(newAlerts);
|
||||
alertFingerprints.add(singleAlert.getFingerprint());
|
||||
singleAlertDao.save(singleAlert);
|
||||
});
|
||||
|
||||
// 3. Process resolved alerts
|
||||
if (existGroupAlert != null) {
|
||||
List<String> existFingerprints = existGroupAlert.getAlertFingerprints();
|
||||
@@ -130,8 +120,6 @@ final class DbAlertStoreHandlerImpl implements AlertStoreHandler {
|
||||
|
||||
// 4. Save alert group
|
||||
groupAlert.setAlertFingerprints(alertFingerprints.stream().toList());
|
||||
GroupAlert savedGroupAlert = groupAlertDao.save(groupAlert);
|
||||
savedGroupAlert.setAlerts(groupAlert.getAlerts());
|
||||
return savedGroupAlert;
|
||||
groupAlertDao.save(groupAlert);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -21,8 +21,8 @@ package org.apache.hertzbeat.alert.reduce;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@@ -267,7 +267,7 @@ public class AlarmGroupReduce {
|
||||
.groupLabels(alert.getLabels())
|
||||
.commonLabels(alert.getLabels())
|
||||
.commonAnnotations(alert.getAnnotations())
|
||||
.alerts(new LinkedList<>(List.of(alert)))
|
||||
.alerts(Collections.singletonList(alert))
|
||||
.status(alert.getStatus())
|
||||
.build();
|
||||
|
||||
|
||||
+3
-3
@@ -26,10 +26,10 @@ import java.util.Map;
|
||||
public interface DataSourceService {
|
||||
|
||||
/**
|
||||
* execute query expr calculate
|
||||
* execute query
|
||||
* @param datasource datasource
|
||||
* @param expr query expr
|
||||
* @param query query
|
||||
* @return result
|
||||
*/
|
||||
List<Map<String, Object>> calculate(String datasource, String expr);
|
||||
List<Map<String, Object>> query(String datasource, String query);
|
||||
}
|
||||
|
||||
+4
-25
@@ -23,7 +23,6 @@ import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeRule;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeTemplate;
|
||||
import org.springframework.data.domain.Page;
|
||||
|
||||
/**
|
||||
* Message notification configuration interface
|
||||
@@ -33,32 +32,23 @@ public interface NoticeConfigService {
|
||||
/**
|
||||
* Dynamic conditional query
|
||||
* @param name Recipient name,support fuzzy query
|
||||
* @param pageIndex Page number
|
||||
* @param pageSize Number of records per page
|
||||
* @return Search result
|
||||
*/
|
||||
Page<NoticeReceiver> getNoticeReceivers(String name, int pageIndex, int pageSize);
|
||||
List<NoticeReceiver> getNoticeReceivers(String name);
|
||||
|
||||
/**
|
||||
* Dynamic conditional query
|
||||
* @param name Template name,support fuzzy query
|
||||
* @param preset Whether it is a system preset template
|
||||
* true: System preset template
|
||||
* false: Custom template
|
||||
* @param pageIndex Page number
|
||||
* @param pageSize Number of records per page
|
||||
* @return Search result
|
||||
*/
|
||||
Page<NoticeTemplate> getNoticeTemplates(String name, boolean preset, int pageIndex, int pageSize);
|
||||
List<NoticeTemplate> getNoticeTemplates(String name);
|
||||
|
||||
/**
|
||||
* Dynamic conditional query
|
||||
* @param name Recipient name ,support fuzzy query
|
||||
* @param pageIndex Page number
|
||||
* @param pageSize Number of records per page
|
||||
* @param name Recipient name
|
||||
* @return Search result
|
||||
*/
|
||||
Page<NoticeRule> getNoticeRules(String name, int pageIndex, int pageSize);
|
||||
List<NoticeRule> getNoticeRules(String name);
|
||||
|
||||
/**
|
||||
* Add a notification recipient
|
||||
@@ -164,15 +154,4 @@ public interface NoticeConfigService {
|
||||
*/
|
||||
boolean sendTestMsg(NoticeReceiver noticeReceiver);
|
||||
|
||||
/**
|
||||
* Query all notification recipients
|
||||
* @return Recipient List
|
||||
*/
|
||||
List<NoticeReceiver> getAllNoticeReceivers();
|
||||
|
||||
/**
|
||||
* Query all notification policies
|
||||
* @return Notification Policy List
|
||||
*/
|
||||
List<NoticeTemplate> getAllNoticeTemplates();
|
||||
}
|
||||
|
||||
+1
-11
@@ -25,7 +25,6 @@ import jakarta.persistence.criteria.CriteriaBuilder;
|
||||
import jakarta.persistence.criteria.Predicate;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.alert.calculate.PeriodicAlertRuleScheduler;
|
||||
import org.apache.hertzbeat.alert.dao.AlertDefineDao;
|
||||
import org.apache.hertzbeat.alert.service.AlertDefineImExportService;
|
||||
import org.apache.hertzbeat.alert.service.AlertDefineService;
|
||||
@@ -68,9 +67,6 @@ public class AlertDefineServiceImpl implements AlertDefineService {
|
||||
|
||||
@Autowired
|
||||
private AlertDefineDao alertDefineDao;
|
||||
|
||||
@Autowired
|
||||
private PeriodicAlertRuleScheduler periodicAlertRuleScheduler;
|
||||
|
||||
private final Map<String, AlertDefineImExportService> alertDefineImExportServiceMap = new HashMap<>();
|
||||
|
||||
@@ -102,22 +98,19 @@ public class AlertDefineServiceImpl implements AlertDefineService {
|
||||
|
||||
@Override
|
||||
public void addAlertDefine(AlertDefine alertDefine) throws RuntimeException {
|
||||
alertDefine = alertDefineDao.save(alertDefine);
|
||||
periodicAlertRuleScheduler.updateSchedule(alertDefine);
|
||||
alertDefineDao.save(alertDefine);
|
||||
CacheFactory.clearAlertDefineCache();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void modifyAlertDefine(AlertDefine alertDefine) throws RuntimeException {
|
||||
alertDefineDao.save(alertDefine);
|
||||
periodicAlertRuleScheduler.updateSchedule(alertDefine);
|
||||
CacheFactory.clearAlertDefineCache();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteAlertDefine(long alertId) throws RuntimeException {
|
||||
alertDefineDao.deleteById(alertId);
|
||||
periodicAlertRuleScheduler.cancelSchedule(alertId);
|
||||
CacheFactory.clearAlertDefineCache();
|
||||
}
|
||||
|
||||
@@ -130,9 +123,6 @@ public class AlertDefineServiceImpl implements AlertDefineService {
|
||||
@Override
|
||||
public void deleteAlertDefines(Set<Long> alertIds) throws RuntimeException {
|
||||
alertDefineDao.deleteAlertDefinesByIdIn(alertIds);
|
||||
for (Long alertId : alertIds) {
|
||||
periodicAlertRuleScheduler.cancelSchedule(alertId);
|
||||
}
|
||||
CacheFactory.clearAlertDefineCache();
|
||||
}
|
||||
|
||||
|
||||
+9
-303
@@ -17,21 +17,13 @@
|
||||
|
||||
package org.apache.hertzbeat.alert.service.impl;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Stack;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.alert.service.DataSourceService;
|
||||
import org.apache.hertzbeat.warehouse.db.QueryExecutor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.ArrayList;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* datasource service
|
||||
@@ -41,307 +33,21 @@ import org.springframework.util.StringUtils;
|
||||
public class DataSourceServiceImpl implements DataSourceService {
|
||||
|
||||
@Autowired(required = false)
|
||||
private List<QueryExecutor> executors;
|
||||
|
||||
private static final Pattern EXPR_TOKEN = Pattern.compile("\\(|\\)|[a-zA-Z_][a-zA-Z0-9_=~{}\\[\\]\".]*|\\d+(\\.\\d+)?|>=|<=|==|!=|>|<|and|or|unless");
|
||||
private static final String THRESHOLD = "__threshold__";
|
||||
private static final String VALUE = "__value__";
|
||||
private Map<String, QueryExecutor> executors;
|
||||
|
||||
@Override
|
||||
public List<Map<String, Object>> calculate(String datasource, String expr) {
|
||||
if (!StringUtils.hasText(expr)) {
|
||||
throw new IllegalArgumentException("Empty expression");
|
||||
}
|
||||
if (executors == null || executors.isEmpty()) {
|
||||
throw new IllegalArgumentException("No query executor found");
|
||||
}
|
||||
QueryExecutor executor = executors.stream().filter(e -> e.support(datasource)).findFirst().orElse(null);
|
||||
public List<Map<String, Object>> query(String datasource, String query) {
|
||||
QueryExecutor executor = executors.get(datasource);
|
||||
if (executor == null) {
|
||||
throw new IllegalArgumentException("Unsupported datasource: " + datasource);
|
||||
}
|
||||
// replace all white space
|
||||
expr = expr.replaceAll("\\s+", " ");
|
||||
try {
|
||||
return evaluate(expr, executor);
|
||||
} catch (Exception e) {
|
||||
log.error("Error executing query on datasource {}: {}", datasource, e.getMessage());
|
||||
throw new RuntimeException("Query execution failed", e);
|
||||
}
|
||||
return executor.execute(query);
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> evaluate(String expr, QueryExecutor executor) {
|
||||
Stack<List<Map<String, Object>>> values = new Stack<>();
|
||||
Stack<String> operators = new Stack<>();
|
||||
Matcher matcher = EXPR_TOKEN.matcher(expr);
|
||||
List<String> tokens = new ArrayList<>();
|
||||
while (matcher.find()) {
|
||||
tokens.add(matcher.group());
|
||||
}
|
||||
for (String token : tokens) {
|
||||
if (token.equals("(")) {
|
||||
operators.push(token);
|
||||
} else if (token.equals(")")) {
|
||||
while (!operators.isEmpty() && !operators.peek().equals("(")) {
|
||||
applyOperator(values, operators.pop());
|
||||
}
|
||||
// remove the left parenthesis
|
||||
operators.pop();
|
||||
} else if (token.matches(">=|<=|==|!=|>|<")) {
|
||||
operators.push(token);
|
||||
} else if (token.equals("and") || token.equals("or") || token.equals("unless")) {
|
||||
while (!operators.isEmpty() && precedence(operators.peek()) >= precedence(token)) {
|
||||
applyOperator(values, operators.pop());
|
||||
}
|
||||
operators.push(token);
|
||||
} else if (token.matches("\\d+(\\.\\d+)?")) {
|
||||
double value = Double.parseDouble(token);
|
||||
List<Map<String, Object>> numAsList = new ArrayList<>();
|
||||
numAsList.add(Map.of(THRESHOLD, value));
|
||||
values.push(numAsList);
|
||||
} else if (token.matches("[a-zA-Z_][a-zA-Z0-9_=~{}\\[\\]\".]*")) {
|
||||
List<Map<String, Object>> results = executor.execute(token);
|
||||
values.push(results);
|
||||
}
|
||||
}
|
||||
while (!operators.isEmpty()) {
|
||||
applyOperator(values, operators.pop());
|
||||
}
|
||||
return values.isEmpty() ? new LinkedList<>() : values.pop();
|
||||
}
|
||||
|
||||
private int precedence(String op) {
|
||||
return switch (op) {
|
||||
case "or" -> 1;
|
||||
case "unless" -> 2;
|
||||
case "and" -> 3;
|
||||
case ">", "<", ">=", "<=", "==", "!=" -> 4;
|
||||
default -> 0;
|
||||
};
|
||||
}
|
||||
|
||||
private void applyOperator(Stack<List<Map<String, Object>>> values, String op) {
|
||||
if (values.size() < 2) {
|
||||
return;
|
||||
};
|
||||
List<Map<String, Object>> rightOperand = values.pop();
|
||||
List<Map<String, Object>> leftOperand = values.pop();
|
||||
if (rightOperand.size() == 1 && rightOperand.get(0).containsKey(THRESHOLD)) {
|
||||
double threshold = (double) rightOperand.get(0).get(THRESHOLD);
|
||||
List<Map<String, Object>> result = new ArrayList<>();
|
||||
for (Map<String, Object> item : leftOperand) {
|
||||
Object queryValues = item.get(VALUE);
|
||||
if (queryValues == null) {
|
||||
// ignore the query result data is empty
|
||||
continue;
|
||||
}
|
||||
// queryValues may be a list of values, or a single value
|
||||
Object matchValue = evaluateCondition(queryValues, op, threshold);
|
||||
item.put(VALUE, matchValue);
|
||||
// if matchValue is null, mean not match the threshold
|
||||
// if not null, mean match the threshold
|
||||
result.add(new HashMap<>(item));
|
||||
}
|
||||
if (!result.isEmpty()) {
|
||||
values.push(result);
|
||||
}
|
||||
return;
|
||||
}
|
||||
Map<String, Object> leftMap = null;
|
||||
boolean leftMatch = false;
|
||||
Map<String, Object> rightMap = null;
|
||||
boolean rightMatch = false;
|
||||
switch (op) {
|
||||
case "and" -> {
|
||||
for (Map<String, Object> item : leftOperand) {
|
||||
if (leftMap == null) {
|
||||
leftMap = item;
|
||||
}
|
||||
if (item.get(VALUE) != null) {
|
||||
leftMap = item;
|
||||
leftMatch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (Map<String, Object> item : rightOperand) {
|
||||
if (rightMap == null) {
|
||||
rightMap = item;
|
||||
}
|
||||
if (item.get(VALUE) != null) {
|
||||
rightMap = item;
|
||||
rightMatch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (leftMatch && rightMatch) {
|
||||
rightMap.putAll(leftMap);
|
||||
values.push(new LinkedList<>(List.of(rightMap)));
|
||||
} else if (leftMap != null) {
|
||||
leftMap.put(VALUE, null);
|
||||
values.push(new LinkedList<>(List.of(leftMap)));
|
||||
} else if (rightMap != null) {
|
||||
rightMap.put(VALUE, null);
|
||||
values.push(new LinkedList<>(List.of(rightMap)));
|
||||
}
|
||||
}
|
||||
case "or" -> {
|
||||
for (Map<String, Object> item : leftOperand) {
|
||||
if (leftMap == null) {
|
||||
leftMap = item;
|
||||
}
|
||||
if (item.get(VALUE) != null) {
|
||||
leftMap = item;
|
||||
leftMatch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (Map<String, Object> item : rightOperand) {
|
||||
if (rightMap == null) {
|
||||
rightMap = item;
|
||||
}
|
||||
if (item.get(VALUE) != null) {
|
||||
rightMap = item;
|
||||
rightMatch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (leftMatch && rightMatch) {
|
||||
rightMap.putAll(leftMap);
|
||||
values.push(new LinkedList<>(List.of(rightMap)));
|
||||
} else if (leftMatch) {
|
||||
values.push(new LinkedList<>(List.of(leftMap)));
|
||||
} else if (rightMatch) {
|
||||
values.push(new LinkedList<>(List.of(rightMap)));
|
||||
} else {
|
||||
if (leftMap != null && rightMap != null) {
|
||||
rightMap.putAll(leftMap);
|
||||
values.push(new LinkedList<>(List.of(rightMap)));
|
||||
} else if (leftMap != null) {
|
||||
values.push(new LinkedList<>(List.of(leftMap)));
|
||||
} else if (rightMap != null){
|
||||
values.push(new LinkedList<>(List.of(rightMap)));
|
||||
}
|
||||
}
|
||||
}
|
||||
case "unless" -> {
|
||||
for (Map<String, Object> item : leftOperand) {
|
||||
if (leftMap == null) {
|
||||
leftMap = item;
|
||||
}
|
||||
if (item.get(VALUE) != null) {
|
||||
leftMap = item;
|
||||
leftMatch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (Map<String, Object> item : rightOperand) {
|
||||
if (rightMap == null) {
|
||||
rightMap = item;
|
||||
}
|
||||
if (item.get(VALUE) != null) {
|
||||
rightMap = item;
|
||||
rightMatch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (leftMatch && !rightMatch) {
|
||||
values.push(new LinkedList<>(List.of(leftMap)));
|
||||
} else {
|
||||
if (leftMap != null) {
|
||||
leftMap.put(VALUE, null);
|
||||
values.push(new LinkedList<>(List.of(leftMap)));
|
||||
} else {
|
||||
if (rightMap != null) {
|
||||
rightMap.put(VALUE, null);
|
||||
values.push(new LinkedList<>(List.of(rightMap)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
default -> throw new IllegalArgumentException("Unsupported operator: " + op);
|
||||
}
|
||||
}
|
||||
|
||||
private Object evaluateCondition(Object value, String operator, Double threshold) {
|
||||
// value may be a list of values, or a single value
|
||||
switch (operator) {
|
||||
case ">":
|
||||
// if value is list, return the max value
|
||||
if (value instanceof List<?> values) {
|
||||
Double doubleValue = values.stream().map(v -> Double.valueOf(v.toString()))
|
||||
.max(Double::compareTo).orElse(null);
|
||||
if (doubleValue != null) {
|
||||
return doubleValue > threshold ? doubleValue : null;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return Double.parseDouble(value.toString()) > threshold ? value : null;
|
||||
}
|
||||
case ">=":
|
||||
if (value instanceof List<?> values) {
|
||||
Double doubleValue = values.stream().map(v -> Double.valueOf(v.toString()))
|
||||
.max(Double::compareTo).orElse(null);
|
||||
if (doubleValue != null) {
|
||||
return doubleValue >= threshold ? doubleValue : null;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return Double.parseDouble(value.toString()) >= threshold ? value : null;
|
||||
}
|
||||
case "<":
|
||||
if (value instanceof List<?> values) {
|
||||
Double doubleValue = values.stream().map(v -> Double.valueOf(v.toString()))
|
||||
.min(Double::compareTo).orElse(null);
|
||||
if (doubleValue != null) {
|
||||
return doubleValue < threshold ? doubleValue : null;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return Double.parseDouble(value.toString()) < threshold ? value : null;
|
||||
}
|
||||
case "<=":
|
||||
if (value instanceof List<?> values) {
|
||||
Double doubleValue = values.stream().map(v -> Double.valueOf(v.toString()))
|
||||
.min(Double::compareTo).orElse(null);
|
||||
if (doubleValue != null) {
|
||||
return doubleValue <= threshold ? doubleValue : null;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return Double.parseDouble(value.toString()) <= threshold ? value : null;
|
||||
}
|
||||
case "==":
|
||||
if (value instanceof List<?> values) {
|
||||
for (Object v : values) {
|
||||
if (v.equals(threshold)) {
|
||||
return v;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
} else {
|
||||
return value.equals(threshold) ? value : null;
|
||||
}
|
||||
case "!=":
|
||||
if (value instanceof List<?> values) {
|
||||
for (Object v : values) {
|
||||
if (v.equals(threshold)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
} else {
|
||||
return value.equals(threshold) ? null : value;
|
||||
}
|
||||
default:
|
||||
// unsupported operator todo add more operator
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void setExecutors(List<QueryExecutor> mockExecutor) {
|
||||
this.executors = mockExecutor;
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public interface QueryExecutor {
|
||||
List<Map<String, Object>> execute(String query);
|
||||
}
|
||||
}
|
||||
|
||||
+27
-76
@@ -18,6 +18,18 @@
|
||||
package org.apache.hertzbeat.alert.service.impl;
|
||||
|
||||
import jakarta.persistence.criteria.Predicate;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.hertzbeat.common.cache.CacheFactory;
|
||||
@@ -39,28 +51,10 @@ import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Message notification configuration implementation
|
||||
*/
|
||||
@@ -86,87 +80,44 @@ public class NoticeConfigServiceImpl implements NoticeConfigService, CommandLine
|
||||
private AlertNoticeDispatch dispatcherAlarm;
|
||||
|
||||
@Override
|
||||
public Page<NoticeReceiver> getNoticeReceivers(String name, int pageIndex, int pageSize) {
|
||||
public List<NoticeReceiver> getNoticeReceivers(String name) {
|
||||
Specification<NoticeReceiver> specification = (root, query, criteriaBuilder) -> {
|
||||
Predicate predicate = criteriaBuilder.conjunction();
|
||||
if (StringUtils.isNotBlank(name)) {
|
||||
Predicate predicateName = criteriaBuilder.like(
|
||||
criteriaBuilder.lower(root.get("name")), "%" + name.toLowerCase() + "%"
|
||||
);
|
||||
Predicate predicateName = criteriaBuilder.like(root.get("name"), "%" + name + "%");
|
||||
predicate = criteriaBuilder.and(predicateName);
|
||||
}
|
||||
return predicate;
|
||||
};
|
||||
return noticeReceiverDao.findAll(specification, PageRequest.of(pageIndex, pageSize, Sort.by(Sort.Direction.DESC, "id")));
|
||||
return noticeReceiverDao.findAll(specification);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NoticeReceiver> getAllNoticeReceivers() {
|
||||
return noticeReceiverDao.findAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<NoticeTemplate> getNoticeTemplates(String name, boolean preset, int pageIndex, int pageSize) {
|
||||
if (preset) {
|
||||
// Query preset templates
|
||||
List<NoticeTemplate> defaultTemplates = new LinkedList<>(PRESET_TEMPLATE.values());
|
||||
|
||||
// Filter by name (case-insensitive)
|
||||
List<NoticeTemplate> filteredDefaultTemplates = defaultTemplates.stream()
|
||||
.filter(template -> StringUtils.isBlank(name)
|
||||
|| template.getName().toLowerCase().contains(name.toLowerCase()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// Pagination logic
|
||||
int totalItems = filteredDefaultTemplates.size();
|
||||
int fromIndex = Math.min(pageIndex * pageSize, totalItems);
|
||||
int toIndex = Math.min(fromIndex + pageSize, totalItems);
|
||||
|
||||
if (fromIndex >= totalItems) {
|
||||
return new PageImpl<>(Collections.emptyList(), PageRequest.of(pageIndex, pageSize), totalItems);
|
||||
public List<NoticeTemplate> getNoticeTemplates(String name) {
|
||||
Specification<NoticeTemplate> specification = (root, query, criteriaBuilder) -> {
|
||||
Predicate predicate = criteriaBuilder.conjunction();
|
||||
if (StringUtils.isNotBlank(name)) {
|
||||
Predicate predicateName = criteriaBuilder.like(root.get("name"), "%" + name + "%");
|
||||
predicate = criteriaBuilder.and(predicateName);
|
||||
}
|
||||
|
||||
List<NoticeTemplate> paginatedTemplates = filteredDefaultTemplates.subList(fromIndex, toIndex);
|
||||
return new PageImpl<>(paginatedTemplates, PageRequest.of(pageIndex, pageSize), totalItems);
|
||||
} else {
|
||||
// Query custom templates
|
||||
Specification<NoticeTemplate> specification = (root, query, criteriaBuilder) -> {
|
||||
Predicate predicate = criteriaBuilder.conjunction();
|
||||
if (StringUtils.isNotBlank(name)) {
|
||||
Predicate predicateName = criteriaBuilder.like(
|
||||
criteriaBuilder.lower(root.get("name")), "%" + name.toLowerCase() + "%"
|
||||
);
|
||||
predicate = criteriaBuilder.and(predicateName);
|
||||
}
|
||||
return predicate;
|
||||
};
|
||||
PageRequest pageRequest = PageRequest.of(pageIndex, pageSize, Sort.by(Sort.Direction.DESC, "id"));
|
||||
return noticeTemplateDao.findAll(specification, pageRequest);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public List<NoticeTemplate> getAllNoticeTemplates() {
|
||||
return predicate;
|
||||
};
|
||||
List<NoticeTemplate> defaultTemplates = new LinkedList<>(PRESET_TEMPLATE.values());
|
||||
defaultTemplates.addAll(noticeTemplateDao.findAll());
|
||||
defaultTemplates.addAll(noticeTemplateDao.findAll(specification));
|
||||
return defaultTemplates;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<NoticeRule> getNoticeRules(String name, int pageIndex, int pageSize) {
|
||||
public List<NoticeRule> getNoticeRules(String name) {
|
||||
Specification<NoticeRule> specification = (root, query, criteriaBuilder) -> {
|
||||
Predicate predicate = criteriaBuilder.conjunction();
|
||||
if (StringUtils.isNotBlank(name)) {
|
||||
Predicate predicateName = criteriaBuilder.like(
|
||||
criteriaBuilder.lower(root.get("name")), "%" + name.toLowerCase() + "%"
|
||||
);
|
||||
Predicate predicateName = criteriaBuilder.like(root.get("name"), "%" + name + "%");
|
||||
predicate = criteriaBuilder.and(predicateName);
|
||||
}
|
||||
return predicate;
|
||||
};
|
||||
return noticeRuleDao.findAll(specification, PageRequest.of(pageIndex, pageSize, Sort.by(Sort.Direction.DESC, "id")));
|
||||
return noticeRuleDao.findAll(specification);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+12
-1
@@ -28,6 +28,7 @@ import org.apache.hertzbeat.common.entity.alerter.AlertDefineMonitorBind;
|
||||
import org.apache.hertzbeat.common.entity.manager.Monitor;
|
||||
import org.apache.hertzbeat.common.util.JsonUtil;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
@@ -42,6 +43,7 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
/**
|
||||
* Test case for {@link AlertDefineController}
|
||||
*/
|
||||
@Disabled
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class AlertDefineControllerTest {
|
||||
|
||||
@@ -64,7 +66,6 @@ class AlertDefineControllerTest {
|
||||
|
||||
this.alertDefine = AlertDefine.builder()
|
||||
.id(1L)
|
||||
.name("alertDefine")
|
||||
.expr("1 > 0")
|
||||
.times(1)
|
||||
.template("template")
|
||||
@@ -137,4 +138,14 @@ class AlertDefineControllerTest {
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.andReturn();
|
||||
}
|
||||
|
||||
@Test
|
||||
void applyAlertDefineMonitorsBind() throws Exception {
|
||||
mockMvc.perform(MockMvcRequestBuilders.post("/api/alert/define/" + this.alertDefine.getId() + "/monitors")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(JsonUtil.toJson(this.alertDefineMonitorBinds)))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.andReturn();
|
||||
}
|
||||
}
|
||||
|
||||
+24
-16
@@ -19,6 +19,7 @@ package org.apache.hertzbeat.alert.controller;
|
||||
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -30,6 +31,7 @@ import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertDefine;
|
||||
import org.apache.hertzbeat.common.util.JsonUtil;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
@@ -48,6 +50,7 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
* Test case for {@link AlertDefinesController}
|
||||
* Test whether the data mocked at the mock is correct, and test whether the format of the returned data is correct
|
||||
*/
|
||||
@Disabled
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class AlertDefinesControllerTest {
|
||||
|
||||
@@ -90,7 +93,8 @@ class AlertDefinesControllerTest {
|
||||
pageRequest = PageRequest.of((Integer) content.get("pageIndex"), (Integer) content.get("pageSize"), sortExp);
|
||||
}
|
||||
|
||||
@Test
|
||||
// @Test
|
||||
// todo: fix this test
|
||||
void getAlertDefines() throws Exception {
|
||||
|
||||
// Test the correctness of the mock
|
||||
@@ -108,27 +112,31 @@ class AlertDefinesControllerTest {
|
||||
// }
|
||||
// }))).thenReturn(new PageImpl<AlertDefine>(new ArrayList<AlertDefine>()));
|
||||
AlertDefine define = AlertDefine.builder().id(9L).expr("x").times(1).build();
|
||||
PageRequest pageRequest = PageRequest.of(pageIndex, pageSize, Sort.by(Sort.Order.asc(sort)));
|
||||
Mockito.when(alertDefineService.getAlertDefines(
|
||||
Mockito.eq(ids),
|
||||
Mockito.isNull(),
|
||||
Mockito.eq(sort),
|
||||
Mockito.eq(order),
|
||||
Mockito.eq(pageIndex),
|
||||
Mockito.eq(pageSize)
|
||||
)).thenReturn(new PageImpl<>(Collections.singletonList(define), pageRequest, 1));
|
||||
Mockito.when(alertDefineService.getAlertDefines(null, null, "id", "desc", 1, 10)).thenReturn(new PageImpl<>(Collections.singletonList(define)));
|
||||
|
||||
mockMvc.perform(MockMvcRequestBuilders.get("/api/alert/defines")
|
||||
mockMvc.perform(MockMvcRequestBuilders.get(
|
||||
"/api/alert/defines")
|
||||
.param("ids", ids.toString().substring(1, ids.toString().length() - 1))
|
||||
.param("priority", priority.toString())
|
||||
.param("sort", sort)
|
||||
.param("order", order)
|
||||
.param("pageIndex", String.valueOf(pageIndex))
|
||||
.param("pageSize", String.valueOf(pageSize)))
|
||||
.param("pageIndex", pageIndex.toString())
|
||||
.param("pageSize", pageSize.toString()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.andExpect(jsonPath("$.data.content[0].id").value(9))
|
||||
.andExpect(jsonPath("$.data.content[0].expr").value("x"))
|
||||
.andExpect(jsonPath("$.data.content[0].times").value(1))
|
||||
.andExpect(jsonPath("$.data.content").value(new ArrayList<>()))
|
||||
.andExpect(jsonPath("$.data.pageable").value("INSTANCE"))
|
||||
.andExpect(jsonPath("$.data.totalPages").value(1))
|
||||
.andExpect(jsonPath("$.data.totalElements").value(0))
|
||||
.andExpect(jsonPath("$.data.last").value(true))
|
||||
.andExpect(jsonPath("$.data.number").value(0))
|
||||
.andExpect(jsonPath("$.data.size").value(0))
|
||||
.andExpect(jsonPath("$.data.first").value(true))
|
||||
.andExpect(jsonPath("$.data.numberOfElements").value(0))
|
||||
.andExpect(jsonPath("$.data.empty").value(true))
|
||||
.andExpect(jsonPath("$.data.sort.empty").value(true))
|
||||
.andExpect(jsonPath("$.data.sort.sorted").value(false))
|
||||
.andExpect(jsonPath("$.data.sort.unsorted").value(true))
|
||||
.andReturn();
|
||||
}
|
||||
|
||||
|
||||
+8
-6
@@ -27,12 +27,12 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup;
|
||||
|
||||
import org.apache.hertzbeat.alert.service.AlertGroupConvergeService;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertGroupConverge;
|
||||
import org.apache.hertzbeat.common.util.JsonUtil;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
@@ -44,6 +44,7 @@ import org.springframework.test.web.servlet.MockMvc;
|
||||
/**
|
||||
* test case for {@link AlertGroupConvergeController}
|
||||
*/
|
||||
@Disabled
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class AlertGroupConvergeControllerTest {
|
||||
|
||||
@@ -78,8 +79,8 @@ public class AlertGroupConvergeControllerTest {
|
||||
|
||||
mockMvc.perform(post("/api/alert/group")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(JsonUtil.toJson(alertGroupConverge)))
|
||||
.andExpect(status().isOk())
|
||||
.content(JsonUtil.toJson(alertGroupConverge))
|
||||
).andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.andExpect(jsonPath("$.msg").value("Add success"));
|
||||
}
|
||||
@@ -92,8 +93,8 @@ public class AlertGroupConvergeControllerTest {
|
||||
|
||||
mockMvc.perform(put("/api/alert/group")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(JsonUtil.toJson(alertGroupConverge)))
|
||||
.andExpect(status().isOk())
|
||||
.content(JsonUtil.toJson(alertGroupConverge))
|
||||
).andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.andExpect(jsonPath("$.msg").value("Modify success"));
|
||||
}
|
||||
@@ -118,6 +119,7 @@ public class AlertGroupConvergeControllerTest {
|
||||
.accept(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.MONITOR_NOT_EXIST_CODE))
|
||||
.andExpect(jsonPath("$.msg").value("Alert Group Converge not exist."));
|
||||
.andExpect(jsonPath("$.msg").value("AlertGroupConverge not exist."));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+4
-2
@@ -33,6 +33,7 @@ import org.apache.hertzbeat.alert.service.AlertGroupConvergeService;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertGroupConverge;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
@@ -51,6 +52,7 @@ import org.springframework.test.web.servlet.MockMvc;
|
||||
*/
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@Disabled
|
||||
class AlertGroupConvergesControllerTest {
|
||||
|
||||
private MockMvc mockMvc;
|
||||
@@ -99,7 +101,6 @@ class AlertGroupConvergesControllerTest {
|
||||
.param("order", "desc")
|
||||
.accept(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.andExpect(jsonPath("$.data.content[0].id").value(1))
|
||||
.andExpect(jsonPath("$.data.content[0].name").value("Converge1"))
|
||||
.andExpect(jsonPath("$.data.content[1].id").value(2))
|
||||
@@ -117,4 +118,5 @@ class AlertGroupConvergesControllerTest {
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+23
-113
@@ -17,6 +17,7 @@
|
||||
|
||||
package org.apache.hertzbeat.alert.controller;
|
||||
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
@@ -43,10 +44,6 @@ import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
@@ -171,38 +168,12 @@ class NoticeConfigControllerTest {
|
||||
|
||||
@Test
|
||||
void getReceivers() throws Exception {
|
||||
NoticeReceiver receiver1 = new NoticeReceiver();
|
||||
receiver1.setId(1L);
|
||||
receiver1.setName("Receiver1");
|
||||
|
||||
NoticeReceiver receiver2 = new NoticeReceiver();
|
||||
receiver2.setId(2L);
|
||||
receiver2.setName("Receiver2");
|
||||
|
||||
Page<NoticeReceiver> receiverPage = new PageImpl<>(
|
||||
Arrays.asList(receiver1, receiver2),
|
||||
PageRequest.of(0, 8, Sort.by("id").descending()),
|
||||
2
|
||||
);
|
||||
|
||||
when(noticeConfigService.getNoticeReceivers("Receiver", 0, 8)).thenReturn(receiverPage);
|
||||
|
||||
this.mockMvc.perform(MockMvcRequestBuilders.get("/api/notice/receivers")
|
||||
.param("name", "Receiver")
|
||||
.param("pageIndex", "0")
|
||||
.param("pageSize", "8")
|
||||
.param("sort", "id")
|
||||
.param("order", "desc")
|
||||
.accept(MediaType.APPLICATION_JSON))
|
||||
//Mockito.when(noticeConfigService.getNoticeReceivers())
|
||||
this.mockMvc.perform(MockMvcRequestBuilders.get("/api/notice/receivers?name={name}", "tom"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.data.content[0].id").value(1))
|
||||
.andExpect(jsonPath("$.data.content[0].name").value("Receiver1"))
|
||||
.andExpect(jsonPath("$.data.content[1].id").value(2))
|
||||
.andExpect(jsonPath("$.data.content[1].name").value("Receiver2"))
|
||||
.andExpect(jsonPath("$.data.totalElements").value(2))
|
||||
.andExpect(jsonPath("$.data.totalPages").value(1))
|
||||
.andExpect(jsonPath("$.data.size").value(8))
|
||||
.andExpect(jsonPath("$.data.number").value(0));
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.andReturn();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -274,38 +245,15 @@ class NoticeConfigControllerTest {
|
||||
|
||||
@Test
|
||||
void getRules() throws Exception {
|
||||
NoticeRule rule1 = new NoticeRule();
|
||||
rule1.setId(1L);
|
||||
rule1.setName("Rule1");
|
||||
|
||||
NoticeRule rule2 = new NoticeRule();
|
||||
rule2.setId(2L);
|
||||
rule2.setName("Rule2");
|
||||
|
||||
Page<NoticeRule> rulePage = new PageImpl<>(
|
||||
Arrays.asList(rule1, rule2),
|
||||
PageRequest.of(0, 8, Sort.by("id").descending()),
|
||||
2
|
||||
);
|
||||
|
||||
when(noticeConfigService.getNoticeRules("Rule", 0, 8)).thenReturn(rulePage);
|
||||
|
||||
this.mockMvc.perform(MockMvcRequestBuilders.get("/api/notice/rules")
|
||||
.param("name", "Rule")
|
||||
.param("pageIndex", "0")
|
||||
.param("pageSize", "8")
|
||||
.param("sort", "id")
|
||||
.param("order", "desc")
|
||||
.accept(MediaType.APPLICATION_JSON))
|
||||
this.mockMvc.perform(MockMvcRequestBuilders.get("/api/notice/rules"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.data.content[0].id").value(1))
|
||||
.andExpect(jsonPath("$.data.content[0].name").value("Rule1"))
|
||||
.andExpect(jsonPath("$.data.content[1].id").value(2))
|
||||
.andExpect(jsonPath("$.data.content[1].name").value("Rule2"))
|
||||
.andExpect(jsonPath("$.data.totalElements").value(2))
|
||||
.andExpect(jsonPath("$.data.totalPages").value(1))
|
||||
.andExpect(jsonPath("$.data.size").value(8))
|
||||
.andExpect(jsonPath("$.data.number").value(0));
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.andReturn();
|
||||
|
||||
this.mockMvc.perform(MockMvcRequestBuilders.get("/api/notice/rules?name={name}", "tom"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.andReturn();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -414,40 +362,22 @@ class NoticeConfigControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void getTemplates() throws Exception {
|
||||
void testGetTemplates() throws Exception {
|
||||
// Mock the service response
|
||||
NoticeTemplate template1 = new NoticeTemplate();
|
||||
template1.setId(1L);
|
||||
template1.setName("Template1");
|
||||
|
||||
NoticeTemplate template2 = new NoticeTemplate();
|
||||
template2.setId(2L);
|
||||
template2.setName("Template2");
|
||||
List<NoticeTemplate> templates = Arrays.asList(template1, template2);
|
||||
when(noticeConfigService.getNoticeTemplates(any())).thenReturn(templates);
|
||||
|
||||
Page<NoticeTemplate> templatePage = new PageImpl<>(
|
||||
Arrays.asList(template1, template2),
|
||||
PageRequest.of(0, 8, Sort.by("id").descending()),
|
||||
2
|
||||
);
|
||||
|
||||
when(noticeConfigService.getNoticeTemplates("Template", true, 0, 8)).thenReturn(templatePage);
|
||||
|
||||
this.mockMvc.perform(MockMvcRequestBuilders.get("/api/notice/templates")
|
||||
.param("name", "Template")
|
||||
.param("preset", "true")
|
||||
.param("pageIndex", "0")
|
||||
.param("pageSize", "8")
|
||||
.param("sort", "id")
|
||||
.param("order", "desc")
|
||||
.accept(MediaType.APPLICATION_JSON))
|
||||
// Perform the GET request and verify the response
|
||||
this.mockMvc.perform(get("/api/notice/templates")
|
||||
.param("name", "Template"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.data.content[0].id").value(1))
|
||||
.andExpect(jsonPath("$.data.content[0].name").value("Template1"))
|
||||
.andExpect(jsonPath("$.data.content[1].id").value(2))
|
||||
.andExpect(jsonPath("$.data.content[1].name").value("Template2"))
|
||||
.andExpect(jsonPath("$.data.totalElements").value(2))
|
||||
.andExpect(jsonPath("$.data.totalPages").value(1))
|
||||
.andExpect(jsonPath("$.data.size").value(8))
|
||||
.andExpect(jsonPath("$.data.number").value(0));
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.andExpect(jsonPath("$.data[0].name").value("Template1"))
|
||||
.andExpect(jsonPath("$.data[1].name").value("Template2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -484,25 +414,5 @@ class NoticeConfigControllerTest {
|
||||
verify(noticeConfigService, times(1)).sendTestMsg(noticeReceiver);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAllTemplates() throws Exception {
|
||||
List<NoticeTemplate> templates = Arrays.asList(new NoticeTemplate(), new NoticeTemplate());
|
||||
when(noticeConfigService.getAllNoticeTemplates()).thenReturn(templates);
|
||||
|
||||
this.mockMvc.perform(MockMvcRequestBuilders.get("/api/notice/templates/all"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.andReturn();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAllReceivers() throws Exception {
|
||||
List<NoticeReceiver> receivers = Arrays.asList(new NoticeReceiver(), new NoticeReceiver());
|
||||
when(noticeConfigService.getAllNoticeReceivers()).thenReturn(receivers);
|
||||
|
||||
this.mockMvc.perform(MockMvcRequestBuilders.get("/api/notice/receivers/all"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.andReturn();
|
||||
}
|
||||
}
|
||||
|
||||
+1
-6
@@ -27,7 +27,6 @@ import static org.mockito.Mockito.when;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.apache.hertzbeat.alert.AlerterWorkerPool;
|
||||
import org.apache.hertzbeat.alert.config.AlertSseManager;
|
||||
import org.apache.hertzbeat.alert.service.NoticeConfigService;
|
||||
import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeReceiver;
|
||||
@@ -61,9 +60,6 @@ class AlertNoticeDispatchTest {
|
||||
@Mock
|
||||
private AlertNotifyHandler alertNotifyHandler;
|
||||
|
||||
@Mock
|
||||
private AlertSseManager emitterManager;
|
||||
|
||||
private AlertNoticeDispatch alertNoticeDispatch;
|
||||
|
||||
private static final int DISPATCH_THREADS = 3;
|
||||
@@ -81,8 +77,7 @@ class AlertNoticeDispatchTest {
|
||||
noticeConfigService,
|
||||
alertStoreHandler,
|
||||
alertNotifyHandlerList,
|
||||
pluginRunner,
|
||||
emitterManager
|
||||
pluginRunner
|
||||
);
|
||||
|
||||
receiver = NoticeReceiver.builder()
|
||||
|
||||
+10
-19
@@ -27,6 +27,7 @@ import org.apache.hertzbeat.alert.dao.SingleAlertDao;
|
||||
import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
|
||||
import org.apache.hertzbeat.common.entity.alerter.SingleAlert;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
@@ -38,6 +39,7 @@ import java.util.List;
|
||||
/**
|
||||
* Test case for {@link DbAlertStoreHandlerImpl}
|
||||
*/
|
||||
@Disabled
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class DbAlertStoreHandlerImplTest {
|
||||
|
||||
@@ -73,34 +75,27 @@ class DbAlertStoreHandlerImplTest {
|
||||
public void testStoreNewAlert() {
|
||||
String groupKey = "test-group";
|
||||
groupAlert.setGroupKey(groupKey);
|
||||
|
||||
|
||||
when(groupAlertDao.findByGroupKey(groupKey)).thenReturn(null);
|
||||
|
||||
SingleAlert savedSingleAlert = new SingleAlert();
|
||||
when(singleAlertDao.save(any(SingleAlert.class))).thenReturn(savedSingleAlert);
|
||||
|
||||
GroupAlert savedGroupAlert = new GroupAlert();
|
||||
when(groupAlertDao.save(any(GroupAlert.class))).thenReturn(savedGroupAlert);
|
||||
|
||||
|
||||
dbAlertStoreHandler.store(groupAlert);
|
||||
|
||||
|
||||
verify(singleAlertDao).save(any(SingleAlert.class));
|
||||
verify(groupAlertDao).save(groupAlert);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testStoreExistingAlert() {
|
||||
String groupKey = "test-group";
|
||||
String fingerprint = "test-fingerprint";
|
||||
|
||||
|
||||
groupAlert.setGroupKey(groupKey);
|
||||
singleAlert.setFingerprint(fingerprint);
|
||||
|
||||
|
||||
GroupAlert existingGroup = new GroupAlert();
|
||||
existingGroup.setId(1L);
|
||||
when(groupAlertDao.findByGroupKey(groupKey)).thenReturn(existingGroup);
|
||||
|
||||
|
||||
SingleAlert existingAlert = new SingleAlert();
|
||||
existingAlert.setId(1L);
|
||||
existingAlert.setStatus("firing");
|
||||
@@ -108,15 +103,11 @@ class DbAlertStoreHandlerImplTest {
|
||||
existingAlert.setActiveAt(2000L);
|
||||
existingAlert.setTriggerTimes(1);
|
||||
when(singleAlertDao.findByFingerprint(fingerprint)).thenReturn(existingAlert);
|
||||
|
||||
when(singleAlertDao.save(any(SingleAlert.class))).thenReturn(existingAlert);
|
||||
when(groupAlertDao.save(any(GroupAlert.class))).thenReturn(existingGroup);
|
||||
|
||||
|
||||
dbAlertStoreHandler.store(groupAlert);
|
||||
|
||||
|
||||
verify(singleAlertDao).save(any(SingleAlert.class));
|
||||
verify(groupAlertDao).save(groupAlert);
|
||||
assertEquals(1L, groupAlert.getId());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+32
-11
@@ -30,17 +30,18 @@ import java.util.Map;
|
||||
|
||||
import org.apache.hertzbeat.alert.dao.AlertSilenceDao;
|
||||
import org.apache.hertzbeat.alert.notice.AlertNoticeDispatch;
|
||||
import org.apache.hertzbeat.common.cache.CacheFactory;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertSilence;
|
||||
import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
/**
|
||||
* Test for {@link AlarmSilenceReduce}
|
||||
* Test for AlarmSilenceReduce
|
||||
*/
|
||||
@Disabled
|
||||
class AlarmSilenceReduceTest {
|
||||
|
||||
@Mock
|
||||
@@ -54,23 +55,21 @@ class AlarmSilenceReduceTest {
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
MockitoAnnotations.openMocks(this);
|
||||
CacheFactory.clearAlertSilenceCache();
|
||||
when(alertSilenceDao.findAll()).thenReturn(Collections.emptyList());
|
||||
alarmSilenceReduce = new AlarmSilenceReduce(alertSilenceDao, alertNoticeDispatch);
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenNoSilenceRules_shouldForwardAlert() {
|
||||
when(alertSilenceDao.findAlertSilencesByEnableTrue()).thenReturn(Collections.emptyList());
|
||||
|
||||
GroupAlert alert = createGroupAlert("firing", createLabels("service", "web"));
|
||||
|
||||
alarmSilenceReduce.silenceAlarm(alert);
|
||||
|
||||
|
||||
verify(alertNoticeDispatch).dispatchAlarm(alert);
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenMatchingSilenceRule_shouldNotForwardAlert() {
|
||||
|
||||
// Create silence rule
|
||||
AlertSilence silenceRule = AlertSilence.builder()
|
||||
.enable(true)
|
||||
@@ -82,7 +81,7 @@ class AlarmSilenceReduceTest {
|
||||
.times(0)
|
||||
.build();
|
||||
|
||||
when(alertSilenceDao.findAlertSilencesByEnableTrue()).thenReturn(Collections.singletonList(silenceRule));
|
||||
when(alertSilenceDao.findAll()).thenReturn(Collections.singletonList(silenceRule));
|
||||
when(alertSilenceDao.save(any(AlertSilence.class))).thenReturn(silenceRule);
|
||||
|
||||
GroupAlert alert = createGroupAlert("firing", createLabels("service", "web"));
|
||||
@@ -108,9 +107,9 @@ class AlarmSilenceReduceTest {
|
||||
.times(0)
|
||||
.build();
|
||||
|
||||
when(alertSilenceDao.findAlertSilencesByEnableTrue()).thenReturn(Collections.singletonList(silenceRule));
|
||||
when(alertSilenceDao.findAll()).thenReturn(Collections.singletonList(silenceRule));
|
||||
when(alertSilenceDao.save(any(AlertSilence.class))).thenReturn(silenceRule);
|
||||
|
||||
|
||||
GroupAlert alert = createGroupAlert("firing", createLabels("service", "web"));
|
||||
|
||||
alarmSilenceReduce.silenceAlarm(alert);
|
||||
@@ -131,7 +130,29 @@ class AlarmSilenceReduceTest {
|
||||
.times(0)
|
||||
.build();
|
||||
|
||||
when(alertSilenceDao.findAlertSilencesByEnableTrue()).thenReturn(Collections.singletonList(silenceRule));
|
||||
when(alertSilenceDao.findAll()).thenReturn(Collections.singletonList(silenceRule));
|
||||
|
||||
GroupAlert alert = createGroupAlert("firing", createLabels("service", "web"));
|
||||
|
||||
alarmSilenceReduce.silenceAlarm(alert);
|
||||
|
||||
verify(alertNoticeDispatch).dispatchAlarm(alert);
|
||||
verify(alertSilenceDao, never()).save(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenSilenceRuleDisabled_shouldForwardAlert() {
|
||||
AlertSilence silenceRule = AlertSilence.builder()
|
||||
.enable(false)
|
||||
.matchAll(false)
|
||||
.type((byte) 0)
|
||||
.labels(createLabels("service", "web"))
|
||||
.periodStart(LocalDateTime.now().minusHours(1).atZone(ZoneId.systemDefault()))
|
||||
.periodEnd(LocalDateTime.now().plusHours(1).atZone(ZoneId.systemDefault()))
|
||||
.times(0)
|
||||
.build();
|
||||
|
||||
when(alertSilenceDao.findAll()).thenReturn(Collections.singletonList(silenceRule));
|
||||
|
||||
GroupAlert alert = createGroupAlert("firing", createLabels("service", "web"));
|
||||
|
||||
|
||||
+25
-35
@@ -24,18 +24,16 @@ import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.hertzbeat.alert.dto.AlertDefineDTO;
|
||||
import org.apache.hertzbeat.alert.dto.ExportAlertDefineDTO;
|
||||
import org.apache.hertzbeat.alert.service.impl.AlertDefineExcelImExportServiceImpl;
|
||||
import org.apache.hertzbeat.common.util.JsonUtil;
|
||||
import org.apache.hertzbeat.common.util.export.ExcelExportUtils;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.apache.poi.ss.usermodel.WorkbookFactory;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
@@ -44,6 +42,7 @@ import org.mockito.junit.jupiter.MockitoExtension;
|
||||
/**
|
||||
* test case for {@link AlertDefineExcelImExportServiceImpl}
|
||||
*/
|
||||
@Disabled
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class AlertDefineExcelImExportServiceTest {
|
||||
|
||||
@@ -62,13 +61,15 @@ public class AlertDefineExcelImExportServiceTest {
|
||||
Row row = initialSheet.createRow(1);
|
||||
row.createCell(0).setCellValue("app1");
|
||||
row.createCell(1).setCellValue("metric1");
|
||||
row.createCell(2).setCellValue("expr1");
|
||||
row.createCell(3).setCellValue(10);
|
||||
row.createCell(4).setCellValue(1);
|
||||
row.createCell(5).setCellValue(JsonUtil.toJson(Map.of("key", "value")));
|
||||
row.createCell(6).setCellValue(JsonUtil.toJson(Map.of("key", "value")));
|
||||
row.createCell(7).setCellValue("template1");
|
||||
row.createCell(2).setCellValue("field1");
|
||||
row.createCell(3).setCellValue(true);
|
||||
row.createCell(4).setCellValue("expr1");
|
||||
row.createCell(5).setCellValue(1);
|
||||
row.createCell(6).setCellValue(10);
|
||||
row.createCell(7).setCellValue("[{\"name\":\"tag1\",\"value\":\"value1\"}]");
|
||||
row.createCell(8).setCellValue(true);
|
||||
row.createCell(9).setCellValue(true);
|
||||
row.createCell(10).setCellValue("template1");
|
||||
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(toByteArray(initialWorkbook));
|
||||
|
||||
@@ -87,12 +88,9 @@ public class AlertDefineExcelImExportServiceTest {
|
||||
assertEquals("app1", alertDefineDTO.getName());
|
||||
assertEquals("metric1", alertDefineDTO.getType());
|
||||
assertEquals("expr1", alertDefineDTO.getExpr());
|
||||
assertEquals(10, alertDefineDTO.getPeriod());
|
||||
assertEquals(1, alertDefineDTO.getTimes());
|
||||
assertEquals(Map.of("key", "value"), alertDefineDTO.getLabels());
|
||||
assertEquals(Map.of("key", "value"), alertDefineDTO.getAnnotations());
|
||||
assertEquals("template1", alertDefineDTO.getTemplate());
|
||||
assertEquals(10, alertDefineDTO.getTimes());
|
||||
assertTrue(alertDefineDTO.getEnable());
|
||||
assertEquals("template1", alertDefineDTO.getTemplate());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,12 +103,9 @@ public class AlertDefineExcelImExportServiceTest {
|
||||
alertDefineDTO.setName("app1");
|
||||
alertDefineDTO.setType("metric1");
|
||||
alertDefineDTO.setExpr("expr1");
|
||||
alertDefineDTO.setPeriod(10);
|
||||
alertDefineDTO.setTimes(1);
|
||||
alertDefineDTO.setLabels(Map.of("key", "value"));
|
||||
alertDefineDTO.setAnnotations(Map.of("key", "value"));
|
||||
alertDefineDTO.setTemplate("template1");
|
||||
alertDefineDTO.setTimes(10);
|
||||
alertDefineDTO.setEnable(true);
|
||||
alertDefineDTO.setTemplate("template1");
|
||||
exportAlertDefineDTO.setAlertDefine(alertDefineDTO);
|
||||
exportAlertDefineList.add(exportAlertDefineDTO);
|
||||
|
||||
@@ -120,26 +115,21 @@ public class AlertDefineExcelImExportServiceTest {
|
||||
try (Workbook resultWorkbook = WorkbookFactory.create(new ByteArrayInputStream(outputStream.toByteArray()))) {
|
||||
Sheet resultSheet = resultWorkbook.getSheetAt(0);
|
||||
Row headerRow = resultSheet.getRow(0);
|
||||
assertEquals("Name", headerRow.getCell(0).getStringCellValue());
|
||||
assertEquals("Type", headerRow.getCell(1).getStringCellValue());
|
||||
assertEquals("Expr", headerRow.getCell(2).getStringCellValue());
|
||||
assertEquals("Period", headerRow.getCell(3).getStringCellValue());
|
||||
assertEquals("Times", headerRow.getCell(4).getStringCellValue());
|
||||
assertEquals("Labels", headerRow.getCell(5).getStringCellValue());
|
||||
assertEquals("Annotations", headerRow.getCell(6).getStringCellValue());
|
||||
assertEquals("Template", headerRow.getCell(7).getStringCellValue());
|
||||
assertEquals("Enable", headerRow.getCell(8).getStringCellValue());
|
||||
assertEquals("app", headerRow.getCell(0).getStringCellValue());
|
||||
assertEquals("metric", headerRow.getCell(1).getStringCellValue());
|
||||
|
||||
Row dataRow = resultSheet.getRow(1);
|
||||
assertEquals("app1", dataRow.getCell(0).getStringCellValue());
|
||||
assertEquals("metric1", dataRow.getCell(1).getStringCellValue());
|
||||
assertEquals("expr1", dataRow.getCell(2).getStringCellValue());
|
||||
assertEquals(10, (int) dataRow.getCell(3).getNumericCellValue());
|
||||
assertEquals(1, (int) dataRow.getCell(4).getNumericCellValue());
|
||||
assertEquals(JsonUtil.toJson(Map.of("key", "value")), dataRow.getCell(5).getStringCellValue());
|
||||
assertEquals(JsonUtil.toJson(Map.of("key", "value")), dataRow.getCell(6).getStringCellValue());
|
||||
assertEquals("template1", dataRow.getCell(7).getStringCellValue());
|
||||
assertEquals("field1", dataRow.getCell(2).getStringCellValue());
|
||||
assertTrue(dataRow.getCell(3).getBooleanCellValue());
|
||||
assertEquals("expr1", dataRow.getCell(4).getStringCellValue());
|
||||
assertEquals(1, (int) dataRow.getCell(5).getNumericCellValue());
|
||||
assertEquals(10, (int) dataRow.getCell(6).getNumericCellValue());
|
||||
assertEquals("[{\"name\":\"tag1\",\"value\":\"value1\"}]", dataRow.getCell(7).getStringCellValue());
|
||||
assertTrue(dataRow.getCell(8).getBooleanCellValue());
|
||||
assertTrue(dataRow.getCell(9).getBooleanCellValue());
|
||||
assertEquals("template1", dataRow.getCell(10).getStringCellValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -152,4 +142,4 @@ public class AlertDefineExcelImExportServiceTest {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
-5
@@ -30,7 +30,6 @@ import static org.mockito.Mockito.when;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.apache.hertzbeat.alert.calculate.PeriodicAlertRuleScheduler;
|
||||
import org.apache.hertzbeat.alert.dao.AlertDefineDao;
|
||||
import org.apache.hertzbeat.alert.service.impl.AlertDefineServiceImpl;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertDefine;
|
||||
@@ -55,9 +54,6 @@ class AlertDefineServiceTest {
|
||||
|
||||
@Mock
|
||||
private AlertDefineDao alertDefineDao;
|
||||
|
||||
@Mock
|
||||
private PeriodicAlertRuleScheduler periodicAlertRuleScheduler;
|
||||
|
||||
@Mock
|
||||
private List<AlertDefineImExportService> alertDefineImExportServiceList;
|
||||
@@ -68,7 +64,6 @@ class AlertDefineServiceTest {
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
ReflectionTestUtils.setField(this.alertDefineService, "alertDefineDao", alertDefineDao);
|
||||
ReflectionTestUtils.setField(this.alertDefineService, "periodicAlertRuleScheduler", periodicAlertRuleScheduler);
|
||||
|
||||
this.alertDefine = AlertDefine.builder()
|
||||
.id(1L)
|
||||
|
||||
-547
@@ -1,547 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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.alert.service;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import java.util.HashMap;
|
||||
import org.apache.hertzbeat.alert.service.impl.DataSourceServiceImpl;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.mockito.Mockito;
|
||||
import org.apache.hertzbeat.warehouse.db.QueryExecutor;
|
||||
|
||||
/**
|
||||
* test case for {@link DataSourceService}
|
||||
*/
|
||||
class DataSourceServiceTest {
|
||||
|
||||
private DataSourceServiceImpl dataSourceService;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
dataSourceService = new DataSourceServiceImpl();
|
||||
}
|
||||
|
||||
@Test
|
||||
void calculate1() {
|
||||
List<Map<String, Object>> prometheusData = List.of(
|
||||
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
|
||||
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
|
||||
);
|
||||
|
||||
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
|
||||
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
|
||||
Mockito.when(mockExecutor.execute(Mockito.anyString())).thenReturn(prometheusData);
|
||||
dataSourceService.setExecutors(List.of(mockExecutor));
|
||||
|
||||
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total > 150");
|
||||
assertEquals(2, result.size());
|
||||
assertNull(result.get(0).get("__value__"));
|
||||
assertEquals(200.0, result.get(1).get("__value__"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void calculate2() {
|
||||
List<Map<String, Object>> prometheusData = List.of(
|
||||
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
|
||||
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
|
||||
);
|
||||
|
||||
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
|
||||
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
|
||||
Mockito.when(mockExecutor.execute(Mockito.anyString())).thenReturn(prometheusData);
|
||||
dataSourceService.setExecutors(List.of(mockExecutor));
|
||||
|
||||
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total <= 100");
|
||||
assertEquals(2, result.size());
|
||||
assertEquals(100.0, result.get(0).get("__value__"));
|
||||
assertNull(result.get(1).get("__value__"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void calculate3() {
|
||||
List<Map<String, Object>> prometheusData = List.of(
|
||||
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
|
||||
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
|
||||
);
|
||||
|
||||
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
|
||||
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
|
||||
Mockito.when(mockExecutor.execute(Mockito.anyString())).thenReturn(prometheusData);
|
||||
dataSourceService.setExecutors(List.of(mockExecutor));
|
||||
|
||||
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total >= 200");
|
||||
assertEquals(2, result.size());
|
||||
assertNull(result.get(0).get("__value__"));
|
||||
assertEquals(200.0, result.get(1).get("__value__"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void calculate4() {
|
||||
List<Map<String, Object>> prometheusData = List.of(
|
||||
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
|
||||
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
|
||||
);
|
||||
|
||||
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
|
||||
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
|
||||
Mockito.when(mockExecutor.execute(Mockito.anyString())).thenReturn(prometheusData);
|
||||
dataSourceService.setExecutors(List.of(mockExecutor));
|
||||
|
||||
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total > 250");
|
||||
assertEquals(2, result.size());
|
||||
assertNull(result.get(0).get("__value__"));
|
||||
assertNull(result.get(1).get("__value__"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void calculate5() {
|
||||
List<Map<String, Object>> prometheusData = List.of(
|
||||
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
|
||||
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
|
||||
);
|
||||
|
||||
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
|
||||
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
|
||||
Mockito.when(mockExecutor.execute(Mockito.anyString())).thenReturn(prometheusData);
|
||||
dataSourceService.setExecutors(List.of(mockExecutor));
|
||||
|
||||
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total < 100");
|
||||
assertEquals(2, result.size());
|
||||
assertNull(result.get(0).get("__value__"));
|
||||
assertNull(result.get(1).get("__value__"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void calculate6() {
|
||||
List<Map<String, Object>> prometheusData = List.of(
|
||||
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
|
||||
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
|
||||
);
|
||||
|
||||
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
|
||||
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
|
||||
Mockito.when(mockExecutor.execute(Mockito.anyString())).thenReturn(prometheusData);
|
||||
dataSourceService.setExecutors(List.of(mockExecutor));
|
||||
|
||||
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total > 200");
|
||||
assertEquals(2, result.size());
|
||||
assertNull(result.get(0).get("__value__"));
|
||||
assertNull(result.get(1).get("__value__"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void calculate7() {
|
||||
List<Map<String, Object>> prometheusData = List.of(
|
||||
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
|
||||
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
|
||||
);
|
||||
|
||||
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
|
||||
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
|
||||
Mockito.when(mockExecutor.execute(Mockito.anyString())).thenReturn(prometheusData);
|
||||
dataSourceService.setExecutors(List.of(mockExecutor));
|
||||
|
||||
List<Map<String, Object>> result = dataSourceService.calculate("promql", "(node_cpu_seconds_total <= 100)");
|
||||
assertEquals(2, result.size());
|
||||
assertEquals(100.0, result.get(0).get("__value__"));
|
||||
assertNull(result.get(1).get("__value__"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void calculate8() {
|
||||
List<Map<String, Object>> prometheusData = List.of(
|
||||
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
|
||||
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
|
||||
);
|
||||
|
||||
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
|
||||
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
|
||||
Mockito.when(mockExecutor.execute(Mockito.anyString())).thenReturn(prometheusData);
|
||||
dataSourceService.setExecutors(List.of(mockExecutor));
|
||||
|
||||
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"}[4m] <= 100");
|
||||
assertEquals(2, result.size());
|
||||
assertEquals(100.0, result.get(0).get("__value__"));
|
||||
assertNull(result.get(1).get("__value__"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void calculate9() {
|
||||
List<Map<String, Object>> prometheusData = List.of(
|
||||
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
|
||||
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
|
||||
);
|
||||
|
||||
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
|
||||
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
|
||||
Mockito.when(mockExecutor.execute(Mockito.anyString())).thenReturn(prometheusData);
|
||||
dataSourceService.setExecutors(List.of(mockExecutor));
|
||||
|
||||
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"} == 100");
|
||||
assertEquals(2, result.size());
|
||||
assertEquals(100.0, result.get(0).get("__value__"));
|
||||
assertNull(result.get(1).get("__value__"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void calculate10() {
|
||||
List<Map<String, Object>> prometheusData = List.of(
|
||||
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
|
||||
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
|
||||
);
|
||||
|
||||
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
|
||||
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
|
||||
Mockito.when(mockExecutor.execute(Mockito.anyString())).thenReturn(prometheusData);
|
||||
dataSourceService.setExecutors(List.of(mockExecutor));
|
||||
|
||||
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"} != 100");
|
||||
assertEquals(2, result.size());
|
||||
assertNull(result.get(0).get("__value__"));
|
||||
assertEquals(200.0, result.get(1).get("__value__"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void calculate11() {
|
||||
List<Map<String, Object>> prometheusData = List.of(
|
||||
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
|
||||
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
|
||||
);
|
||||
|
||||
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
|
||||
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
|
||||
Mockito.when(mockExecutor.execute(Mockito.anyString())).thenReturn(prometheusData);
|
||||
dataSourceService.setExecutors(List.of(mockExecutor));
|
||||
|
||||
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"} > 50 and node_cpu_seconds_total{mode=\"idle\"} < 120");
|
||||
assertEquals(1, result.size());
|
||||
assertEquals(100.0, result.get(0).get("__value__"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void calculate12() {
|
||||
List<Map<String, Object>> prometheusData = List.of(
|
||||
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
|
||||
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
|
||||
);
|
||||
|
||||
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
|
||||
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
|
||||
Mockito.when(mockExecutor.execute(Mockito.anyString())).thenReturn(prometheusData);
|
||||
dataSourceService.setExecutors(List.of(mockExecutor));
|
||||
|
||||
List<Map<String, Object>> result = dataSourceService.calculate("promql", "(node_cpu_seconds_total{mode=\"user\"} > 50) and (node_cpu_seconds_total{mode=\"idle\"} < 120)");
|
||||
assertEquals(1, result.size());
|
||||
assertEquals(100.0, result.get(0).get("__value__"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void calculate13() {
|
||||
List<Map<String, Object>> prometheusData = List.of(
|
||||
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
|
||||
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
|
||||
);
|
||||
|
||||
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
|
||||
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
|
||||
Mockito.when(mockExecutor.execute(Mockito.anyString())).thenReturn(prometheusData);
|
||||
dataSourceService.setExecutors(List.of(mockExecutor));
|
||||
|
||||
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"} > 150 and node_cpu_seconds_total{mode=\"idle\"} < 220");
|
||||
assertEquals(1, result.size());
|
||||
assertEquals(200.0, result.get(0).get("__value__"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void calculate14() {
|
||||
List<Map<String, Object>> prometheusData = List.of(
|
||||
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
|
||||
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
|
||||
);
|
||||
|
||||
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
|
||||
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
|
||||
Mockito.when(mockExecutor.execute(Mockito.anyString())).thenReturn(prometheusData);
|
||||
dataSourceService.setExecutors(List.of(mockExecutor));
|
||||
|
||||
List<Map<String, Object>> result = dataSourceService.calculate("promql", "(node_cpu_seconds_total{mode=\"user\"} > 150) and (node_cpu_seconds_total{mode=\"idle\"} < 220)");
|
||||
assertEquals(1, result.size());
|
||||
assertEquals(200.0, result.get(0).get("__value__"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void calculate15() {
|
||||
List<Map<String, Object>> prometheusData1 = List.of(
|
||||
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
|
||||
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
|
||||
);
|
||||
List<Map<String, Object>> prometheusData2 = List.of(
|
||||
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
|
||||
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
|
||||
);
|
||||
|
||||
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
|
||||
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
|
||||
Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"user\"}")).thenReturn(prometheusData1);
|
||||
Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"idle\"}")).thenReturn(prometheusData2);
|
||||
dataSourceService.setExecutors(List.of(mockExecutor));
|
||||
|
||||
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"} > 250 and node_cpu_seconds_total{mode=\"idle\"} < 220");
|
||||
assertEquals(1, result.size());
|
||||
assertNull(result.get(0).get("__value__"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void calculate16() {
|
||||
List<Map<String, Object>> prometheusData1 = List.of(
|
||||
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
|
||||
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
|
||||
);
|
||||
List<Map<String, Object>> prometheusData2 = List.of(
|
||||
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
|
||||
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
|
||||
);
|
||||
|
||||
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
|
||||
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
|
||||
Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"user\"}")).thenReturn(prometheusData1);
|
||||
Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"idle\"}")).thenReturn(prometheusData2);
|
||||
dataSourceService.setExecutors(List.of(mockExecutor));
|
||||
|
||||
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"} > 50 and node_cpu_seconds_total{mode=\"idle\"} < 20");
|
||||
assertEquals(1, result.size());
|
||||
assertNull(result.get(0).get("__value__"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void calculate17() {
|
||||
List<Map<String, Object>> prometheusData1 = List.of(
|
||||
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
|
||||
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
|
||||
);
|
||||
List<Map<String, Object>> prometheusData2 = List.of(
|
||||
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
|
||||
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
|
||||
);
|
||||
|
||||
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
|
||||
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
|
||||
Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"user\"}")).thenReturn(prometheusData1);
|
||||
Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"idle\"}")).thenReturn(prometheusData2);
|
||||
dataSourceService.setExecutors(List.of(mockExecutor));
|
||||
|
||||
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"} > 150 or node_cpu_seconds_total{mode=\"idle\"} < 20");
|
||||
assertEquals(1, result.size());
|
||||
assertEquals(200.0, result.get(0).get("__value__"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void calculate18() {
|
||||
List<Map<String, Object>> prometheusData1 = List.of(
|
||||
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
|
||||
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
|
||||
);
|
||||
List<Map<String, Object>> prometheusData2 = List.of(
|
||||
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
|
||||
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
|
||||
);
|
||||
|
||||
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
|
||||
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
|
||||
Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"user\"}")).thenReturn(prometheusData1);
|
||||
Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"idle\"}")).thenReturn(prometheusData2);
|
||||
dataSourceService.setExecutors(List.of(mockExecutor));
|
||||
|
||||
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"} > 250 or node_cpu_seconds_total{mode=\"idle\"} < 120");
|
||||
assertEquals(1, result.size());
|
||||
assertEquals(100.0, result.get(0).get("__value__"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void calculate19() {
|
||||
List<Map<String, Object>> prometheusData1 = List.of(
|
||||
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
|
||||
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
|
||||
);
|
||||
List<Map<String, Object>> prometheusData2 = List.of(
|
||||
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
|
||||
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
|
||||
);
|
||||
|
||||
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
|
||||
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
|
||||
Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"user\"}")).thenReturn(prometheusData1);
|
||||
Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"idle\"}")).thenReturn(prometheusData2);
|
||||
dataSourceService.setExecutors(List.of(mockExecutor));
|
||||
|
||||
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"} > 250 or node_cpu_seconds_total{mode=\"idle\"} < 20");
|
||||
assertEquals(1, result.size());
|
||||
assertNull(result.get(0).get("__value__"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void calculate20() {
|
||||
List<Map<String, Object>> prometheusData1 = List.of(
|
||||
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
|
||||
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
|
||||
);
|
||||
List<Map<String, Object>> prometheusData2 = List.of(
|
||||
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "key", "node1")),
|
||||
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "book", "node2"))
|
||||
);
|
||||
|
||||
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
|
||||
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
|
||||
Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"user\"}")).thenReturn(prometheusData1);
|
||||
Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"idle\"}")).thenReturn(prometheusData2);
|
||||
dataSourceService.setExecutors(List.of(mockExecutor));
|
||||
|
||||
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"} > 50 or node_cpu_seconds_total{mode=\"idle\"} < 320");
|
||||
assertEquals(1, result.size());
|
||||
assertNotNull(result.get(0).get("__value__"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void calculate21() {
|
||||
List<Map<String, Object>> prometheusData1 = List.of(
|
||||
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
|
||||
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
|
||||
);
|
||||
List<Map<String, Object>> prometheusData2 = List.of(
|
||||
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "key", "node1")),
|
||||
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "book", "node2"))
|
||||
);
|
||||
|
||||
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
|
||||
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
|
||||
Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"user\"}")).thenReturn(prometheusData1);
|
||||
Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"idle\"}")).thenReturn(prometheusData2);
|
||||
dataSourceService.setExecutors(List.of(mockExecutor));
|
||||
|
||||
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"} > 50 unless node_cpu_seconds_total{mode=\"idle\"} < 320");
|
||||
assertEquals(1, result.size());
|
||||
assertNull(result.get(0).get("__value__"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void calculate22() {
|
||||
List<Map<String, Object>> prometheusData1 = List.of(
|
||||
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
|
||||
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
|
||||
);
|
||||
List<Map<String, Object>> prometheusData2 = List.of(
|
||||
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "key", "node1")),
|
||||
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "book", "node2"))
|
||||
);
|
||||
|
||||
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
|
||||
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
|
||||
Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"user\"}")).thenReturn(prometheusData1);
|
||||
Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"idle\"}")).thenReturn(prometheusData2);
|
||||
dataSourceService.setExecutors(List.of(mockExecutor));
|
||||
|
||||
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"} > 50 unless node_cpu_seconds_total{mode=\"idle\"} < 20");
|
||||
assertEquals(1, result.size());
|
||||
assertNotNull(result.get(0).get("__value__"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void calculate23() {
|
||||
List<Map<String, Object>> prometheusData1 = List.of(
|
||||
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "instance", "node1")),
|
||||
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
|
||||
);
|
||||
List<Map<String, Object>> prometheusData2 = List.of(
|
||||
new HashMap<>(Map.of("__value__", 100.0, "timestamp", 1343554, "key", "node1")),
|
||||
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "book", "node2"))
|
||||
);
|
||||
|
||||
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
|
||||
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
|
||||
Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"user\"}")).thenReturn(prometheusData1);
|
||||
Mockito.when(mockExecutor.execute("node_cpu_seconds_total{mode=\"idle\"}")).thenReturn(prometheusData2);
|
||||
dataSourceService.setExecutors(List.of(mockExecutor));
|
||||
|
||||
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"} > 250 unless node_cpu_seconds_total{mode=\"idle\"} < 20");
|
||||
assertEquals(1, result.size());
|
||||
assertNull(result.get(0).get("__value__"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void calculate24() {
|
||||
List<Map<String, Object>> prometheusData = List.of();
|
||||
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
|
||||
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
|
||||
Mockito.when(mockExecutor.execute(Mockito.anyString())).thenReturn(prometheusData);
|
||||
dataSourceService.setExecutors(List.of(mockExecutor));
|
||||
|
||||
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total > 150");
|
||||
assertEquals(0, result.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void calculate25() {
|
||||
List<Map<String, Object>> prometheusData = List.of();
|
||||
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
|
||||
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
|
||||
Mockito.when(mockExecutor.execute(Mockito.anyString())).thenReturn(prometheusData);
|
||||
dataSourceService.setExecutors(List.of(mockExecutor));
|
||||
|
||||
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total{mode=\"user\"} > 250 unless node_cpu_seconds_total{mode=\"idle\"} < 20");
|
||||
assertEquals(0, result.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void calculate26() {
|
||||
List<Map<String, Object>> prometheusData = List.of(
|
||||
new HashMap<>(Map.of("__value__", 200.0, "timestamp", 1343555, "instance", "node2"))
|
||||
);
|
||||
QueryExecutor mockExecutor = Mockito.mock(QueryExecutor.class);
|
||||
Mockito.when(mockExecutor.support("promql")).thenReturn(true);
|
||||
Mockito.when(mockExecutor.execute(Mockito.anyString())).thenReturn(prometheusData);
|
||||
dataSourceService.setExecutors(List.of(mockExecutor));
|
||||
|
||||
List<Map<String, Object>> result = dataSourceService.calculate("promql", "node_cpu_seconds_total > 150");
|
||||
assertEquals(1, result.size());
|
||||
assertEquals(200.0, result.get(0).get("__value__"));
|
||||
}
|
||||
}
|
||||
+24
-137
@@ -17,42 +17,33 @@
|
||||
|
||||
package org.apache.hertzbeat.alert.service;
|
||||
|
||||
import org.apache.hertzbeat.alert.dao.NoticeReceiverDao;
|
||||
import org.apache.hertzbeat.alert.dao.NoticeRuleDao;
|
||||
import org.apache.hertzbeat.alert.dao.NoticeTemplateDao;
|
||||
import org.apache.hertzbeat.alert.notice.AlertNoticeDispatch;
|
||||
import org.apache.hertzbeat.alert.service.impl.NoticeConfigServiceImpl;
|
||||
import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeRule;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeTemplate;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import org.apache.hertzbeat.alert.service.impl.NoticeConfigServiceImpl;
|
||||
import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeRule;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeTemplate;
|
||||
import org.apache.hertzbeat.alert.notice.AlertNoticeDispatch;
|
||||
import org.apache.hertzbeat.alert.dao.NoticeReceiverDao;
|
||||
import org.apache.hertzbeat.alert.dao.NoticeRuleDao;
|
||||
import org.apache.hertzbeat.alert.dao.NoticeTemplateDao;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
|
||||
/**
|
||||
* Test case for {@link NoticeConfigService}
|
||||
*/
|
||||
@Disabled
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class NoticeConfigServiceTest {
|
||||
|
||||
@@ -67,130 +58,26 @@ class NoticeConfigServiceTest {
|
||||
@InjectMocks
|
||||
private NoticeConfigServiceImpl noticeConfigService;
|
||||
|
||||
private NoticeReceiver receiver1;
|
||||
private NoticeReceiver receiver2;
|
||||
private NoticeTemplate template1;
|
||||
private NoticeTemplate template2;
|
||||
private NoticeRule rule1;
|
||||
private NoticeRule rule2;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
receiver1 = new NoticeReceiver();
|
||||
receiver1.setId(1L);
|
||||
receiver1.setName("Receiver1");
|
||||
|
||||
receiver2 = new NoticeReceiver();
|
||||
receiver2.setId(2L);
|
||||
receiver2.setName("Receiver2");
|
||||
|
||||
template1 = new NoticeTemplate();
|
||||
template1.setId(1L);
|
||||
template1.setName("Template1");
|
||||
|
||||
template2 = new NoticeTemplate();
|
||||
template2.setId(2L);
|
||||
template2.setName("Template2");
|
||||
|
||||
rule1 = new NoticeRule();
|
||||
rule1.setId(1L);
|
||||
rule1.setName("Rule1");
|
||||
|
||||
rule2 = new NoticeRule();
|
||||
rule2.setId(2L);
|
||||
rule2.setName("Rule2");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getNoticeReceivers() {
|
||||
Page<NoticeReceiver> receiverPage = new PageImpl<>(
|
||||
Arrays.asList(receiver1, receiver2),
|
||||
PageRequest.of(0, 8, Sort.by(Sort.Direction.DESC, "id")),
|
||||
2
|
||||
);
|
||||
|
||||
when(noticeReceiverDao.findAll(any(Specification.class), any(PageRequest.class))).thenReturn(receiverPage);
|
||||
|
||||
Page<NoticeReceiver> result = noticeConfigService.getNoticeReceivers("Receiver", 0, 8);
|
||||
|
||||
assertEquals(2, result.getTotalElements());
|
||||
assertEquals(1, result.getTotalPages());
|
||||
assertEquals(8, result.getSize());
|
||||
assertEquals(0, result.getNumber());
|
||||
assertEquals(receiver1, result.getContent().get(0));
|
||||
assertEquals(receiver2, result.getContent().get(1));
|
||||
|
||||
verify(noticeReceiverDao, times(1)).findAll(any(Specification.class), any(PageRequest.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAllNoticeReceivers() {
|
||||
when(noticeReceiverDao.findAll()).thenReturn(Arrays.asList(receiver1, receiver2));
|
||||
|
||||
List<NoticeReceiver> result = noticeConfigService.getAllNoticeReceivers();
|
||||
|
||||
assertEquals(2, result.size());
|
||||
assertEquals(receiver1, result.get(0));
|
||||
assertEquals(receiver2, result.get(1));
|
||||
|
||||
verify(noticeReceiverDao, times(1)).findAll();
|
||||
noticeConfigService.getNoticeReceivers(null);
|
||||
verify(noticeReceiverDao, times(1)).findAll(any(Specification.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getNoticeTemplates() {
|
||||
Page<NoticeTemplate> templatePage = new PageImpl<>(
|
||||
Arrays.asList(template1, template2),
|
||||
PageRequest.of(0, 8, Sort.by(Sort.Direction.DESC, "id")),
|
||||
2
|
||||
);
|
||||
|
||||
when(noticeTemplateDao.findAll(any(Specification.class), any(PageRequest.class))).thenReturn(templatePage);
|
||||
|
||||
Page<NoticeTemplate> result = noticeConfigService.getNoticeTemplates("Template", false, 0, 8);
|
||||
|
||||
assertEquals(2, result.getTotalElements());
|
||||
assertEquals(1, result.getTotalPages());
|
||||
assertEquals(8, result.getSize());
|
||||
assertEquals(0, result.getNumber());
|
||||
assertEquals(template1, result.getContent().get(0));
|
||||
assertEquals(template2, result.getContent().get(1));
|
||||
|
||||
verify(noticeTemplateDao, times(1)).findAll(any(Specification.class), any(PageRequest.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAllNoticeTemplates() {
|
||||
when(noticeTemplateDao.findAll()).thenReturn(Arrays.asList(template1, template2));
|
||||
|
||||
List<NoticeTemplate> result = noticeConfigService.getAllNoticeTemplates();
|
||||
|
||||
assert result.size() >= 2;
|
||||
assertEquals(template1, result.get(result.size() - 2));
|
||||
assertEquals(template2, result.get(result.size() - 1));
|
||||
|
||||
verify(noticeTemplateDao, times(1)).findAll();
|
||||
noticeConfigService.getNoticeTemplates(null);
|
||||
verify(noticeTemplateDao, times(1)).findAll(any(Specification.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getNoticeRules() {
|
||||
Page<NoticeRule> rulePage = new PageImpl<>(
|
||||
Arrays.asList(rule1, rule2),
|
||||
PageRequest.of(0, 8, Sort.by(Sort.Direction.DESC, "id")),
|
||||
2
|
||||
);
|
||||
|
||||
when(noticeRuleDao.findAll(any(Specification.class), any(PageRequest.class))).thenReturn(rulePage);
|
||||
|
||||
Page<NoticeRule> result = noticeConfigService.getNoticeRules("Rule", 0, 8);
|
||||
|
||||
assertEquals(2, result.getTotalElements());
|
||||
assertEquals(1, result.getTotalPages());
|
||||
assertEquals(8, result.getSize());
|
||||
assertEquals(0, result.getNumber());
|
||||
assertEquals(rule1, result.getContent().get(0));
|
||||
assertEquals(rule2, result.getContent().get(1));
|
||||
|
||||
verify(noticeRuleDao, times(1)).findAll(any(Specification.class), any(PageRequest.class));
|
||||
noticeConfigService.getNoticeRules(null);
|
||||
verify(noticeRuleDao, times(1)).findAll(any(Specification.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -155,9 +155,5 @@
|
||||
<artifactId>plc4j-driver-modbus</artifactId>
|
||||
<version>0.12.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.sshd</groupId>
|
||||
<artifactId>sshd-sftp</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
+26
-104
@@ -17,11 +17,9 @@
|
||||
|
||||
package org.apache.hertzbeat.collector.collect.ftp;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.net.ftp.FTPClient;
|
||||
import org.apache.hertzbeat.collector.collect.AbstractCollect;
|
||||
@@ -31,10 +29,6 @@ import org.apache.hertzbeat.common.entity.job.Metrics;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.FtpProtocol;
|
||||
import org.apache.hertzbeat.common.entity.message.CollectRep;
|
||||
import org.apache.hertzbeat.common.util.CommonUtil;
|
||||
import org.apache.sshd.client.SshClient;
|
||||
import org.apache.sshd.client.session.ClientSession;
|
||||
import org.apache.sshd.sftp.client.SftpClient;
|
||||
import org.apache.sshd.sftp.client.SftpClientFactory;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -62,14 +56,33 @@ public class FtpCollectImpl extends AbstractCollect {
|
||||
Assert.hasText(ftpProtocol.getTimeout(), "Ftp Protocol timeout is required.");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void collect(CollectRep.MetricsData.Builder builder, Metrics metrics) {
|
||||
boolean ssl = Boolean.parseBoolean(metrics.getFtp().getSsl());
|
||||
if (ssl){
|
||||
handleSftpCollect(builder, metrics);
|
||||
} else {
|
||||
handleFtpCollect(builder, metrics);
|
||||
FTPClient ftpClient = new FTPClient();
|
||||
FtpProtocol ftpProtocol = metrics.getFtp();
|
||||
// Set timeout
|
||||
ftpClient.setControlKeepAliveReplyTimeout(Integer.parseInt(ftpProtocol.getTimeout()));
|
||||
|
||||
// Collect data to load in CollectRep.ValueRow.Builder's object
|
||||
CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder();
|
||||
Map<String, String> valueMap;
|
||||
try {
|
||||
valueMap = collectValue(ftpClient, ftpProtocol);
|
||||
metrics.getAliasFields().forEach(it -> {
|
||||
if (valueMap.containsKey(it)) {
|
||||
String fieldValue = valueMap.get(it);
|
||||
valueRowBuilder.addColumn(Objects.requireNonNullElse(fieldValue, CommonConstants.NULL_VALUE));
|
||||
} else {
|
||||
valueRowBuilder.addColumn(CommonConstants.NULL_VALUE);
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
builder.setCode(CollectRep.Code.UN_CONNECTABLE);
|
||||
builder.setMsg(e.getMessage());
|
||||
return;
|
||||
}
|
||||
builder.addValueRow(valueRowBuilder.build());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,23 +113,8 @@ public class FtpCollectImpl extends AbstractCollect {
|
||||
};
|
||||
}
|
||||
|
||||
private Map<String, String> collectValue(SftpClient sftpClient, FtpProtocol ftpProtocol) {
|
||||
boolean isActive;
|
||||
String responseTime;
|
||||
try {
|
||||
long startTime = System.currentTimeMillis();
|
||||
sftpClient.stat(ftpProtocol.getDirection());
|
||||
isActive = true;
|
||||
long endTime = System.currentTimeMillis();
|
||||
responseTime = String.valueOf(endTime - startTime);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalArgumentException("[SFTPClient] error: {}" + CommonUtil.getMessageFromThrowable(e), e);
|
||||
}
|
||||
return Map.of("isActive", Boolean.toString(isActive), "responseTime", responseTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* ftp login
|
||||
* login
|
||||
*/
|
||||
private void login(FTPClient ftpClient, FtpProtocol ftpProtocol) {
|
||||
try {
|
||||
@@ -149,84 +147,8 @@ public class FtpCollectImpl extends AbstractCollect {
|
||||
}
|
||||
}
|
||||
|
||||
private ClientSession connect(SshClient client, FtpProtocol ftpProtocol) {
|
||||
client.start();
|
||||
try {
|
||||
ClientSession session = client.connect(ftpProtocol.getUsername(), ftpProtocol.getHost(), Integer.parseInt(ftpProtocol.getPort()))
|
||||
.verify(Integer.parseInt(ftpProtocol.getTimeout()))
|
||||
.getSession();
|
||||
session.addPasswordIdentity(ftpProtocol.getPassword());
|
||||
session.auth().verify(Integer.parseInt(ftpProtocol.getTimeout()));
|
||||
return session;
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException("[sftp connection] error: {}" + CommonUtil.getMessageFromThrowable(e), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String supportProtocol() {
|
||||
return DispatchConstants.PROTOCOL_FTP;
|
||||
}
|
||||
|
||||
private void handleFtpCollect(CollectRep.MetricsData.Builder builder, Metrics metrics) {
|
||||
FTPClient ftpClient = new FTPClient();
|
||||
FtpProtocol ftpProtocol = metrics.getFtp();
|
||||
// Set timeout
|
||||
ftpClient.setControlKeepAliveReplyTimeout(Integer.parseInt(ftpProtocol.getTimeout()));
|
||||
|
||||
// Collect data to load in CollectRep.ValueRow.Builder's object
|
||||
CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder();
|
||||
Map<String, String> valueMap;
|
||||
try {
|
||||
valueMap = collectValue(ftpClient, ftpProtocol);
|
||||
metrics.getAliasFields().forEach(it -> {
|
||||
if (valueMap.containsKey(it)) {
|
||||
String fieldValue = valueMap.get(it);
|
||||
valueRowBuilder.addColumn(Objects.requireNonNullElse(fieldValue, CommonConstants.NULL_VALUE));
|
||||
} else {
|
||||
valueRowBuilder.addColumn(CommonConstants.NULL_VALUE);
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
builder.setCode(CollectRep.Code.UN_CONNECTABLE);
|
||||
builder.setMsg(e.getMessage());
|
||||
return;
|
||||
}
|
||||
builder.addValueRow(valueRowBuilder.build());
|
||||
}
|
||||
|
||||
private void handleSftpCollect(CollectRep.MetricsData.Builder builder, Metrics metrics) {
|
||||
FtpProtocol ftpProtocol = metrics.getFtp();
|
||||
ClientSession session = null;
|
||||
SftpClient sftpClient = null;
|
||||
SshClient client = null;
|
||||
try {
|
||||
client = SshClient.setUpDefaultClient();
|
||||
session = connect(client, ftpProtocol);
|
||||
sftpClient = SftpClientFactory.instance().createSftpClient(session);
|
||||
Map<String, String> valueMap = collectValue(sftpClient, ftpProtocol);
|
||||
CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder();
|
||||
metrics.getAliasFields().forEach(it ->
|
||||
valueRowBuilder.addColumn(valueMap.getOrDefault(it, CommonConstants.NULL_VALUE))
|
||||
);
|
||||
builder.addValueRow(valueRowBuilder.build());
|
||||
} catch (Exception e) {
|
||||
builder.setCode(CollectRep.Code.UN_CONNECTABLE);
|
||||
builder.setMsg(e.getMessage());
|
||||
} finally {
|
||||
try {
|
||||
if (sftpClient != null && sftpClient.isOpen()){
|
||||
sftpClient.close();
|
||||
}
|
||||
if (session != null && session.isOpen()){
|
||||
session.close();
|
||||
}
|
||||
if (client != null && client.isOpen()){
|
||||
client.close();
|
||||
}
|
||||
} catch (Exception e){
|
||||
log.error("[SFTPClient] error while closing: {}", CommonUtil.getMessageFromThrowable(e), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+50
-45
@@ -17,63 +17,68 @@
|
||||
|
||||
package org.apache.hertzbeat.collector.collect.prometheus.parser;
|
||||
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
class OnlineParserTest {
|
||||
|
||||
@Disabled // Disabled due to the fact that the URL is not reachable unless you have the Prometheus server running
|
||||
@Test
|
||||
void parseMetrics() throws Exception {
|
||||
URL url = new URL("http://localhost:9090/metrics");
|
||||
InputStream inputStream = url.openStream();
|
||||
Map<String, MetricFamily> metricFamilyMap = OnlineParser.parseMetrics(inputStream);
|
||||
assertNotNull(metricFamilyMap);
|
||||
void parseMetrics() {
|
||||
try {
|
||||
URL url = new URL("http://localhost:9090/metrics");
|
||||
InputStream inputStream = url.openStream();
|
||||
Map<String, MetricFamily> metricFamilyMap = OnlineParser.parseMetrics(inputStream);
|
||||
System.out.println(1);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void parseMetrics2() throws Exception {
|
||||
String str = """
|
||||
# HELP go_gc_duration_seconds A summary of the pause duration of garbage collection cycles.
|
||||
# TYPE go_gc_duration_seconds summary
|
||||
go_gc_duration_seconds { quantile="0"} 2.0209e-05 321312
|
||||
go_gc_duration_seconds{ quantile = "0.25" } 6.6917e-05
|
||||
go_gc_duration_seconds{quantile="0.5"} -Inf
|
||||
go_gc_duration_seconds{ quantile = "0.75"} +Inf
|
||||
go_gc_duration_seconds{quantile="1"} NaN
|
||||
go_gc_duration_seconds_sum 0.001134793 321314
|
||||
go_gc_duration_seconds_count 5 43
|
||||
# HELP go_goroutines Number of goroutines that currently exist.
|
||||
# TYPE go_goroutines gauge
|
||||
go_goroutines 32
|
||||
# HELP go_info Information about the Go environment.
|
||||
# TYPE go_info gauge
|
||||
go_info{version="go1.21.6"} 1
|
||||
# HELP go_memstats_alloc_bytes Number of bytes allocated and still in use.
|
||||
# TYPE go_memstats_alloc_bytes gauge
|
||||
go_memstats_alloc_bytes 1.5716224e+07
|
||||
# HELP go_memstats_alloc_bytes_total Total number of bytes allocated, even if freed.
|
||||
# TYPE go_memstats_alloc_bytes_total counter
|
||||
go_memstats_alloc_bytes_total 2.0707544e+07
|
||||
# HELP go_memstats_buck_hash_sys_bytes Number of bytes used by the profiling bucket hash table.
|
||||
# TYPE go_memstats_buck_hash_sys_bytes gauge
|
||||
go_memstats_buck_hash_sys_bytes 1.457881e+06
|
||||
# HELP go_memstats_frees_total Total number of frees.
|
||||
# TYPE go_memstats_frees_total counter
|
||||
go_memstats_frees_total 50438
|
||||
# HELP go_memstats_gc_sys_bytes Number of bytes used for garbage collection system metadata.
|
||||
# TYPE go_memstats_gc_sys_bytes gauge
|
||||
go_memstats_gc_sys_bytes 4.614808e+06""";
|
||||
InputStream inputStream = new ByteArrayInputStream(str.getBytes(StandardCharsets.UTF_8));
|
||||
Map<String, MetricFamily> metricFamilyMap = OnlineParser.parseMetrics(inputStream);
|
||||
assertNotNull(metricFamilyMap);
|
||||
void parseMetrics2() {
|
||||
try {
|
||||
String str = """
|
||||
# HELP go_gc_duration_seconds A summary of the pause duration of garbage collection cycles.
|
||||
# TYPE go_gc_duration_seconds summary
|
||||
go_gc_duration_seconds { quantile="0"} 2.0209e-05 321312
|
||||
go_gc_duration_seconds{ quantile = "0.25" } 6.6917e-05
|
||||
go_gc_duration_seconds{quantile="0.5"} -Inf
|
||||
go_gc_duration_seconds{ quantile = "0.75"} +Inf
|
||||
go_gc_duration_seconds{quantile="1"} NaN
|
||||
go_gc_duration_seconds_sum 0.001134793 321314
|
||||
go_gc_duration_seconds_count 5 43
|
||||
# HELP go_goroutines Number of goroutines that currently exist.
|
||||
# TYPE go_goroutines gauge
|
||||
go_goroutines 32
|
||||
# HELP go_info Information about the Go environment.
|
||||
# TYPE go_info gauge
|
||||
go_info{version="go1.21.6"} 1
|
||||
# HELP go_memstats_alloc_bytes Number of bytes allocated and still in use.
|
||||
# TYPE go_memstats_alloc_bytes gauge
|
||||
go_memstats_alloc_bytes 1.5716224e+07
|
||||
# HELP go_memstats_alloc_bytes_total Total number of bytes allocated, even if freed.
|
||||
# TYPE go_memstats_alloc_bytes_total counter
|
||||
go_memstats_alloc_bytes_total 2.0707544e+07
|
||||
# HELP go_memstats_buck_hash_sys_bytes Number of bytes used by the profiling bucket hash table.
|
||||
# TYPE go_memstats_buck_hash_sys_bytes gauge
|
||||
go_memstats_buck_hash_sys_bytes 1.457881e+06
|
||||
# HELP go_memstats_frees_total Total number of frees.
|
||||
# TYPE go_memstats_frees_total counter
|
||||
go_memstats_frees_total 50438
|
||||
# HELP go_memstats_gc_sys_bytes Number of bytes used for garbage collection system metadata.
|
||||
# TYPE go_memstats_gc_sys_bytes gauge
|
||||
go_memstats_gc_sys_bytes 4.614808e+06""";
|
||||
InputStream inputStream = new ByteArrayInputStream(str.getBytes());
|
||||
Map<String, MetricFamily> metricFamilyMap = OnlineParser.parseMetrics(inputStream);
|
||||
System.out.println(1);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
-3
@@ -18,7 +18,6 @@
|
||||
package org.apache.hertzbeat.common.entity.alerter;
|
||||
|
||||
import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_ONLY;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Convert;
|
||||
@@ -98,12 +97,10 @@ public class GroupAlert {
|
||||
|
||||
@Schema(title = "This record creation time (millisecond timestamp)")
|
||||
@CreatedDate
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime gmtCreate;
|
||||
|
||||
@Schema(title = "Record the latest modification time (timestamp in milliseconds)")
|
||||
@LastModifiedDate
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime gmtUpdate;
|
||||
|
||||
@Transient
|
||||
|
||||
-3
@@ -18,7 +18,6 @@
|
||||
package org.apache.hertzbeat.common.entity.alerter;
|
||||
|
||||
import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_ONLY;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Convert;
|
||||
@@ -101,12 +100,10 @@ public class SingleAlert {
|
||||
|
||||
@Schema(title = "This record creation time (millisecond timestamp)")
|
||||
@CreatedDate
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime gmtCreate;
|
||||
|
||||
@Schema(title = "Record the latest modification time (timestamp in milliseconds)")
|
||||
@LastModifiedDate
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime gmtUpdate;
|
||||
|
||||
@Override
|
||||
|
||||
-6
@@ -59,10 +59,4 @@ public class FtpProtocol implements CommonRequestProtocol, Protocol {
|
||||
* Timeout
|
||||
*/
|
||||
private String timeout;
|
||||
|
||||
/**
|
||||
* Whether ftp uses link encryption ssl/tls, i.e. ftp or sftp
|
||||
*
|
||||
*/
|
||||
private String ssl = "false";
|
||||
}
|
||||
|
||||
@@ -53,10 +53,5 @@
|
||||
<version>${hertzbeat.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>testcontainers</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
+1
-7
@@ -56,7 +56,6 @@ public class HttpMonitorE2eTest extends AbstractCollectE2eTest {
|
||||
private static final int MOCK_SERVER_PORT = 52376;
|
||||
private static final String LOCALHOST = "127.0.0.1";
|
||||
private static final String RELATIVE_PATH = "/";
|
||||
private static final List<String> ALLOW_EMPTY_WHITE_LIST = List.of("header");
|
||||
private static HttpServer mockServer;
|
||||
|
||||
@AfterAll
|
||||
@@ -97,12 +96,7 @@ public class HttpMonitorE2eTest extends AbstractCollectE2eTest {
|
||||
List<Map<String, Configmap>> configmapFromPreCollectData = new LinkedList<>();
|
||||
for (Metrics metricsDef : dockerJob.getMetrics()) {
|
||||
metricsDef = CollectUtil.replaceCryPlaceholderToMetrics(metricsDef, configmapFromPreCollectData.size() > 0 ? configmapFromPreCollectData.get(0) : new HashMap<>());
|
||||
CollectRep.MetricsData metricsData;
|
||||
if (ALLOW_EMPTY_WHITE_LIST.contains(metricsDef.getName())) {
|
||||
metricsData = validateMetricsCollection(metricsDef, metricsDef.getName(), true);
|
||||
} else {
|
||||
metricsData = validateMetricsCollection(metricsDef, metricsDef.getName());
|
||||
}
|
||||
CollectRep.MetricsData metricsData = validateMetricsCollection(metricsDef, metricsDef.getName());
|
||||
configmapFromPreCollectData = CollectUtil.getConfigmapFromPreCollectData(metricsData);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-10
@@ -38,8 +38,6 @@ import org.testcontainers.lifecycle.Startables;
|
||||
import org.testcontainers.utility.DockerImageName;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
@@ -56,7 +54,6 @@ public class SshCollectE2eTest extends AbstractCollectE2eTest {
|
||||
private static final String ROOT_USER = "root";
|
||||
private static final int SSH_PORT = 22;
|
||||
private static final int PASSWORD_LENGTH = 12;
|
||||
private static final List<String> ALLOW_EMPTY_WHITE_LIST = Arrays.asList("top_mem_process", "top_cpu_process");
|
||||
|
||||
private static GenericContainer<?> linuxContainer;
|
||||
|
||||
@@ -91,13 +88,8 @@ public class SshCollectE2eTest extends AbstractCollectE2eTest {
|
||||
Assertions.assertTrue(linuxContainer.isRunning(), "Ubuntu container should be running");
|
||||
|
||||
Job ubuntuJob = appService.getAppDefine("ubuntu");
|
||||
ubuntuJob.getMetrics().forEach(metricsDef -> {
|
||||
if (ALLOW_EMPTY_WHITE_LIST.contains(metricsDef.getName())) {
|
||||
validateMetricsCollection(metricsDef, metricsDef.getName(), true);
|
||||
} else {
|
||||
validateMetricsCollection(metricsDef, metricsDef.getName());
|
||||
}
|
||||
});
|
||||
ubuntuJob.getMetrics().forEach(metricsDef ->
|
||||
validateMetricsCollection(metricsDef, metricsDef.getName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
-128
@@ -1,128 +0,0 @@
|
||||
/*
|
||||
* 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.collector.collect.basic.telnet;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.collector.collect.AbstractCollectE2eTest;
|
||||
import org.apache.hertzbeat.collector.collect.telnet.TelnetCollectImpl;
|
||||
import org.apache.hertzbeat.collector.util.CollectUtil;
|
||||
import org.apache.hertzbeat.common.entity.job.Configmap;
|
||||
import org.apache.hertzbeat.common.entity.job.Job;
|
||||
import org.apache.hertzbeat.common.entity.job.Metrics;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.Protocol;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.TelnetProtocol;
|
||||
import org.apache.hertzbeat.common.entity.message.CollectRep;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.testcontainers.containers.GenericContainer;
|
||||
import org.testcontainers.containers.wait.strategy.Wait;
|
||||
import org.testcontainers.utility.DockerImageName;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Integration test for Zookeeper monitoring functionality
|
||||
*/
|
||||
@Slf4j
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class ZookeeperMonitorE2eTest extends AbstractCollectE2eTest {
|
||||
|
||||
private static final String ZOOKEEPER_IMAGE_NAME = "zookeeper:3.8.4";
|
||||
private static final String ZOOKEEPER_NAME = "zookeeper";
|
||||
private static final Integer ZOOKEEPER_PORT = 2181;
|
||||
private static GenericContainer<?> zookeeperContainer;
|
||||
|
||||
@AfterAll
|
||||
public static void tearDown() {
|
||||
if (zookeeperContainer != null) {
|
||||
zookeeperContainer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
collect = new TelnetCollectImpl();
|
||||
metrics = new Metrics();
|
||||
|
||||
try {
|
||||
// Start Zookeeper container with custom configuration
|
||||
zookeeperContainer = new GenericContainer<>(DockerImageName.parse(ZOOKEEPER_IMAGE_NAME))
|
||||
.withExposedPorts(ZOOKEEPER_PORT)
|
||||
.withEnv("ZOO_4LW_COMMANDS_WHITELIST", "*")
|
||||
.withNetworkAliases(ZOOKEEPER_NAME)
|
||||
.waitingFor(
|
||||
Wait.forLogMessage(".*Started AdminServer on address.*\\n", 1)
|
||||
.withStartupTimeout(Duration.ofSeconds(60))
|
||||
)
|
||||
.withLogConsumer(outputFrame -> {
|
||||
log.info(outputFrame.getUtf8String());
|
||||
});
|
||||
|
||||
zookeeperContainer.start();
|
||||
log.info("Zookeeper container started at {}:{}",
|
||||
zookeeperContainer.getHost(),
|
||||
zookeeperContainer.getMappedPort(ZOOKEEPER_PORT));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
log.error("Failed to start Zookeeper container", e);
|
||||
throw e;
|
||||
}
|
||||
Thread.sleep(30000);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CollectRep.MetricsData.Builder collectMetrics(Metrics metricsDef) {
|
||||
TelnetProtocol telnetProtocol = (TelnetProtocol) buildProtocol(metricsDef);
|
||||
metrics.setTelnet(telnetProtocol);
|
||||
CollectRep.MetricsData.Builder metricsData = CollectRep.MetricsData.newBuilder();
|
||||
metricsData.setApp(ZOOKEEPER_NAME);
|
||||
metrics.setAliasFields(metricsDef.getAliasFields());
|
||||
return collectMetricsData(metrics, metricsDef, metricsData);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Protocol buildProtocol(Metrics metricsDef) {
|
||||
TelnetProtocol protocol = new TelnetProtocol();
|
||||
protocol.setHost(zookeeperContainer.getHost());
|
||||
protocol.setPort(String.valueOf(zookeeperContainer.getMappedPort(ZOOKEEPER_PORT)));
|
||||
protocol.setCmd(metricsDef.getTelnet().getCmd());
|
||||
return protocol;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testZookeeperMonitor() {
|
||||
Assertions.assertTrue(zookeeperContainer.isRunning(), "Zookeeper container should be running");
|
||||
|
||||
Job dockerJob = appService.getAppDefine("zookeeper");
|
||||
List<Map<String, Configmap>> configmapFromPreCollectData = new LinkedList<>();
|
||||
for (Metrics metricsDef : dockerJob.getMetrics()) {
|
||||
metricsDef = CollectUtil.replaceCryPlaceholderToMetrics(metricsDef, configmapFromPreCollectData.size() > 0 ? configmapFromPreCollectData.get(0) : new HashMap<>());
|
||||
CollectRep.MetricsData metricsData = validateMetricsCollection(metricsDef, metricsDef.getName());
|
||||
configmapFromPreCollectData = CollectUtil.getConfigmapFromPreCollectData(metricsData);
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
-38
@@ -22,7 +22,6 @@ import org.apache.hertzbeat.collector.dispatch.CollectDataDispatch;
|
||||
import org.apache.hertzbeat.collector.dispatch.MetricsCollect;
|
||||
import org.apache.hertzbeat.collector.dispatch.timer.Timeout;
|
||||
import org.apache.hertzbeat.collector.dispatch.timer.WheelTimerTask;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.job.Job;
|
||||
import org.apache.hertzbeat.common.entity.job.Metrics;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.Protocol;
|
||||
@@ -78,39 +77,20 @@ public abstract class AbstractCollectE2eTest {
|
||||
|
||||
/**
|
||||
* Validate metrics collection, check if the metrics values are not empty <br/>
|
||||
* @param metricsDef metrics definition
|
||||
* @param metricName metric name
|
||||
* @return metrics data
|
||||
* We believe that all monitoring metrics should have data
|
||||
*/
|
||||
protected CollectRep.MetricsData validateMetricsCollection(Metrics metricsDef, String metricName) {
|
||||
// By default, we do not allow empty values
|
||||
return validateMetricsCollection(metricsDef, metricName, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate metrics collection, check if the metrics values are not empty <br/>
|
||||
* We believe that all monitoring metrics should have data
|
||||
*
|
||||
* @param metricsDef metrics definition
|
||||
* @param metricName metric name
|
||||
* @param allowEmpty In some special scenarios, it is not necessary to check if the value is ` `
|
||||
*/
|
||||
protected CollectRep.MetricsData validateMetricsCollection(Metrics metricsDef, String metricName, boolean allowEmpty) {
|
||||
CollectRep.MetricsData.Builder metricsData = collectMetrics(metricsDef);
|
||||
|
||||
metricsCollect.calculateFields(metricsDef, metricsData);
|
||||
|
||||
Assertions.assertTrue(metricsData.getValuesList().size() > 0,
|
||||
String.format("%s metrics values should not be empty, detail: %s", metricName, metricsData.getMsg()));
|
||||
String.format("%s metrics values should not be empty", metricName));
|
||||
|
||||
for (CollectRep.ValueRow valueRow : metricsData.getValuesList()) {
|
||||
for (int i = 0; i < valueRow.getColumnsCount(); i++) {
|
||||
Assertions.assertFalse(valueRow.getColumns(i).isEmpty(),
|
||||
String.format("%s metric column %d should not be empty", metricName, i));
|
||||
if (!allowEmpty) {
|
||||
// Check if the value is not null
|
||||
Assertions.assertNotEquals(CommonConstants.NULL_VALUE, valueRow.getColumns(i), String.format("%s metric column %d should not be null", metricName, i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,31 +98,21 @@ public abstract class AbstractCollectE2eTest {
|
||||
return metricsData.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set alias fields for metrics
|
||||
*
|
||||
* @param metrics metrics
|
||||
* @param metricsDef metrics definition
|
||||
*/
|
||||
protected void setMetricsAliasFields(Metrics metrics, Metrics metricsDef) {
|
||||
List<String> aliasFields = metricsDef.getAliasFields() == null
|
||||
? metricsDef.getFields().stream().map(Metrics.Field::getField).collect(Collectors.toList())
|
||||
: metricsDef.getAliasFields();
|
||||
metrics.setAliasFields(aliasFields);
|
||||
metricsDef.setAliasFields(aliasFields);
|
||||
metrics.setAliasFields(metricsDef.getAliasFields() == null
|
||||
? metricsDef.getFields().stream()
|
||||
.map(Metrics.Field::getField)
|
||||
.collect(Collectors.toList()) :
|
||||
metricsDef.getAliasFields());
|
||||
}
|
||||
|
||||
protected abstract CollectRep.MetricsData.Builder collectMetrics(Metrics metricsDef);
|
||||
|
||||
protected CollectRep.MetricsData.Builder collectMetricsData(Metrics metrics, Metrics metricsDef) {
|
||||
CollectRep.MetricsData.Builder metricsData = CollectRep.MetricsData.newBuilder();
|
||||
return this.collectMetricsData(metrics, metricsDef, metricsData);
|
||||
}
|
||||
|
||||
protected CollectRep.MetricsData.Builder collectMetricsData(Metrics metrics, Metrics metricsDef, CollectRep.MetricsData.Builder metricsData) {
|
||||
setMetricsAliasFields(metrics, metricsDef);
|
||||
|
||||
// Collect metrics
|
||||
CollectRep.MetricsData.Builder metricsData = CollectRep.MetricsData.newBuilder();
|
||||
collect.collect(metricsData, metrics);
|
||||
return metricsData;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.ImportRuntimeHints;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
|
||||
/**
|
||||
* start up class.
|
||||
@@ -40,7 +39,6 @@ import org.springframework.scheduling.annotation.EnableAsync;
|
||||
@ComponentScan(basePackages = {"org.apache.hertzbeat"})
|
||||
@ConfigurationPropertiesScan(basePackages = {"org.apache.hertzbeat"})
|
||||
@ImportRuntimeHints(HertzbeatRuntimeHintsRegistrar.class)
|
||||
@EnableAsync
|
||||
public class Manager {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Manager.class, args);
|
||||
|
||||
+1
-1
@@ -71,7 +71,7 @@ public class CollectorServiceImpl implements CollectorService {
|
||||
Specification<Collector> specification = (root, query, criteriaBuilder) -> {
|
||||
Predicate predicate = criteriaBuilder.conjunction();
|
||||
if (StringUtils.isNotBlank(name)) {
|
||||
Predicate predicateName = criteriaBuilder.like(criteriaBuilder.lower(root.get("name")), "%" + name.toLowerCase() + "%");
|
||||
Predicate predicateName = criteriaBuilder.like(root.get("name"), "%" + name + "%");
|
||||
predicate = criteriaBuilder.and(predicateName);
|
||||
}
|
||||
return predicate;
|
||||
|
||||
+3
-5
@@ -566,13 +566,11 @@ public class MonitorServiceImpl implements MonitorService {
|
||||
List<Predicate> orList = new ArrayList<>();
|
||||
if (StringUtils.isNotBlank(search)) {
|
||||
Predicate predicateHost = criteriaBuilder.like(root.get("host"), "%" + search + "%");
|
||||
Predicate predicateName = criteriaBuilder.like(criteriaBuilder.lower(root.get("name")), "%" + search.toLowerCase() + "%");
|
||||
if (StringUtils.isNumeric(search)){
|
||||
Predicate predicateId = criteriaBuilder.equal(root.get("id"), Long.parseLong(search));
|
||||
orList.add(predicateId);
|
||||
}
|
||||
Predicate predicateName = criteriaBuilder.like(root.get("name"), "%" + search + "%");
|
||||
Predicate predicateId = criteriaBuilder.like(root.get("id"), "%" + search + "%");
|
||||
orList.add(predicateHost);
|
||||
orList.add(predicateName);
|
||||
orList.add(predicateId);
|
||||
}
|
||||
if (StringUtils.isNotBlank(labels)) {
|
||||
String[] labelAres = labels.split(",");
|
||||
|
||||
+2
-2
@@ -92,9 +92,9 @@ public class TagServiceImpl implements TagService {
|
||||
|
||||
List<Predicate> orList = new ArrayList<>();
|
||||
if (StringUtils.isNotBlank(search)) {
|
||||
Predicate predicateName = criteriaBuilder.like(criteriaBuilder.lower(root.get("name")), "%" + search.toLowerCase() + "%");
|
||||
Predicate predicateName = criteriaBuilder.like(root.get("name"), "%" + search + "%");
|
||||
orList.add(predicateName);
|
||||
Predicate predicateValue = criteriaBuilder.like(criteriaBuilder.lower(root.get("tagValue")), "%" + search.toLowerCase() + "%");
|
||||
Predicate predicateValue = criteriaBuilder.like(root.get("tagValue"), "%" + search + "%");
|
||||
orList.add(predicateValue);
|
||||
}
|
||||
Predicate[] orPredicates = new Predicate[orList.size()];
|
||||
|
||||
@@ -88,15 +88,6 @@ params:
|
||||
range: '[0,100000]'
|
||||
required: true
|
||||
defaultValue: 1000
|
||||
- field: ssl
|
||||
# name-param field display i18n name
|
||||
name:
|
||||
zh-CN: 启用SFTP
|
||||
en-US: SFTP
|
||||
# type-param field type(most mapping the html input type)
|
||||
type: boolean
|
||||
# required-true or false
|
||||
required: true
|
||||
# collect metrics config list
|
||||
metrics:
|
||||
# metrics - basic
|
||||
@@ -131,4 +122,3 @@ metrics:
|
||||
password: ^_^password^_^
|
||||
direction: ^_^direction^_^
|
||||
timeout: ^_^timeout^_^
|
||||
ssl: ^_^ssl^_^
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
# limitations under the License.
|
||||
|
||||
# The monitoring type category:service-application service monitoring db-database monitoring custom-custom monitoring os-operating system monitoring
|
||||
category: llm
|
||||
category: server
|
||||
# The monitoring type eg: linux windows tomcat mysql aws...
|
||||
app: nvidia
|
||||
# The monitoring i18n name
|
||||
|
||||
@@ -72,7 +72,6 @@ resourceRole:
|
||||
# eg: /api/v1/source3===get means /api/v1/source3===get can be access by anyone, no need auth.
|
||||
excludedResource:
|
||||
- /api/alerts/report/**===*
|
||||
- /api/alert/sse/**===*
|
||||
- /api/account/auth/**===*
|
||||
- /api/i18n/**===get
|
||||
- /api/apps/hierarchy===get
|
||||
@@ -80,7 +79,6 @@ excludedResource:
|
||||
- /api/status/page/public/**===*
|
||||
# web ui resource
|
||||
- /===get
|
||||
- /assets/**===get
|
||||
- /dashboard/**===get
|
||||
- /monitors/**===get
|
||||
- /alert/**===get
|
||||
|
||||
-118
@@ -1,118 +0,0 @@
|
||||
/*
|
||||
* 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.warehouse.db;
|
||||
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.common.constants.NetworkConstants;
|
||||
import org.apache.hertzbeat.common.constants.SignConstants;
|
||||
import org.apache.hertzbeat.common.util.Base64Util;
|
||||
import org.apache.hertzbeat.warehouse.store.history.greptime.GreptimeProperties;
|
||||
import org.apache.hertzbeat.warehouse.store.history.vm.PromQlQueryContent;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
/**
|
||||
* query executor for victor metrics
|
||||
*/
|
||||
@Component
|
||||
@ConditionalOnProperty(prefix = "warehouse.store.greptime", name = "enabled", havingValue = "true")
|
||||
@Slf4j
|
||||
public class GreptimePromqlQueryExecutor implements QueryExecutor {
|
||||
|
||||
private static final String QUERY_PATH = "/v1/prometheus/api/v1/query";
|
||||
|
||||
private final GreptimeProperties greptimeProperties;
|
||||
|
||||
private final RestTemplate restTemplate;
|
||||
|
||||
public GreptimePromqlQueryExecutor(GreptimeProperties greptimeProperties, RestTemplate restTemplate) {
|
||||
this.greptimeProperties = greptimeProperties;
|
||||
this.restTemplate = restTemplate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Map<String, Object>> execute(String query) {
|
||||
// http run the promql query
|
||||
List<Map<String, Object>> results = new LinkedList<>();
|
||||
try {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
headers.setAccept(List.of(MediaType.APPLICATION_JSON));
|
||||
if (StringUtils.hasText(greptimeProperties.username())
|
||||
&& StringUtils.hasText(greptimeProperties.password())) {
|
||||
String authStr = greptimeProperties.username() + ":" + greptimeProperties.password();
|
||||
String encodedAuth = Base64Util.encode(authStr);
|
||||
headers.add(HttpHeaders.AUTHORIZATION, NetworkConstants.BASIC + SignConstants.BLANK + encodedAuth);
|
||||
}
|
||||
HttpEntity<Void> httpEntity = new HttpEntity<>(headers);
|
||||
URI uri = UriComponentsBuilder.fromHttpUrl(greptimeProperties.httpEndpoint() + QUERY_PATH)
|
||||
.queryParam("query", URLEncoder.encode(query, StandardCharsets.UTF_8))
|
||||
.build(true).toUri();
|
||||
ResponseEntity<PromQlQueryContent> responseEntity = restTemplate.exchange(uri,
|
||||
HttpMethod.GET, httpEntity, PromQlQueryContent.class);
|
||||
if (responseEntity.getStatusCode().is2xxSuccessful()) {
|
||||
if (responseEntity.getBody() != null && responseEntity.getBody().getData() != null
|
||||
&& responseEntity.getBody().getData().getResult() != null) {
|
||||
List<PromQlQueryContent.ContentData.Content> contents = responseEntity.getBody().getData().getResult();
|
||||
for (PromQlQueryContent.ContentData.Content content : contents) {
|
||||
Map<String, String> labels = content.getMetric();
|
||||
Map<String, Object> queryResult = new HashMap<>(8);
|
||||
queryResult.putAll(labels);
|
||||
if (content.getValue() != null && content.getValue().length == 2) {
|
||||
queryResult.put("__timestamp__", content.getValue()[0]);
|
||||
queryResult.put("__value__", content.getValue()[1]);
|
||||
} else if (content.getValues() != null && !content.getValues().isEmpty()) {
|
||||
List<Object> values = new LinkedList<>();
|
||||
for (Object[] valueArr : content.getValues()) {
|
||||
values.add(valueArr[1]);
|
||||
}
|
||||
queryResult.put("__value__", values);
|
||||
}
|
||||
results.add(queryResult);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.error("query metrics data from greptime failed. {}", responseEntity);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error(e.toString(), e);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean support(String datasource) {
|
||||
return "promql".equals(datasource);
|
||||
}
|
||||
}
|
||||
-31
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* 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.warehouse.db;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* query executor interface
|
||||
*/
|
||||
public interface QueryExecutor {
|
||||
|
||||
List<Map<String, Object>> execute(String query);
|
||||
|
||||
boolean support(String datasource);
|
||||
}
|
||||
-118
@@ -1,118 +0,0 @@
|
||||
/*
|
||||
* 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.warehouse.db;
|
||||
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.common.constants.NetworkConstants;
|
||||
import org.apache.hertzbeat.common.constants.SignConstants;
|
||||
import org.apache.hertzbeat.common.util.Base64Util;
|
||||
import org.apache.hertzbeat.warehouse.store.history.vm.PromQlQueryContent;
|
||||
import org.apache.hertzbeat.warehouse.store.history.vm.VictoriaMetricsProperties;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
/**
|
||||
* query executor for victor metrics
|
||||
*/
|
||||
@Component
|
||||
@ConditionalOnProperty(prefix = "warehouse.store.victoria-metrics", name = "enabled", havingValue = "true")
|
||||
@Slf4j
|
||||
public class VictoriaMetricsQueryExecutor implements QueryExecutor {
|
||||
|
||||
private static final String QUERY_PATH = "/api/v1/query";
|
||||
|
||||
private final VictoriaMetricsProperties victoriaMetricsProp;
|
||||
|
||||
private final RestTemplate restTemplate;
|
||||
|
||||
public VictoriaMetricsQueryExecutor(VictoriaMetricsProperties victoriaMetricsProp, RestTemplate restTemplate) {
|
||||
this.victoriaMetricsProp = victoriaMetricsProp;
|
||||
this.restTemplate = restTemplate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Map<String, Object>> execute(String query) {
|
||||
// http run the promql query
|
||||
List<Map<String, Object>> results = new LinkedList<>();
|
||||
try {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
headers.setAccept(List.of(MediaType.APPLICATION_JSON));
|
||||
if (StringUtils.hasText(victoriaMetricsProp.username())
|
||||
&& StringUtils.hasText(victoriaMetricsProp.password())) {
|
||||
String authStr = victoriaMetricsProp.username() + ":" + victoriaMetricsProp.password();
|
||||
String encodedAuth = Base64Util.encode(authStr);
|
||||
headers.add(HttpHeaders.AUTHORIZATION, NetworkConstants.BASIC + SignConstants.BLANK + encodedAuth);
|
||||
}
|
||||
HttpEntity<Void> httpEntity = new HttpEntity<>(headers);
|
||||
URI uri = UriComponentsBuilder.fromHttpUrl(victoriaMetricsProp.url() + QUERY_PATH)
|
||||
.queryParam("query", URLEncoder.encode(query, StandardCharsets.UTF_8))
|
||||
.build(true).toUri();
|
||||
ResponseEntity<PromQlQueryContent> responseEntity = restTemplate.exchange(uri,
|
||||
HttpMethod.GET, httpEntity, PromQlQueryContent.class);
|
||||
if (responseEntity.getStatusCode().is2xxSuccessful()) {
|
||||
if (responseEntity.getBody() != null && responseEntity.getBody().getData() != null
|
||||
&& responseEntity.getBody().getData().getResult() != null) {
|
||||
List<PromQlQueryContent.ContentData.Content> contents = responseEntity.getBody().getData().getResult();
|
||||
for (PromQlQueryContent.ContentData.Content content : contents) {
|
||||
Map<String, String> labels = content.getMetric();
|
||||
Map<String, Object> queryResult = new HashMap<>(8);
|
||||
queryResult.putAll(labels);
|
||||
if (content.getValue() != null && content.getValue().length == 2) {
|
||||
queryResult.put("__timestamp__", content.getValue()[0]);
|
||||
queryResult.put("__value__", content.getValue()[1]);
|
||||
} else if (content.getValues() != null && !content.getValues().isEmpty()) {
|
||||
List<Object> values = new LinkedList<>();
|
||||
for (Object[] valueArr : content.getValues()) {
|
||||
values.add(valueArr[1]);
|
||||
}
|
||||
queryResult.put("__value__", values);
|
||||
}
|
||||
results.add(queryResult);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.error("query metrics data from victor-metrics failed. {}", responseEntity);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error(e.toString(), e);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean support(String datasource) {
|
||||
return "promql".equals(datasource);
|
||||
}
|
||||
}
|
||||
+16
-12
@@ -18,11 +18,8 @@
|
||||
package org.apache.hertzbeat.warehouse.store;
|
||||
|
||||
import java.util.Optional;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.PersistenceContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.manager.Monitor;
|
||||
import org.apache.hertzbeat.common.entity.message.CollectRep;
|
||||
import org.apache.hertzbeat.common.queue.CommonDataQueue;
|
||||
import org.apache.hertzbeat.plugin.PostCollectPlugin;
|
||||
@@ -30,6 +27,7 @@ import org.apache.hertzbeat.plugin.runner.PluginRunner;
|
||||
import org.apache.hertzbeat.warehouse.WarehouseWorkerPool;
|
||||
import org.apache.hertzbeat.warehouse.store.history.HistoryDataWriter;
|
||||
import org.apache.hertzbeat.warehouse.store.realtime.RealTimeDataWriter;
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@@ -46,8 +44,6 @@ public class DataStorageDispatch {
|
||||
private final RealTimeDataWriter realTimeDataWriter;
|
||||
private final Optional<HistoryDataWriter> historyDataWriter;
|
||||
private final PluginRunner pluginRunner;
|
||||
@PersistenceContext
|
||||
private EntityManager entityManager;
|
||||
|
||||
public DataStorageDispatch(CommonDataQueue commonDataQueue,
|
||||
WarehouseWorkerPool workerPool,
|
||||
@@ -91,16 +87,24 @@ public class DataStorageDispatch {
|
||||
if (metricsData.getPriority() == 0) {
|
||||
long id = metricsData.getId();
|
||||
CollectRep.Code code = metricsData.getCode();
|
||||
// query current status
|
||||
String queryStatusSql = "SELECT status FROM hzb_monitor WHERE id = ?";
|
||||
try {
|
||||
String sql = "UPDATE hzb_monitor SET status = ? WHERE id = ? AND status != ?";
|
||||
int status = code == CollectRep.Code.SUCCESS ? CommonConstants.MONITOR_UP_CODE : CommonConstants.MONITOR_DOWN_CODE;
|
||||
int matchedRows = jdbcTemplate.update(sql, status, id, status);
|
||||
if (matchedRows > 0) {
|
||||
entityManager.getEntityManagerFactory().getCache().evict(Monitor.class, id);
|
||||
int currentStatus = jdbcTemplate.queryForObject(queryStatusSql, Integer.class, id);
|
||||
if (code == CollectRep.Code.SUCCESS && currentStatus == CommonConstants.MONITOR_DOWN_CODE) {
|
||||
// if collect success and current status is DOWN, update to UP
|
||||
String sql = "UPDATE hzb_monitor SET status = ? WHERE id = ?";
|
||||
jdbcTemplate.update(sql, CommonConstants.MONITOR_UP_CODE, id);
|
||||
} else if (code != CollectRep.Code.SUCCESS && currentStatus == CommonConstants.MONITOR_UP_CODE) {
|
||||
// if collect failed and current status is UP, update to DOWN
|
||||
String sql = "UPDATE hzb_monitor SET status = ? WHERE id = ?";
|
||||
jdbcTemplate.update(sql, CommonConstants.MONITOR_DOWN_CODE, id);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Update monitor status failed for monitor id: {}", id, e);
|
||||
} catch (EmptyResultDataAccessException ignored) {
|
||||
// when query currentStatus result is null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -352,9 +352,8 @@ The text of each license is the standard Apache 2.0 license.
|
||||
https://mvnrepository.com/artifact/org.apache.rocketmq/rocketmq-remoting/4.9.4 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.rocketmq/rocketmq-srvutil/4.9.4 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.rocketmq/rocketmq-tools/4.9.4 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.sshd/sshd-common/2.13.1 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.sshd/sshd-core/2.13.1 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.sshd/sshd-sftp/2.13.1 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.sshd/sshd-common/2.8.0 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.sshd/sshd-core/2.8.0 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-el/10.1.19 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-websocket/10.1.19 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.xmlbeans/xmlbeans/3.1.0 Apache-2.0
|
||||
|
||||
@@ -352,9 +352,8 @@ The text of each license is the standard Apache 2.0 license.
|
||||
https://mvnrepository.com/artifact/org.apache.rocketmq/rocketmq-remoting/4.9.4 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.rocketmq/rocketmq-srvutil/4.9.4 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.rocketmq/rocketmq-tools/4.9.4 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.sshd/sshd-common/2.13.1 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.sshd/sshd-core/2.13.1 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.sshd/sshd-sftp/2.13.1 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.sshd/sshd-common/2.8.0 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.sshd/sshd-core/2.8.0 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-el/10.1.19 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-websocket/10.1.19 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.xmlbeans/xmlbeans/3.1.0 Apache-2.0
|
||||
|
||||
@@ -284,9 +284,8 @@ The text of each license is the standard Apache 2.0 license.
|
||||
https://mvnrepository.com/artifact/org.apache.rocketmq/rocketmq-remoting/4.9.4 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.rocketmq/rocketmq-srvutil/4.9.4 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.rocketmq/rocketmq-tools/4.9.4 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.sshd/sshd-common/2.13.1 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.sshd/sshd-core/2.13.1 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.sshd/sshd-sftp/2.13.1 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.sshd/sshd-common/2.8.0 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.sshd/sshd-core/2.8.0 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-el/10.1.19 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-websocket/10.1.19 Apache-2.0
|
||||
https://mvnrepository.com/artifact/org.apache.plc4x/plc4j-api/0.12.0 Apache-2.0
|
||||
|
||||
@@ -174,7 +174,6 @@
|
||||
<mysql-jdbcdriver.version>8.0.33</mysql-jdbcdriver.version>
|
||||
<arrow.version>18.1.0</arrow.version>
|
||||
<snappy-java.version>1.1.10.7</snappy-java.version>
|
||||
<sshd-sftp.version>2.13.1</sshd-sftp.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
@@ -475,11 +474,6 @@
|
||||
<artifactId>arrow-memory-netty</artifactId>
|
||||
<version>${arrow.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.sshd</groupId>
|
||||
<artifactId>sshd-sftp</artifactId>
|
||||
<version>${sshd-sftp.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
|
||||
@@ -72,7 +72,6 @@ resourceRole:
|
||||
# eg: /api/v1/source3===get means /api/v1/source3===get can be access by anyone, no need auth.
|
||||
excludedResource:
|
||||
- /api/alerts/report/**===*
|
||||
- /api/alert/sse/**===*
|
||||
- /api/account/auth/**===*
|
||||
- /api/i18n/**===get
|
||||
- /api/apps/hierarchy===get
|
||||
@@ -80,7 +79,6 @@ excludedResource:
|
||||
- /api/status/page/public/**===*
|
||||
# web ui resource
|
||||
- /===get
|
||||
- /assets/**===get
|
||||
- /dashboard/**===get
|
||||
- /monitors/**===get
|
||||
- /alert/**===get
|
||||
|
||||
@@ -72,7 +72,6 @@ resourceRole:
|
||||
# eg: /api/v1/source3===get means /api/v1/source3===get can be access by anyone, no need auth.
|
||||
excludedResource:
|
||||
- /api/alerts/report/**===*
|
||||
- /api/alert/sse/**===*
|
||||
- /api/account/auth/**===*
|
||||
- /api/i18n/**===get
|
||||
- /api/apps/hierarchy===get
|
||||
@@ -80,7 +79,6 @@ excludedResource:
|
||||
- /api/status/page/public/**===*
|
||||
# web ui resource
|
||||
- /===get
|
||||
- /assets/**===get
|
||||
- /dashboard/**===get
|
||||
- /monitors/**===get
|
||||
- /alert/**===get
|
||||
|
||||
@@ -72,7 +72,6 @@ resourceRole:
|
||||
# eg: /api/v1/source3===get means /api/v1/source3===get can be access by anyone, no need auth.
|
||||
excludedResource:
|
||||
- /api/alerts/report/**===*
|
||||
- /api/alert/sse/**===*
|
||||
- /api/account/auth/**===*
|
||||
- /api/i18n/**===get
|
||||
- /api/apps/hierarchy===get
|
||||
@@ -80,7 +79,6 @@ excludedResource:
|
||||
- /api/status/page/public/**===*
|
||||
# web ui resource
|
||||
- /===get
|
||||
- /assets/**===get
|
||||
- /dashboard/**===get
|
||||
- /monitors/**===get
|
||||
- /alert/**===get
|
||||
|
||||
@@ -72,7 +72,6 @@ resourceRole:
|
||||
# eg: /api/v1/source3===get means /api/v1/source3===get can be access by anyone, no need auth.
|
||||
excludedResource:
|
||||
- /api/alerts/report/**===*
|
||||
- /api/alert/sse/**===*
|
||||
- /api/account/auth/**===*
|
||||
- /api/i18n/**===get
|
||||
- /api/apps/hierarchy===get
|
||||
@@ -80,7 +79,6 @@ excludedResource:
|
||||
- /api/status/page/public/**===*
|
||||
# web ui resource
|
||||
- /===get
|
||||
- /assets/**===get
|
||||
- /dashboard/**===get
|
||||
- /monitors/**===get
|
||||
- /alert/**===get
|
||||
|
||||
@@ -72,7 +72,6 @@ resourceRole:
|
||||
# eg: /api/v1/source3===get means /api/v1/source3===get can be access by anyone, no need auth.
|
||||
excludedResource:
|
||||
- /api/alerts/report/**===*
|
||||
- /api/alert/sse/**===*
|
||||
- /api/account/auth/**===*
|
||||
- /api/i18n/**===get
|
||||
- /api/apps/hierarchy===get
|
||||
@@ -80,7 +79,6 @@ excludedResource:
|
||||
- /api/status/page/public/**===*
|
||||
# web ui resource
|
||||
- /===get
|
||||
- /assets/**===get
|
||||
- /dashboard/**===get
|
||||
- /monitors/**===get
|
||||
- /alert/**===get
|
||||
|
||||
@@ -19,6 +19,7 @@ export class AppComponent implements OnInit {
|
||||
private router: Router,
|
||||
private titleSrv: TitleService,
|
||||
private modalSrv: NzModalService,
|
||||
private themeService: ThemeService,
|
||||
@Inject(ALAIN_I18N_TOKEN) private i18nSvc: I18NService
|
||||
) {
|
||||
renderer.setAttribute(el.nativeElement, 'ng-alain-version', VERSION_ALAIN.full);
|
||||
@@ -48,5 +49,10 @@ export class AppComponent implements OnInit {
|
||||
this.modalSrv.closeAll();
|
||||
}
|
||||
});
|
||||
// set theme
|
||||
const storedTheme = localStorage.getItem('theme');
|
||||
if (storedTheme) {
|
||||
this.themeService.changeTheme(storedTheme);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ import { Platform } from '@angular/cdk/platform';
|
||||
import { registerLocaleData } from '@angular/common';
|
||||
import { HttpHeaders } from '@angular/common/http';
|
||||
import ngEn from '@angular/common/locales/en';
|
||||
import ngJa from '@angular/common/locales/ja';
|
||||
import ngZh from '@angular/common/locales/zh';
|
||||
import ngZhTw from '@angular/common/locales/zh-Hant';
|
||||
import { Injectable } from '@angular/core';
|
||||
@@ -13,13 +12,12 @@ import {
|
||||
en_US as delonEnUS,
|
||||
SettingsService,
|
||||
zh_CN as delonZhCn,
|
||||
zh_TW as delonZhTw,
|
||||
ja_JP as delonJaJP
|
||||
zh_TW as delonZhTw
|
||||
} from '@delon/theme';
|
||||
import { AlainConfigService } from '@delon/util/config';
|
||||
import { enUS as dfEn, zhCN as dfZhCn, zhTW as dfZhTw, ja as dfJa } from 'date-fns/locale';
|
||||
import { enUS as dfEn, zhCN as dfZhCn, zhTW as dfZhTw } from 'date-fns/locale';
|
||||
import { NzSafeAny } from 'ng-zorro-antd/core/types';
|
||||
import { en_US as zorroEnUS, NzI18nService, zh_CN as zorroZhCN, zh_TW as zorroZhTW, ja_JP as zorroJaJP } from 'ng-zorro-antd/i18n';
|
||||
import { en_US as zorroEnUS, NzI18nService, zh_CN as zorroZhCN, zh_TW as zorroZhTW } from 'ng-zorro-antd/i18n';
|
||||
import { Observable, zip } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
@@ -59,14 +57,6 @@ const LANGS: { [key: string]: LangConfigData } = {
|
||||
date: dfZhTw,
|
||||
delon: delonZhTw,
|
||||
abbr: '🇭🇰'
|
||||
},
|
||||
'ja-JP': {
|
||||
text: '日本語',
|
||||
ng: ngJa,
|
||||
zorro: zorroJaJP,
|
||||
date: dfJa,
|
||||
delon: delonJaJP,
|
||||
abbr: '🇯🇵'
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ import { catchError, map } from 'rxjs/operators';
|
||||
import { ICONS } from '../../../style-icons';
|
||||
import { ICONS_AUTO } from '../../../style-icons-auto';
|
||||
import { MemoryStorageService } from '../../service/memory-storage.service';
|
||||
import { ThemeService } from '../../service/theme.service';
|
||||
import { I18NService } from '../i18n/i18n.service';
|
||||
|
||||
@Injectable({
|
||||
@@ -29,8 +28,7 @@ export class StartupService {
|
||||
@Inject(DA_SERVICE_TOKEN) private tokenService: ITokenService,
|
||||
private httpClient: HttpClient,
|
||||
private router: Router,
|
||||
private storageService: MemoryStorageService,
|
||||
private themeService: ThemeService
|
||||
private storageService: MemoryStorageService
|
||||
) {
|
||||
iconSrv.addIcon(...ICONS_AUTO, ...ICONS);
|
||||
}
|
||||
@@ -88,7 +86,6 @@ export class StartupService {
|
||||
this.storageService.putData('hierarchy', menuData.data);
|
||||
this.menuService.resume();
|
||||
this.titleService.suffix = appData.app.name;
|
||||
this.themeService.changeTheme(null);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -41,6 +41,9 @@ import { CONSTANTS } from '../../shared/constants';
|
||||
<div nz-menu-item>
|
||||
<header-fullscreen></header-fullscreen>
|
||||
</div>
|
||||
<div nz-menu-item>
|
||||
<header-clear-storage></header-clear-storage>
|
||||
</div>
|
||||
<div nz-menu-item routerLink="/setting/labels">
|
||||
<i nz-icon nzType="tag" class="mr-sm"></i>
|
||||
<span style="margin-left: 4px">{{ 'menu.advanced.labels' | i18n }}</span>
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import { ChangeDetectionStrategy, Component, HostListener, Inject } from '@angular/core';
|
||||
import { I18NService } from '@core';
|
||||
import { ALAIN_I18N_TOKEN } from '@delon/theme';
|
||||
import { NzMessageService } from 'ng-zorro-antd/message';
|
||||
import { NzModalService } from 'ng-zorro-antd/modal';
|
||||
|
||||
@Component({
|
||||
selector: 'header-clear-storage',
|
||||
template: `
|
||||
<i nz-icon class="mr-sm" nzType="tool"></i>
|
||||
{{ 'menu.clear.local.storage' | i18n }}
|
||||
`,
|
||||
host: {
|
||||
'[class.d-block]': 'true'
|
||||
},
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class HeaderClearStorageComponent {
|
||||
constructor(
|
||||
private modalSrv: NzModalService,
|
||||
private messageSrv: NzMessageService,
|
||||
@Inject(ALAIN_I18N_TOKEN) private i18nSvc: I18NService
|
||||
) {}
|
||||
|
||||
@HostListener('click')
|
||||
_click(): void {
|
||||
this.modalSrv.confirm({
|
||||
nzTitle: this.i18nSvc.fanyi('common.confirm.clear-cache'),
|
||||
nzOnOk: () => {
|
||||
localStorage.clear();
|
||||
this.messageSrv.success(this.i18nSvc.fanyi('common.notify.clear-success'));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,6 @@ import { NzNotificationService } from 'ng-zorro-antd/notification';
|
||||
import { finalize } from 'rxjs/operators';
|
||||
|
||||
import { Mute } from '../../../pojo/Mute';
|
||||
import { SingleAlert } from '../../../pojo/SingleAlert';
|
||||
import { AlertSoundService } from '../../../service/alert-sound.service';
|
||||
import { AlertService } from '../../../service/alert.service';
|
||||
import { GeneralConfigService } from '../../../service/general-config.service';
|
||||
@@ -123,7 +122,6 @@ export class HeaderNotifyComponent implements OnInit, OnDestroy {
|
||||
private previousCount = 0;
|
||||
// default to mute status
|
||||
mute: Mute = { mute: true };
|
||||
private eventSource!: EventSource;
|
||||
constructor(
|
||||
private router: Router,
|
||||
@Inject(ALAIN_I18N_TOKEN) private i18nSvc: I18NService,
|
||||
@@ -156,7 +154,9 @@ export class HeaderNotifyComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
);
|
||||
this.loadData();
|
||||
this.initSSEConnection();
|
||||
this.refreshInterval = setInterval(() => {
|
||||
this.loadData();
|
||||
}, 10000); // every 10 seconds refresh the tabs
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
@@ -207,6 +207,7 @@ export class HeaderNotifyComponent implements OnInit, OnDestroy {
|
||||
let item = {
|
||||
id: alert.id,
|
||||
avatar: '/assets/img/notification.svg',
|
||||
// title: `${alert.tags?.monitorName}--${this.i18nSvc.fanyi(`alert.severity.${alert.severity}`)}`,
|
||||
title: alert.content,
|
||||
datetime: new Date(alert.activeAt).toLocaleString(),
|
||||
color: 'blue',
|
||||
@@ -216,6 +217,11 @@ export class HeaderNotifyComponent implements OnInit, OnDestroy {
|
||||
list.push(item);
|
||||
});
|
||||
this.data = this.updateNoticeData(list);
|
||||
|
||||
if (page.totalElements > this.previousCount && !this.mute.mute) {
|
||||
this.alertSound.playAlertSound(this.i18nSvc.currentLang);
|
||||
}
|
||||
this.previousCount = page.totalElements;
|
||||
this.count = page.totalElements;
|
||||
} else {
|
||||
console.warn(message.msg);
|
||||
@@ -285,36 +291,4 @@ export class HeaderNotifyComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private initSSEConnection(): void {
|
||||
const sseUrl = '/api/alert/sse/subscribe';
|
||||
|
||||
this.eventSource = new EventSource(sseUrl);
|
||||
|
||||
this.eventSource.addEventListener('ALERT_EVENT', (evt: MessageEvent) => {
|
||||
let list: any[] = [];
|
||||
let alert: SingleAlert = JSON.parse(evt.data);
|
||||
let item = {
|
||||
id: alert.id,
|
||||
avatar: '/assets/img/notification.svg',
|
||||
// title: `${alert.tags?.monitorName}--${this.i18nSvc.fanyi(`alert.severity.${alert.severity}`)}`,
|
||||
title: alert.content,
|
||||
datetime: new Date(alert.activeAt).toLocaleString(),
|
||||
color: 'blue',
|
||||
status: alert.status,
|
||||
type: this.i18nSvc.fanyi('dashboard.alerts.title-no')
|
||||
};
|
||||
list.push(item);
|
||||
|
||||
this.data = this.updateNoticeData(list);
|
||||
if (!this.mute.mute) {
|
||||
this.alertSound.playAlertSound(this.i18nSvc.currentLang);
|
||||
}
|
||||
this.cdr.detectChanges();
|
||||
});
|
||||
this.eventSource.onerror = error => {
|
||||
console.error('SSE connection error:', error);
|
||||
setTimeout(() => this.initSSEConnection(), 3000);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,7 +129,12 @@ export class HeaderUserComponent {
|
||||
}
|
||||
|
||||
logout(): void {
|
||||
this.localStorageSvc.clearAuthorization();
|
||||
let tmp = this.localStorageSvc.getData(this.notShowAgainKey);
|
||||
if (tmp === null) {
|
||||
tmp = 'false';
|
||||
}
|
||||
this.localStorageSvc.clear();
|
||||
this.localStorageSvc.putData(this.notShowAgainKey, tmp);
|
||||
this.router.navigateByUrl('/passport/login');
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import { NzInputModule } from 'ng-zorro-antd/input';
|
||||
import { NzSpinModule } from 'ng-zorro-antd/spin';
|
||||
|
||||
import { LayoutBasicComponent } from './basic/basic.component';
|
||||
import { HeaderClearStorageComponent } from './basic/widgets/clear-storage.component';
|
||||
import { HeaderFullScreenComponent } from './basic/widgets/fullscreen.component';
|
||||
import { HeaderI18nComponent } from './basic/widgets/i18n.component';
|
||||
import { HeaderSearchComponent } from './basic/widgets/search.component';
|
||||
@@ -32,6 +33,7 @@ const HEADER_COMPONENTS = [
|
||||
HeaderSearchComponent,
|
||||
HeaderFullScreenComponent,
|
||||
HeaderI18nComponent,
|
||||
HeaderClearStorageComponent,
|
||||
HeaderUserComponent,
|
||||
HeaderNotifyComponent
|
||||
];
|
||||
|
||||
@@ -29,6 +29,10 @@
|
||||
<app-toolbar>
|
||||
<ng-template #center>
|
||||
<div class="center-content">
|
||||
<button nz-button (click)="sync()" nz-tooltip [nzTooltipTitle]="'common.refresh' | i18n">
|
||||
<i nz-icon nzType="sync" nzTheme="outline"></i>
|
||||
</button>
|
||||
|
||||
<div class="search-wrapper">
|
||||
<nz-input-group [nzPrefix]="prefixTemplate" class="search-input">
|
||||
<input
|
||||
@@ -59,13 +63,7 @@
|
||||
</app-toolbar>
|
||||
|
||||
<div class="alert-cards">
|
||||
<nz-card
|
||||
*ngFor="let group of groupAlerts"
|
||||
class="alert-card"
|
||||
[class.new-alert]="group.isNew"
|
||||
[class]="'status-' + group.status"
|
||||
[nzBordered]="false"
|
||||
>
|
||||
<nz-card *ngFor="let group of groupAlerts" class="alert-card" [class]="'status-' + group.status" [nzBordered]="false">
|
||||
<!-- Alert Group Header -->
|
||||
<div class="alert-header">
|
||||
<div class="alert-info">
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
@import "~src/styles/theme";
|
||||
|
||||
/* 调整工具栏布局 */
|
||||
:host ::ng-deep app-toolbar {
|
||||
.center-content {
|
||||
display: flex;
|
||||
@@ -69,40 +68,17 @@
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
padding: 16px;
|
||||
transform-style: preserve-3d;
|
||||
perspective: 1200px;
|
||||
}
|
||||
|
||||
.alert-card {
|
||||
position: relative;
|
||||
background: @common-background-color;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
border-left: 4px solid #ff4d4f;
|
||||
transition: all 0.3s;
|
||||
z-index: 1;
|
||||
|
||||
&.expanded {
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
&.status-firing {
|
||||
border-left: 4px solid #ff4d4f;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
&.status-resolved {
|
||||
border-left: 4px solid #52c41a;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
&.status-pending {
|
||||
border-left: 4px solid #faad14;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.15);
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
::ng-deep .ant-card-body {
|
||||
@@ -114,7 +90,7 @@
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
|
||||
.alert-info {
|
||||
flex: 1;
|
||||
@@ -134,7 +110,7 @@
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
margin-top: 8px;
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
color: #8c8c8c;
|
||||
font-size: 12px;
|
||||
|
||||
i {
|
||||
@@ -167,22 +143,16 @@
|
||||
|
||||
.alert-details {
|
||||
margin-top: 12px;
|
||||
position: relative;
|
||||
z-index: 4;
|
||||
|
||||
::ng-deep {
|
||||
.ant-collapse {
|
||||
background: transparent;
|
||||
border: none;
|
||||
position: relative;
|
||||
z-index: 5;
|
||||
|
||||
.ant-collapse-item {
|
||||
border-radius: 2px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
border: 1px solid #f0f0f0;
|
||||
margin-bottom: 8px;
|
||||
position: relative;
|
||||
z-index: 6;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
@@ -192,11 +162,9 @@
|
||||
padding: 8px 12px;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
z-index: 7;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
.ant-collapse-header-text {
|
||||
@@ -205,20 +173,19 @@
|
||||
|
||||
.ant-collapse-extra {
|
||||
margin: 0;
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
color: #8c8c8c;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.alert-content {
|
||||
font-size: 13px;
|
||||
color: #333;
|
||||
line-height: 1.5;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-collapse-content {
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.1);
|
||||
position: relative;
|
||||
z-index: 6;
|
||||
border-top: 1px solid #f0f0f0;
|
||||
|
||||
.ant-collapse-content-box {
|
||||
padding: 12px;
|
||||
@@ -302,94 +269,16 @@
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
@keyframes slideInFromRight {
|
||||
0% {
|
||||
transform: translate3d(120%, 0, 0) scale(0.95) rotate(3deg);
|
||||
opacity: 0;
|
||||
filter: blur(2px);
|
||||
box-shadow: 0 24px 48px -12px rgba(0, 0, 0, 0.18);
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
filter: blur(0);
|
||||
transform: translate3d(-5%, 0, 0) scale(1) rotate(-1deg);
|
||||
}
|
||||
75% {
|
||||
transform: translate3d(2%, 0, 0) scale(1) rotate(0.5deg);
|
||||
}
|
||||
100% {
|
||||
transform: translate3d(0, 0, 0) scale(1) rotate(0deg);
|
||||
box-shadow: 0 8px 16px -4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
.alert-card {
|
||||
transition:
|
||||
transform 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94),
|
||||
opacity 0.5s ease-out,
|
||||
box-shadow 0.3s ease;
|
||||
will-change: transform, opacity;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-2px) scale(1.005);
|
||||
box-shadow: 0 12px 24px -8px rgba(0, 0, 0, 0.15);
|
||||
&.status-firing {
|
||||
border-left-color: #ff4d4f;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
rgba(255, 255, 255, 0) 0%,
|
||||
rgba(255, 255, 255, 0.3) 50%,
|
||||
rgba(255, 255, 255, 0) 100%
|
||||
);
|
||||
opacity: 0;
|
||||
animation: slideGlow 1s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
pointer-events: none;
|
||||
&.status-resolved {
|
||||
border-left-color: #52c41a;
|
||||
}
|
||||
|
||||
&.new-alert {
|
||||
animation:
|
||||
slideInFromRight 0.8s cubic-bezier(0.34, 1.56, 0.64, 1) forwards,
|
||||
cardLanding 0.6s 0.3s ease-out forwards;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideGlow {
|
||||
0% {
|
||||
transform: translateX(-100%);
|
||||
opacity: 0;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.6;
|
||||
}
|
||||
100% {
|
||||
transform: translateX(100%);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes cardLanding {
|
||||
0% { transform: translateY(0); }
|
||||
50% { transform: translateY(-8px); }
|
||||
80% { transform: translateY(2px); }
|
||||
100% { transform: translateY(0); }
|
||||
}
|
||||
|
||||
.alert-card:nth-child(1) { animation-delay: 0.1s; }
|
||||
.alert-card:nth-child(2) { animation-delay: 0.15s; }
|
||||
.alert-card:nth-child(3) { animation-delay: 0.2s; }
|
||||
.alert-card:nth-child(n+4) { animation-delay: 0.25s; }
|
||||
|
||||
[data-theme='dark'] {
|
||||
:host {
|
||||
.alert-card {
|
||||
background-color: @common-background-color-dark;
|
||||
}
|
||||
&.status-pending {
|
||||
border-left-color: #faad14;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { Component, Inject, OnDestroy, OnInit } from '@angular/core';
|
||||
import { Component, Inject, OnInit } from '@angular/core';
|
||||
import { I18NService } from '@core';
|
||||
import { ALAIN_I18N_TOKEN } from '@delon/theme';
|
||||
import { NzModalService } from 'ng-zorro-antd/modal';
|
||||
@@ -26,15 +26,12 @@ import { NzNotificationService } from 'ng-zorro-antd/notification';
|
||||
import { GroupAlert } from '../../../pojo/GroupAlert';
|
||||
import { AlertService } from '../../../service/alert.service';
|
||||
|
||||
interface ExtendedGroupAlert extends GroupAlert {
|
||||
isNew?: boolean;
|
||||
}
|
||||
@Component({
|
||||
selector: 'app-alert-center',
|
||||
templateUrl: './alert-center.component.html',
|
||||
styleUrl: './alert-center.component.less'
|
||||
})
|
||||
export class AlertCenterComponent implements OnInit, OnDestroy {
|
||||
export class AlertCenterComponent implements OnInit {
|
||||
constructor(
|
||||
private notifySvc: NzNotificationService,
|
||||
private modal: NzModalService,
|
||||
@@ -45,109 +42,18 @@ export class AlertCenterComponent implements OnInit, OnDestroy {
|
||||
pageIndex: number = 1;
|
||||
pageSize: number = 8;
|
||||
total: number = 0;
|
||||
groupAlerts: ExtendedGroupAlert[] = [];
|
||||
groupAlerts!: GroupAlert[];
|
||||
tableLoading: boolean = false;
|
||||
checkedAlertIds = new Set<number>();
|
||||
filterStatus!: string;
|
||||
filterContent: string | undefined;
|
||||
private eventSource!: EventSource;
|
||||
|
||||
ngOnInit(): void {
|
||||
this.loadAlertsTable();
|
||||
this.initSSESubscription();
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (this.eventSource) {
|
||||
this.eventSource.close();
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize SSE subscription for real-time alerts
|
||||
private initSSESubscription(): void {
|
||||
this.eventSource = new EventSource('/api/alert/sse/subscribe');
|
||||
this.eventSource.addEventListener('ALERT_EVENT', (evt: MessageEvent) => {
|
||||
try {
|
||||
const newAlert: GroupAlert = JSON.parse(evt.data);
|
||||
this.updateAlertList(newAlert);
|
||||
} catch (error) {
|
||||
console.error('Error parsing SSE data:', error);
|
||||
}
|
||||
});
|
||||
|
||||
// Handle SSE errors
|
||||
this.eventSource.onerror = error => {
|
||||
console.error('SSE connection error:', error);
|
||||
this.eventSource.close();
|
||||
};
|
||||
}
|
||||
|
||||
private updateAlertList(newAlert: GroupAlert): void {
|
||||
const extendedAlert: ExtendedGroupAlert = {
|
||||
...newAlert,
|
||||
isNew: true
|
||||
};
|
||||
|
||||
if (!extendedAlert.alerts) {
|
||||
extendedAlert.alerts = [];
|
||||
}
|
||||
|
||||
const matchesFilter = this.checkAlertMatchesFilter(extendedAlert);
|
||||
if (!matchesFilter) {
|
||||
return;
|
||||
}
|
||||
|
||||
const existingIndex = this.groupAlerts.findIndex(a => a.id === extendedAlert.id);
|
||||
|
||||
if (existingIndex === -1) {
|
||||
this.groupAlerts = [extendedAlert, ...this.groupAlerts];
|
||||
this.total += 1;
|
||||
|
||||
setTimeout(() => {
|
||||
const index = this.groupAlerts.findIndex(a => a.id === extendedAlert.id);
|
||||
if (index !== -1) {
|
||||
this.groupAlerts[index].isNew = false;
|
||||
// 触发变更检测
|
||||
this.groupAlerts = [...this.groupAlerts];
|
||||
}
|
||||
}, 1000);
|
||||
} else {
|
||||
this.groupAlerts[existingIndex] = {
|
||||
...extendedAlert,
|
||||
isNew: true
|
||||
};
|
||||
|
||||
setTimeout(() => {
|
||||
if (this.groupAlerts[existingIndex]) {
|
||||
this.groupAlerts[existingIndex].isNew = false;
|
||||
this.groupAlerts = [...this.groupAlerts];
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
this.groupAlerts = [...this.groupAlerts];
|
||||
}
|
||||
}
|
||||
|
||||
private checkAlertMatchesFilter(alert: ExtendedGroupAlert): boolean {
|
||||
if (this.filterStatus && alert.status !== this.filterStatus) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.filterContent) {
|
||||
const searchContent = this.filterContent.toLowerCase();
|
||||
|
||||
const hasMatchingContent = alert.alerts?.some(singleAlert => singleAlert.content?.toLowerCase().includes(searchContent));
|
||||
|
||||
const hasMatchingLabels = Object.entries(alert.groupLabels || {}).some(
|
||||
([key, value]) => key.toLowerCase().includes(searchContent) || value.toLowerCase().includes(searchContent)
|
||||
);
|
||||
|
||||
if (!hasMatchingContent && !hasMatchingLabels) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
sync() {
|
||||
this.loadAlertsTable();
|
||||
}
|
||||
|
||||
loadAlertsTable() {
|
||||
|
||||
@@ -1,23 +1,20 @@
|
||||
@import "~src/styles/theme";
|
||||
|
||||
.alert-integration-container {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
background: @common-background-color;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
|
||||
|
||||
.data-sources {
|
||||
width: 240px;
|
||||
border-right: 1px solid #f0f0f0;
|
||||
padding: 16px;
|
||||
background: @common-background-color;
|
||||
|
||||
|
||||
h2 {
|
||||
margin-bottom: 16px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
|
||||
.source-list {
|
||||
.source-item {
|
||||
display: flex;
|
||||
@@ -26,30 +23,33 @@
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
transition: all 0.3s;
|
||||
|
||||
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
|
||||
&.active {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
background: #e6f7ff;
|
||||
}
|
||||
|
||||
|
||||
img {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.doc-content {
|
||||
flex: 1;
|
||||
padding: 24px;
|
||||
overflow-y: auto;
|
||||
background: @common-background-color;
|
||||
|
||||
|
||||
h2 {
|
||||
margin-bottom: 24px;
|
||||
font-size: 20px;
|
||||
@@ -57,28 +57,3 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[data-theme='dark'] {
|
||||
:host {
|
||||
.alert-integration-container {
|
||||
background: @common-background-color-dark;
|
||||
.data-sources {
|
||||
background: @common-background-color-dark;
|
||||
}
|
||||
.doc-content {
|
||||
background: @common-background-color-dark;
|
||||
}
|
||||
.source-list {
|
||||
.source-item {
|
||||
&:hover {
|
||||
background: rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-26
@@ -28,30 +28,8 @@
|
||||
{{ 'alert.notice.receiver.new' | i18n }}
|
||||
</button>
|
||||
</ng-template>
|
||||
<ng-template #right>
|
||||
<app-multi-func-input
|
||||
groupStyle="width: 250px;"
|
||||
[placeholder]="'alert.notice.receiver.people.name' | i18n"
|
||||
[(value)]="name"
|
||||
(valueChange)="onSearch()"
|
||||
/>
|
||||
</ng-template>
|
||||
</app-toolbar>
|
||||
<nz-table
|
||||
#fixedTable
|
||||
[nzPageIndex]="pageIndex"
|
||||
[nzPageSize]="pageSize"
|
||||
[nzTotal]="total"
|
||||
nzFrontPagination="false"
|
||||
nzShowSizeChanger
|
||||
[nzShowTotal]="rangeTemplate"
|
||||
[nzPageSizeOptions]="[8, 15, 25]"
|
||||
(nzQueryParams)="onTablePageChange($event)"
|
||||
nzShowPagination="true"
|
||||
[nzData]="receivers"
|
||||
[nzLoading]="receiverTableLoading"
|
||||
[nzScroll]="{ x: '1240px' }"
|
||||
>
|
||||
<nz-table #fixedTable [nzData]="receivers" [nzLoading]="receiverTableLoading" [nzScroll]="{ x: '1240px' }" nzFrontPagination="false">
|
||||
<thead>
|
||||
<tr>
|
||||
<th nzAlign="center" nzWidth="15%">{{ 'alert.notice.receiver.people' | i18n }}</th>
|
||||
@@ -166,9 +144,6 @@
|
||||
</tr>
|
||||
</tbody>
|
||||
</nz-table>
|
||||
|
||||
<ng-template #rangeTemplate> {{ 'common.total' | i18n }} {{ total }} </ng-template>
|
||||
|
||||
<!-- new or update notice medium pop-up box -->
|
||||
<nz-modal
|
||||
(nzOnCancel)="onManageReceiverModalCancel()"
|
||||
|
||||
+2
-28
@@ -23,7 +23,6 @@ import { I18NService } from '@core';
|
||||
import { ALAIN_I18N_TOKEN } from '@delon/theme';
|
||||
import { NzModalService } from 'ng-zorro-antd/modal';
|
||||
import { NzNotificationService } from 'ng-zorro-antd/notification';
|
||||
import { NzTableQueryParams } from 'ng-zorro-antd/table';
|
||||
import { finalize } from 'rxjs/operators';
|
||||
|
||||
import { NoticeReceiver } from '../../../../pojo/NoticeReceiver';
|
||||
@@ -42,10 +41,6 @@ export class AlertNoticeReceiverComponent implements OnInit {
|
||||
isManageReceiverModalOkLoading: boolean = false;
|
||||
isSendTestButtonLoading: boolean = false;
|
||||
receiver!: NoticeReceiver;
|
||||
name!: string;
|
||||
pageIndex: number = 1;
|
||||
pageSize: number = 8;
|
||||
total: number = 0;
|
||||
@ViewChild('receiverForm', { static: false }) receiverForm: NgForm | undefined;
|
||||
|
||||
constructor(
|
||||
@@ -65,14 +60,11 @@ export class AlertNoticeReceiverComponent implements OnInit {
|
||||
|
||||
loadReceiversTable() {
|
||||
this.receiverTableLoading = true;
|
||||
let receiverInit$ = this.noticeReceiverSvc.getReceivers(this.name, this.pageIndex - 1, this.pageSize).subscribe(
|
||||
let receiverInit$ = this.noticeReceiverSvc.getReceivers().subscribe(
|
||||
message => {
|
||||
this.receiverTableLoading = false;
|
||||
if (message.code === 0) {
|
||||
let page = message.data;
|
||||
this.receivers = page.content;
|
||||
this.total = page.totalElements;
|
||||
this.pageIndex = page.number + 1;
|
||||
this.receivers = message.data;
|
||||
} else {
|
||||
console.warn(message.msg);
|
||||
}
|
||||
@@ -110,7 +102,6 @@ export class AlertNoticeReceiverComponent implements OnInit {
|
||||
message => {
|
||||
if (message.code === 0) {
|
||||
this.notifySvc.success(this.i18nSvc.fanyi('common.notify.delete-success'), '');
|
||||
this.updatePageIndex(1);
|
||||
this.loadReceiversTable();
|
||||
} else {
|
||||
this.notifySvc.error(this.i18nSvc.fanyi('common.notify.delete-fail'), message.msg);
|
||||
@@ -122,11 +113,6 @@ export class AlertNoticeReceiverComponent implements OnInit {
|
||||
);
|
||||
}
|
||||
|
||||
updatePageIndex(delSize: number) {
|
||||
const lastPage = Math.max(1, Math.ceil((this.total - delSize) / this.pageSize));
|
||||
this.pageIndex = this.pageIndex > lastPage ? lastPage : this.pageIndex;
|
||||
}
|
||||
|
||||
onSplitTokenStr(type: number) {
|
||||
let index = -1;
|
||||
switch (this.receiver?.type) {
|
||||
@@ -279,16 +265,4 @@ export class AlertNoticeReceiverComponent implements OnInit {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
onTablePageChange(params: NzTableQueryParams) {
|
||||
const { pageSize, pageIndex } = params;
|
||||
this.pageIndex = pageIndex;
|
||||
this.pageSize = pageSize;
|
||||
this.loadReceiversTable();
|
||||
}
|
||||
|
||||
onSearch() {
|
||||
this.pageIndex = 1;
|
||||
this.loadReceiversTable();
|
||||
}
|
||||
}
|
||||
|
||||
+1
-25
@@ -28,30 +28,8 @@
|
||||
{{ 'alert.notice.rule.new' | i18n }}
|
||||
</button>
|
||||
</ng-template>
|
||||
<ng-template #right>
|
||||
<app-multi-func-input
|
||||
groupStyle="width: 250px;"
|
||||
[placeholder]="'alert.notice.rule.name' | i18n"
|
||||
[(value)]="name"
|
||||
(valueChange)="onSearch()"
|
||||
/>
|
||||
</ng-template>
|
||||
</app-toolbar>
|
||||
<nz-table
|
||||
#ruleFixedTable
|
||||
[nzPageIndex]="pageIndex"
|
||||
[nzPageSize]="pageSize"
|
||||
[nzTotal]="total"
|
||||
nzFrontPagination="false"
|
||||
nzShowSizeChanger
|
||||
[nzShowTotal]="rangeTemplate"
|
||||
[nzPageSizeOptions]="[8, 15, 25]"
|
||||
(nzQueryParams)="onTablePageChange($event)"
|
||||
nzShowPagination="true"
|
||||
[nzData]="rules"
|
||||
[nzLoading]="ruleTableLoading"
|
||||
[nzScroll]="{ x: '1240px' }"
|
||||
>
|
||||
<nz-table #ruleFixedTable [nzData]="rules" [nzLoading]="ruleTableLoading" [nzScroll]="{ x: '1240px' }" nzFrontPagination="false">
|
||||
<thead>
|
||||
<tr>
|
||||
<th nzAlign="center" nzWidth="15%">{{ 'alert.notice.rule.name' | i18n }}</th>
|
||||
@@ -110,8 +88,6 @@
|
||||
</tbody>
|
||||
</nz-table>
|
||||
|
||||
<ng-template #rangeTemplate> {{ 'common.total' | i18n }} {{ total }} </ng-template>
|
||||
|
||||
<!-- new or update notice strategy pop-up box -->
|
||||
<nz-modal
|
||||
(nzOnCancel)="onManageRuleModalCancel()"
|
||||
|
||||
+4
-30
@@ -23,7 +23,6 @@ import { I18NService } from '@core';
|
||||
import { ALAIN_I18N_TOKEN } from '@delon/theme';
|
||||
import { NzModalService } from 'ng-zorro-antd/modal';
|
||||
import { NzNotificationService } from 'ng-zorro-antd/notification';
|
||||
import { NzTableQueryParams } from 'ng-zorro-antd/table';
|
||||
import { finalize } from 'rxjs/operators';
|
||||
|
||||
import { NoticeReceiver } from '../../../../pojo/NoticeReceiver';
|
||||
@@ -48,10 +47,6 @@ export class AlertNoticeRuleComponent implements OnInit {
|
||||
switchReceiver!: NoticeReceiver;
|
||||
receiversOption: any[] = [];
|
||||
isLimit: boolean = false;
|
||||
name!: string;
|
||||
pageIndex: number = 1;
|
||||
pageSize: number = 8;
|
||||
total: number = 0;
|
||||
@ViewChild('ruleForm', { static: false }) ruleForm: NgForm | undefined;
|
||||
|
||||
dayCheckOptions = [
|
||||
@@ -83,14 +78,11 @@ export class AlertNoticeRuleComponent implements OnInit {
|
||||
|
||||
loadRulesTable() {
|
||||
this.ruleTableLoading = true;
|
||||
let rulesInit$ = this.noticeRuleSvc.getNoticeRules(this.name, this.pageIndex - 1, this.pageSize).subscribe(
|
||||
let rulesInit$ = this.noticeRuleSvc.getNoticeRules().subscribe(
|
||||
message => {
|
||||
this.ruleTableLoading = false;
|
||||
if (message.code === 0) {
|
||||
let page = message.data;
|
||||
this.rules = page.content;
|
||||
this.total = page.totalElements;
|
||||
this.pageIndex = page.number + 1;
|
||||
this.rules = message.data;
|
||||
} else {
|
||||
console.warn(message.msg);
|
||||
}
|
||||
@@ -130,7 +122,6 @@ export class AlertNoticeRuleComponent implements OnInit {
|
||||
message => {
|
||||
if (message.code === 0) {
|
||||
this.notifySvc.success(this.i18nSvc.fanyi('common.notify.delete-success'), '');
|
||||
this.updatePageIndex(1);
|
||||
this.loadRulesTable();
|
||||
} else {
|
||||
this.notifySvc.error(this.i18nSvc.fanyi('common.notify.delete-fail'), message.msg);
|
||||
@@ -142,11 +133,6 @@ export class AlertNoticeRuleComponent implements OnInit {
|
||||
);
|
||||
}
|
||||
|
||||
updatePageIndex(delSize: number) {
|
||||
const lastPage = Math.max(1, Math.ceil((this.total - delSize) / this.pageSize));
|
||||
this.pageIndex = this.pageIndex > lastPage ? lastPage : this.pageIndex;
|
||||
}
|
||||
|
||||
onNewNoticeRule() {
|
||||
this.rule = new NoticeRule();
|
||||
this.rule.templateId = -1;
|
||||
@@ -227,7 +213,7 @@ export class AlertNoticeRuleComponent implements OnInit {
|
||||
}
|
||||
|
||||
loadReceiversOption() {
|
||||
let receiverOption$ = this.noticeReceiverSvc.getAllReceivers().subscribe(
|
||||
let receiverOption$ = this.noticeReceiverSvc.getReceivers().subscribe(
|
||||
message => {
|
||||
if (message.code === 0) {
|
||||
let data = message.data;
|
||||
@@ -296,7 +282,7 @@ export class AlertNoticeRuleComponent implements OnInit {
|
||||
}
|
||||
|
||||
loadTemplatesOption() {
|
||||
let templateOption$ = this.noticeTemplateSvc.getAllNoticeTemplates().subscribe(
|
||||
let templateOption$ = this.noticeTemplateSvc.getNoticeTemplates().subscribe(
|
||||
message => {
|
||||
if (message.code === 0) {
|
||||
let data = message.data;
|
||||
@@ -440,16 +426,4 @@ export class AlertNoticeRuleComponent implements OnInit {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
onTablePageChange(params: NzTableQueryParams) {
|
||||
const { pageSize, pageIndex } = params;
|
||||
this.pageIndex = pageIndex;
|
||||
this.pageSize = pageSize;
|
||||
this.loadRulesTable();
|
||||
}
|
||||
|
||||
onSearch() {
|
||||
this.pageIndex = 1;
|
||||
this.loadRulesTable();
|
||||
}
|
||||
}
|
||||
|
||||
-23
@@ -27,29 +27,9 @@
|
||||
{{ 'alert.notice.template.new' | i18n }}
|
||||
</button>
|
||||
</ng-template>
|
||||
<ng-template #right>
|
||||
<nz-select class="mobile-hide" [nzPlaceHolder]="'monitor.status' | i18n" [(ngModel)]="preset" (ngModelChange)="onPresetStatusChanged()">
|
||||
<nz-option [nzLabel]="'alert.notice.template.preset.true' | i18n" [nzValue]="true"></nz-option>
|
||||
<nz-option [nzLabel]="'alert.notice.template.preset.false' | i18n" [nzValue]="false"></nz-option>
|
||||
</nz-select>
|
||||
<app-multi-func-input
|
||||
groupStyle="width: 250px;"
|
||||
[placeholder]="'alert.notice.template.name' | i18n"
|
||||
[(value)]="name"
|
||||
(valueChange)="onSearch()"
|
||||
/>
|
||||
</ng-template>
|
||||
</app-toolbar>
|
||||
<nz-table
|
||||
#templateFixedTable
|
||||
[nzPageIndex]="pageIndex"
|
||||
[nzPageSize]="pageSize"
|
||||
[nzTotal]="total"
|
||||
nzShowSizeChanger
|
||||
[nzShowTotal]="rangeTemplate"
|
||||
[nzPageSizeOptions]="[8, 15, 25]"
|
||||
(nzQueryParams)="onTablePageChange($event)"
|
||||
nzShowPagination="true"
|
||||
[nzData]="templates"
|
||||
[nzLoading]="templateTableLoading"
|
||||
[nzScroll]="{ x: '1240px' }"
|
||||
@@ -162,9 +142,6 @@
|
||||
</tr>
|
||||
</tbody>
|
||||
</nz-table>
|
||||
|
||||
<ng-template #rangeTemplate> {{ 'common.total' | i18n }} {{ total }} </ng-template>
|
||||
|
||||
<!-- new or update notice template pop-up box -->
|
||||
<nz-modal
|
||||
(nzOnCancel)="onManageTemplateModalCancel()"
|
||||
|
||||
+3
-34
@@ -23,7 +23,6 @@ import { I18NService } from '@core';
|
||||
import { ALAIN_I18N_TOKEN } from '@delon/theme';
|
||||
import { NzModalService } from 'ng-zorro-antd/modal';
|
||||
import { NzNotificationService } from 'ng-zorro-antd/notification';
|
||||
import { NzTableQueryParams } from 'ng-zorro-antd/table';
|
||||
import { finalize } from 'rxjs/operators';
|
||||
|
||||
import { NoticeRule } from '../../../../pojo/NoticeRule';
|
||||
@@ -44,11 +43,6 @@ export class AlertNoticeTemplateComponent implements OnInit {
|
||||
isShowTemplateModalVisible: boolean = false;
|
||||
template: NoticeTemplate = new NoticeTemplate();
|
||||
rule: NoticeRule = new NoticeRule();
|
||||
name!: string;
|
||||
pageIndex: number = 1;
|
||||
pageSize: number = 8;
|
||||
total: number = 0;
|
||||
preset: boolean = true;
|
||||
@ViewChild('templateForm', { static: false }) templateForm: NgForm | undefined;
|
||||
|
||||
constructor(
|
||||
@@ -68,14 +62,12 @@ export class AlertNoticeTemplateComponent implements OnInit {
|
||||
|
||||
loadTemplatesTable() {
|
||||
this.templateTableLoading = true;
|
||||
let templatesInit$ = this.noticeTemplateSvc.getNoticeTemplates(this.name, this.preset, this.pageIndex - 1, this.pageSize).subscribe(
|
||||
let templatesInit$ = this.noticeTemplateSvc.getNoticeTemplates().subscribe(
|
||||
message => {
|
||||
this.templateTableLoading = false;
|
||||
if (message.code === 0) {
|
||||
let page = message.data;
|
||||
this.templates = page.content;
|
||||
this.total = page.totalElements;
|
||||
this.pageIndex = page.number + 1;
|
||||
this.templates = message.data;
|
||||
// this.templates=this.templates.concat(this.defaultTemplates);
|
||||
} else {
|
||||
console.warn(message.msg);
|
||||
}
|
||||
@@ -113,7 +105,6 @@ export class AlertNoticeTemplateComponent implements OnInit {
|
||||
message => {
|
||||
if (message.code === 0) {
|
||||
this.notifySvc.success(this.i18nSvc.fanyi('common.notify.delete-success'), '');
|
||||
this.updatePageIndex(1);
|
||||
this.loadTemplatesTable();
|
||||
} else {
|
||||
this.notifySvc.error(this.i18nSvc.fanyi('common.notify.delete-fail'), message.msg);
|
||||
@@ -125,11 +116,6 @@ export class AlertNoticeTemplateComponent implements OnInit {
|
||||
);
|
||||
}
|
||||
|
||||
updatePageIndex(delSize: number) {
|
||||
const lastPage = Math.max(1, Math.ceil((this.total - delSize) / this.pageSize));
|
||||
this.pageIndex = this.pageIndex > lastPage ? lastPage : this.pageIndex;
|
||||
}
|
||||
|
||||
onNewNoticeTemplate() {
|
||||
this.template = new NoticeTemplate();
|
||||
this.isManageTemplateModalVisible = true;
|
||||
@@ -223,21 +209,4 @@ export class AlertNoticeTemplateComponent implements OnInit {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
onTablePageChange(params: NzTableQueryParams) {
|
||||
const { pageSize, pageIndex } = params;
|
||||
this.pageIndex = pageIndex;
|
||||
this.pageSize = pageSize;
|
||||
this.loadTemplatesTable();
|
||||
}
|
||||
|
||||
onPresetStatusChanged() {
|
||||
this.pageIndex = 1;
|
||||
this.loadTemplatesTable();
|
||||
}
|
||||
|
||||
onSearch() {
|
||||
this.pageIndex = 1;
|
||||
this.loadTemplatesTable();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,6 @@
|
||||
[placeholder]="'alert.setting.search' | i18n"
|
||||
[(value)]="search"
|
||||
(keydown.enter)="onFilterChange()"
|
||||
(cleared)="onFilterChange()"
|
||||
/>
|
||||
</ng-template>
|
||||
</app-toolbar>
|
||||
@@ -418,13 +417,13 @@
|
||||
</nz-form-control>
|
||||
</nz-form-item>
|
||||
<nz-form-item *ngIf="define.type == 'periodic'" [ngStyle]="{ marginBottom: '5px' }">
|
||||
<nz-form-label [nzSpan]="7" nzFor="datasource" nzRequired="true" [nzTooltipTitle]="'alert.setting.rule.label' | i18n">
|
||||
<nz-form-label [nzSpan]="7" nzFor="promql" nzRequired="true" [nzTooltipTitle]="'alert.setting.rule.label' | i18n">
|
||||
{{ 'alert.setting.rule' | i18n }}
|
||||
</nz-form-label>
|
||||
<nz-form-control [nzSpan]="12" [nzErrorTip]="'validation.required' | i18n">
|
||||
<ng-container *ngIf="cascadeValues[1] !== 'availability'">
|
||||
<nz-radio-group [(ngModel)]="define.datasource" nzButtonStyle="solid" name="datasource" id="datasource">
|
||||
<label nz-radio-button [nzValue]="'promql'">
|
||||
<nz-radio-group [(ngModel)]="define.datasource" nzButtonStyle="solid" id="promql">
|
||||
<label nz-radio-button [nzValue]="'periodic'">
|
||||
{{ 'PromQL' | i18n }}
|
||||
</label>
|
||||
</nz-radio-group>
|
||||
@@ -477,7 +476,6 @@
|
||||
[nzPlaceHolder]="'alert.notice.rule.priority.placeholder' | i18n"
|
||||
name="severity"
|
||||
id="severity"
|
||||
required
|
||||
>
|
||||
<nz-option [nzValue]="'emergency'" [nzLabel]="'alert.severity.0' | i18n"></nz-option>
|
||||
<nz-option [nzValue]="'critical'" [nzLabel]="'alert.severity.1' | i18n"></nz-option>
|
||||
@@ -500,7 +498,7 @@
|
||||
</nz-form-label>
|
||||
<nz-form-control [nzSpan]="12" [nzErrorTip]="'validation.required' | i18n">
|
||||
<div class="template-input-wrapper">
|
||||
<div *ngIf="define.type === 'realtime'" class="draggable-vars-container">
|
||||
<div class="draggable-vars-container">
|
||||
<div *ngFor="let env of templateEnvVars" class="draggable-tag" draggable="true" (dragstart)="onDragStart($event, env)">
|
||||
<div class="key-value-block">
|
||||
<span class="var-key">{{ env.name }}</span>
|
||||
|
||||
@@ -200,7 +200,7 @@ export class AlertSettingComponent implements OnInit {
|
||||
this.tableLoading = true;
|
||||
const translationSearchList: string[] = [];
|
||||
let trimSearch = '';
|
||||
if (this.search && this.search.trim() !== '') {
|
||||
if (this.search !== undefined && this.search.trim() !== '') {
|
||||
trimSearch = this.search.trim();
|
||||
}
|
||||
// Filter entries based on search input
|
||||
@@ -240,7 +240,6 @@ export class AlertSettingComponent implements OnInit {
|
||||
this.isSelectTypeModalVisible = false;
|
||||
this.define = new AlertDefine();
|
||||
this.define.type = type;
|
||||
this.severity = '';
|
||||
this.userExpr = '';
|
||||
this.selectedMonitorIds = new Set<number>();
|
||||
// Set default period for periodic alert
|
||||
|
||||
@@ -306,7 +306,7 @@
|
||||
[delay]="300"
|
||||
[zoomOnHover]="{ scale: 1.4, transitionTime: 0.6, delay: 0.4 }"
|
||||
[overflow]="false"
|
||||
[background]="theme == 'dark' ? '#141414' : 'white'"
|
||||
[background]="'white no-repeat fixed center'"
|
||||
>
|
||||
</angular-tag-cloud>
|
||||
</nz-spin>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
@import '@delon/theme/index';
|
||||
:host ::ng-deep {
|
||||
.ant-timeline {
|
||||
.ant-timeline-label {
|
||||
|
||||
@@ -34,7 +34,6 @@ import { AlertService } from '../../service/alert.service';
|
||||
import { CollectorService } from '../../service/collector.service';
|
||||
import { MonitorService } from '../../service/monitor.service';
|
||||
import { TagService } from '../../service/tag.service';
|
||||
import { ThemeService } from '../../service/theme.service';
|
||||
import { formatTagName } from '../../shared/utils/common-util';
|
||||
|
||||
@Component({
|
||||
@@ -52,11 +51,9 @@ export class DashboardComponent implements OnInit, OnDestroy {
|
||||
private collectorSvc: CollectorService,
|
||||
@Inject(ALAIN_I18N_TOKEN) private i18nSvc: I18NService,
|
||||
private router: Router,
|
||||
private themeSvc: ThemeService,
|
||||
private cdr: ChangeDetectorRef
|
||||
) {}
|
||||
|
||||
theme: string = 'default';
|
||||
// Tag Word Cloud
|
||||
wordCloudData: CloudData[] = [];
|
||||
wordCloudDataLoading: boolean = false;
|
||||
@@ -181,7 +178,6 @@ export class DashboardComponent implements OnInit, OnDestroy {
|
||||
alertContentLoading: boolean = false;
|
||||
|
||||
ngOnInit(): void {
|
||||
this.theme = this.themeSvc.getTheme() || 'default';
|
||||
this.appsCountTheme = {
|
||||
title: {
|
||||
text: `{a|${this.i18nSvc.fanyi('dashboard.monitors.title')}}`,
|
||||
|
||||
@@ -113,7 +113,7 @@
|
||||
<nz-table
|
||||
*ngIf="!monitor && isTable"
|
||||
nzSize="small"
|
||||
[nzNoResult]="'monitor.detail.chart.no-data' | i18n"
|
||||
nzNoResult="No Metrics Data"
|
||||
[nzFrontPagination]="false"
|
||||
[nzShowPagination]="false"
|
||||
[nzData]="valueRows"
|
||||
@@ -143,7 +143,7 @@
|
||||
<nz-table
|
||||
*ngIf="!monitor && !isTable"
|
||||
nzSize="small"
|
||||
[nzNoResult]="'monitor.detail.chart.no-data' | i18n"
|
||||
nzNoResult="No Metrics Data"
|
||||
[nzFrontPagination]="false"
|
||||
[nzShowPagination]="false"
|
||||
[nzData]="valueRows"
|
||||
|
||||
@@ -150,32 +150,17 @@ export class MonitorFormComponent implements OnChanges {
|
||||
}
|
||||
|
||||
onParamBooleanChanged(booleanValue: boolean, field: string) {
|
||||
if (this.monitor.app === 'api') {
|
||||
if (field === 'ssl') {
|
||||
const portParam = this.params.find(param => param.field === 'port');
|
||||
if (portParam) {
|
||||
if (booleanValue && (portParam.paramValue == null || parseInt(portParam.paramValue) === 80)) {
|
||||
portParam.paramValue = 443;
|
||||
this.notifySvc.info(this.i18nSvc.fanyi('common.notice'), this.i18nSvc.fanyi('monitor.new.notify.change-to-https'));
|
||||
}
|
||||
if (!booleanValue && (portParam.paramValue == null || parseInt(portParam.paramValue) === 443)) {
|
||||
portParam.paramValue = 80;
|
||||
this.notifySvc.info(this.i18nSvc.fanyi('common.notice'), this.i18nSvc.fanyi('monitor.new.notify.change-to-http'));
|
||||
}
|
||||
// For SSL port linkage, port 80 by default is not enabled, but port 443 by default is enabled
|
||||
if (field === 'ssl') {
|
||||
const portParam = this.params.find(param => param.field === 'port');
|
||||
if (portParam) {
|
||||
if (booleanValue && (portParam.paramValue == null || parseInt(portParam.paramValue) === 80)) {
|
||||
portParam.paramValue = 443;
|
||||
this.notifySvc.info(this.i18nSvc.fanyi('common.notice'), this.i18nSvc.fanyi('monitor.new.notify.change-to-https'));
|
||||
}
|
||||
}
|
||||
} else if (this.monitor.app === 'ftp') {
|
||||
if (field === 'ssl') {
|
||||
const portParam = this.params.find(param => param.field === 'port');
|
||||
if (portParam) {
|
||||
if (booleanValue && (portParam.paramValue == null || parseInt(portParam.paramValue) === 21)) {
|
||||
portParam.paramValue = 22;
|
||||
this.notifySvc.info(this.i18nSvc.fanyi('common.notice'), this.i18nSvc.fanyi('monitor.new.notify.change-to-sftp'));
|
||||
}
|
||||
if (!booleanValue && (portParam.paramValue == null || parseInt(portParam.paramValue) === 22)) {
|
||||
portParam.paramValue = 21;
|
||||
this.notifySvc.info(this.i18nSvc.fanyi('common.notice'), this.i18nSvc.fanyi('monitor.new.notify.change-to-ftp'));
|
||||
}
|
||||
if (!booleanValue && (portParam.paramValue == null || parseInt(portParam.paramValue) === 443)) {
|
||||
portParam.paramValue = 80;
|
||||
this.notifySvc.info(this.i18nSvc.fanyi('common.notice'), this.i18nSvc.fanyi('monitor.new.notify.change-to-http'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@
|
||||
<app-multi-func-input
|
||||
groupStyle="width: 120px;"
|
||||
class="mobile-hide"
|
||||
[placeholder]="'monitor.search.label' | i18n"
|
||||
[placeholder]="'monitor.search.tag' | i18n"
|
||||
[(value)]="labels"
|
||||
(valueChange)="onTagChanged()"
|
||||
/>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
~ under the License.
|
||||
-->
|
||||
|
||||
<div class="br-8" style="background-color: rgb(198 189 189 / 39%); padding: 20px; box-shadow: 7px 5px #b421cc">
|
||||
<div class="br-8" style="background-color: snow; padding: 20px; box-shadow: 7px 5px #b421cc">
|
||||
<form nz-form [formGroup]="form" (ngSubmit)="submit()" role="form">
|
||||
<nz-tabset [nzAnimated]="false" class="tabs" (nzSelectChange)="switch($event)">
|
||||
<nz-tab [nzTitle]="'app.login.tab-login-credentials' | i18n">
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<nz-divider></nz-divider>
|
||||
|
||||
<nz-layout style="height: 100vh; overflow: hidden">
|
||||
<nz-sider [nzTheme]="theme == 'dark' ? 'dark' : 'light'" style="height: 100%; overflow: hidden" [nzTrigger]="null">
|
||||
<nz-sider nzTheme="light" style="height: 100%; overflow: hidden" [nzTrigger]="null">
|
||||
<app-monitor-select-menu
|
||||
[loading]="menuLoading"
|
||||
[data]="appMenusArr"
|
||||
@@ -107,7 +107,7 @@
|
||||
[nzOriginalText]="originalCode"
|
||||
[(ngModel)]="code"
|
||||
[nzEditorMode]="'diff'"
|
||||
[nzEditorOption]="{ language: 'yaml', theme: 'vs', folding: true, automaticLayout: true }"
|
||||
[nzEditorOption]="{ language: 'yaml', theme: 'vs-dark', folding: true, automaticLayout: true }"
|
||||
></nz-code-editor>
|
||||
</div>
|
||||
</nz-content>
|
||||
|
||||
@@ -28,7 +28,6 @@ import { finalize } from 'rxjs/operators';
|
||||
|
||||
import { AppDefineService } from '../../../service/app-define.service';
|
||||
import { GeneralConfigService } from '../../../service/general-config.service';
|
||||
import { ThemeService } from '../../../service/theme.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-define',
|
||||
@@ -45,7 +44,6 @@ export class DefineComponent implements OnInit {
|
||||
private startUpSvc: StartupService,
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private themeSvc: ThemeService,
|
||||
@Inject(ALAIN_I18N_TOKEN) private i18nSvc: I18NService
|
||||
) {}
|
||||
|
||||
@@ -56,8 +54,7 @@ export class DefineComponent implements OnInit {
|
||||
loading = false;
|
||||
code: string = '';
|
||||
originalCode: string = '';
|
||||
dark: boolean = false;
|
||||
theme: string = 'default';
|
||||
dark: boolean = true;
|
||||
currentApp: any = null;
|
||||
saveLoading = false;
|
||||
deleteLoading = false;
|
||||
@@ -69,7 +66,6 @@ export class DefineComponent implements OnInit {
|
||||
this.loadAppDefineContent(this.currentApp);
|
||||
}
|
||||
});
|
||||
this.theme = this.themeSvc.getTheme() || 'default';
|
||||
this.loadMenus();
|
||||
this.code = `${this.i18nSvc.fanyi('define.new.code')}\n\n\n\n\n`;
|
||||
this.originalCode = this.i18nSvc.fanyi('define.new.code');
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
<nz-option [nzValue]="'en_US'" [nzLabel]="'settings.system-config.locale.en_US' | i18n"></nz-option>
|
||||
<nz-option [nzValue]="'zh_CN'" [nzLabel]="'settings.system-config.locale.zh_CN' | i18n"></nz-option>
|
||||
<nz-option [nzValue]="'zh_TW'" [nzLabel]="'settings.system-config.locale.zh_TW' | i18n"></nz-option>
|
||||
<nz-option [nzValue]="'ja_JP'" [nzLabel]="'settings.system-config.locale.ja-JP' | i18n"></nz-option>
|
||||
</nz-select>
|
||||
</se>
|
||||
<se [label]="'settings.system-config.timezone' | i18n" [error]="'validation.required' | i18n">
|
||||
|
||||
@@ -58,7 +58,7 @@ export class SystemConfigComponent implements OnInit {
|
||||
if (message.code === 0) {
|
||||
if (message.data) {
|
||||
this.config = message.data;
|
||||
this.config.theme = this.themeService.getTheme() || 'default';
|
||||
this.changeTheme(this.config.theme); // update theme after config is loaded
|
||||
} else {
|
||||
this.config = new SystemConfig();
|
||||
}
|
||||
@@ -94,7 +94,6 @@ export class SystemConfigComponent implements OnInit {
|
||||
this.i18nSvc.loadLangData(language).subscribe(res => {
|
||||
this.i18nSvc.use(language, res);
|
||||
this.settings.setLayout('lang', language);
|
||||
this.themeService.setTheme(this.config.theme);
|
||||
setTimeout(() => this.doc.location.reload());
|
||||
});
|
||||
} else {
|
||||
@@ -106,4 +105,8 @@ export class SystemConfigComponent implements OnInit {
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
changeTheme(theme: string): void {
|
||||
this.themeService.changeTheme(theme);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
@import "~src/styles/theme";
|
||||
|
||||
.tag-cards-container {
|
||||
padding: 24px 0;
|
||||
|
||||
.tag-card {
|
||||
transition: all 0.3s;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-2px);
|
||||
@@ -24,7 +23,8 @@
|
||||
|
||||
.ant-card-actions {
|
||||
border-radius: 0 0 8px 8px;
|
||||
border-top: 1px solid rgba(240, 240, 240, 0.5);
|
||||
background: #fafafa;
|
||||
border-top: 1px solid #f0f0f0;
|
||||
min-height: 32px;
|
||||
|
||||
> li {
|
||||
@@ -34,7 +34,7 @@
|
||||
padding: 4px 0;
|
||||
|
||||
&:hover {
|
||||
color: @primary-color;
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
i {
|
||||
@@ -51,8 +51,20 @@
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 8px;
|
||||
|
||||
.tag-name {
|
||||
font-size: 18px;
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
margin: 0;
|
||||
font-weight: 500;
|
||||
line-height: 1.4;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.tag-description {
|
||||
color: rgba(35, 34, 34, 0.65);
|
||||
font-size: 12px;
|
||||
margin: 0;
|
||||
display: -webkit-box;
|
||||
@@ -65,27 +77,31 @@
|
||||
}
|
||||
}
|
||||
|
||||
.tag-card {
|
||||
:global {
|
||||
.ant-card-body {
|
||||
padding: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tag-content {
|
||||
min-height: 80px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.tag-name {
|
||||
font-size: 18px;
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
margin: 0;
|
||||
font-weight: 500;
|
||||
line-height: 1.4;
|
||||
text-align: center;
|
||||
background: @common-background-color;
|
||||
.tag-header {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
[data-theme='dark'] {
|
||||
:host {
|
||||
.tag-name {
|
||||
background: @common-background-color-dark;
|
||||
}
|
||||
}
|
||||
.tag-description {
|
||||
font-size: 12px;
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
@@ -57,8 +57,7 @@ export class LocalStorageService {
|
||||
return localStorage.getItem(AuthorizationConst) != null;
|
||||
}
|
||||
|
||||
public clearAuthorization() {
|
||||
localStorage.removeItem(AuthorizationConst);
|
||||
localStorage.removeItem(RefreshTokenConst);
|
||||
public clear() {
|
||||
localStorage.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,17 +17,15 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { HttpClient, HttpParams } from '@angular/common/http';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
import { Message } from '../pojo/Message';
|
||||
import { NoticeReceiver } from '../pojo/NoticeReceiver';
|
||||
import { Page } from '../pojo/Page';
|
||||
|
||||
const notice_receiver_uri = '/notice/receiver';
|
||||
const notice_receivers_uri = '/notice/receivers';
|
||||
const notice_receivers_all_uri = '/notice/receivers/all';
|
||||
const notice_receiver_send_test_msg_uri = '/notice/receiver/send-test-msg';
|
||||
|
||||
@Injectable({
|
||||
@@ -48,21 +46,8 @@ export class NoticeReceiverService {
|
||||
return this.http.delete<Message<any>>(`${notice_receiver_uri}/${receiverId}`);
|
||||
}
|
||||
|
||||
public getReceivers(name: string, pageIndex: number, pageSize: number): Observable<Message<Page<NoticeReceiver>>> {
|
||||
pageIndex = pageIndex ? pageIndex : 0;
|
||||
pageSize = pageSize ? pageSize : 8;
|
||||
let httpParams = new HttpParams();
|
||||
httpParams = httpParams.append('pageIndex', pageIndex);
|
||||
httpParams = httpParams.append('pageSize', pageSize);
|
||||
if (name != undefined && name != null && name != '') {
|
||||
httpParams = httpParams.append('name', name);
|
||||
}
|
||||
const options = { params: httpParams };
|
||||
return this.http.get<Message<Page<NoticeReceiver>>>(notice_receivers_uri, options);
|
||||
}
|
||||
|
||||
public getAllReceivers(): Observable<Message<NoticeReceiver[]>> {
|
||||
return this.http.get<Message<NoticeReceiver[]>>(notice_receivers_all_uri);
|
||||
public getReceivers(): Observable<Message<NoticeReceiver[]>> {
|
||||
return this.http.get<Message<NoticeReceiver[]>>(notice_receivers_uri);
|
||||
}
|
||||
|
||||
public getReceiver(receiverId: number): Observable<Message<NoticeReceiver>> {
|
||||
|
||||
@@ -17,13 +17,12 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { HttpClient, HttpParams } from '@angular/common/http';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
import { Message } from '../pojo/Message';
|
||||
import { NoticeRule } from '../pojo/NoticeRule';
|
||||
import { Page } from '../pojo/Page';
|
||||
|
||||
const notice_rule_uri = '/notice/rule';
|
||||
const notice_rules_uri = '/notice/rules';
|
||||
@@ -46,17 +45,8 @@ export class NoticeRuleService {
|
||||
return this.http.delete<Message<any>>(`${notice_rule_uri}/${ruleId}`);
|
||||
}
|
||||
|
||||
public getNoticeRules(name: string, pageIndex: number, pageSize: number): Observable<Message<Page<NoticeRule>>> {
|
||||
pageIndex = pageIndex ? pageIndex : 0;
|
||||
pageSize = pageSize ? pageSize : 8;
|
||||
let httpParams = new HttpParams();
|
||||
httpParams = httpParams.append('pageIndex', pageIndex);
|
||||
httpParams = httpParams.append('pageSize', pageSize);
|
||||
if (name != undefined && name != null && name != '') {
|
||||
httpParams = httpParams.append('name', name);
|
||||
}
|
||||
const options = { params: httpParams };
|
||||
return this.http.get<Message<Page<NoticeRule>>>(notice_rules_uri, options);
|
||||
public getNoticeRules(): Observable<Message<NoticeRule[]>> {
|
||||
return this.http.get<Message<NoticeRule[]>>(notice_rules_uri);
|
||||
}
|
||||
|
||||
public getNoticeRuleById(ruleId: number): Observable<Message<NoticeRule>> {
|
||||
|
||||
@@ -17,17 +17,15 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { HttpClient, HttpParams } from '@angular/common/http';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
import { Message } from '../pojo/Message';
|
||||
import { NoticeTemplate } from '../pojo/NoticeTemplate';
|
||||
import { Page } from '../pojo/Page';
|
||||
|
||||
const notice_template_uri = '/notice/template';
|
||||
const notice_templates_uri = '/notice/templates';
|
||||
const notice_templates_all_uri = '/notice/templates/all';
|
||||
const default_notice_templates_uri = '/notice/default_templates';
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
@@ -47,22 +45,8 @@ export class NoticeTemplateService {
|
||||
return this.http.delete<Message<any>>(`${notice_template_uri}/${templateId}`);
|
||||
}
|
||||
|
||||
public getNoticeTemplates(name: string, preset: boolean, pageIndex: number, pageSize: number): Observable<Message<Page<NoticeTemplate>>> {
|
||||
pageIndex = pageIndex ? pageIndex : 0;
|
||||
pageSize = pageSize ? pageSize : 8;
|
||||
let httpParams = new HttpParams();
|
||||
httpParams = httpParams.append('pageIndex', pageIndex);
|
||||
httpParams = httpParams.append('pageSize', pageSize);
|
||||
httpParams = httpParams.append('preset', preset);
|
||||
if (name != undefined && name != null && name != '') {
|
||||
httpParams = httpParams.append('name', name);
|
||||
}
|
||||
const options = { params: httpParams };
|
||||
return this.http.get<Message<Page<NoticeTemplate>>>(notice_templates_uri, options);
|
||||
}
|
||||
|
||||
public getAllNoticeTemplates(): Observable<Message<NoticeTemplate[]>> {
|
||||
return this.http.get<Message<NoticeTemplate[]>>(notice_templates_all_uri);
|
||||
public getNoticeTemplates(): Observable<Message<NoticeTemplate[]>> {
|
||||
return this.http.get<Message<NoticeTemplate[]>>(notice_templates_uri);
|
||||
}
|
||||
|
||||
public getDefaultNoticeTemplates(): Observable<Message<NoticeTemplate[]>> {
|
||||
|
||||
@@ -36,10 +36,11 @@ export class ThemeService {
|
||||
return localStorage.getItem(this.themeKey);
|
||||
}
|
||||
|
||||
changeTheme(theme: string | null): void {
|
||||
if (theme == null) {
|
||||
theme = this.getTheme();
|
||||
}
|
||||
clearTheme(): void {
|
||||
localStorage.removeItem(this.themeKey);
|
||||
}
|
||||
|
||||
changeTheme(theme: string): void {
|
||||
const style = this.doc.createElement('link');
|
||||
style.type = 'text/css';
|
||||
style.rel = 'stylesheet';
|
||||
@@ -56,6 +57,9 @@ export class ThemeService {
|
||||
|
||||
const compactDom = this.doc.getElementById('compact-theme');
|
||||
if (compactDom) compactDom.remove();
|
||||
|
||||
this.clearTheme();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -69,6 +73,5 @@ export class ThemeService {
|
||||
|
||||
// add new theme
|
||||
this.doc.body.appendChild(style);
|
||||
this.doc.body.setAttribute('data-theme', theme);
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user