Merge branch 'master' into shiyang-locales

This commit is contained in:
shiyang 2021-09-05 18:52:53 +08:00
commit 22c66b01df
36 changed files with 1324 additions and 136 deletions

View File

@ -43,6 +43,27 @@ func Init(cfg *config.AppConfig) {
ui.HandleUIMethod(api.POST, "/elasticsearch/:id/alerting/_aliases", alerting.GetAliases)
ui.HandleUIMethod(api.POST, "/elasticsearch/:id/alerting/_mappings", alerting.GetMappings)
ui.HandleUIMethod(api.GET, "/elasticsearch/:id/alerting/_plugins", alerting.GetPlugins)
ui.HandleUIMethod(api.GET, "/elasticsearch/:id/alerting/monitors/:monitorID", alerting.GetMonitor)
ui.HandleUIMethod(api.PUT, "/elasticsearch/:id/alerting/monitors/:monitorID", alerting.UpdateMonitor)
ui.HandleUIMethod(api.GET, "/elasticsearch/:id/alerting/monitors", alerting.GetMonitors)
ui.HandleUIMethod(api.POST, "/elasticsearch/:id/alerting/monitors", alerting.CreateMonitor)
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.GET, "/elasticsearch/:id/alerting/_settings", alerting.GetSettings)
ui.HandleUIMethod(api.GET, "/elasticsearch/:id/alerting/destinations", alerting.GetDestinations)
ui.HandleUIMethod(api.POST, "/elasticsearch/:id/alerting/destinations", alerting.CreateDestination)
ui.HandleUIMethod(api.PUT, "/elasticsearch/:id/alerting/destinations/:destinationId", alerting.UpdateDestination)
ui.HandleUIMethod(api.DELETE, "/elasticsearch/:id/alerting/destinations/:destinationId", alerting.DeleteDestination)
ui.HandleUIMethod(api.POST, "/elasticsearch/:id/alerting/destinations/email_accounts", alerting.CreateEmailAccount)
ui.HandleUIMethod(api.GET, "/elasticsearch/:id/alerting/destinations/email_accounts", alerting.GetEmailAccounts)
ui.HandleUIMethod(api.DELETE, "/elasticsearch/:id/alerting/email_accounts/:emailAccountId", alerting.DeleteEmailAccount)
ui.HandleUIMethod(api.GET, "/elasticsearch/:id/alerting/email_accounts/:emailAccountId", alerting.GetEmailAccount)
ui.HandleUIMethod(api.PUT, "/elasticsearch/:id/alerting/email_accounts/:emailAccountId", alerting.UpdateEmailAccount)
ui.HandleUIMethod(api.POST, "/elasticsearch/:id/alerting/destinations/email_groups", alerting.CreateEmailGroup)
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.PUT, "/elasticsearch/:id/alerting/email_groups/:emailGroupId", alerting.UpdateEmailGroup)
task.RegisterScheduleTask(task.ScheduleTask{

View File

@ -34,7 +34,7 @@ func GetAlerts (w http.ResponseWriter, req *http.Request, ps httprouter.Params){
var (
from = getQueryParam(req, "from", "0")
size = getQueryParam(req, "size", "20")
search = getQueryParam(req, "search")
search = getQueryParam(req, "search", "")
sortDirection = getQueryParam(req, "sortDirection", "desc")
sortField = getQueryParam(req, "sortField", "start_time")
severityLevel = getQueryParam(req, "severityLevel", "ALL")
@ -74,57 +74,65 @@ func GetAlerts (w http.ResponseWriter, req *http.Request, ps httprouter.Params){
clearSearch = strings.Join(searches, "* *")
params["searchString"] = fmt.Sprintf("*%s*", clearSearch)
}
reqUrl := conf.Endpoint + "/_opendistro/_alerting/monitors/alerts"
reqUrl := fmt.Sprintf("%s/%s/_alerting/monitors/alerts", conf.Endpoint, API_PREFIX )
res, err := doRequest(reqUrl, http.MethodGet, params, nil)
if err != nil {
writeError(w, err)
return
}
var alertRes = AlertResponse{}
var alertRes = IfaceMap{}
err = decodeJSON(res.Body, &alertRes)
defer res.Body.Close()
if err != nil {
writeError(w, err)
return
}
alerts := []IfaceMap{}
rawAlerts := queryValue(alertRes, "alerts", nil)
if ds, ok := rawAlerts.([]interface{}); ok {
for _, alert := range ds {
if alertItem, ok := alert.(map[string]interface{}); ok {
var alerts = []IfaceMap{}
for _, hit := range alertRes.Alerts {
alert := IfaceMap{
"id": hit["alert_id"],
alertItem["version"] = queryValue(alertItem, "alert_version", "")
alertItem["id"] = queryValue(alertItem, "alert_id", "")
alerts = append(alerts, alertItem)
}
}
for k, v := range hit {
alert[k] = v
}
alert["version"] = hit["alert_version"]
}
writeJSON(w, IfaceMap{
"ok": true,
"alerts": alerts,
"totalAlerts": alertRes.TotalAlerts,
"totalAlerts": queryValue(alertRes, "totalAlerts", 0),
}, http.StatusOK)
}
func writeError(w http.ResponseWriter, err error) {
writeJSON(w, map[string]interface{}{
"body": map[string]interface{}{
"ok": false,
"err": err.Error(),
},
"ok": false,
"resp": err.Error(),
}, http.StatusOK)
}
type IfaceMap map[string]interface{}
type AlertResponse struct {
Alerts []IfaceMap `json:"alerts"`
TotalAlerts int `json:"totalAlerts"`
}
func decodeJSON(reader io.Reader, obj interface{}) error{
dec := json.NewDecoder(reader)
return dec.Decode(obj)
err := dec.Decode(obj)
if err != nil {
return err
}
if m, ok := obj.(*IfaceMap); ok {
if errStr := queryValue(*m,"error", nil); errStr != nil {
if str, ok := errStr.(string); ok {
errors.New(str)
}
buf, _ := json.Marshal(errStr)
return errors.New(string(buf))
}
}
return nil
}
func writeJSON(w http.ResponseWriter, data interface{}, statusCode int){
@ -168,5 +176,36 @@ func doRequest(requestUrl string, method string, params map[string]string, body
}
req, _ = http.NewRequest(method, requestUrl, reader)
req.Header.Set("content-type", "application/json")
req.Header.Set("User-Agent", "Kibana")
return alertClient.Do(req)
}
func queryValue(obj map[string]interface{}, key string, defaultValue interface{}) interface{} {
if key == "" {
return obj
}
idx := strings.Index(key, ".")
if idx == -1 {
if v, ok := obj[key]; ok {
return v
}
return defaultValue
}
ckey := key[0:idx]
if v, ok := obj[ckey]; ok {
if vmap, ok := v.(map[string]interface{}); ok {
return queryValue(vmap, key[idx+1:], defaultValue)
}
}
return defaultValue
}
func assignTo(dst IfaceMap, src IfaceMap){
if dst == nil || src == nil {
return
}
for k, v := range src {
dst[k] = v
}
}

View File

@ -0,0 +1,10 @@
package alerting
const INFINI_PREFIX = "opendistro"
const PLUGIN_NAME = INFINI_PREFIX + "-alerting"
const INDEX_PREFIX = INFINI_PREFIX +"-alerting"
const INDEX_ALL_ALERTS = "."+INDEX_PREFIX +`-alert*`
const API_PREFIX = "_opendistro"

View File

@ -0,0 +1,720 @@
package alerting
import (
"errors"
"fmt"
httprouter "infini.sh/framework/core/api/router"
"infini.sh/framework/core/elastic"
"net/http"
"strings"
)
func GetDestination(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
}
dstID := ps.ByName("destID")
reqUrl := fmt.Sprintf("%s/_opendistro/_alerting/monitors/%s", conf.Endpoint, dstID)
res, err := doRequest(reqUrl, http.MethodGet, nil, nil)
if err != nil {
writeError(w, err)
return
}
var resBody = IfaceMap{}
err = decodeJSON(res.Body, &resBody)
if err != nil {
writeError(w, err)
return
}
res.Body.Close()
if _, ok := resBody["monitor"]; !ok {
writeJSON(w, IfaceMap{
"ok": false,
}, http.StatusOK)
return
}
queryDSL := ` {
"size": 0,
"query"": {
"bool": {
"must": {
"term"": {
"monitor_id": "%s",
},
},
},
},
"aggs": {
"active_count": {
"terms": {
"field": "state",
}
},
"24_hour_count": {
"date_range": {
"field": "start_time",
"ranges": [{ "from": "now-24h/h" }]
}
}
}
}`
queryDSL = fmt.Sprintf(queryDSL, id)
reqUrl = fmt.Sprintf("%s/_opendistro/_alerting/monitors/_search", conf.Endpoint)
res, err = doRequest(reqUrl, http.MethodPost, map[string]string{
"index": ".opendistro-alerting-alert*",
}, queryDSL)
if err != nil {
writeError(w, err)
return
}
var searchResBody = IfaceMap{}
err = decodeJSON(res.Body, &searchResBody)
if err != nil {
writeError(w, err)
return
}
dayCount := queryValue(searchResBody, "aggregations.24_hour_count.buckets.0.doc_count", 0)
activeBuckets := queryValue(searchResBody, "aggregations.active_count.buckets",[]interface{}{})
activeCount := 0
if ab, ok := activeBuckets.([]IfaceMap); ok {
for _, curr := range ab {
if curr["key"].(string) == "ACTIVE" {
activeCount = int(curr["doc_count"].(float64))
}
}
}
writeJSON(w, IfaceMap{
"ok": true,
"resp": resBody["monitor"],
"activeCount": activeCount,
"dayCount": dayCount,
"version": queryValue(resBody, "_version", nil),
"ifSeqNo": queryValue(resBody, "_seq_no", nil),
"ifPrimaryTerm": queryValue(resBody, "_primary_term", nil),
}, http.StatusOK)
}
func GetDestinations(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
}
var (
from = getQueryParam(req, "from", "0")
size = getQueryParam(req, "size", "20")
search = getQueryParam(req, "search", "")
sortDirection = getQueryParam(req, "sortDirection", "desc")
sortField = getQueryParam(req, "sortField", "start_time")
typ = getQueryParam(req, "type", "ALL")
)
var params = map[string]string{}
switch (sortField) {
case "name":
params = map[string]string{
"sortString": "destination.name.keyword",
"sortOrder": sortDirection,
}
case "type":
params = map[string]string{
"sortString": "destination.type",
"sortOrder": sortDirection,
}
default:
}
params["startIndex"] = from
params["size"] = size
params["searchString"] = search
params["destinationType"] = typ
if clearSearch := strings.TrimSpace(search); clearSearch != "" {
clearSearch = strings.ReplaceAll(clearSearch, " ", "* *")
params["searchString"] = clearSearch
}
reqUrl := fmt.Sprintf("%s/%s/_alerting/destinations", conf.Endpoint, API_PREFIX)
res, err := doRequest(reqUrl, http.MethodGet, params, nil)
if err != nil {
writeError(w, err)
return
}
var resBody = IfaceMap{}
err = decodeJSON(res.Body, &resBody)
if err != nil {
writeError(w, err)
return
}
rawDests := queryValue(resBody, "destinations", []interface{}{})
dests := []IfaceMap{}
if ds, ok := rawDests.([]interface{}); ok {
for _, dest := range ds {
if destination, ok := dest.(map[string]interface{}); ok {
destination["version"] = queryValue(destination, "schema_version", "")
destination["ifSeqNo"] = queryValue(destination, "seq_no", 0)
destination["ifPrimaryTerm"] = queryValue(destination, "primary_term", 0)
dests = append(dests, destination)
}
}
}
writeJSON(w, IfaceMap{
"ok": true,
"destinations": dests,
"totalDestinations": queryValue(resBody, "totalDestinations", 0),
}, http.StatusOK)
}
func CreateDestination(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
}
reqUrl := fmt.Sprintf("%s/%s/_alerting/destinations", conf.Endpoint, API_PREFIX)
res, err := doRequest(reqUrl, http.MethodPost, map[string]string{
"refresh": "wait_for",
}, 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
}
writeJSON(w, IfaceMap{
"ok": true,
"resp": resBody,
}, http.StatusOK)
}
func UpdateDestination(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
}
destinationId := ps.ByName("destinationId")
var (
ifSeqNo = getQueryParam(req, "ifSeqNo")
ifPrimaryTerm = getQueryParam(req, "ifPrimaryTerm")
)
//PUT /_opendistro/_alerting/destinations/2g3CsHsB3EDgQAwRGzgS?if_seq_no=15&if_primary_term=2&refresh=wait_for
reqUrl := fmt.Sprintf("%s/%s/_alerting/destinations/%s", conf.Endpoint, API_PREFIX, destinationId)
res, err := doRequest(reqUrl, http.MethodPut, map[string]string{
"refresh": "wait_for",
"if_seq_no": ifSeqNo,
"if_primary_term": ifPrimaryTerm,
}, 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
}
//TODO error handle: check whether resBody has contains field error
writeJSON(w, IfaceMap{
"ok": true,
"version": queryValue(resBody, "_version", ""),
"id": queryValue(resBody, "_id", ""),
}, http.StatusOK)
}
func DeleteDestination(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
}
destinationId := ps.ByName("destinationId")
reqUrl := fmt.Sprintf("%s/%s/_alerting/destinations/%s", conf.Endpoint, API_PREFIX, destinationId)
res, err := doRequest(reqUrl, http.MethodDelete, nil, nil)
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
}
//TODO error handle: check whether resBody has contains field error
resultIfce := queryValue(resBody, "result", "")
var isOk = false
if result, ok := resultIfce.(string); ok && result == "deleted" {
isOk = true
}
writeJSON(w, IfaceMap{
"ok": isOk,
}, http.StatusOK)
}
func CreateEmailAccount(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
}
reqUrl := fmt.Sprintf("%s/%s/_alerting/destinations/email_accounts", conf.Endpoint, API_PREFIX)
res, err := doRequest(reqUrl, http.MethodPost, map[string]string{
"refresh": "wait_for",
}, 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
}
writeJSON(w, IfaceMap{
"ok": true,
"resp": resBody,
}, http.StatusOK)
}
func UpdateEmailAccount(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("emailAccountId")
var (
ifSeqNo = getQueryParam(req, "ifSeqNo")
ifPrimaryTerm = getQueryParam(req, "ifPrimaryTerm")
)
reqUrl := fmt.Sprintf("%s/%s/_alerting/destinations/email_accounts/%s", conf.Endpoint, API_PREFIX, emailAccountId)
res, err := doRequest(reqUrl, http.MethodPut, map[string]string{
"refresh": "wait_for",
"if_seq_no": ifSeqNo,
"if_primary_term": ifPrimaryTerm,
}, 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
}
//TODO error handle: check whether resBody has contains field error
writeJSON(w, IfaceMap{
"ok": true,
"id": queryValue(resBody, "_id", ""),
}, http.StatusOK)
}
func DeleteEmailAccount(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("emailAccountId")
reqUrl := fmt.Sprintf("%s/%s/_alerting/destinations/email_accounts/%s", conf.Endpoint, API_PREFIX, emailAccountId)
res, err := doRequest(reqUrl, http.MethodDelete, nil, nil)
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
}
//TODO error handle: check whether resBody has contains field error
resultIfce := queryValue(resBody, "result", "")
var isOk = false
if result, ok := resultIfce.(string); ok && result == "deleted" {
isOk = true
}
writeJSON(w, IfaceMap{
"ok": isOk,
}, http.StatusOK)
}
func GetEmailAccounts(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
}
var (
from = getQueryParam(req, "from", "0")
size = getQueryParam(req, "size", "20")
search = getQueryParam(req, "search", "")
sortDirection = getQueryParam(req, "sortDirection", "desc")
sortField = getQueryParam(req, "sortField", "name")
)
must := IfaceMap{
"match_all": IfaceMap{},
}
if clearSearch := strings.TrimSpace(search); clearSearch != "" {
clearSearch = strings.ReplaceAll(clearSearch, " ", "* *")
must = IfaceMap{
"query_string": IfaceMap{
"default_field": "email_account.name",
"default_operator": "AND",
"query": fmt.Sprintf(`*%s*`, clearSearch),
},
}
}
sortQueryMap := IfaceMap{ "name": IfaceMap{ "email_account.name.keyword": sortDirection } }
var sort interface{}
if sortQuery, ok := sortQueryMap[sortField]; ok {
sort = sortQuery
}
reqBody := IfaceMap{
"from": from,
"size": size,
"sort": sort,
"query": IfaceMap{
"bool": IfaceMap{
"must": must,
},
},
}
reqUrl := fmt.Sprintf("%s/%s/_alerting/destinations/email_accounts/_search", conf.Endpoint, API_PREFIX)
res, err := doRequest(reqUrl, http.MethodPost, nil, reqBody)
//TODO to handle api error in doRequest function
if err != nil {
writeError(w, err)
return
}
var resBody = IfaceMap{}
err = decodeJSON(res.Body, &resBody)
if err != nil {
writeError(w, err)
return
}
totalEmailAccounts := queryValue(resBody, "hits.total.value", 0)
rawHits := queryValue(resBody, "hits.hits", []interface{}{})
emailAccounts := []IfaceMap{}
if rh, ok := rawHits.([]interface{}); ok {
for _, hit := range rh {
if emailAccount, ok := hit.(map[string]interface{}); ok {
newItem := IfaceMap{}
newItem["id"] = queryValue(emailAccount, "_id", "")
source := queryValue(emailAccount, "_source", nil)
if ms, ok := source.(map[string]interface{}); ok {
assignTo(newItem, ms)
}
newItem["ifSeqNo"] = queryValue(emailAccount, "_seq_no", 0)
newItem["ifPrimaryTerm"] = queryValue(emailAccount, "_primary_term", 0)
emailAccounts = append(emailAccounts, newItem)
}
}
}
writeJSON(w, IfaceMap{
"ok": true,
"emailAccounts": emailAccounts,
"totalEmailAccounts": totalEmailAccounts,
}, http.StatusOK)
}
func GetEmailAccount(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("emailAccountId")
reqUrl := fmt.Sprintf("%s/%s/_alerting/destinations/email_accounts/%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
}
//TODO error handle: check whether resBody has contains field error
writeJSON(w, IfaceMap{
"ok": true,
"resp": queryValue(resBody, "email_account", nil),
"ifSeqNo": queryValue(resBody, "_seq_no", 0),
"ifPrimaryTerm": queryValue(resBody, "_primary_term", 0),
}, http.StatusOK)
}
// --- email group
func CreateEmailGroup(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
}
reqUrl := fmt.Sprintf("%s/%s/_alerting/destinations/email_groups", conf.Endpoint, API_PREFIX)
res, err := doRequest(reqUrl, http.MethodPost, map[string]string{
"refresh": "wait_for",
}, 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
}
writeJSON(w, IfaceMap{
"ok": true,
"resp": resBody,
}, http.StatusOK)
}
func UpdateEmailGroup(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
}
emailGroupId := ps.ByName("emailGroupId")
var (
ifSeqNo = getQueryParam(req, "ifSeqNo")
ifPrimaryTerm = getQueryParam(req, "ifPrimaryTerm")
)
reqUrl := fmt.Sprintf("%s/%s/_alerting/destinations/email_groups/%s", conf.Endpoint, API_PREFIX, emailGroupId)
res, err := doRequest(reqUrl, http.MethodPut, map[string]string{
"refresh": "wait_for",
"if_seq_no": ifSeqNo,
"if_primary_term": ifPrimaryTerm,
}, 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
}
//TODO error handle: check whether resBody has contains field error
writeJSON(w, IfaceMap{
"ok": true,
"id": queryValue(resBody, "_id", ""),
}, http.StatusOK)
}
func DeleteEmailGroup(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
}
emailGroupId := ps.ByName("emailGroupId")
reqUrl := fmt.Sprintf("%s/%s/_alerting/destinations/email_groups/%s", conf.Endpoint, API_PREFIX, emailGroupId)
res, err := doRequest(reqUrl, http.MethodDelete, nil, nil)
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
}
//TODO error handle: check whether resBody has contains field error
resultIfce := queryValue(resBody, "result", "")
var isOk = false
if result, ok := resultIfce.(string); ok && result == "deleted" {
isOk = true
}
writeJSON(w, IfaceMap{
"ok": isOk,
}, http.StatusOK)
}
func GetEmailGroups(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
}
var (
from = getQueryParam(req, "from", "0")
size = getQueryParam(req, "size", "20")
search = getQueryParam(req, "search", "")
sortDirection = getQueryParam(req, "sortDirection", "desc")
sortField = getQueryParam(req, "sortField", "name")
)
must := IfaceMap{
"match_all": IfaceMap{},
}
if clearSearch := strings.TrimSpace(search); clearSearch != "" {
clearSearch = strings.ReplaceAll(clearSearch, " ", "* *")
must = IfaceMap{
"query_string": IfaceMap{
"default_field": "email_group.name",
"default_operator": "AND",
"query": fmt.Sprintf(`*%s*`, clearSearch),
},
}
}
sortQueryMap := IfaceMap{ "name": IfaceMap{ "email_group.name.keyword": sortDirection } }
var sort interface{}
if sortQuery, ok := sortQueryMap[sortField]; ok {
sort = sortQuery
}
reqBody := IfaceMap{
"from": from,
"size": size,
"sort": sort,
"query": IfaceMap{
"bool": IfaceMap{
"must": must,
},
},
}
reqUrl := fmt.Sprintf("%s/%s/_alerting/destinations/email_groups/_search", conf.Endpoint, API_PREFIX)
res, err := doRequest(reqUrl, http.MethodPost, nil, reqBody)
//TODO to handle api error in doRequest function
if err != nil {
writeError(w, err)
return
}
var resBody = IfaceMap{}
err = decodeJSON(res.Body, &resBody)
if err != nil {
writeError(w, err)
return
}
totalEmailGroups := queryValue(resBody, "hits.total.value", 0)
rawHits := queryValue(resBody, "hits.hits", []interface{}{})
emailGroups := []IfaceMap{}
if rh, ok := rawHits.([]interface{}); ok {
for _, hit := range rh {
if emailGroup, ok := hit.(map[string]interface{}); ok {
newItem := IfaceMap{}
newItem["id"] = queryValue(emailGroup, "_id", "")
source := queryValue(emailGroup, "_source", nil)
if ms, ok := source.(map[string]interface{}); ok {
assignTo(newItem, ms)
}
newItem["ifSeqNo"] = queryValue(emailGroup, "seq_no", 0)
newItem["ifPrimaryTerm"] = queryValue(emailGroup, "primary_term", 0)
emailGroups = append(emailGroups, newItem)
}
}
}
writeJSON(w, IfaceMap{
"ok": true,
"emailGroups": emailGroups,
"totalEmailGroups": totalEmailGroups,
}, http.StatusOK)
}

