diff --git a/alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/AlertDefineServiceImpl.java b/alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/AlertDefineServiceImpl.java index e37fdb86ce8..ac1d406ad66 100644 --- a/alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/AlertDefineServiceImpl.java +++ b/alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/AlertDefineServiceImpl.java @@ -17,20 +17,12 @@ package org.apache.hertzbeat.alert.service.impl; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.Predicate; import jakarta.servlet.http.HttpServletResponse; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.function.Function; -import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; import org.apache.hertzbeat.alert.dao.AlertDefineBindDao; import org.apache.hertzbeat.alert.dao.AlertDefineDao; @@ -56,6 +48,20 @@ import org.springframework.util.StringUtils; import org.springframework.web.multipart.MultipartFile; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; + /** * Alarm definition management interface implementation */ @@ -158,6 +164,18 @@ public AlertDefine getMonitorBindAlertAvaDefine(long monitorId, String app, Stri @Override public Page getAlertDefines(List defineIds, String search, Byte priority, String sort, String order, int pageIndex, int pageSize) { + // parse translation content list + ObjectMapper objectMapper = new ObjectMapper(); + List searchList = Collections.emptyList(); + if (StringUtils.hasText(search)) { + try { + searchList = objectMapper.readValue(URLDecoder.decode(search, StandardCharsets.UTF_8), new TypeReference<>() {}); + } catch (JsonProcessingException e) { + throw new IllegalArgumentException("Failed to parse search parameter", e); + } + } + List finalSearchList = searchList; + // build search condition Specification specification = (root, query, criteriaBuilder) -> { List andList = new ArrayList<>(); if (defineIds != null && !defineIds.isEmpty()) { @@ -167,30 +185,21 @@ public Page getAlertDefines(List defineIds, String search, By } andList.add(inPredicate); } - if (StringUtils.hasText(search)) { - Predicate predicate = criteriaBuilder.or( - criteriaBuilder.like( - criteriaBuilder.lower(root.get("app")), - "%" + search.toLowerCase() + "%" - ), - criteriaBuilder.like( - criteriaBuilder.lower(root.get("metric")), - "%" + search.toLowerCase() + "%" - ), - criteriaBuilder.like( - criteriaBuilder.lower(root.get("field")), - "%" + search.toLowerCase() + "%" - ), - criteriaBuilder.like( - criteriaBuilder.lower(root.get("expr")), - "%" + search.toLowerCase() + "%" - ), - criteriaBuilder.like( - criteriaBuilder.lower(root.get("template")), - "%" + search.toLowerCase() + "%" - ) - ); - andList.add(predicate); + if (null != finalSearchList && !finalSearchList.isEmpty()) { + List searchPredicates = new ArrayList<>(); + for (String searchContent : finalSearchList) { + searchContent = searchContent.toLowerCase(); + Predicate predicate = criteriaBuilder.or( + criteriaBuilder.like(criteriaBuilder.lower(root.get("app")), "%" + searchContent + "%"), + criteriaBuilder.like(criteriaBuilder.lower(root.get("metric")), "%" + searchContent + "%"), + criteriaBuilder.like(criteriaBuilder.lower(root.get("field")), "%" + searchContent + "%"), + criteriaBuilder.like(criteriaBuilder.lower(root.get("expr")), "%" + searchContent + "%"), + criteriaBuilder.like(criteriaBuilder.lower(root.get("template")), "%" + searchContent + "%") + ); + searchPredicates.add(predicate); + } + // all search keywords are connected with or + andList.add(criteriaBuilder.or(searchPredicates.toArray(new Predicate[0]))); } if (priority != null) { Predicate predicate = criteriaBuilder.equal(root.get("priority"), priority); diff --git a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts index cfcb9326d96..876ae918175 100644 --- a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts +++ b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts @@ -28,8 +28,8 @@ import { NzNotificationService } from 'ng-zorro-antd/notification'; import { NzTableQueryParams } from 'ng-zorro-antd/table'; import { TransferChange, TransferItem } from 'ng-zorro-antd/transfer'; import { NzUploadChangeParam } from 'ng-zorro-antd/upload'; -import { zip } from 'rxjs'; -import { finalize, map } from 'rxjs/operators'; +import { EMPTY, zip } from 'rxjs'; +import { catchError, finalize, map, switchMap, take, tap } from 'rxjs/operators'; import { AlertDefine } from '../../../pojo/AlertDefine'; import { AlertDefineBind } from '../../../pojo/AlertDefineBind'; @@ -98,6 +98,8 @@ export class AlertSettingComponent implements OnInit { return null; }; qbFormCtrl: FormControl; + appMap = new Map(); + appEntries: Array<{ value: any; key: string }> = []; ngOnInit(): void { this.loadAlertDefineTable(); @@ -131,6 +133,23 @@ export class AlertSettingComponent implements OnInit { console.warn(error.msg); } ); + // query i18n content + this.appDefineSvc + .getAppDefines(this.i18nSvc.defaultLang) + .pipe() + .subscribe( + message => { + if (message.code === 0) { + this.appMap = message.data; + this.appEntries = Object.entries(this.appMap).map(([key, value]) => ({ key, value })); + } else { + console.warn(message.msg); + } + }, + error => { + console.warn(error.msg); + } + ); } sync() { @@ -139,7 +158,22 @@ export class AlertSettingComponent implements OnInit { loadAlertDefineTable() { this.tableLoading = true; - let alertDefineInit$ = this.alertDefineSvc.getAlertDefines(this.search, this.pageIndex - 1, this.pageSize).subscribe( + const translationSearchList: string[] = []; + let trimSearch = ''; + if (this.search !== undefined && this.search.trim() !== '') { + trimSearch = this.search.trim(); + } + // Filter entries based on search input + this.appEntries.forEach(entry => { + if (trimSearch && entry.value.toLowerCase().includes(trimSearch.toLowerCase())) { + translationSearchList.push(entry.key); + } + }); + // If no match found and search input exists, add search term to list + if (translationSearchList.length === 0 && trimSearch) { + translationSearchList.push(trimSearch); + } + let alertDefineInit$ = this.alertDefineSvc.getAlertDefines(translationSearchList, this.pageIndex - 1, this.pageSize).subscribe( message => { this.tableLoading = false; this.checkedAll = false; diff --git a/web-app/src/app/service/alert-define.service.ts b/web-app/src/app/service/alert-define.service.ts index d602eca2d38..b7f6df0bcb0 100644 --- a/web-app/src/app/service/alert-define.service.ts +++ b/web-app/src/app/service/alert-define.service.ts @@ -67,7 +67,7 @@ export class AlertDefineService { return this.http.delete>(alert_defines_uri, options); } - public getAlertDefines(search: string | undefined, pageIndex: number, pageSize: number): Observable>> { + public getAlertDefines(search: string[] | undefined, pageIndex: number, pageSize: number): Observable>> { pageIndex = pageIndex ? pageIndex : 0; pageSize = pageSize ? pageSize : 8; // HttpParams is unmodifiable, so we need to save the return value of append/set @@ -78,8 +78,9 @@ export class AlertDefineService { pageIndex: pageIndex, pageSize: pageSize }); - if (search != undefined && search.trim() != '') { - httpParams = httpParams.append('search', search.trim()); + if (search != undefined && search.length > 0) { + const searchJson = JSON.stringify(search); + httpParams = httpParams.append('search', encodeURIComponent(searchJson)); } const options = { params: httpParams }; return this.http.get>>(alert_defines_uri, options);