1package cloudwatch 2 3import ( 4 "testing" 5 "time" 6 7 "github.com/grafana/grafana-plugin-sdk-go/backend" 8 "github.com/grafana/grafana/pkg/components/simplejson" 9 "github.com/grafana/grafana/pkg/tsdb/legacydata" 10 "github.com/stretchr/testify/assert" 11 "github.com/stretchr/testify/require" 12) 13 14func TestRequestParser(t *testing.T) { 15 t.Run("Query migration ", func(t *testing.T) { 16 t.Run("legacy statistics field is migrated", func(t *testing.T) { 17 startTime := time.Now() 18 endTime := startTime.Add(2 * time.Hour) 19 oldQuery := &backend.DataQuery{ 20 MaxDataPoints: 0, 21 QueryType: "timeSeriesQuery", 22 Interval: 0, 23 } 24 oldQuery.RefID = "A" 25 oldQuery.JSON = []byte(`{ 26 "region": "us-east-1", 27 "namespace": "ec2", 28 "metricName": "CPUUtilization", 29 "dimensions": { 30 "InstanceId": ["test"] 31 }, 32 "statistics": ["Average", "Sum"], 33 "period": "600", 34 "hide": false 35 }`) 36 migratedQueries, err := migrateLegacyQuery([]backend.DataQuery{*oldQuery}, startTime, endTime) 37 require.NoError(t, err) 38 assert.Equal(t, 1, len(migratedQueries)) 39 40 migratedQuery := migratedQueries[0] 41 assert.Equal(t, "A", migratedQuery.RefID) 42 model, err := simplejson.NewJson(migratedQuery.JSON) 43 require.NoError(t, err) 44 assert.Equal(t, "Average", model.Get("statistic").MustString()) 45 res, err := model.Get("statistic").Array() 46 assert.Error(t, err) 47 assert.Nil(t, res) 48 }) 49 }) 50 51 timeRange := legacydata.NewDataTimeRange("now-1h", "now-2h") 52 from, err := timeRange.ParseFrom() 53 require.NoError(t, err) 54 to, err := timeRange.ParseTo() 55 require.NoError(t, err) 56 57 t.Run("New dimensions structure", func(t *testing.T) { 58 query := simplejson.NewFromAny(map[string]interface{}{ 59 "refId": "ref1", 60 "region": "us-east-1", 61 "namespace": "ec2", 62 "metricName": "CPUUtilization", 63 "id": "", 64 "expression": "", 65 "dimensions": map[string]interface{}{ 66 "InstanceId": []interface{}{"test"}, 67 "InstanceType": []interface{}{"test2", "test3"}, 68 }, 69 "statistic": "Average", 70 "period": "600", 71 "hide": false, 72 }) 73 74 res, err := parseRequestQuery(query, "ref1", from, to) 75 require.NoError(t, err) 76 assert.Equal(t, "us-east-1", res.Region) 77 assert.Equal(t, "ref1", res.RefId) 78 assert.Equal(t, "ec2", res.Namespace) 79 assert.Equal(t, "CPUUtilization", res.MetricName) 80 assert.Equal(t, "queryref1", res.Id) 81 assert.Empty(t, res.Expression) 82 assert.Equal(t, 600, res.Period) 83 assert.True(t, res.ReturnData) 84 assert.Len(t, res.Dimensions, 2) 85 assert.Len(t, res.Dimensions["InstanceId"], 1) 86 assert.Len(t, res.Dimensions["InstanceType"], 2) 87 assert.Equal(t, "test3", res.Dimensions["InstanceType"][1]) 88 assert.Equal(t, "Average", res.Statistic) 89 }) 90 91 t.Run("Old dimensions structure (backwards compatibility)", func(t *testing.T) { 92 query := simplejson.NewFromAny(map[string]interface{}{ 93 "refId": "ref1", 94 "region": "us-east-1", 95 "namespace": "ec2", 96 "metricName": "CPUUtilization", 97 "id": "", 98 "expression": "", 99 "dimensions": map[string]interface{}{ 100 "InstanceId": "test", 101 "InstanceType": "test2", 102 }, 103 "statistic": "Average", 104 "period": "600", 105 "hide": false, 106 }) 107 108 res, err := parseRequestQuery(query, "ref1", from, to) 109 require.NoError(t, err) 110 assert.Equal(t, "us-east-1", res.Region) 111 assert.Equal(t, "ref1", res.RefId) 112 assert.Equal(t, "ec2", res.Namespace) 113 assert.Equal(t, "CPUUtilization", res.MetricName) 114 assert.Equal(t, "queryref1", res.Id) 115 assert.Empty(t, res.Expression) 116 assert.Equal(t, 600, res.Period) 117 assert.True(t, res.ReturnData) 118 assert.Len(t, res.Dimensions, 2) 119 assert.Len(t, res.Dimensions["InstanceId"], 1) 120 assert.Len(t, res.Dimensions["InstanceType"], 1) 121 assert.Equal(t, "test2", res.Dimensions["InstanceType"][0]) 122 assert.Equal(t, "Average", res.Statistic) 123 }) 124 125 t.Run("Period defined in the editor by the user is being used when time range is short", func(t *testing.T) { 126 query := simplejson.NewFromAny(map[string]interface{}{ 127 "refId": "ref1", 128 "region": "us-east-1", 129 "namespace": "ec2", 130 "metricName": "CPUUtilization", 131 "id": "", 132 "expression": "", 133 "dimensions": map[string]interface{}{ 134 "InstanceId": "test", 135 "InstanceType": "test2", 136 }, 137 "statistic": "Average", 138 "hide": false, 139 }) 140 query.Set("period", "900") 141 timeRange := legacydata.NewDataTimeRange("now-1h", "now-2h") 142 from, err := timeRange.ParseFrom() 143 require.NoError(t, err) 144 to, err := timeRange.ParseTo() 145 require.NoError(t, err) 146 147 res, err := parseRequestQuery(query, "ref1", from, to) 148 require.NoError(t, err) 149 assert.Equal(t, 900, res.Period) 150 }) 151 152 t.Run("Period is parsed correctly if not defined by user", func(t *testing.T) { 153 query := simplejson.NewFromAny(map[string]interface{}{ 154 "refId": "ref1", 155 "region": "us-east-1", 156 "namespace": "ec2", 157 "metricName": "CPUUtilization", 158 "id": "", 159 "expression": "", 160 "dimensions": map[string]interface{}{ 161 "InstanceId": "test", 162 "InstanceType": "test2", 163 }, 164 "statistic": "Average", 165 "hide": false, 166 "period": "auto", 167 }) 168 169 t.Run("Time range is 5 minutes", func(t *testing.T) { 170 query.Set("period", "auto") 171 to := time.Now() 172 from := to.Local().Add(time.Minute * time.Duration(5)) 173 174 res, err := parseRequestQuery(query, "ref1", from, to) 175 require.NoError(t, err) 176 assert.Equal(t, 60, res.Period) 177 }) 178 179 t.Run("Time range is 1 day", func(t *testing.T) { 180 query.Set("period", "auto") 181 to := time.Now() 182 from := to.AddDate(0, 0, -1) 183 184 res, err := parseRequestQuery(query, "ref1", from, to) 185 require.NoError(t, err) 186 assert.Equal(t, 60, res.Period) 187 }) 188 189 t.Run("Time range is 2 days", func(t *testing.T) { 190 query.Set("period", "auto") 191 to := time.Now() 192 from := to.AddDate(0, 0, -2) 193 res, err := parseRequestQuery(query, "ref1", from, to) 194 require.NoError(t, err) 195 assert.Equal(t, 300, res.Period) 196 }) 197 198 t.Run("Time range is 7 days", func(t *testing.T) { 199 query.Set("period", "auto") 200 to := time.Now() 201 from := to.AddDate(0, 0, -7) 202 203 res, err := parseRequestQuery(query, "ref1", from, to) 204 require.NoError(t, err) 205 assert.Equal(t, 900, res.Period) 206 }) 207 208 t.Run("Time range is 30 days", func(t *testing.T) { 209 query.Set("period", "auto") 210 to := time.Now() 211 from := to.AddDate(0, 0, -30) 212 213 res, err := parseRequestQuery(query, "ref1", from, to) 214 require.NoError(t, err) 215 assert.Equal(t, 3600, res.Period) 216 }) 217 218 t.Run("Time range is 90 days", func(t *testing.T) { 219 query.Set("period", "auto") 220 to := time.Now() 221 from := to.AddDate(0, 0, -90) 222 223 res, err := parseRequestQuery(query, "ref1", from, to) 224 require.NoError(t, err) 225 assert.Equal(t, 21600, res.Period) 226 }) 227 228 t.Run("Time range is 1 year", func(t *testing.T) { 229 query.Set("period", "auto") 230 to := time.Now() 231 from := to.AddDate(-1, 0, 0) 232 233 res, err := parseRequestQuery(query, "ref1", from, to) 234 require.Nil(t, err) 235 assert.Equal(t, 21600, res.Period) 236 }) 237 238 t.Run("Time range is 2 years", func(t *testing.T) { 239 query.Set("period", "auto") 240 to := time.Now() 241 from := to.AddDate(-2, 0, 0) 242 243 res, err := parseRequestQuery(query, "ref1", from, to) 244 require.NoError(t, err) 245 assert.Equal(t, 86400, res.Period) 246 }) 247 248 t.Run("Time range is 2 days, but 16 days ago", func(t *testing.T) { 249 query.Set("period", "auto") 250 to := time.Now().AddDate(0, 0, -14) 251 from := to.AddDate(0, 0, -2) 252 res, err := parseRequestQuery(query, "ref1", from, to) 253 require.NoError(t, err) 254 assert.Equal(t, 300, res.Period) 255 }) 256 257 t.Run("Time range is 2 days, but 90 days ago", func(t *testing.T) { 258 query.Set("period", "auto") 259 to := time.Now().AddDate(0, 0, -88) 260 from := to.AddDate(0, 0, -2) 261 res, err := parseRequestQuery(query, "ref1", from, to) 262 require.NoError(t, err) 263 assert.Equal(t, 3600, res.Period) 264 }) 265 266 t.Run("Time range is 2 days, but 456 days ago", func(t *testing.T) { 267 query.Set("period", "auto") 268 to := time.Now().AddDate(0, 0, -454) 269 from := to.AddDate(0, 0, -2) 270 res, err := parseRequestQuery(query, "ref1", from, to) 271 require.NoError(t, err) 272 assert.Equal(t, 21600, res.Period) 273 }) 274 }) 275 276 t.Run("Metric query type, metric editor mode and query api mode", func(t *testing.T) { 277 timeRange := legacydata.NewDataTimeRange("now-1h", "now-2h") 278 from, err := timeRange.ParseFrom() 279 require.NoError(t, err) 280 to, err := timeRange.ParseTo() 281 require.NoError(t, err) 282 283 t.Run("when metric query type and metric editor mode is not specified", func(t *testing.T) { 284 t.Run("it should be metric search builder", func(t *testing.T) { 285 query := getBaseJsonQuery() 286 res, err := parseRequestQuery(query, "ref1", from, to) 287 require.NoError(t, err) 288 assert.Equal(t, MetricQueryTypeSearch, res.MetricQueryType) 289 assert.Equal(t, MetricEditorModeBuilder, res.MetricEditorMode) 290 assert.Equal(t, GMDApiModeMetricStat, res.getGMDAPIMode()) 291 }) 292 293 t.Run("and an expression is specified it should be metric search builder", func(t *testing.T) { 294 query := getBaseJsonQuery() 295 query.Set("expression", "SUM(a)") 296 res, err := parseRequestQuery(query, "ref1", from, to) 297 require.NoError(t, err) 298 assert.Equal(t, MetricQueryTypeSearch, res.MetricQueryType) 299 assert.Equal(t, MetricEditorModeRaw, res.MetricEditorMode) 300 assert.Equal(t, GMDApiModeMathExpression, res.getGMDAPIMode()) 301 }) 302 }) 303 304 t.Run("and an expression is specified it should be metric search builder", func(t *testing.T) { 305 query := getBaseJsonQuery() 306 query.Set("expression", "SUM(a)") 307 res, err := parseRequestQuery(query, "ref1", from, to) 308 require.NoError(t, err) 309 assert.Equal(t, MetricQueryTypeSearch, res.MetricQueryType) 310 assert.Equal(t, MetricEditorModeRaw, res.MetricEditorMode) 311 assert.Equal(t, GMDApiModeMathExpression, res.getGMDAPIMode()) 312 }) 313 }) 314} 315 316func getBaseJsonQuery() *simplejson.Json { 317 return simplejson.NewFromAny(map[string]interface{}{ 318 "refId": "ref1", 319 "region": "us-east-1", 320 "namespace": "ec2", 321 "metricName": "CPUUtilization", 322 "statistic": "Average", 323 "period": "900", 324 }) 325} 326