View File

@ -7,6 +7,7 @@ import (
"infini.sh/framework/core/elastic"
"net/http"
"runtime/debug"
"strings"
)
type SearchBody struct {
@ -28,7 +29,7 @@ func Search(w http.ResponseWriter, req *http.Request, ps httprouter.Params){
writeError(w, err)
return
}
reqUrl := conf.Endpoint +"/_opendistro/_alerting/monitors/_search"
reqUrl := fmt.Sprintf("%s/%s/_alerting/monitors/_search", conf.Endpoint, API_PREFIX)
params := map[string]string{
"index": body.Index,
}
@ -47,10 +48,8 @@ func Search(w http.ResponseWriter, req *http.Request, ps httprouter.Params){
}
writeJSON(w, IfaceMap{
"body": IfaceMap{
"ok": true,
"resp": resBody,
},
"ok": true,
"resp": resBody,
}, http.StatusOK)
}
@ -90,10 +89,8 @@ func GetIndices(w http.ResponseWriter, req *http.Request, ps httprouter.Params)
}
writeJSON(w, IfaceMap{
"body": IfaceMap{
"ok": true,
"resp": resBody,
},
"ok": true,
"resp": resBody,
}, http.StatusOK)
}
@ -137,10 +134,8 @@ func GetAliases(w http.ResponseWriter, req *http.Request, ps httprouter.Params)
}
writeJSON(w, IfaceMap{
"body": IfaceMap{
"ok": true,
"resp": resBody,
},
"ok": true,
"resp": resBody,
}, http.StatusOK)
}
@ -153,14 +148,14 @@ func GetMappings(w http.ResponseWriter, req *http.Request, ps httprouter.Params)
}
var body = struct{
Index string `json:"index"`
Index []string `json:"index"`
}{}
err := decodeJSON(req.Body, &body)
if err != nil {
writeError(w, err)
return
}
reqUrl := fmt.Sprintf("%s/%s/_mapping", conf.Endpoint, body.Index)
reqUrl := fmt.Sprintf("%s/%s/_mapping", conf.Endpoint, strings.Join(body.Index, ","))
res, err := doRequest(reqUrl, http.MethodGet, nil, nil)
if err != nil {
writeError(w, err)
@ -175,10 +170,8 @@ func GetMappings(w http.ResponseWriter, req *http.Request, ps httprouter.Params)
}
writeJSON(w, IfaceMap{
"body": IfaceMap{
"ok": true,
"resp": resBody,
},
"ok": true,
"resp": resBody,
}, http.StatusOK)
}
@ -209,9 +202,38 @@ func GetPlugins(w http.ResponseWriter, req *http.Request, ps httprouter.Params)
}
writeJSON(w, IfaceMap{
"body": IfaceMap{
"ok": true,
"resp": resBody,
},
"ok": true,
"resp": resBody,
}, http.StatusOK)
}
func GetSettings(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
}
// /_cluster/settings?include_defaults=true
reqUrl := fmt.Sprintf("%s/_cluster/settings", conf.Endpoint)
res, err := doRequest(reqUrl, http.MethodGet, map[string]string{
"include_defaults": "true",
}, nil)
if err != nil {
writeError(w, err)
return
}
defer res.Body.Close()
var resBody = IfaceMap{}
err = decodeJSON(res.Body, &resBody)
if err != nil {
writeError(w, err)
return
}
writeJSON(w, IfaceMap{
"ok": true,
"resp": resBody,
}, http.StatusOK)
}

