mirror of https://github.com/grafana/grafana.git
Alerting: Encode query model map to string in rule export to avoid html escape sequences (#87663)
* Encode query model map to string to avoid html escape sequences * Remove insignificant whitespace in test request
This commit is contained in:
parent
62d326cf04
commit
563fcb8bf4
|
|
@ -1,6 +1,7 @@
|
||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"embed"
|
"embed"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
@ -42,8 +43,12 @@ func TestExportFromPayload(t *testing.T) {
|
||||||
requestFile := "post-rulegroup-101.json"
|
requestFile := "post-rulegroup-101.json"
|
||||||
rawBody, err := testData.ReadFile(path.Join("test-data", requestFile))
|
rawBody, err := testData.ReadFile(path.Join("test-data", requestFile))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
// compact the json to remove any extra whitespace
|
||||||
|
var buf bytes.Buffer
|
||||||
|
require.NoError(t, json.Compact(&buf, rawBody))
|
||||||
|
// unmarshal the compacted json
|
||||||
var body apimodels.PostableRuleGroupConfig
|
var body apimodels.PostableRuleGroupConfig
|
||||||
require.NoError(t, json.Unmarshal(rawBody, &body))
|
require.NoError(t, json.Unmarshal(buf.Bytes(), &body))
|
||||||
|
|
||||||
createRequest := func() *contextmodel.ReqContext {
|
createRequest := func() *contextmodel.ReqContext {
|
||||||
return createRequestContextWithPerms(orgID, map[int64]map[string][]string{}, nil)
|
return createRequestContextWithPerms(orgID, map[int64]map[string][]string{}, nil)
|
||||||
|
|
@ -212,6 +217,13 @@ func TestExportRules(t *testing.T) {
|
||||||
gen := ngmodels.RuleGen
|
gen := ngmodels.RuleGen
|
||||||
accessQuery := gen.GenerateQuery()
|
accessQuery := gen.GenerateQuery()
|
||||||
noAccessQuery := gen.GenerateQuery()
|
noAccessQuery := gen.GenerateQuery()
|
||||||
|
mdl := map[string]any{
|
||||||
|
"foo": "bar",
|
||||||
|
"baz": "a <=> b", // explicitly check greater/less than characters
|
||||||
|
}
|
||||||
|
model, err := json.Marshal(mdl)
|
||||||
|
require.NoError(t, err)
|
||||||
|
accessQuery.Model = model
|
||||||
|
|
||||||
hasAccess1 := gen.With(gen.WithGroupKey(hasAccessKey1), gen.WithQuery(accessQuery), gen.WithUniqueGroupIndex()).GenerateManyRef(5)
|
hasAccess1 := gen.With(gen.WithGroupKey(hasAccessKey1), gen.WithQuery(accessQuery), gen.WithUniqueGroupIndex()).GenerateManyRef(5)
|
||||||
ruleStore.PutRule(context.Background(), hasAccess1...)
|
ruleStore.PutRule(context.Background(), hasAccess1...)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -204,6 +205,17 @@ func AlertRuleExportFromAlertRule(rule models.AlertRule) (definitions.AlertRuleE
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func encodeQueryModel(m map[string]any) (string, error) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
enc := json.NewEncoder(&buf)
|
||||||
|
enc.SetEscapeHTML(false)
|
||||||
|
err := enc.Encode(m)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(bytes.TrimRight(buf.Bytes(), "\n")), nil
|
||||||
|
}
|
||||||
|
|
||||||
// AlertQueryExportFromAlertQuery creates a definitions.AlertQueryExport DTO from models.AlertQuery.
|
// AlertQueryExportFromAlertQuery creates a definitions.AlertQueryExport DTO from models.AlertQuery.
|
||||||
func AlertQueryExportFromAlertQuery(query models.AlertQuery) (definitions.AlertQueryExport, error) {
|
func AlertQueryExportFromAlertQuery(query models.AlertQuery) (definitions.AlertQueryExport, error) {
|
||||||
// We unmarshal the json.RawMessage model into a map in order to facilitate yaml marshalling.
|
// We unmarshal the json.RawMessage model into a map in order to facilitate yaml marshalling.
|
||||||
|
|
@ -216,6 +228,12 @@ func AlertQueryExportFromAlertQuery(query models.AlertQuery) (definitions.AlertQ
|
||||||
if query.QueryType != "" {
|
if query.QueryType != "" {
|
||||||
queryType = &query.QueryType
|
queryType = &query.QueryType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
modelString, err := encodeQueryModel(mdl)
|
||||||
|
if err != nil {
|
||||||
|
return definitions.AlertQueryExport{}, err
|
||||||
|
}
|
||||||
|
|
||||||
return definitions.AlertQueryExport{
|
return definitions.AlertQueryExport{
|
||||||
RefID: query.RefID,
|
RefID: query.RefID,
|
||||||
QueryType: queryType,
|
QueryType: queryType,
|
||||||
|
|
@ -225,7 +243,7 @@ func AlertQueryExportFromAlertQuery(query models.AlertQuery) (definitions.AlertQ
|
||||||
},
|
},
|
||||||
DatasourceUID: query.DatasourceUID,
|
DatasourceUID: query.DatasourceUID,
|
||||||
Model: mdl,
|
Model: mdl,
|
||||||
ModelString: string(query.Model),
|
ModelString: modelString,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ resource "grafana_rule_group" "rule_group_0000" {
|
||||||
}
|
}
|
||||||
|
|
||||||
datasource_uid = "000000002"
|
datasource_uid = "000000002"
|
||||||
model = "{\n \"expr\": \"http_request_duration_microseconds_count\",\n \"hide\": false,\n \"interval\": \"\",\n \"intervalMs\": 1000,\n \"legendFormat\": \"\",\n \"maxDataPoints\": 100,\n \"refId\": \"query\"\n }"
|
model = "{\"expr\":\"http_request_duration_microseconds_count\",\"hide\":false,\"interval\":\"\",\"intervalMs\":1000,\"legendFormat\":\"\",\"maxDataPoints\":100,\"refId\":\"query\"}"
|
||||||
}
|
}
|
||||||
data {
|
data {
|
||||||
ref_id = "reduced"
|
ref_id = "reduced"
|
||||||
|
|
@ -28,7 +28,7 @@ resource "grafana_rule_group" "rule_group_0000" {
|
||||||
}
|
}
|
||||||
|
|
||||||
datasource_uid = "__expr__"
|
datasource_uid = "__expr__"
|
||||||
model = "{\n \"expression\": \"query\",\n \"hide\": false,\n \"intervalMs\": 1000,\n \"maxDataPoints\": 100,\n \"reducer\": \"mean\",\n \"refId\": \"reduced\",\n \"type\": \"reduce\"\n }"
|
model = "{\"expression\":\"query\",\"hide\":false,\"intervalMs\":1000,\"maxDataPoints\":100,\"reducer\":\"mean\",\"refId\":\"reduced\",\"type\":\"reduce\"}"
|
||||||
}
|
}
|
||||||
data {
|
data {
|
||||||
ref_id = "condition"
|
ref_id = "condition"
|
||||||
|
|
@ -39,7 +39,7 @@ resource "grafana_rule_group" "rule_group_0000" {
|
||||||
}
|
}
|
||||||
|
|
||||||
datasource_uid = "__expr__"
|
datasource_uid = "__expr__"
|
||||||
model = "{\n \"expression\": \"$reduced > 10\",\n \"hide\": false,\n \"intervalMs\": 1000,\n \"maxDataPoints\": 100,\n \"refId\": \"condition\",\n \"type\": \"math\"\n }"
|
model = "{\"expression\":\"$reduced > 10\",\"hide\":false,\"intervalMs\":1000,\"maxDataPoints\":100,\"refId\":\"condition\",\"type\":\"math\"}"
|
||||||
}
|
}
|
||||||
|
|
||||||
no_data_state = "NoData"
|
no_data_state = "NoData"
|
||||||
|
|
@ -60,7 +60,7 @@ resource "grafana_rule_group" "rule_group_0000" {
|
||||||
}
|
}
|
||||||
|
|
||||||
datasource_uid = "000000004"
|
datasource_uid = "000000004"
|
||||||
model = "{\n \"alias\": \"just-testing\",\n \"intervalMs\": 1000,\n \"maxDataPoints\": 100,\n \"orgId\": 0,\n \"refId\": \"A\",\n \"scenarioId\": \"csv_metric_values\",\n \"stringInput\": \"1,20,90,30,5,0\"\n }"
|
model = "{\"alias\":\"just-testing\",\"intervalMs\":1000,\"maxDataPoints\":100,\"orgId\":0,\"refId\":\"A\",\"scenarioId\":\"csv_metric_values\",\"stringInput\":\"1,20,90,30,5,0\"}"
|
||||||
}
|
}
|
||||||
data {
|
data {
|
||||||
ref_id = "B"
|
ref_id = "B"
|
||||||
|
|
@ -71,7 +71,7 @@ resource "grafana_rule_group" "rule_group_0000" {
|
||||||
}
|
}
|
||||||
|
|
||||||
datasource_uid = "__expr__"
|
datasource_uid = "__expr__"
|
||||||
model = "{\n \"expression\": \"$A\",\n \"intervalMs\": 2000,\n \"maxDataPoints\": 200,\n \"orgId\": 0,\n \"reducer\": \"mean\",\n \"refId\": \"B\",\n \"type\": \"reduce\"\n }"
|
model = "{\"expression\":\"$A\",\"intervalMs\":2000,\"maxDataPoints\":200,\"orgId\":0,\"reducer\":\"mean\",\"refId\":\"B\",\"type\":\"reduce\"}"
|
||||||
}
|
}
|
||||||
|
|
||||||
no_data_state = "NoData"
|
no_data_state = "NoData"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue