diff --git a/api/init.go b/api/init.go index 489279ed..16db7fa8 100644 --- a/api/init.go +++ b/api/init.go @@ -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{ diff --git a/service/alerting/alert.go b/service/alerting/alert.go index 5bb55523..966fd58b 100644 --- a/service/alerting/alert.go +++ b/service/alerting/alert.go @@ -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 + } } \ No newline at end of file diff --git a/service/alerting/constants.go b/service/alerting/constants.go new file mode 100644 index 00000000..da9fca8a --- /dev/null +++ b/service/alerting/constants.go @@ -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" \ No newline at end of file diff --git a/service/alerting/destination.go b/service/alerting/destination.go new file mode 100644 index 00000000..815fdc2d --- /dev/null +++ b/service/alerting/destination.go @@ -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) +} + + + diff --git a/service/alerting/elasticsearch.go b/service/alerting/elasticsearch.go index a1e17d1e..f8b023c6 100644 --- a/service/alerting/elasticsearch.go +++ b/service/alerting/elasticsearch.go @@ -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) } \ No newline at end of file diff --git a/service/alerting/monitor.go b/service/alerting/monitor.go index bab92227..929c4801 100644 --- a/service/alerting/monitor.go +++ b/service/alerting/monitor.go @@ -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 -} \ No newline at end of file + + + 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) +} diff --git a/web/package.json b/web/package.json index 95a38560..50df795a 100644 --- a/web/package.json +++ b/web/package.json @@ -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", diff --git a/web/src/pages/Alerting/components/Breadcrumbs/Breadcrumbs.js b/web/src/pages/Alerting/components/Breadcrumbs/Breadcrumbs.js index fddeca60..18805224 100644 --- a/web/src/pages/Alerting/components/Breadcrumbs/Breadcrumbs.js +++ b/web/src/pages/Alerting/components/Breadcrumbs/Breadcrumbs.js @@ -129,7 +129,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; } diff --git a/web/src/pages/Alerting/index.js b/web/src/pages/Alerting/index.js index b424c192..cd975389 100644 --- a/web/src/pages/Alerting/index.js +++ b/web/src/pages/Alerting/index.js @@ -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; diff --git a/web/src/pages/Alerting/pages/CreateMonitor/containers/AnomalyDetectors/AnomalyDetectorData.js b/web/src/pages/Alerting/pages/CreateMonitor/containers/AnomalyDetectors/AnomalyDetectorData.js index 5728f18f..b01a6153 100644 --- a/web/src/pages/Alerting/pages/CreateMonitor/containers/AnomalyDetectors/AnomalyDetectorData.js +++ b/web/src/pages/Alerting/pages/CreateMonitor/containers/AnomalyDetectors/AnomalyDetectorData.js @@ -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) { diff --git a/web/src/pages/Alerting/pages/CreateMonitor/containers/AnomalyDetectors/AnomalyDetectors.js b/web/src/pages/Alerting/pages/CreateMonitor/containers/AnomalyDetectors/AnomalyDetectors.js index ef44d5bc..e916c9ac 100644 --- a/web/src/pages/Alerting/pages/CreateMonitor/containers/AnomalyDetectors/AnomalyDetectors.js +++ b/web/src/pages/Alerting/pages/CreateMonitor/containers/AnomalyDetectors/AnomalyDetectors.js @@ -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) diff --git a/web/src/pages/Alerting/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js b/web/src/pages/Alerting/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js index 32926c73..61ed24c0 100644 --- a/web/src/pages/Alerting/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js +++ b/web/src/pages/Alerting/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js @@ -76,7 +76,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); diff --git a/web/src/pages/Alerting/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.test.js b/web/src/pages/Alerting/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.test.js index a469ef26..fdbf65eb 100644 --- a/web/src/pages/Alerting/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.test.js +++ b/web/src/pages/Alerting/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.test.js @@ -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), }); }); diff --git a/web/src/pages/Alerting/pages/CreateMonitor/containers/DefineMonitor/DefineMonitor.js b/web/src/pages/Alerting/pages/CreateMonitor/containers/DefineMonitor/DefineMonitor.js index 70707057..4391d5ff 100644 --- a/web/src/pages/Alerting/pages/CreateMonitor/containers/DefineMonitor/DefineMonitor.js +++ b/web/src/pages/Alerting/pages/CreateMonitor/containers/DefineMonitor/DefineMonitor.js @@ -126,7 +126,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 { @@ -186,7 +186,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), }); }); @@ -231,7 +231,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) { diff --git a/web/src/pages/Alerting/pages/CreateMonitor/containers/MonitorIndex/MonitorIndex.js b/web/src/pages/Alerting/pages/CreateMonitor/containers/MonitorIndex/MonitorIndex.js index d434c700..2291c1fc 100644 --- a/web/src/pages/Alerting/pages/CreateMonitor/containers/MonitorIndex/MonitorIndex.js +++ b/web/src/pages/Alerting/pages/CreateMonitor/containers/MonitorIndex/MonitorIndex.js @@ -120,7 +120,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) { @@ -150,7 +150,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) { diff --git a/web/src/pages/Alerting/pages/CreateTrigger/containers/ConfigureActions/ConfigureActions.js b/web/src/pages/Alerting/pages/CreateTrigger/containers/ConfigureActions/ConfigureActions.js index 627d2892..9e22c4e6 100644 --- a/web/src/pages/Alerting/pages/CreateTrigger/containers/ConfigureActions/ConfigureActions.js +++ b/web/src/pages/Alerting/pages/CreateTrigger/containers/ConfigureActions/ConfigureActions.js @@ -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), }); diff --git a/web/src/pages/Alerting/pages/CreateTrigger/containers/CreateTrigger/CreateTrigger.js b/web/src/pages/Alerting/pages/CreateTrigger/containers/CreateTrigger/CreateTrigger.js index ed749cc1..2239ccea 100644 --- a/web/src/pages/Alerting/pages/CreateTrigger/containers/CreateTrigger/CreateTrigger.js +++ b/web/src/pages/Alerting/pages/CreateTrigger/containers/CreateTrigger/CreateTrigger.js @@ -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 }); diff --git a/web/src/pages/Alerting/pages/Dashboard/containers/Dashboard.js b/web/src/pages/Alerting/pages/Dashboard/containers/Dashboard.js index f54f1b31..2978feb0 100644 --- a/web/src/pages/Alerting/pages/Dashboard/containers/Dashboard.js +++ b/web/src/pages/Alerting/pages/Dashboard/containers/Dashboard.js @@ -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) => { diff --git a/web/src/pages/Alerting/pages/Dashboard/utils/tableUtils.js b/web/src/pages/Alerting/pages/Dashboard/utils/tableUtils.js index cb289adc..e30a615b 100644 --- a/web/src/pages/Alerting/pages/Dashboard/utils/tableUtils.js +++ b/web/src/pages/Alerting/pages/Dashboard/utils/tableUtils.js @@ -52,7 +52,7 @@ export const columns = [ truncateText: true, textOnly: true, render: (name, alert) => ( - {name} + {name} ), }, { diff --git a/web/src/pages/Alerting/pages/Destinations/containers/CreateDestination/CreateDestination.js b/web/src/pages/Alerting/pages/Destinations/containers/CreateDestination/CreateDestination.js index 08e11b2d..42410832 100644 --- a/web/src/pages/Alerting/pages/Destinations/containers/CreateDestination/CreateDestination.js +++ b/web/src/pages/Alerting/pages/Destinations/containers/CreateDestination/CreateDestination.js @@ -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); diff --git a/web/src/pages/Alerting/pages/Destinations/containers/CreateDestination/EmailRecipients/utils/helpers.js b/web/src/pages/Alerting/pages/Destinations/containers/CreateDestination/EmailRecipients/utils/helpers.js index fe64d6f8..12f9a157 100644 --- a/web/src/pages/Alerting/pages/Destinations/containers/CreateDestination/EmailRecipients/utils/helpers.js +++ b/web/src/pages/Alerting/pages/Destinations/containers/CreateDestination/EmailRecipients/utils/helpers.js @@ -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) { diff --git a/web/src/pages/Alerting/pages/Destinations/containers/CreateDestination/EmailSender/utils/helpers.js b/web/src/pages/Alerting/pages/Destinations/containers/CreateDestination/EmailSender/utils/helpers.js index cfa2f2ef..a29d045a 100644 --- a/web/src/pages/Alerting/pages/Destinations/containers/CreateDestination/EmailSender/utils/helpers.js +++ b/web/src/pages/Alerting/pages/Destinations/containers/CreateDestination/EmailSender/utils/helpers.js @@ -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) { diff --git a/web/src/pages/Alerting/pages/Destinations/containers/CreateDestination/ManageEmailGroups/ManageEmailGroups.js b/web/src/pages/Alerting/pages/Destinations/containers/CreateDestination/ManageEmailGroups/ManageEmailGroups.js index b4e7e702..819034e7 100644 --- a/web/src/pages/Alerting/pages/Destinations/containers/CreateDestination/ManageEmailGroups/ManageEmailGroups.js +++ b/web/src/pages/Alerting/pages/Destinations/containers/CreateDestination/ManageEmailGroups/ManageEmailGroups.js @@ -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({ diff --git a/web/src/pages/Alerting/pages/Destinations/containers/CreateDestination/ManageSenders/ManageSenders.js b/web/src/pages/Alerting/pages/Destinations/containers/CreateDestination/ManageSenders/ManageSenders.js index 7864d6f0..efeca72a 100644 --- a/web/src/pages/Alerting/pages/Destinations/containers/CreateDestination/ManageSenders/ManageSenders.js +++ b/web/src/pages/Alerting/pages/Destinations/containers/CreateDestination/ManageSenders/ManageSenders.js @@ -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({ diff --git a/web/src/pages/Alerting/pages/Destinations/containers/CreateDestination/utils/destinationToFormik.js b/web/src/pages/Alerting/pages/Destinations/containers/CreateDestination/utils/destinationToFormik.js index 60c03fad..f25785d1 100644 --- a/web/src/pages/Alerting/pages/Destinations/containers/CreateDestination/utils/destinationToFormik.js +++ b/web/src/pages/Alerting/pages/Destinations/containers/CreateDestination/utils/destinationToFormik.js @@ -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; } diff --git a/web/src/pages/Alerting/pages/Destinations/containers/CreateDestination/utils/validations.js b/web/src/pages/Alerting/pages/Destinations/containers/CreateDestination/utils/validations.js index 0d146b41..6ff3b58f 100644 --- a/web/src/pages/Alerting/pages/Destinations/containers/CreateDestination/utils/validations.js +++ b/web/src/pages/Alerting/pages/Destinations/containers/CreateDestination/utils/validations.js @@ -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)) { diff --git a/web/src/pages/Alerting/pages/Destinations/containers/DestinationsList/DestinationsList.js b/web/src/pages/Alerting/pages/Destinations/containers/DestinationsList/DestinationsList.js index 0a9e6f6e..79697b40 100644 --- a/web/src/pages/Alerting/pages/Destinations/containers/DestinationsList/DestinationsList.js +++ b/web/src/pages/Alerting/pages/Destinations/containers/DestinationsList/DestinationsList.js @@ -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) { diff --git a/web/src/pages/Alerting/pages/Destinations/utils/helpers.js b/web/src/pages/Alerting/pages/Destinations/utils/helpers.js index 91fa6133..c8ffe93a 100644 --- a/web/src/pages/Alerting/pages/Destinations/utils/helpers.js +++ b/web/src/pages/Alerting/pages/Destinations/utils/helpers.js @@ -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; diff --git a/web/src/pages/Alerting/pages/MonitorDetails/containers/MonitorDetails.js b/web/src/pages/Alerting/pages/MonitorDetails/containers/MonitorDetails.js index 79ded70d..c4e1c82d 100644 --- a/web/src/pages/Alerting/pages/MonitorDetails/containers/MonitorDetails.js +++ b/web/src/pages/Alerting/pages/MonitorDetails/containers/MonitorDetails.js @@ -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 }), }) diff --git a/web/src/pages/Alerting/pages/MonitorDetails/containers/MonitorHistory/MonitorHistory.js b/web/src/pages/Alerting/pages/MonitorDetails/containers/MonitorHistory/MonitorHistory.js index 7c316846..01ad7a34 100644 --- a/web/src/pages/Alerting/pages/MonitorDetails/containers/MonitorHistory/MonitorHistory.js +++ b/web/src/pages/Alerting/pages/MonitorDetails/containers/MonitorHistory/MonitorHistory.js @@ -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; diff --git a/web/src/pages/Alerting/pages/MonitorDetails/containers/Triggers/Triggers.js b/web/src/pages/Alerting/pages/MonitorDetails/containers/Triggers/Triggers.js index 63bfac1d..191f197f 100644 --- a/web/src/pages/Alerting/pages/MonitorDetails/containers/Triggers/Triggers.js +++ b/web/src/pages/Alerting/pages/MonitorDetails/containers/Triggers/Triggers.js @@ -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() }); } } diff --git a/web/src/pages/Alerting/pages/Monitors/containers/Monitors/Monitors.js b/web/src/pages/Alerting/pages/Monitors/containers/Monitors/Monitors.js index 523ac867..ae1661c8 100644 --- a/web/src/pages/Alerting/pages/Monitors/containers/Monitors/Monitors.js +++ b/web/src/pages/Alerting/pages/Monitors/containers/Monitors/Monitors.js @@ -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; diff --git a/web/src/pages/Alerting/pages/Monitors/containers/Monitors/Monitors.test.js b/web/src/pages/Alerting/pages/Monitors/containers/Monitors/Monitors.test.js index 60a5851b..29e520d6 100644 --- a/web/src/pages/Alerting/pages/Monitors/containers/Monitors/Monitors.test.js +++ b/web/src/pages/Alerting/pages/Monitors/containers/Monitors/Monitors.test.js @@ -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' }), }); diff --git a/web/src/pages/Alerting/pages/Monitors/containers/Monitors/utils/tableUtils.js b/web/src/pages/Alerting/pages/Monitors/containers/Monitors/utils/tableUtils.js index 6ab566e6..6aeb6b27 100644 --- a/web/src/pages/Alerting/pages/Monitors/containers/Monitors/utils/tableUtils.js +++ b/web/src/pages/Alerting/pages/Monitors/containers/Monitors/utils/tableUtils.js @@ -34,7 +34,7 @@ export const columns = [ truncateText: true, textOnly: true, // width: '150px', - render: (name, item) => {name}, + render: (name, item) => {name}, }, { field: 'user', diff --git a/web/src/pages/Alerting/utils/constants.js b/web/src/pages/Alerting/utils/constants.js index 22fca51a..cd16eec2 100644 --- a/web/src/pages/Alerting/utils/constants.js +++ b/web/src/pages/Alerting/utils/constants.js @@ -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`, diff --git a/web/src/pages/Alerting/utils/validate.js b/web/src/pages/Alerting/utils/validate.js index 2153882e..4fc3777d 100644 --- a/web/src/pages/Alerting/utils/validate.js +++ b/web/src/pages/Alerting/utils/validate.js @@ -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)) {