diff --git a/go.mod b/go.mod index d792b94de..48fc1e9a6 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/mitchellh/go-homedir v1.1.0 github.com/newrelic/go-agent/v3 v3.30.0 github.com/newrelic/go-insights v1.0.3 - github.com/newrelic/newrelic-client-go/v2 v2.43.2 + github.com/newrelic/newrelic-client-go/v2 v2.44.0 github.com/stretchr/testify v1.9.0 golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 ) diff --git a/go.sum b/go.sum index bc8fd481f..30b796c41 100644 --- a/go.sum +++ b/go.sum @@ -270,8 +270,8 @@ github.com/newrelic/go-agent/v3 v3.30.0 h1:ZXHCT/Cot4iIPwcegCZURuRQOsfmGA6wilW+S github.com/newrelic/go-agent/v3 v3.30.0/go.mod h1:9utrgxlSryNqRrTvII2XBL+0lpofXbqXApvVWPpbzUg= github.com/newrelic/go-insights v1.0.3 h1:zSNp1CEZnXktzSIEsbHJk8v6ZihdPFP2WsO/fzau3OQ= github.com/newrelic/go-insights v1.0.3/go.mod h1:A20BoT8TNkqPGX2nS/Z2fYmKl3Cqa3iKZd4whzedCY4= -github.com/newrelic/newrelic-client-go/v2 v2.43.2 h1:tWcXYxz1oO63kEWoGtiMXGKJ03VyW+l0UJKwOnMAyyU= -github.com/newrelic/newrelic-client-go/v2 v2.43.2/go.mod h1:pDFY24/6iIMEbPIdowTRrRn9YYwkXc3j+B+XpTb4oF4= +github.com/newrelic/newrelic-client-go/v2 v2.44.0 h1:n4zP64Hfui8pjW/D3tbE1Hi+Acbpz8nBIrI2miEAGiI= +github.com/newrelic/newrelic-client-go/v2 v2.44.0/go.mod h1:pDFY24/6iIMEbPIdowTRrRn9YYwkXc3j+B+XpTb4oF4= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= diff --git a/newrelic/resource_newrelic_one_dashboard.go b/newrelic/resource_newrelic_one_dashboard.go index 40a78fa5a..2530c237b 100644 --- a/newrelic/resource_newrelic_one_dashboard.go +++ b/newrelic/resource_newrelic_one_dashboard.go @@ -319,6 +319,17 @@ func dashboardWidgetSchemaBase() map[string]*schema.Schema { Required: true, Elem: dashboardWidgetNRQLQuerySchemaElem(), }, + "refresh_rate": { + Type: schema.TypeString, + Optional: true, + }, + "initial_sorting": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + MinItems: 1, + Elem: dashboardWidgetInitialSortingSchemaElem(), + }, "ignore_time_range": { Type: schema.TypeBool, Optional: true, @@ -444,6 +455,22 @@ func dashboardWidgetNullValuesSchemaElem() *schema.Resource { }, } } +func dashboardWidgetInitialSortingSchemaElem() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "direction": { + Type: schema.TypeString, + Required: true, + Description: "Defines the sort order. Either ascending or descending.", + }, + "name": { + Type: schema.TypeString, + Required: true, + Description: "The column name to be sorted", + }, + }, + } +} // dashboardWidgetNRQLQuerySchemaElem defines a NRQL query for use on a dashboard // diff --git a/newrelic/resource_newrelic_one_dashboard_test.go b/newrelic/resource_newrelic_one_dashboard_test.go index 528892773..522bcf714 100644 --- a/newrelic/resource_newrelic_one_dashboard_test.go +++ b/newrelic/resource_newrelic_one_dashboard_test.go @@ -594,6 +594,7 @@ func testAccCheckNewRelicOneDashboardConfig_PageSimple(pageName string) string { title = "foo" row = 4 column = 1 + refresh_rate = 30000 nrql_query { query = "FROM Transaction SELECT count(*) FACET name" } @@ -644,6 +645,8 @@ func testAccCheckNewRelicOneDashboardConfig_PageFull(pageName string, accountID height = 3 width = 12 + refresh_rate = 30000 + nrql_query { account_id = ` + accountID + ` query = "FROM Transaction SELECT 51 TIMESERIES" @@ -774,6 +777,11 @@ func testAccCheckNewRelicOneDashboardConfig_PageFull(pageName string, accountID severity = "unavailable" } linked_entity_guids = ["MjUyMDUyOHxWSVp8REFTSEJPQVJEfDE2NDYzMDQ"] + refresh_rate = 30000 + initial_sorting { + direction = "desc" + name = "appName" + } } widget_json { diff --git a/newrelic/structures_newrelic_one_dashboard.go b/newrelic/structures_newrelic_one_dashboard.go index ccc739948..594fa4865 100644 --- a/newrelic/structures_newrelic_one_dashboard.go +++ b/newrelic/structures_newrelic_one_dashboard.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "log" + "reflect" "strconv" "strings" @@ -356,6 +357,9 @@ func expandDashboardPageInput(d *schema.ResourceData, pages []interface{}, meta // Set thresholds rawConfiguration.Thresholds = expandDashboardTableWidgetConfigurationThresholdInput(d, pageIndex, widgetIndex) + // Set initalSorting + rawConfiguration.InitialSorting = expandDashboardTableWidgetConfigInitialSortingInput(v.(map[string]interface{})) + widget.RawConfiguration, err = json.Marshal(rawConfiguration) if err != nil { return nil, err @@ -530,6 +534,25 @@ func expandDashboardLineWidgetConfigurationThresholdInput(d *schema.ResourceData return lineWidgetThresholdsRoot } +func expandDashboardTableWidgetConfigInitialSortingInput(w map[string]interface{}) *dashboards.DashboardWidgetInitialSorting { + var tableWidgetInitialSorting dashboards.DashboardWidgetInitialSorting + + if q, ok := w["initial_sorting"]; ok && len(q.([]interface{})) == 1 && q.([]interface{})[0] != nil { + dashboardInitialSortingMap := q.([]interface{})[0].(map[string]interface{}) + + if i, ok := dashboardInitialSortingMap["direction"]; ok { + tableWidgetInitialSorting.Direction = i.(string) + } + + if i, ok := dashboardInitialSortingMap["name"]; ok { + tableWidgetInitialSorting.Name = i.(string) + } + return &tableWidgetInitialSorting + } + + return nil +} + func expandDashboardTableWidgetConfigurationThresholdInput(d *schema.ResourceData, pageIndex int, widgetIndex int) []dashboards.DashboardTableWidgetThresholdInput { // initialize an object of []DashboardTableWidgetThresholdInput, which would include a list of tableWidgetThresholdsToBeAdded as specified // in the Terraform configuration, with the attribute "threshold" in table widgets @@ -628,6 +651,19 @@ func expandDashboardWidgetInput(w map[string]interface{}, meta interface{}, visu l.ShowOtherSeries = q.(bool) cfg.Facet = &l } + if q, ok := w["refresh_rate"]; ok { + var l dashboards.DashboardWidgetRefreshRate + + // Acceptable values for refresh rate could be string such as "auto", or number such as 5000 + // If we try to send numerical values as string to NerdGraph eg: "5000", the refresh rate isn't reflected in the UI + // Hence we need to convert numerical values from string type to int type + if v, err := strconv.Atoi(q.(string)); err == nil { + l.Frequency = v + } else { + l.Frequency = q.(string) + } + cfg.RefreshRate = &l + } cfg = expandDashboardWidgetYAxisAttributesVizClassified(w, cfg, visualisation) cfg = expandDashboardWidgetNullValuesInput(w, cfg) @@ -1191,6 +1227,16 @@ func flattenDashboardWidget(in *entities.DashboardWidget, pageGUID string) (stri if rawCfg.Colors != nil { out["colors"] = flattenDashboardWidgetColors(rawCfg.Colors) } + if rawCfg.RefreshRate != nil { + // Since schema for refresh_rate is defined as string + // If we get integer values in graphQL response, we need to convert to string + if reflect.TypeOf(rawCfg.RefreshRate.Frequency).Kind() == reflect.String { + out["refresh_rate"] = rawCfg.RefreshRate.Frequency + } else { + s := strconv.FormatFloat(rawCfg.RefreshRate.Frequency.(float64), 'f', -1, 64) + out["refresh_rate"] = s + } + } // Set widget type and arguments switch in.Visualization.ID { @@ -1275,6 +1321,14 @@ func flattenDashboardWidget(in *entities.DashboardWidget, pageGUID string) (stri widgetType = "widget_table" out["nrql_query"] = flattenDashboardWidgetNRQLQuery(&rawCfg.NRQLQueries) out["filter_current_dashboard"] = filterCurrentDashboard + + if rawCfg.InitialSorting != nil { + initialSorting := flattenDashboardWidgetInitialSorting(rawCfg.InitialSorting) + if initialSorting != nil { + out["initial_sorting"] = initialSorting + } + } + if rawCfg.Thresholds != nil { thresholds := flattenDashboardTableWidgetThresholds(rawCfg.Thresholds) if thresholds != nil { @@ -1290,6 +1344,17 @@ func flattenDashboardWidget(in *entities.DashboardWidget, pageGUID string) (stri return widgetType, out } +func flattenDashboardWidgetInitialSorting(in *dashboards.DashboardWidgetInitialSorting) []interface{} { + out := make([]interface{}, 1) + k := make(map[string]interface{}) + + k["direction"] = in.Direction + k["name"] = in.Name + + out[0] = k + return out +} + func flattenDashboardWidgetNRQLQuery(in *[]dashboards.DashboardWidgetNRQLQueryInput) []interface{} { out := make([]interface{}, len(*in)) diff --git a/website/docs/r/one_dashboard.html.markdown b/website/docs/r/one_dashboard.html.markdown index 1b6e84ac5..acf572919 100644 --- a/website/docs/r/one_dashboard.html.markdown +++ b/website/docs/r/one_dashboard.html.markdown @@ -19,6 +19,25 @@ resource "newrelic_one_dashboard" "exampledash" { page { name = "New Relic Terraform Example" + widget_table { + title = "List of Transactions" + row = 1 + column = 4 + width = 6 + height = 3 + + refresh_rate = 60000 // data refreshes every 60 seconds + + nrql_query { + query = "FROM Transaction SELECT *" + } + + initial_sorting { + direction = "desc" + name = "timestamp" + } + } + widget_billboard { title = "Requests per minute" row = 1 @@ -26,6 +45,8 @@ resource "newrelic_one_dashboard" "exampledash" { width = 6 height = 3 + refresh_rate = 60000 // 60 seconds + nrql_query { query = "FROM Transaction SELECT rate(count(*), 1 minute)" } @@ -54,6 +75,8 @@ resource "newrelic_one_dashboard" "exampledash" { width = 6 height = 3 + refresh_rate = 300000 // 5 minutes + nrql_query { account_id = 12345 query = "FROM Transaction SELECT average(duration) FACET appName" @@ -83,6 +106,8 @@ resource "newrelic_one_dashboard" "exampledash" { width = 6 height = 3 + refresh_rate = 30000 // 30 seconds + nrql_query { account_id = 12345 query = "FROM Transaction select max(duration) as 'max duration' where httpResponseCode = '504' timeseries since 5 minutes ago" @@ -270,6 +295,7 @@ All nested `widget` blocks support the following common arguments: * `null_values` - (Optional) A nested block that describes a Null Values. See [Nested Null Values blocks](#nested-null-values-blocks) below for details. * `units` - (Optional) A nested block that describes units on your Y axis. See [Nested Units blocks](#nested-units-blocks) below for details. * `colors` - (Optional) A nested block that describes colors of your charts per series. See [Nested Colors blocks](#nested-colors-blocks) below for details. + * `refresh_rate` - (Optional) This attribute determines the frequency for data refresh specified in milliseconds. Accepted values are `auto` for default value, `0` for no refresh, `5000` for 5 seconds, `30000` for 30 seconds, `60000` for 60 seconds, `300000` for 5 minutes, `1800000` for 30 minutes, `3600000` for 60 minute, `10800000` for 3 hours, `43200000` for 12 hours and `86400000` for 24 hours. Each widget type supports an additional set of arguments: @@ -328,6 +354,9 @@ Each widget type supports an additional set of arguments: * `from` - The value 'from' which the threshold would need to be applied. * `to` - The value until which the threshold would need to be applied. * `severity` - The severity of the threshold, which would affect the visual appearance of the threshold (such as its color) accordingly. The value of this attribute would need to be one of the following - `warning`, `severe`, `critical`, `success`, `unavailable` which correspond to the severity labels _Warning_, _Approaching critical_, _Critical_, _Good_, _Neutral_ in the dropdown that helps specify the severity of thresholds in table widgets in the UI, respectively. + * `initial_sorting` - (Optional) An attribute that describes the sorting mechanism for the table. This attribute requires specifying the following attributes in a nested block - + * `name` - (Required) The name of column to be sorted. Examples of few valid values are `timestamp`, `appId`, `appName`, etc. + * `direction` - (Required) Defines the sort order. Accepted values are `asc` for ascending or `desc` for descending. ### Nested `nrql_query` blocks