Skip to content

Commit

Permalink
feat(dashboards): adds support for data_format to the `newrelic_one…
Browse files Browse the repository at this point in the history
…_dashboard` resource (#2747)

Co-authored-by: pranav-new-relic <[email protected]>
  • Loading branch information
vinay-newrelic and pranav-new-relic authored Sep 25, 2024
1 parent 058207e commit e4486dd
Show file tree
Hide file tree
Showing 7 changed files with 270 additions and 19 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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.45.0
github.com/newrelic/newrelic-client-go/v2 v2.46.0
github.com/stretchr/testify v1.9.0
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8
)
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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.45.0 h1:3zaJqE4V2n07b8Fx3byV+WeJSdeTeR4sW6yVSXrrNN4=
github.com/newrelic/newrelic-client-go/v2 v2.45.0/go.mod h1:pDFY24/6iIMEbPIdowTRrRn9YYwkXc3j+B+XpTb4oF4=
github.com/newrelic/newrelic-client-go/v2 v2.46.0 h1:J1dKQFRKfQJQQFbP4EQGRs6JsYL20gXJxRmTjXLuB9E=
github.com/newrelic/newrelic-client-go/v2 v2.46.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=
Expand Down
34 changes: 34 additions & 0 deletions newrelic/resource_newrelic_one_dashboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,12 @@ func dashboardWidgetSchemaBase() map[string]*schema.Schema {
MinItems: 1,
Elem: dashboardWidgetInitialSortingSchemaElem(),
},
"data_format": {
Type: schema.TypeList,
Optional: true,
MinItems: 1,
Elem: dashboardWidgetDataFormatSchemaElem(),
},
"ignore_time_range": {
Type: schema.TypeBool,
Optional: true,
Expand Down Expand Up @@ -456,6 +462,7 @@ func dashboardWidgetNullValuesSchemaElem() *schema.Resource {
},
}
}

func dashboardWidgetInitialSortingSchemaElem() *schema.Resource {
return &schema.Resource{
Schema: map[string]*schema.Schema{
Expand All @@ -473,6 +480,33 @@ func dashboardWidgetInitialSortingSchemaElem() *schema.Resource {
}
}

func dashboardWidgetDataFormatSchemaElem() *schema.Resource {
return &schema.Resource{
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
Description: "The column name to be sorted",
},
"type": {
Type: schema.TypeString,
Required: true,
Description: "Defines the type of the mentioned column",
},
"format": {
Type: schema.TypeString,
Optional: true,
Description: "Defines the format of the mentioned type",
},
"precision": {
Type: schema.TypeInt,
Optional: true,
Description: "The precision of the type",
},
},
}
}