View File

@ -6,10 +6,18 @@ import (
httprouter "infini.sh/framework/core/api/router"
"infini.sh/framework/core/elastic"
"net/http"
"runtime/debug"
"strconv"
"strings"
"time"
)
func GetMonitor(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
defer func() {
if err := recover(); err != nil {
debug.PrintStack()
}
}()
id := ps.ByName("id")
conf := elastic.GetConfig(id)
if conf == nil {
@ -19,7 +27,7 @@ func GetMonitor(w http.ResponseWriter, req *http.Request, ps httprouter.Params)
mid := ps.ByName("monitorID")
// /_opendistro/_alerting/monitors/uiSjqXsBHT9Hsiy5Dq6g
reqUrl := fmt.Sprintf("%s/_opendistro/_alerting/monitors/%s", conf.Endpoint, mid)
reqUrl := fmt.Sprintf("%s/%s/_alerting/monitors/%s", conf.Endpoint, API_PREFIX, mid)
res, err := doRequest(reqUrl, http.MethodGet, nil, nil)
if err != nil {
writeError(w, err)
@ -35,29 +43,26 @@ func GetMonitor(w http.ResponseWriter, req *http.Request, ps httprouter.Params)
if _, ok := resBody["monitor"]; !ok {
writeJSON(w, IfaceMap{
"body": IfaceMap{
"ok": false,
},
}, http.StatusOK)
return
}
///_opendistro/_alerting/monitors/_search?index=.opendistro-alerting-alert*
queryDSL := ` {
"size": 0,
"query"": {
"query": {
"bool": {
"must": {
"term"": {
"monitor_id": "%s",
},
},
},
"term": {
"monitor_id": "%s"
}
}
}
},
"aggs": {
"active_count": {
"terms": {
"field": "state",
"field": "state"
}
},
"24_hour_count": {
@ -69,9 +74,9 @@ func GetMonitor(w http.ResponseWriter, req *http.Request, ps httprouter.Params)
}
}`
queryDSL = fmt.Sprintf(queryDSL, id)
reqUrl = fmt.Sprintf("%s/_opendistro/_alerting/monitors/_search", conf.Endpoint)
reqUrl = fmt.Sprintf("%s/%s/_alerting/monitors/_search", conf.Endpoint, API_PREFIX)
res, err = doRequest(reqUrl, http.MethodPost, map[string]string{
"index": ".opendistro-alerting-alert*",
"index": INDEX_ALL_ALERTS,
}, queryDSL)
if err != nil {
@ -85,34 +90,400 @@ func GetMonitor(w http.ResponseWriter, req *http.Request, ps httprouter.Params)
writeError(w, err)
return
}
//dayCount := queryValue(searchResBody, "aggregations.24_hour_count.buckets.0.doc_count", 0)
//activeBuckets := queryValue(searchResBody, "aggregations.active_count.buckets",[]interface{}{})
dayCount := queryValue(searchResBody, "aggregations.24_hour_count.buckets.0.doc_count", 0)
activeBuckets := queryValue(searchResBody, "aggregations.active_count.buckets",[]interface{}{})
activeCount := 0
if ab, ok := activeBuckets.([]IfaceMap); ok {
for _, curr := range ab {
if curr["key"].(string) == "ACTIVE" {
activeCount = int(curr["doc_count"].(float64))
}
}
}
writeJSON(w, IfaceMap{
"body": IfaceMap{
"ok": true,
"resp": resBody,
},
"ok": true,
"resp": resBody["monitor"],
"activeCount": activeCount,
"dayCount": dayCount,
"version": queryValue(resBody, "_version", nil),
"ifSeqNo": queryValue(resBody, "_seq_no", nil),
"ifPrimaryTerm": queryValue(resBody, "_primary_term", nil),
}, http.StatusOK)
}
func queryValue(obj map[string]interface{}, key string, defaultValue interface{}) interface{} {
if key == "" {
return obj
}
idx := strings.Index(key, ".")
if idx == -1 {
if v, ok := obj[key]; ok {
return v
func GetMonitors(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
defer func() {
if err := recover(); err != nil {
debug.PrintStack()
}
return defaultValue
}()
id := ps.ByName("id")
conf := elastic.GetConfig(id)
if conf == nil {
writeError(w, errors.New("cluster not found"))
return
}
var (
from = getQueryParam(req, "from")
size = getQueryParam(req, "size")
search = getQueryParam(req, "search")
sortDirection = getQueryParam(req, "sortDirection")
sortField = getQueryParam(req, "sortField")
state = getQueryParam(req, "state")
must = IfaceMap{ "match_all": IfaceMap{} }
)
if clearSearch := strings.TrimSpace(search); clearSearch != "" {
clearSearch = strings.ReplaceAll(clearSearch, " ", "* *")
must = IfaceMap{
"query_string": IfaceMap{
"default_field": "monitor.name",
"default_operator": "AND",
"query": fmt.Sprintf("*%s*", clearSearch),
},
}
}
var filter = []IfaceMap{
IfaceMap{ "term": IfaceMap{ "monitor.type": "monitor" }},
}
if state != "all" {
filter = append(filter, IfaceMap{
"term": IfaceMap{ "monitor.enabled": state == "enabled" },
})
}
var monitorSorts = IfaceMap{ "name": "monitor.name.keyword" }
sortPageData := IfaceMap{
"size": 1000,
"from": 0,
}
var (
intSize int
intFrom int
)
if msort, ok := monitorSorts[sortField]; ok {
sortPageData["sort"] = []IfaceMap{
{ msort.(string): sortDirection },
}
intSize, _ = strconv.Atoi(size)
if intSize < 0 {
intSize = 1000
}
sortPageData["size"] = intSize
intFrom, _ = strconv.Atoi(from)
if intFrom < 0 {
intFrom = 0
}
sortPageData["from"] = intFrom
}
var params = IfaceMap{
"seq_no_primary_term": true,
"version": true,
"query": IfaceMap{
"bool": IfaceMap{
"filter": filter,
"must": must,
},
},
}
assignTo(params, sortPageData)
reqUrl := fmt.Sprintf("%s/%s/_alerting/monitors/_search", conf.Endpoint, API_PREFIX )
res, err := doRequest(reqUrl, http.MethodPost, nil, params)
if err != nil {
writeError(w, err)
return
}
ckey := key[0:idx]
var resBody = IfaceMap{}
err = decodeJSON(res.Body, &resBody)
if err != nil {
writeError(w, err)
return
}
if v, ok := obj[ckey]; ok {
if vmap, ok := v.(map[string]interface{}); ok {
return queryValue(vmap, key[idx+1:], defaultValue)
totalMonitors := queryValue(resBody, "hits.total.value", 0)
monitorMap:= map[string]IfaceMap{}
var hits = queryValue(resBody, "hits.hits", []IfaceMap{})
monitorIDs := []interface{}{}
if hitsArr, ok := hits.([]interface{}); ok {
for _, hitIface := range hitsArr {
if hit, ok := hitIface.(map[string]interface{}); ok {
id := queryValue(hit, "_id", "")
monitorIDs = append(monitorIDs, id)
monitor := queryValue(hit, "_source", IfaceMap{}).(map[string]interface{})
monitorMap[id.(string)] = IfaceMap{
"id": id,
"version": queryValue(hit, "_version", ""),
"ifSeqNo": queryValue(hit, "_seq_no", false),
"ifPrimaryTerm": queryValue(hit, "_primary_term", false),
"name": queryValue(monitor, "name", ""),
"enabled": queryValue(monitor, "enabled", false),
"monitor": monitor,
}
}
}
}
return defaultValue
}
aggsOrderData := IfaceMap{}
aggsSorts := IfaceMap{
"active": "active",
"acknowledged": "acknowledged",
"errors": "errors",
"ignored": "ignored",
"lastNotificationTime": "last_notification_time",
}
if sortF, ok := aggsSorts[sortField]; ok {
aggsOrderData["order"] = IfaceMap{ sortF.(string): sortDirection }
}
var queryParams = map[string]string{
"index": INDEX_ALL_ALERTS,
}
var termsMap = IfaceMap{
"field": "monitor_id",
"size": intFrom + intSize,
}
assignTo(termsMap, aggsOrderData)
var aggsParams = IfaceMap{
"size": 0,
"query": IfaceMap{ "terms": IfaceMap{ "monitor_id": monitorIDs } },
"aggregations": IfaceMap{
"uniq_monitor_ids": IfaceMap{
"terms": termsMap,
"aggregations": IfaceMap{
"active": IfaceMap{ "filter": IfaceMap{ "term": IfaceMap{ "state": "ACTIVE" } } },
"acknowledged": IfaceMap{ "filter": IfaceMap{ "term": IfaceMap{ "state": "ACKNOWLEDGED" } } },
"errors": IfaceMap{ "filter": IfaceMap{ "term": IfaceMap{ "state": "ERROR" } } },
"ignored": IfaceMap{
"filter": IfaceMap{
"bool": IfaceMap{
"filter": IfaceMap{ "term": IfaceMap{ "state": "COMPLETED" } },
"must_not": IfaceMap{ "exists": IfaceMap{ "field": "acknowledged_time" } },
},
},
},
"last_notification_time": IfaceMap{ "max": IfaceMap{ "field": "last_notification_time" } },
"latest_alert": IfaceMap{
"top_hits": IfaceMap{
"size": 1,
"sort": []IfaceMap{ { "start_time": IfaceMap{ "order": "desc" }} },
"_source": IfaceMap{
"includes": []string{"last_notification_time", "trigger_name"},
},
},
},
},
},
},
}
reqUrl = fmt.Sprintf("%s/%s/_alerting/monitors/_search", conf.Endpoint, API_PREFIX)
searchRes, err := doRequest(reqUrl, http.MethodPost, queryParams, aggsParams)
if err != nil {
writeError(w, err)
return
}
var searchResBody = IfaceMap{}
err = decodeJSON(searchRes.Body, &searchResBody)
if err != nil {
writeError(w, err)
return
}
buckets := queryValue(searchResBody, "aggregations.uniq_monitor_ids.buckets",[]IfaceMap{})
usedMonitors := []IfaceMap{}
if bks, ok := buckets.([]interface{}); ok {
for _, bk := range bks {
if bk, ok := bk.(map[string]interface{}); ok {
id := queryValue(bk, "key", "")
monitor := monitorMap[id.(string)]
monitor["lastNotificationTime"] = queryValue(bk, "last_notification_time.value", "")
monitor["ignored"] = queryValue(bk, "ignored.doc_count", 0)
alertHits := queryValue(bk, "latest_alert.hits.hits", nil)
var latestAlert interface{}
if hits, ok := alertHits.([]interface{}); ok && len(hits) > 0 {
if hitMap, ok := hits[0].(map[string]interface{}); ok {
latestAlert = queryValue(hitMap, "_source.trigger_name", nil)
}
}
monitor["latestAlert"] = latestAlert
monitor["active"] = queryValue(bk, "active.doc_count", 0)
monitor["errors"] = queryValue(bk, "errors.doc_count", 0)
monitor["currentTime"] = time.Now().UnixNano() / 1e6
usedMonitors = append(usedMonitors, monitor)
delete(monitorMap, id.(string))
}
}
}
unusedMonitors := []IfaceMap{}
for _, m := range monitorMap {
assignTo(m, IfaceMap{
"lastNotificationTime": nil,
"ignored": 0,
"active": 0,
"acknowledged": 0,
"errors": 0,
"latestAlert": "--",
"currentTime": time.Now().UnixNano()/1e6,
})
unusedMonitors = append(unusedMonitors,m)
}
results := append(usedMonitors, unusedMonitors...)
if _, ok := monitorSorts[sortField]; !ok {
results = results[intFrom: intFrom + intSize]
}
writeJSON(w, IfaceMap{
"ok": true,
"monitors": results,
"totalMonitors": totalMonitors,
}, http.StatusOK)
}
func CreateMonitor(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
}
reqUrl := fmt.Sprintf("%s/%s/_alerting/monitors", conf.Endpoint, API_PREFIX)
res, err := doRequest(reqUrl, http.MethodPost, map[string]string{
"refresh": "wait_for",
}, 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
}
writeJSON(w, IfaceMap{
"ok": true,
"resp": resBody,
}, http.StatusOK)
}
func DeleteMonitor(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", conf.Endpoint, API_PREFIX, monitorId)
res, err := doRequest(reqUrl, http.MethodDelete, nil, nil)
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
}
//TODO error handle: check whether resBody has contains field error
resultIfce := queryValue(resBody, "result", "")
var isOk = false
if result, ok := resultIfce.(string); ok && result == "deleted" {
isOk = true
}
writeJSON(w, IfaceMap{
"ok": isOk,
}, http.StatusOK)
}
func UpdateMonitor(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")
var (
ifSeqNo = getQueryParam(req, "ifSeqNo")
ifPrimaryTerm = getQueryParam(req, "ifPrimaryTerm")
)
reqUrl := fmt.Sprintf("%s/%s/_alerting/monitors/%s", conf.Endpoint, API_PREFIX, monitorId)
res, err := doRequest(reqUrl, http.MethodPut, map[string]string{
"refresh": "wait_for",
"if_seq_no": ifSeqNo,
"if_primary_term": ifPrimaryTerm,
}, 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
}
//TODO error handle: check whether resBody has contains field error
writeJSON(w, IfaceMap{
"ok": true,
"version": queryValue(resBody, "_version", ""),
"id": queryValue(resBody, "_id", ""),
}, http.StatusOK)
}
func ExecuteMonitor(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
}
var (
dryrun = getQueryParam(req, "dryrun", "true")
)
reqUrl := fmt.Sprintf("%s/%s/_alerting/monitors/_execute", conf.Endpoint, API_PREFIX)
res, err := doRequest(reqUrl, http.MethodPost, map[string]string{
"dryrun": dryrun,
}, 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
}
//TODO error handle: check whether resBody has contains field error
writeJSON(w, IfaceMap{
"ok": true,
"resp": resBody,
}, http.StatusOK)
}

View File

@ -72,7 +72,7 @@
"rxjs": "^7.2.0",
"sass-loader": "^8.0.2",
"use-query-params": "^1.2.3",
"uuidv4": "^6.2.12"
"uuid-v4": "^0.1.0"
},
"devDependencies": {
"antd-pro-merge-less": "^0.1.0",

View File

@ -130,7 +130,7 @@ export async function getBreadcrumb(route, routeState, httpClient) {
// TODO::Everything else is considered as monitor, we should break this.
let monitorName = base;
try {
const response = await httpClient.get(`../api/alerting/monitors/${base}`);
const response = await httpClient.get(`/alerting/monitors/${base}`);
if (response.ok) {
monitorName = response.resp.name;
}

View File

@ -12,7 +12,7 @@ const httpClient = new Fetch({
get: () => '',
prepend: (url) => url,
remove: (url) => url,
serverBasePath: '/elasticsearch',
serverBasePath: '',
}
});
const notifications = {
@ -23,6 +23,11 @@ const notifications = {
description: text,
duration: toastLifeTimeMs/1000,
})
},
addSuccess: (message) => {
notification.success({
description: message,
})
}
}
}
@ -32,8 +37,8 @@ const AlertingUI = (props)=>{
return null;
}
useMemo(()=>{
httpClient.getServerBasePath = ()=>{
return '/api/elasticsearch/'+ props.selectedCluster.id;
httpClient.params.basePath.prepend = (url)=>{
return '/elasticsearch/'+ props.selectedCluster.id + url;
}
}, [props.selectedCluster]);
const isDarkMode = false;

View File

@ -56,7 +56,7 @@ class AnomalyDetectorData extends React.Component {
preview: this.props.preview,
};
try {
const response = await httpClient.get(`../api/alerting/detectors/${detectorId}/results`, {
const response = await httpClient.get(`/alerting/detectors/${detectorId}/results`, {
query: requestParams,
});
if (response.ok) {

View File

@ -38,7 +38,7 @@ class AnomalyDetectors extends React.Component {
async searchDetectors() {
const { http: httpClient, notifications } = this.context;
try {
const response = await httpClient.post('../api/alerting/detectors/_search');
const response = await httpClient.post('/alerting/detectors/_search');
if (response.ok) {
const detectorOptions = response.detectors
.filter((detector) => detector.detectionDateRange === undefined)

View File

@ -77,7 +77,7 @@ export default class CreateMonitor extends Component {
async onCreate(monitor, { setSubmitting, setErrors }) {
const { httpClient, notifications } = this.props;
try {
const resp = await httpClient.post('../api/alerting/monitors', {
const resp = await httpClient.post('/alerting/monitors', {
body: JSON.stringify(monitor),
});
setSubmitting(false);

View File

@ -249,7 +249,7 @@ describe('CreateMonitor', () => {
);
wrapper.instance().onCreate(monitor, formikBag);
expect(httpClientMock.post).toHaveBeenCalledTimes(1);
expect(httpClientMock.post).toHaveBeenCalledWith('../api/alerting/monitors', {
expect(httpClientMock.post).toHaveBeenCalledWith('/alerting/monitors', {
body: JSON.stringify(monitor),
});
});

View File

@ -127,7 +127,7 @@ class DefineMonitor extends Component {
async getPlugins() {
const { httpClient } = this.props;
try {
const pluginsResponse = await httpClient.get('../api/alerting/_plugins');
const pluginsResponse = await httpClient.get('/alerting/_plugins');
if (pluginsResponse.ok) {
this.setState({ plugins: pluginsResponse.resp.map((plugin) => plugin.component) });
} else {
@ -187,7 +187,7 @@ class DefineMonitor extends Component {
_.set(monitor, 'name', 'TEMP_MONITOR');
_.set(monitor, 'triggers', []);
_.set(monitor, 'inputs[0].search', searchRequest);
return httpClient.post('../api/alerting/monitors/_execute', {
return httpClient.post('/alerting/monitors/_execute', {
body: JSON.stringify(monitor),
});
});
@ -232,7 +232,7 @@ class DefineMonitor extends Component {
}
try {
const response = await this.props.httpClient.post('../api/alerting/_mappings', {
const response = await this.props.httpClient.post('/alerting/_mappings', {
body: JSON.stringify({ index }),
});
if (response.ok) {

View File

@ -121,7 +121,7 @@ class MonitorIndex extends React.Component {
return [];
}
try {
const response = await this.props.httpClient.post('../api/alerting/_indices', {
const response = await this.props.httpClient.post('/alerting/_indices', {
body: JSON.stringify({ index }),
});
if (response.ok) {
@ -151,7 +151,7 @@ class MonitorIndex extends React.Component {
}
try {
const response = await this.props.httpClient.post('../api/alerting/_aliases', {
const response = await this.props.httpClient.post('/alerting/_aliases', {
body: JSON.stringify({ alias }),
});
if (response.ok) {

View File

@ -62,7 +62,7 @@ class ConfigureActions extends React.Component {
return destination.type;
};
try {
const response = await httpClient.get('../api/alerting/destinations', {
const response = await httpClient.get('/alerting/destinations', {
query: { search: searchText, size: MAX_QUERY_RESULT_SIZE },
});
if (response.ok) {
@ -97,7 +97,7 @@ class ConfigureActions extends React.Component {
const condition = { script: { lang: 'painless', source: 'return true' } };
const testMonitor = { ...monitor, triggers: [{ ...trigger, actions: [action], condition }] };
try {
const response = await httpClient.post('../api/alerting/monitors/_execute', {
const response = await httpClient.post('/alerting/monitors/_execute', {
query: { dryrun: false },
body: JSON.stringify(testMonitor),
});

View File

@ -128,7 +128,7 @@ export default class CreateTrigger extends Component {
_.set(monitorToExecute, 'inputs[0].search', searchRequest);
}
httpClient
.post('../api/alerting/monitors/_execute', { body: JSON.stringify(monitorToExecute) })
.post('/alerting/monitors/_execute', { body: JSON.stringify(monitorToExecute) })
.then((resp) => {
if (resp.ok) {
this.setState({ executeResponse: resp.resp });

View File

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

View File

@ -52,7 +52,7 @@ export const columns = [
truncateText: true,
textOnly: true,
render: (name, alert) => (
<EuiLink href={`${PLUGIN_NAME}#/monitors/${alert.monitor_id}`}>{name}</EuiLink>
<EuiLink href={`#/${PLUGIN_NAME}/monitors/${alert.monitor_id}`}>{name}</EuiLink>
),
},
{

View File

@ -89,7 +89,7 @@ class CreateDestination extends React.Component {
getDestination = async (destinationId) => {
const { httpClient, history, notifications } = this.props;
try {
const resp = await httpClient.get(`../api/alerting/destinations/${destinationId}`);
const resp = await httpClient.get(`/alerting/destinations/${destinationId}`);
if (resp.ok) {
const ifSeqNo = _.get(resp, 'ifSeqNo');
const ifPrimaryTerm = _.get(resp, 'ifPrimaryTerm');
@ -118,7 +118,7 @@ class CreateDestination extends React.Component {
} = this.props;
const { ifSeqNo, ifPrimaryTerm } = this.state;
try {
const resp = await httpClient.put(`../api/alerting/destinations/${destinationId}`, {
const resp = await httpClient.put(`/alerting/destinations/${destinationId}`, {
query: { ifSeqNo, ifPrimaryTerm },
body: JSON.stringify(requestData),
});
@ -139,7 +139,7 @@ class CreateDestination extends React.Component {
handleCreate = async (requestData, { setSubmitting }) => {
const { httpClient, history, notifications } = this.props;
try {
const resp = await httpClient.post('../api/alerting/destinations', {
const resp = await httpClient.post('/alerting/destinations', {
body: JSON.stringify(requestData),
});
setSubmitting(false);

View File

@ -17,7 +17,7 @@ import { MAX_QUERY_RESULT_SIZE } from '../../../../../../utils/constants';
export default async function getEmailGroups(httpClient, searchText = '') {
try {
const response = await httpClient.get('../api/alerting/destinations/email_groups', {
const response = await httpClient.get('/alerting/destinations/email_groups', {
query: { search: searchText, size: MAX_QUERY_RESULT_SIZE },
});
if (response.ok) {

View File

@ -17,7 +17,7 @@ import { MAX_QUERY_RESULT_SIZE } from '../../../../../../utils/constants';
export default async function getSenders(httpClient, searchText = '') {
try {
const response = await httpClient.get('../api/alerting/destinations/email_accounts', {
const response = await httpClient.get('/alerting/destinations/email_accounts', {
query: { search: searchText, size: MAX_QUERY_RESULT_SIZE },
});
if (response.ok) {

View File

@ -119,7 +119,7 @@ export default class ManageEmailGroups extends React.Component {
emails: emailGroup.emails.map((email) => ({ email: email.label })),
};
try {
const response = await httpClient.post(`../api/alerting/destinations/email_groups`, {
const response = await httpClient.post(`/alerting/destinations/email_groups`, {
body: JSON.stringify(body),
});
if (!response.ok) {
@ -147,7 +147,7 @@ export default class ManageEmailGroups extends React.Component {
emails: updatedEmailGroup.emails.map((email) => ({ email: email.label })),
};
try {
const response = await httpClient.put(`../api/alerting/destinations/email_groups/${id}`, {
const response = await httpClient.put(`/alerting/email_groups/${id}`, {
query: { ifSeqNo, ifPrimaryTerm },
body: JSON.stringify(body),
});
@ -172,7 +172,7 @@ export default class ManageEmailGroups extends React.Component {
const { httpClient, notifications } = this.props;
const { id } = emailGroup;
try {
const response = await httpClient.delete(`../api/alerting/destinations/email_groups/${id}`);
const response = await httpClient.delete(`/alerting/email_groups/${id}`);
if (!response.ok) {
this.setState({ failedEmailGroups: true });
notifications.toasts.addDanger({

View File

@ -107,7 +107,7 @@ export default class ManageSenders extends React.Component {
method: sender.method,
};
try {
const response = await httpClient.post(`../api/alerting/destinations/email_accounts`, {
const response = await httpClient.post(`/alerting/destinations/email_accounts`, {
body: JSON.stringify(body),
});
if (!response.ok) {
@ -138,7 +138,7 @@ export default class ManageSenders extends React.Component {
method: updatedSender.method,
};
try {
const response = await httpClient.put(`../api/alerting/destinations/email_accounts/${id}`, {
const response = await httpClient.put(`/alerting/email_accounts/${id}`, {
query: { ifSeqNo, ifPrimaryTerm },
body: JSON.stringify(body),
});
@ -163,7 +163,7 @@ export default class ManageSenders extends React.Component {
const { httpClient, notifications } = this.props;
const { id } = sender;
try {
const response = await httpClient.delete(`../api/alerting/destinations/email_accounts/${id}`);
const response = await httpClient.delete(`/alerting/email_accounts/${id}`);
if (!response.ok) {
this.setState({ failedSenders: true });
notifications.toasts.addDanger({

View File

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

View File

@ -24,7 +24,7 @@ export const validateDestinationName = (httpClient, destinationToEdit) => async
index: INDEX.SCHEDULED_JOBS,
query: { query: { term: { 'destination.name.keyword': value } } },
};
const response = await httpClient.post('../api/alerting/monitors/_search', {
const response = await httpClient.post('/alerting/monitors/_search', {
body: JSON.stringify(options),
});
if (_.get(response, 'resp.hits.total.value', 0)) {

View File

@ -119,7 +119,7 @@ class DestinationsList extends React.Component {
query: isDeleteAllowedQuery(type, id),
index: INDEX.SCHEDULED_JOBS,
};
const resp = await httpClient.post('../api/alerting/monitors/_search', {
const resp = await httpClient.post('/alerting/monitors/_search', {
body: JSON.stringify(requestBody),
});
const total = _.get(resp, 'resp.hits.total.value');
@ -151,7 +151,7 @@ class DestinationsList extends React.Component {
const { id: destinationId } = this.state.destinationToDelete;
const { httpClient, notifications } = this.props;
try {
const resp = await httpClient.delete(`../api/alerting/destinations/${destinationId}`);
const resp = await httpClient.delete(`/alerting/destinations/${destinationId}`);
if (resp.ok) {
await this.getDestinations();
} else {
@ -214,7 +214,7 @@ class DestinationsList extends React.Component {
// search: queryParms,
// });
try {
const resp = await httpClient.get('../api/alerting/destinations', {
const resp = await httpClient.get('/alerting/destinations', {
query: { from, ...params },
});
if (resp.ok) {

View File

@ -19,7 +19,7 @@ import { backendErrorNotification } from '../../../utils/helpers';
export async function getAllowList(httpClient) {
try {
const response = await httpClient.get('../api/alerting/_settings');
const response = await httpClient.get('/alerting/_settings');
if (response.ok) {
// Attempt to resolve the value of allow_list in the order of 'persistent, 'transient' and 'defaults' settings
const { defaults, transient, persistent } = response.resp;

View File

@ -81,7 +81,7 @@ export default class MonitorDetails extends Component {
getDetector = (id) => {
const { httpClient, notifications } = this.props;
httpClient
.get(`../api/alerting/detectors/${id}`)
.get(`/alerting/detectors/${id}`)
.then((resp) => {
const { ok, detector, version: detectorVersion, seqNo, primaryTerm } = resp;
if (ok) {
@ -101,7 +101,7 @@ export default class MonitorDetails extends Component {
getMonitor = (id) => {
const { httpClient } = this.props;
httpClient
.get(`../api/alerting/monitors/${id}`)
.get(`/alerting/monitors/${id}`)
.then((resp) => {
const {
ok,
@ -148,7 +148,7 @@ export default class MonitorDetails extends Component {
const { monitor, ifSeqNo, ifPrimaryTerm } = this.state;
this.setState({ updating: true });
return httpClient
.put(`../api/alerting/monitors/${monitorId}`, {
.put(`/alerting/monitors/${monitorId}`, {
query: { ifSeqNo, ifPrimaryTerm },
body: JSON.stringify({ ...monitor, ...update }),
})

View File

@ -200,7 +200,7 @@ class MonitorHistory extends PureComponent {
),
index: INDEX.ALL_ALERTS,
};
const resp = await httpClient.post('../api/alerting/monitors/_search', {
const resp = await httpClient.post('/alerting/monitors/_search', {
body: JSON.stringify(requestBody),
});
if (resp.ok) {
@ -242,7 +242,7 @@ class MonitorHistory extends PureComponent {
monitorIds: monitorId,
};
const resp = await httpClient.get('../api/alerting/alerts', { query: params });
const resp = await httpClient.get('/alerting/alerts', { query: params });
var alerts;
if (resp.ok) {
alerts = resp.alerts;

View File

@ -15,7 +15,7 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import uuidv4 from 'uuidv4';
import uuid from 'uuid-v4';
import { EuiButton, EuiInMemoryTable } from '@elastic/eui';
import ContentPanel from '../../../../components/ContentPanel';
@ -28,7 +28,7 @@ export default class Triggers extends Component {
this.state = {
field: 'name',
tableKey: uuidv4(),
tableKey: uuid(),
direction: 'asc',
selectedItems: [],
};
@ -45,7 +45,7 @@ export default class Triggers extends Component {
// which EuiInMemoryTable uses which causes items to not be updated correctly.
// Whenever the monitor is updated we'll generate a new key for the table
// which will cause the table component to remount
this.setState({ tableKey: uuidv4() });
this.setState({ tableKey: uuid() });
}
}

View File

@ -144,8 +144,8 @@ export default class Monitors extends Component {
const params = { from, size, search, sortField, sortDirection, state };
const queryParamsString = queryString.stringify(params);
const { httpClient, history } = this.props;
history.replace({ ...this.props.location, search: queryParamsString });
const response = await httpClient.get('../api/alerting/monitors', { query: params });
//history.replace({ ...this.props.location, search: queryParamsString });
const response = await httpClient.get('/alerting/monitors', { query: params });
if (response.ok) {
const { monitors, totalMonitors } = response;
this.setState({ monitors, totalMonitors });
@ -182,7 +182,7 @@ export default class Monitors extends Component {
const { httpClient, notifications } = this.props;
const { id, ifSeqNo, ifPrimaryTerm, monitor } = item;
return httpClient
.put(`../api/alerting/monitors/${id}`, {
.put(`/alerting/monitors/${id}`, {
query: { ifSeqNo, ifPrimaryTerm },
body: JSON.stringify({ ...monitor, ...update }),
})
@ -199,7 +199,7 @@ export default class Monitors extends Component {
const { httpClient, notifications } = this.props;
const { id, version } = item;
return httpClient
.delete(`../api/alerting/monitors/${id}`, { query: { version } })
.delete(`/alerting/monitors/${id}`, { query: { version } })
.then((resp) => {
if (!resp.ok) {
backendErrorNotification(notifications, 'delete', 'monitor', resp.resp);
@ -249,7 +249,7 @@ export default class Monitors extends Component {
const promises = Object.entries(monitorAlerts).map(([monitorId, alerts]) =>
httpClient
.post(`../api/alerting/monitors/${monitorId}/_acknowledge/alerts`, {
.post(`/alerting/monitors/${monitorId}/_acknowledge/alerts`, {
body: JSON.stringify({ alerts }),
})
.then((resp) => {
@ -321,7 +321,7 @@ export default class Monitors extends Component {
const { httpClient, notifications } = this.props;
const response = await httpClient.get('../api/alerting/alerts', { query: params });
const response = await httpClient.get('/alerting/alerts', { query: params });
if (response.ok) {
const { alerts, totalAlerts } = response;

View File

@ -160,7 +160,7 @@ describe('Monitors', () => {
expect(updateMonitor).toHaveBeenCalled();
expect(httpClientMock.put).toHaveBeenCalled();
expect(httpClientMock.put).toHaveBeenCalledWith(`../api/alerting/monitors/random_id`, {
expect(httpClientMock.put).toHaveBeenCalledWith(`/alerting/monitors/random_id`, {
query: { ifSeqNo: 17, ifPrimaryTerm: 20 },
body: JSON.stringify({ ...monitor, name: 'UNIQUE_NAME' }),
});

View File

@ -34,7 +34,7 @@ export const columns = [
truncateText: true,
textOnly: true,
// width: '150px',
render: (name, item) => <EuiLink href={`${PLUGIN_NAME}#/monitors/${item.id}`}>{name}</EuiLink>,
render: (name, item) => <EuiLink href={`#/${PLUGIN_NAME}/monitors/${item.id}`}>{name}</EuiLink>,
},
{
field: 'user',

View File

@ -66,10 +66,10 @@ export const AD_PREVIEW_DAYS = 7;
export const MAX_QUERY_RESULT_SIZE = 200;
export const OPEN_DISTRO_PREFIX = '';
export const OPEN_DISTRO_PREFIX = 'opendistro';
export const PLUGIN_NAME = `alerting`;
export const INDEX_PREFIX = `alerting`;
export const INDEX_PREFIX = `${OPEN_DISTRO_PREFIX}-alerting`;
export const INDEX = {
SCHEDULED_JOBS: `.${INDEX_PREFIX}-config`,
ALERTS: `.${INDEX_PREFIX}-alerts`,

View File

@ -56,7 +56,7 @@ export const validateMonitorName = (httpClient, monitorToEdit) => async (value)
index: INDEX.SCHEDULED_JOBS,
query: { query: { term: { 'monitor.name.keyword': value } } },
};
const response = await httpClient.post('../api/alerting/monitors/_search', {
const response = await httpClient.post('/alerting/monitors/_search', {
body: JSON.stringify(options),
});
if (_.get(response, 'resp.hits.total.value', 0)) {