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:
William Wernert 2024-05-14 09:29:50 -04:00 committed by GitHub
parent 62d326cf04
commit 563fcb8bf4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 37 additions and 7 deletions

View File

@ -1,6 +1,7 @@
package api
import (
"bytes"
"context"
"embed"
"encoding/json"
@ -42,8 +43,12 @@ func TestExportFromPayload(t *testing.T) {
requestFile := "post-rulegroup-101.json"
rawBody, err := testData.ReadFile(path.Join("test-data", requestFile))
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
require.NoError(t, json.Unmarshal(rawBody, &body))
require.NoError(t, json.Unmarshal(buf.Bytes(), &body))
createRequest := func() *contextmodel.ReqContext {
return createRequestContextWithPerms(orgID, map[int64]map[string][]string{}, nil)
@ -212,6 +217,13 @@ func TestExportRules(t *testing.T) {
gen := ngmodels.RuleGen
accessQuery := 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)
ruleStore.PutRule(context.Background(), hasAccess1...)

View File

@ -1,6 +1,7 @@
package api
import (
"bytes"
"encoding/json"
"time"
@ -204,6 +205,17 @@ func AlertRuleExportFromAlertRule(rule models.AlertRule) (definitions.AlertRuleE
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.
func AlertQueryExportFromAlertQuery(query models.AlertQuery) (definitions.AlertQueryExport, error) {
// 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 != "" {
queryType = &query.QueryType
}
modelString, err := encodeQueryModel(mdl)
if err != nil {
return definitions.AlertQueryExport{}, err
}
return definitions.AlertQueryExport{
RefID: query.RefID,
QueryType: queryType,
@ -225,7 +243,7 @@ func AlertQueryExportFromAlertQuery(query models.AlertQuery) (definitions.AlertQ
},
DatasourceUID: query.DatasourceUID,
Model: mdl,
ModelString: string(query.Model),
ModelString: modelString,
}, nil
}

View File

@ -17,7 +17,7 @@ resource "grafana_rule_group" "rule_group_0000" {
}
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 {
ref_id = "reduced"
@ -28,7 +28,7 @@ resource "grafana_rule_group" "rule_group_0000" {
}
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 {
ref_id = "condition"
@ -39,7 +39,7 @@ resource "grafana_rule_group" "rule_group_0000" {
}
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"
@ -60,7 +60,7 @@ resource "grafana_rule_group" "rule_group_0000" {
}
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 {
ref_id = "B"
@ -71,7 +71,7 @@ resource "grafana_rule_group" "rule_group_0000" {
}
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"