// dashboardWidgetNRQLQuerySchemaElem defines a NRQL query for use on a dashboard
//
// see: newrelic/newrelic-client-go/pkg/entities/DashboardWidgetQuery
Expand Down
9 changes: 8 additions & 1 deletion newrelic/resource_newrelic_one_dashboard_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -670,7 +670,10 @@ func testAccCheckNewRelicOneDashboardConfig_PageFull(pageName string, accountID
nrql_query {
query = "FROM Transaction SELECT count(*)"
}
data_format {
name = "count"
type = "decimal"
}
warning = 0
critical = 2
}
Expand Down Expand Up @@ -782,6 +785,10 @@ func testAccCheckNewRelicOneDashboardConfig_PageFull(pageName string, accountID
direction = "desc"
name = "appName"
}
data_format {
name = "Avg duration"
type = "decimal"
}
}
widget_json {
Expand Down
109 changes: 109 additions & 0 deletions newrelic/structures_newrelic_one_dashboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"fmt"
"log"
"reflect"
"slices"
"strconv"
"strings"

Expand Down Expand Up @@ -242,6 +243,8 @@ func expandDashboardPageInput(d *schema.ResourceData, pages []interface{}, meta

// Set thresholds
rawConfiguration.Thresholds = expandDashboardBillboardWidgetConfigurationInput(d, v.(map[string]interface{}), meta, pageIndex, widgetIndex)
// Set data formatting
rawConfiguration.DataFormat = expandDashboardTableWidgetConfigDataFormatInput(v.(map[string]interface{}))

widget.RawConfiguration, err = json.Marshal(rawConfiguration)
if err != nil {
Expand Down Expand Up @@ -377,6 +380,8 @@ func expandDashboardPageInput(d *schema.ResourceData, pages []interface{}, meta
rawConfiguration.Thresholds = expandDashboardTableWidgetConfigurationThresholdInput(d, pageIndex, widgetIndex)
// Set initalSorting
rawConfiguration.InitialSorting = expandDashboardTableWidgetConfigInitialSortingInput(v.(map[string]interface{}))
// Set data formatting
rawConfiguration.DataFormat = expandDashboardTableWidgetConfigDataFormatInput(v.(map[string]interface{}))

widget.RawConfiguration, err = json.Marshal(rawConfiguration)
if err != nil {
Expand Down Expand Up @@ -571,6 +576,38 @@ func expandDashboardTableWidgetConfigInitialSortingInput(w map[string]interface{
return nil
}

func expandDashboardTableWidgetConfigDataFormatInput(w map[string]interface{}) []*dashboards.DashboardWidgetDataFormat {
var tableWidgetDataFormat []*dashboards.DashboardWidgetDataFormat
if q, ok := w["data_format"]; ok {
for _, v := range q.([]interface{}) {
dashboardDatFormatMap := v.(map[string]interface{})

var dataFormat dashboards.DashboardWidgetDataFormat

if t, ok := dashboardDatFormatMap["type"]; ok {
dataFormat.Type = t.(string)
}

if n, ok := dashboardDatFormatMap["name"]; ok {
dataFormat.Name = n.(string)
}

if f, ok := dashboardDatFormatMap["format"]; ok {
dataFormat.Format = f.(string)
}

if p, ok := dashboardDatFormatMap["precision"]; ok {
dataFormat.Precision = p.(int)
}

tableWidgetDataFormat = append(tableWidgetDataFormat, &dataFormat)
}
return tableWidgetDataFormat
}

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
Expand Down Expand Up @@ -1286,6 +1323,14 @@ func flattenDashboardWidget(in *entities.DashboardWidget, pageGUID string) (stri
}
}
}

if rawCfg.DataFormat != nil {
dataformat := flattenDashboardWidgetDataFormat(rawCfg.DataFormat)
if len(dataformat) > 0 {
out["data_format"] = dataformat
}
}

case "viz.bullet":
widgetType = "widget_bullet"
out["limit"] = rawCfg.Limit
Expand Down Expand Up @@ -1347,6 +1392,13 @@ func flattenDashboardWidget(in *entities.DashboardWidget, pageGUID string) (stri
}
}

if rawCfg.DataFormat != nil {
dataformat := flattenDashboardWidgetDataFormat(rawCfg.DataFormat)
if len(dataformat) > 0 {
out["data_format"] = dataformat
}
}

if rawCfg.Thresholds != nil {
thresholds := flattenDashboardTableWidgetThresholds(rawCfg.Thresholds)
if thresholds != nil {
Expand All @@ -1373,6 +1425,22 @@ func flattenDashboardWidgetInitialSorting(in *dashboards.DashboardWidgetInitialS
return out
}

func flattenDashboardWidgetDataFormat(in []*dashboards.DashboardWidgetDataFormat) []interface{} {
out := make([]interface{}, len(in))

for i, v := range in {
k := make(map[string]interface{})

k["name"] = v.Name
k["type"] = v.Type
k["format"] = v.Format
k["precision"] = v.Precision
out[i] = k
}

return out
}

func flattenDashboardWidgetNRQLQuery(in *[]dashboards.DashboardWidgetNRQLQueryInput) []interface{} {
out := make([]interface{}, len(*in))

Expand Down Expand Up @@ -1644,6 +1712,8 @@ func validateDashboardArguments(ctx context.Context, d *schema.ResourceDiff, met
validateThresholdFields(d, &errorsList, "widget_table")
validateThresholdFields(d, &errorsList, "widget_line")

validateWidgetDataFormatterStructure(d, &errorsList, "widget_table")
validateWidgetDataFormatterStructure(d, &errorsList, "widget_billboard")
// add any other validation functions here

if len(errorsList) == 0 {
Expand Down Expand Up @@ -1721,3 +1791,42 @@ func validateThresholdFields(d *schema.ResourceDiff, errorsList *[]string, widge

}
}

func validateWidgetDataFormatterStructure(d *schema.ResourceDiff, errorsList *[]string, widgetType string) {
_, pagesListObtained := d.GetChange("page")
pages := pagesListObtained.([]interface{})
for _, p := range pages {
page := p.(map[string]interface{})
widget, widgetOk := page[widgetType]
if widgetOk {
for _, w := range widget.([]interface{}) {
widget := w.(map[string]interface{})
dataFormats, dataFormatsOk := widget["data_format"]
if dataFormatsOk {

for _, t := range dataFormats.([]interface{}) {
dataFormat := t.(map[string]interface{})
acceptedTypes := []string{"duration", "recent-relative"}
if dataFormat["type"] != nil && (dataFormat["format"] != nil || dataFormat["format"] != "") {
if dataFormat["format"] != "" && slices.Contains(acceptedTypes, dataFormat["type"].(string)) {
*errorsList = append(*errorsList, fmt.Sprintf("'format' should not be provided if 'type' has the value %s", dataFormat["type"]))
}
}
if dataFormat["type"] != nil && dataFormat["precision"] != 0 {
if dataFormat["precision"] != "" && slices.Contains(acceptedTypes, dataFormat["type"].(string)) {
*errorsList = append(*errorsList, fmt.Sprintf("'precision' should not be provided if 'type' has the value %s", dataFormat["type"]))
}
}

if dataFormat["type"] != nil && dataFormat["format"] != 0 {
if dataFormat["type"].(string) == "date" && dataFormat["format"] != "" {
log.Printf("[WARN] 'type' should be set as 'custom' if format needs to be applied.")
}
}
}
}
}
}

}
}
2 changes: 1 addition & 1 deletion website/docs/r/api_access_key.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,4 @@ $ terraform import newrelic_api_access_key.foobar "1234567:INGEST"
## Extended Usage
This module may be used to create a user or ingest key using the `create_access_keys_service` resource, and fetch the created key using `fetch_access_keys_service`, by performing a NerdGraph query under the hood, using the ID of the key created via the resource to fetch the created key.
Please refer
[create access keys and fetch access keys](https://github.com/newrelic/terraform-provider-newrelic/blob/main/examples/modules/golden-signal-alerts-new/README.md) for more info.
[create access keys and fetch access keys](https://github.com/newrelic/terraform-provider-newrelic/blob/main/examples/modules/newrelic_api_access_key_extended/README.md) for more info.
Loading

0 comments on commit e4486dd

Please sign in to comment.