modify alert bug

This commit is contained in:
silenceqi 2021-09-07 09:39:44 +08:00
parent 22c66b01df
commit 13ce75d197
17 changed files with 143 additions and 39 deletions

View File

@ -49,6 +49,7 @@ func Init(cfg *config.AppConfig) {
ui.HandleUIMethod(api.POST, "/elasticsearch/:id/alerting/monitors", alerting.CreateMonitor) ui.HandleUIMethod(api.POST, "/elasticsearch/:id/alerting/monitors", alerting.CreateMonitor)
ui.HandleUIMethod(api.POST, "/elasticsearch/:id/alerting/monitors/_execute", alerting.ExecuteMonitor) ui.HandleUIMethod(api.POST, "/elasticsearch/:id/alerting/monitors/_execute", alerting.ExecuteMonitor)
ui.HandleUIMethod(api.DELETE, "/elasticsearch/:id/alerting/monitors/:monitorID", alerting.DeleteMonitor) ui.HandleUIMethod(api.DELETE, "/elasticsearch/:id/alerting/monitors/:monitorID", alerting.DeleteMonitor)
ui.HandleUIMethod(api.POST, "/elasticsearch/:id/alerting/_monitors/:monitorID/_acknowledge/alerts", alerting.AcknowledgeAlerts)
ui.HandleUIMethod(api.GET, "/elasticsearch/:id/alerting/_settings", alerting.GetSettings) ui.HandleUIMethod(api.GET, "/elasticsearch/:id/alerting/_settings", alerting.GetSettings)
ui.HandleUIMethod(api.GET, "/elasticsearch/:id/alerting/destinations", alerting.GetDestinations) ui.HandleUIMethod(api.GET, "/elasticsearch/:id/alerting/destinations", alerting.GetDestinations)
ui.HandleUIMethod(api.POST, "/elasticsearch/:id/alerting/destinations", alerting.CreateDestination) ui.HandleUIMethod(api.POST, "/elasticsearch/:id/alerting/destinations", alerting.CreateDestination)
@ -63,6 +64,7 @@ func Init(cfg *config.AppConfig) {
ui.HandleUIMethod(api.GET, "/elasticsearch/:id/alerting/destinations/email_groups", alerting.GetEmailGroups) ui.HandleUIMethod(api.GET, "/elasticsearch/:id/alerting/destinations/email_groups", alerting.GetEmailGroups)
ui.HandleUIMethod(api.DELETE, "/elasticsearch/:id/alerting/email_groups/:emailGroupId", alerting.DeleteEmailGroup) ui.HandleUIMethod(api.DELETE, "/elasticsearch/:id/alerting/email_groups/:emailGroupId", alerting.DeleteEmailGroup)
ui.HandleUIMethod(api.PUT, "/elasticsearch/:id/alerting/email_groups/:emailGroupId", alerting.UpdateEmailGroup) ui.HandleUIMethod(api.PUT, "/elasticsearch/:id/alerting/email_groups/:emailGroupId", alerting.UpdateEmailGroup)
ui.HandleUIMethod(api.GET, "/elasticsearch/:id/alerting/email_groups/:emailGroupId", alerting.GetEmailGroup)

View File

@ -39,7 +39,7 @@ func GetAlerts (w http.ResponseWriter, req *http.Request, ps httprouter.Params){
sortField = getQueryParam(req, "sortField", "start_time") sortField = getQueryParam(req, "sortField", "start_time")
severityLevel = getQueryParam(req, "severityLevel", "ALL") severityLevel = getQueryParam(req, "severityLevel", "ALL")
alertState = getQueryParam(req, "alertState", "ALL") alertState = getQueryParam(req, "alertState", "ALL")
//monitorIds = getQueryParam(req, "monitorIds") monitorIds = req.URL.Query()["monitorIds"]
params = map[string]string{ params = map[string]string{
"startIndex": from, "startIndex": from,
"size": size, "size": size,
@ -68,6 +68,9 @@ func GetAlerts (w http.ResponseWriter, req *http.Request, ps httprouter.Params){
params["sortOrder"] = sortDirection params["sortOrder"] = sortDirection
params["missing"] = "_last" params["missing"] = "_last"
} }
if len(monitorIds) > 0{
params["monitorId"] = monitorIds[0]
}
if clearSearch := strings.TrimSpace(search); clearSearch != ""{ if clearSearch := strings.TrimSpace(search); clearSearch != ""{
searches := strings.Split(clearSearch, " ") searches := strings.Split(clearSearch, " ")
@ -93,9 +96,10 @@ func GetAlerts (w http.ResponseWriter, req *http.Request, ps httprouter.Params){
if ds, ok := rawAlerts.([]interface{}); ok { if ds, ok := rawAlerts.([]interface{}); ok {
for _, alert := range ds { for _, alert := range ds {
if alertItem, ok := alert.(map[string]interface{}); ok { if alertItem, ok := alert.(map[string]interface{}); ok {
alertItem["version"] = queryValue(alertItem, "alert_version", "") alertItem["version"] = queryValue(alertItem, "alert_version", "")
alertItem["id"] = queryValue(alertItem, "alert_id", "") if queryValue(alertItem, "id", nil) == nil {
alertItem["id"] = queryValue(alertItem, "alert_id", nil)
}
alerts = append(alerts, alertItem) alerts = append(alerts, alertItem)
} }
} }

View File

@ -445,7 +445,6 @@ func GetEmailAccounts(w http.ResponseWriter, req *http.Request, ps httprouter.Pa
reqUrl := fmt.Sprintf("%s/%s/_alerting/destinations/email_accounts/_search", conf.Endpoint, API_PREFIX) reqUrl := fmt.Sprintf("%s/%s/_alerting/destinations/email_accounts/_search", conf.Endpoint, API_PREFIX)
res, err := doRequest(reqUrl, http.MethodPost, nil, reqBody) res, err := doRequest(reqUrl, http.MethodPost, nil, reqBody)
//TODO to handle api error in doRequest function
if err != nil { if err != nil {
writeError(w, err) writeError(w, err)
return return
@ -507,7 +506,6 @@ func GetEmailAccount(w http.ResponseWriter, req *http.Request, ps httprouter.Par
writeError(w, err) writeError(w, err)
return return
} }
//TODO error handle: check whether resBody has contains field error
writeJSON(w, IfaceMap{ writeJSON(w, IfaceMap{
"ok": true, "ok": true,
@ -703,8 +701,8 @@ func GetEmailGroups(w http.ResponseWriter, req *http.Request, ps httprouter.Para
if ms, ok := source.(map[string]interface{}); ok { if ms, ok := source.(map[string]interface{}); ok {
assignTo(newItem, ms) assignTo(newItem, ms)
} }
newItem["ifSeqNo"] = queryValue(emailGroup, "seq_no", 0) newItem["ifSeqNo"] = queryValue(emailGroup, "_seq_no", 0)
newItem["ifPrimaryTerm"] = queryValue(emailGroup, "primary_term", 0) newItem["ifPrimaryTerm"] = queryValue(emailGroup, "_primary_term", 0)
emailGroups = append(emailGroups, newItem) emailGroups = append(emailGroups, newItem)
} }
} }
@ -716,5 +714,44 @@ func GetEmailGroups(w http.ResponseWriter, req *http.Request, ps httprouter.Para
}, http.StatusOK) }, http.StatusOK)
} }
func GetEmailGroup(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
id := ps.ByName("id")
conf := elastic.GetConfig(id)
if conf == nil {
writeError(w, errors.New("cluster not found"))
return
}
emailAccountId := ps.ByName("emailGroupId")
reqUrl := fmt.Sprintf("%s/%s/_alerting/destinations/email_groups/%s", conf.Endpoint, API_PREFIX, emailAccountId)
res, err := doRequest(reqUrl, http.MethodGet,nil, req.Body)
if err != nil {
writeError(w, err)
return
}
var resBody = IfaceMap{}
err = decodeJSON(res.Body, &resBody)
res.Body.Close()
if err != nil {
writeError(w, err)
return
}
emailGroup := queryValue(resBody, "email_group", nil)
if emailGroup == nil {
writeJSON(w, IfaceMap{
"ok": false,
}, http.StatusOK)
return
}
writeJSON(w, IfaceMap{
"ok": true,
"resp": emailGroup,
"ifSeqNo": queryValue(resBody, "_seq_no", 0),
"ifPrimaryTerm": queryValue(resBody, "_primary_term", 0),
}, http.StatusOK)
}

View File

@ -443,7 +443,6 @@ func UpdateMonitor(w http.ResponseWriter, req *http.Request, ps httprouter.Param
writeError(w, err) writeError(w, err)
return return
} }
//TODO error handle: check whether resBody has contains field error
writeJSON(w, IfaceMap{ writeJSON(w, IfaceMap{
"ok": true, "ok": true,
@ -453,6 +452,43 @@ func UpdateMonitor(w http.ResponseWriter, req *http.Request, ps httprouter.Param
} }
func AcknowledgeAlerts(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
id := ps.ByName("id")
conf := elastic.GetConfig(id)
if conf == nil {
writeError(w, errors.New("cluster not found"))
return
}
monitorId := ps.ByName("monitorID")
reqUrl := fmt.Sprintf("%s/%s/_alerting/monitors/%s/_acknowledge/alerts", conf.Endpoint, API_PREFIX, monitorId)
res, err := doRequest(reqUrl, http.MethodPost,nil, req.Body)
if err != nil {
writeError(w, err)
return
}
var resBody = IfaceMap{}
err = decodeJSON(res.Body, &resBody)
res.Body.Close()
if err != nil {
writeError(w, err)
return
}
var isOk = false
if failed, ok := resBody["failed"].([]interface{}); ok && len(failed) == 0 {
isOk = true
}
writeJSON(w, IfaceMap{
"ok": isOk,
"resp": resBody,
}, http.StatusOK)
}
func ExecuteMonitor(w http.ResponseWriter, req *http.Request, ps httprouter.Params) { func ExecuteMonitor(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
id := ps.ByName("id") id := ps.ByName("id")
conf := elastic.GetConfig(id) conf := elastic.GetConfig(id)

View File

@ -125,6 +125,7 @@ export class ScopedHistory<HistoryLocationState = unknown>
if (typeof pathOrLocation === 'string') { if (typeof pathOrLocation === 'string') {
this.parentHistory.push(this.prependBasePath(pathOrLocation), state); this.parentHistory.push(this.prependBasePath(pathOrLocation), state);
} else { } else {
!pathOrLocation.key && (pathOrLocation.key = pathOrLocation.pathname+(pathOrLocation.search||''))
this.parentHistory.push(this.prependBasePath(pathOrLocation)); this.parentHistory.push(this.prependBasePath(pathOrLocation));
} }
}; };

View File

@ -14,9 +14,9 @@ export default {
'alert.dashboard.alerts': '告警管理', 'alert.dashboard.alerts': '告警管理',
'alert.dashboard.severity-options.all': '所有告警级别', 'alert.dashboard.severity-options.all': '所有告警级别',
'alert.dashboard.state-options.all': '所有状态', 'alert.dashboard.state-options.all': '所有状态',
'alert.dashboard.state-options.active': '激活的', 'alert.dashboard.state-options.active': '告警中',
'alert.dashboard.state-options.acknowledged': '已确认', 'alert.dashboard.state-options.acknowledged': '已知晓',
'alert.dashboard.state-options.completed': '已完成', 'alert.dashboard.state-options.completed': '已恢复',
'alert.dashboard.state-options.error': '错误', 'alert.dashboard.state-options.error': '错误',
'alert.dashboard.state-options.deleted': '已删除', 'alert.dashboard.state-options.deleted': '已删除',
'alert.dashboard.create-monitor-text': '暂无监控项。 创建监控项以添加触发器和操作。 一旦触发警报,状态将显示在此表中。', 'alert.dashboard.create-monitor-text': '暂无监控项。 创建监控项以添加触发器和操作。 一旦触发警报,状态将显示在此表中。',

View File

@ -126,15 +126,15 @@ export default {
}); });
}, },
*rewriteURL({payload}, {select}){ *rewriteURL({payload}, {select}){
const {pathname, history} = payload; const {pathname, history, search} = payload;
const global = yield select(state=>state.global); const global = yield select(state=>state.global);
if(pathname && global.selectedClusterID){ if(pathname && global.selectedClusterID){
const newPart = `/elasticsearch/${global.selectedClusterID}/`; const newPart = `/elasticsearch/${global.selectedClusterID}/`;
if(!pathname.includes('elasticsearch')){ if(!pathname.includes('elasticsearch')){
history.replace(pathname+newPart) history.replace(pathname+newPart+search)
}else{ }else{
const newPath = pathname.replace(/\/elasticsearch\/(\w+)\/?/, newPart); const newPath = pathname.replace(/\/elasticsearch\/(\w+)\/?/, newPart);
history.replace(newPath) history.replace(newPath+search)
} }
} }
}, },
@ -230,14 +230,17 @@ export default {
if(pathname.startsWith("/system")){ if(pathname.startsWith("/system")){
clusterVisible = false; clusterVisible = false;
}else{ }else{
if(!pathname.includes('elasticsearch')){ if(!pathname.startsWith("/exception") && pathname != '/alerting'){
dispatch({ if(!pathname.includes('elasticsearch')){
type: 'rewriteURL', dispatch({
payload: { type: 'rewriteURL',
pathname, payload: {
history, pathname,
} history,
}) search,
}
})
}
} }
} }
dispatch({ dispatch({

View File

@ -32,6 +32,8 @@ const notifications = {
} }
} }
const AlertingMain = React.memo(Main)
const AlertingUI = (props)=>{ const AlertingUI = (props)=>{
if(!props.selectedCluster.id){ if(!props.selectedCluster.id){
return null; return null;
@ -52,13 +54,14 @@ const AlertingUI = (props)=>{
> >
<Router history={history}> <Router history={history}>
<div style={{background:'#fff'}}> <div style={{background:'#fff'}}>
<Main title="Alerting" {...props} /> <AlertingMain title="Alerting" {...props} />
</div> </div>
</Router> </Router>
</CoreContext.Provider> </CoreContext.Provider>
) )
} }
export default connect(({ export default connect(({
global global
})=>({ })=>({

View File

@ -22,7 +22,7 @@ const actionEmptyText = 'Add an action to perform when this trigger is triggered
const destinationEmptyText = const destinationEmptyText =
'There are no existing destinations. Add a destinations to create an action'; 'There are no existing destinations. Add a destinations to create an action';
const createDestinationButton = ( const createDestinationButton = (
<EuiButton fill href={`${PLUGIN_NAME}#/create-destination`}> <EuiButton fill href={`#/${PLUGIN_NAME}/create-destination`}>
Add destination Add destination
</EuiButton> </EuiButton>
); );

View File

@ -184,7 +184,7 @@ export default class Dashboard extends Component {
const queryParamsString = queryString.stringify(params); const queryParamsString = queryString.stringify(params);
location.search; location.search;
const { httpClient, history, notifications } = this.props; const { httpClient, history, notifications } = this.props;
history.replace({ ...this.props.location, search: queryParamsString }); // history.replace({ ...this.props.location, search: queryParamsString });
httpClient.get('/alerting/alerts', { query: params }).then((resp) => { httpClient.get('/alerting/alerts', { query: params }).then((resp) => {
if (resp.ok) { if (resp.ok) {
const { alerts, totalAlerts } = resp; const { alerts, totalAlerts } = resp;
@ -218,7 +218,7 @@ export default class Dashboard extends Component {
const promises = Object.entries(monitorAlerts).map(([monitorId, alerts]) => const promises = Object.entries(monitorAlerts).map(([monitorId, alerts]) =>
httpClient httpClient
.post(`/alerting/monitors/${monitorId}/_acknowledge/alerts`, { .post(`/alerting/_monitors/${monitorId}/_acknowledge/alerts`, {
body: JSON.stringify({ alerts }), body: JSON.stringify({ alerts }),
}) })
.then((resp) => { .then((resp) => {

View File

@ -28,6 +28,15 @@ const renderTime = (time) => {
return DEFAULT_EMPTY_DATA; return DEFAULT_EMPTY_DATA;
}; };
const stateOptions = {
'ALL': formatMessage({ id: 'alert.dashboard.state-options.all' }),
[ALERT_STATE.ACTIVE]: formatMessage({ id: 'alert.dashboard.state-options.active' }),
[ALERT_STATE.ACKNOWLEDGED]: formatMessage({ id: 'alert.dashboard.state-options.acknowledged' }),
[ALERT_STATE.COMPLETED]: formatMessage({ id: 'alert.dashboard.state-options.completed' }),
[ALERT_STATE.ERROR]: formatMessage({ id: 'alert.dashboard.state-options.error' }),
[ALERT_STATE.DELETED]: formatMessage({ id: 'alert.dashboard.state-options.deleted' }),
};
export const columns = [ export const columns = [
{ {
field: 'start_time', field: 'start_time',
@ -76,7 +85,7 @@ export const columns = [
render: (state, alert) => { render: (state, alert) => {
const stateText = const stateText =
typeof state !== 'string' ? DEFAULT_EMPTY_DATA : _.capitalize(state.toLowerCase()); typeof state !== 'string' ? DEFAULT_EMPTY_DATA : _.capitalize(state.toLowerCase());
return state === ALERT_STATE.ERROR ? `${stateText}: ${alert.error_message}` : stateText; return state === ALERT_STATE.ERROR ? `${stateText}: ${alert.error_message}` : (stateOptions[state] || stateText);
}, },
}, },
{ {

View File

@ -165,11 +165,11 @@ class CreateDestination extends React.Component {
handleCancel = () => { handleCancel = () => {
const { edit, history } = this.props; const { edit, history } = this.props;
if (edit) { // if (edit) {
history.goBack(); // history.goBack();
} else { // } else {
history.push('/destinations'); history.push('/destinations');
} // }
}; };
render() { render() {

View File

@ -62,7 +62,7 @@ const getSender = async (httpClient, id) => {
const getEmailGroup = async (httpClient, id) => { const getEmailGroup = async (httpClient, id) => {
try { try {
const response = await httpClient.get(`/alerting/destinations/email_groups/${id}`); const response = await httpClient.get(`/alerting/email_groups/${id}`);
if (response.ok) { if (response.ok) {
return response.resp; return response.resp;
} }

View File

@ -97,7 +97,7 @@ export default class Home extends Component {
<div style={{ padding: '25px 25px' }}> <div style={{ padding: '25px 25px' }}>
<Switch> <Switch>
<Route <Route
exact // exact
path="/dashboard" path="/dashboard"
render={(props) => ( render={(props) => (
<Dashboard {...props} httpClient={httpClient} notifications={notifications} /> <Dashboard {...props} httpClient={httpClient} notifications={notifications} />
@ -121,7 +121,15 @@ export default class Home extends Component {
/> />
)} )}
/> />
<Redirect to="/dashboard" /> <Route
exact
path="/"
render={() => {
return (
<Redirect to="/dashboard" />
)
}}
/>
</Switch> </Switch>
</div> </div>
</div> </div>

View File

@ -96,7 +96,7 @@ class TriggersTimeSeries extends Component {
align={{ vertical: 'top', horizontal: 'auto' }} align={{ vertical: 'top', horizontal: 'auto' }}
value={hints[currentTrigger.name]} value={hints[currentTrigger.name]}
format={formatTooltip} format={formatTooltip}
style={{ title: { fontWeight: 'bold' } }} style={{ title: { fontWeight: 'bold' }, backgroundColor: '#fff'}}
/> />
) : null} ) : null}
</FlexibleWidthXYPlot> </FlexibleWidthXYPlot>

View File

@ -178,7 +178,8 @@ export default class MonitorDetails extends Component {
}; };
onCloseTrigger = () => { onCloseTrigger = () => {
this.props.history.push({ ...this.props.location, search: '' }); const {pathname} = this.props.location; //important change
this.props.history.push({ pathname, search: '' });
this.setState({ triggerToEdit: null }); this.setState({ triggerToEdit: null });
}; };
@ -301,7 +302,7 @@ export default class MonitorDetails extends Component {
</h1> </h1>
</EuiTitle> </EuiTitle>
{detector ? ( {/* {detector ? (
<EuiFlexItem grow={false}> <EuiFlexItem grow={false}>
<EuiText size="s"> <EuiText size="s">
Created from detector:{' '} Created from detector:{' '}
@ -310,7 +311,7 @@ export default class MonitorDetails extends Component {
</EuiLink> </EuiLink>
</EuiText> </EuiText>
</EuiFlexItem> </EuiFlexItem>
) : null} ) : null} */}
</EuiFlexItem> </EuiFlexItem>
<EuiFlexItem grow={false}> <EuiFlexItem grow={false}>

View File

@ -249,7 +249,7 @@ export default class Monitors extends Component {
const promises = Object.entries(monitorAlerts).map(([monitorId, alerts]) => const promises = Object.entries(monitorAlerts).map(([monitorId, alerts]) =>
httpClient httpClient
.post(`/alerting/monitors/${monitorId}/_acknowledge/alerts`, { .post(`/alerting/_monitors/${monitorId}/_acknowledge/alerts`, {
body: JSON.stringify({ alerts }), body: JSON.stringify({ alerts }),
}) })
.then((resp) => { .then((resp) => {