Skip to content

Commit 10f0ab8

Browse files
Allow configuring a report using a dashboard UID (#743)
* Allow configuring a report using a dashboard UID The whole structure of the reports API has changed. It now allows multiple dashboards. However, I'm not implementing this in this PR. I'm just changing the API to allow using UIDs because they are now the standard way to identify dashboards. If we want to support multiple dashboards, it should be a completely different attribute anyways. * Remove deprecated attributes from docs * Mention that `dashboard_id` is deprecated in the docs
1 parent 6f47efe commit 10f0ab8

File tree

8 files changed

+113
-29
lines changed

8 files changed

+113
-29
lines changed

docs/resources/report.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ EOD
2828
}
2929
3030
resource "grafana_report" "test" {
31-
name = "my report"
32-
dashboard_id = grafana_dashboard.test.dashboard_id
33-
recipients = ["[email protected]"]
31+
name = "my report"
32+
dashboard_uid = grafana_dashboard.test.uid
33+
recipients = ["[email protected]"]
3434
schedule {
3535
frequency = "hourly"
3636
}
@@ -42,13 +42,14 @@ resource "grafana_report" "test" {
4242

4343
### Required
4444

45-
- `dashboard_id` (Number) Dashboard to be sent in the report.
4645
- `name` (String) Name of the report.
4746
- `recipients` (List of String) List of recipients of the report.
4847
- `schedule` (Block List, Min: 1, Max: 1) Schedule of the report. (see [below for nested schema](#nestedblock--schedule))
4948

5049
### Optional
5150

51+
- `dashboard_id` (Number, Deprecated) Dashboard to be sent in the report. This field is deprecated, use `dashboard_uid` instead.
52+
- `dashboard_uid` (String) Dashboard to be sent in the report.
5253
- `include_dashboard_link` (Boolean) Whether to include a link to the dashboard in the report. Defaults to `true`.
5354
- `include_table_csv` (Boolean) Whether to include a CSV file of table panel data. Defaults to `false`.
5455
- `layout` (String) Layout of the report. Allowed values: `simple`, `grid`. Defaults to `grid`.

examples/resources/grafana_report/all-options.tf

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ EOD
1010

1111
resource "grafana_report" "test" {
1212
// Required attributes
13-
name = "my report updated"
14-
dashboard_id = grafana_dashboard.test.dashboard_id
15-
13+
name = "my report updated"
14+
dashboard_uid = grafana_dashboard.test.uid
15+
1616
schedule {
1717
frequency = "daily"
1818
workdays_only = true

examples/resources/grafana_report/monthly.tf

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ EOD
99
}
1010

1111
resource "grafana_report" "test" {
12-
name = "my report"
13-
dashboard_id = grafana_dashboard.test.dashboard_id
14-
recipients = ["[email protected]"]
12+
name = "my report"
13+
dashboard_uid = grafana_dashboard.test.uid
14+
recipients = ["[email protected]"]
1515
schedule {
1616
frequency = "monthly"
1717
last_day_of_month = true

examples/resources/grafana_report/resource.tf

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ EOD
99
}
1010

1111
resource "grafana_report" "test" {
12-
name = "my report"
13-
dashboard_id = grafana_dashboard.test.dashboard_id
14-
recipients = ["[email protected]"]
12+
name = "my report"
13+
dashboard_uid = grafana_dashboard.test.uid
14+
recipients = ["[email protected]"]
1515
schedule {
1616
frequency = "hourly"
1717
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ go 1.18
55
require (
66
github.com/Masterminds/semver/v3 v3.2.0
77
github.com/grafana/amixr-api-go-client v0.0.5
8-
github.com/grafana/grafana-api-golang-client v0.15.0
8+
github.com/grafana/grafana-api-golang-client v0.16.0
99
github.com/grafana/machine-learning-go-client v0.2.0
1010
github.com/grafana/synthetic-monitoring-agent v0.12.0
1111
github.com/grafana/synthetic-monitoring-api-go-client v0.6.3

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,8 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
7777
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
7878
github.com/grafana/amixr-api-go-client v0.0.5 h1:jqmljnd5FozuOsCNuyhZVpooxmj0BW9MmeLA7PaLK6U=
7979
github.com/grafana/amixr-api-go-client v0.0.5/go.mod h1:N6x26XUrM5zGtK5zL5vNJnAn2JFMxLFPPLTw/6pDkFE=
80-
github.com/grafana/grafana-api-golang-client v0.15.0 h1:eyTVvD0vlNuLhRLucOuY0/vv4ZR8Uai4N/RytMCcNng=
81-
github.com/grafana/grafana-api-golang-client v0.15.0/go.mod h1:24W29gPe9yl0/3A9X624TPkAOR8DpHno490cPwnkv8E=
80+
github.com/grafana/grafana-api-golang-client v0.16.0 h1:GcmWn5T0UdGEWkMvDwhYrDQjvtxP8ZP5+V5qvDObWxo=
81+
github.com/grafana/grafana-api-golang-client v0.16.0/go.mod h1:24W29gPe9yl0/3A9X624TPkAOR8DpHno490cPwnkv8E=
8282
github.com/grafana/machine-learning-go-client v0.2.0 h1:5JgfJn5Q72D0jZlXnM0gZ9lV4Q4zzq9X0GVfPu8Vxis=
8383
github.com/grafana/machine-learning-go-client v0.2.0/go.mod h1:QFfZz8NkqVF8++skjkKQXJEZfpCYd8S0yTWJUpsLLTA=
8484
github.com/grafana/synthetic-monitoring-agent v0.12.0 h1:CZXNegX7j+gII+WnHyU6Tanaipqr8PhlluqcKKZh97I=

grafana/resource_report.go

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,19 @@ func ResourceReport() *schema.Resource {
6767
Description: "Name of the report.",
6868
},
6969
"dashboard_id": {
70-
Type: schema.TypeInt,
71-
Required: true,
72-
Description: "Dashboard to be sent in the report.",
70+
Type: schema.TypeInt,
71+
ExactlyOneOf: []string{"dashboard_id", "dashboard_uid"},
72+
Computed: true,
73+
Optional: true,
74+
Deprecated: "Use dashboard_uid instead",
75+
Description: "Dashboard to be sent in the report. This field is deprecated, use `dashboard_uid` instead.",
76+
},
77+
"dashboard_uid": {
78+
Type: schema.TypeString,
79+
ExactlyOneOf: []string{"dashboard_id", "dashboard_uid"},
80+
Computed: true,
81+
Optional: true,
82+
Description: "Dashboard to be sent in the report.",
7383
},
7484
"recipients": {
7585
Type: schema.TypeList,
@@ -217,7 +227,7 @@ func ResourceReport() *schema.Resource {
217227
func CreateReport(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
218228
client := meta.(*client).gapi
219229

220-
report, err := schemaToReport(d)
230+
report, err := schemaToReport(client, d)
221231
if err != nil {
222232
return diag.FromErr(err)
223233
}
@@ -249,7 +259,8 @@ func ReadReport(ctx context.Context, d *schema.ResourceData, meta interface{}) d
249259
return diag.FromErr(err)
250260
}
251261

252-
d.Set("dashboard_id", r.DashboardID)
262+
d.Set("dashboard_id", r.Dashboards[0].Dashboard.ID)
263+
d.Set("dashboard_uid", r.Dashboards[0].Dashboard.UID)
253264
d.Set("name", r.Name)
254265
d.Set("recipients", strings.Split(r.Recipients, ","))
255266
d.Set("reply_to", r.ReplyTo)
@@ -259,11 +270,12 @@ func ReadReport(ctx context.Context, d *schema.ResourceData, meta interface{}) d
259270
d.Set("layout", r.Options.Layout)
260271
d.Set("orientation", r.Options.Orientation)
261272

262-
if r.Options.TimeRange.From != "" {
273+
timeRange := r.Dashboards[0].TimeRange
274+
if timeRange.From != "" {
263275
d.Set("time_range", []interface{}{
264276
map[string]interface{}{
265-
"from": r.Options.TimeRange.From,
266-
"to": r.Options.TimeRange.To,
277+
"from": timeRange.From,
278+
"to": timeRange.To,
267279
},
268280
})
269281
}
@@ -293,7 +305,7 @@ func ReadReport(ctx context.Context, d *schema.ResourceData, meta interface{}) d
293305
func UpdateReport(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
294306
client := meta.(*client).gapi
295307

296-
report, err := schemaToReport(d)
308+
report, err := schemaToReport(client, d)
297309
if err != nil {
298310
return diag.FromErr(err)
299311
}
@@ -323,10 +335,34 @@ func DeleteReport(ctx context.Context, d *schema.ResourceData, meta interface{})
323335
return nil
324336
}
325337

326-
func schemaToReport(d *schema.ResourceData) (gapi.Report, error) {
338+
func schemaToReport(client *gapi.Client, d *schema.ResourceData) (gapi.Report, error) {
339+
id := int64(d.Get("dashboard_id").(int))
340+
uid := d.Get("dashboard_uid").(string)
341+
if uid == "" {
342+
dashboards, err := client.DashboardsByIDs([]int64{id})
343+
if err != nil {
344+
return gapi.Report{}, fmt.Errorf("error getting dashboard %d: %v", id, err)
345+
}
346+
for _, dashboard := range dashboards {
347+
if int64(dashboard.ID) == id {
348+
uid = dashboard.UID
349+
break
350+
}
351+
}
352+
if uid == "" {
353+
return gapi.Report{}, fmt.Errorf("dashboard %d not found", id)
354+
}
355+
}
356+
327357
frequency := d.Get("schedule.0.frequency").(string)
328358
report := gapi.Report{
329-
DashboardID: int64(d.Get("dashboard_id").(int)),
359+
Dashboards: []gapi.ReportDashboard{
360+
{
361+
Dashboard: gapi.ReportDashboardIdentifier{
362+
UID: uid,
363+
},
364+
},
365+
},
330366
Name: d.Get("name").(string),
331367
Recipients: strings.Join(listToStringSlice(d.Get("recipients").([]interface{})), ","),
332368
ReplyTo: d.Get("reply_to").(string),
@@ -347,7 +383,7 @@ func schemaToReport(d *schema.ResourceData) (gapi.Report, error) {
347383
timeRange := d.Get("time_range").([]interface{})
348384
if len(timeRange) > 0 {
349385
timeRange := timeRange[0].(map[string]interface{})
350-
report.Options.TimeRange = gapi.ReportTimeRange{From: timeRange["from"].(string), To: timeRange["to"].(string)}
386+
report.Dashboards[0].TimeRange = gapi.ReportDashboardTimeRange{From: timeRange["from"].(string), To: timeRange["to"].(string)}
351387
}
352388

353389
// Set schedule start time

grafana/resource_report_test.go

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ func TestAccResourceReport(t *testing.T) {
2626
resource.TestCheckResourceAttrSet("grafana_report.test", "id"),
2727
resource.TestCheckResourceAttrSet("grafana_report.test", "dashboard_id"),
2828
resource.TestCheckResourceAttr("grafana_report.test", "name", "my report"),
29+
resource.TestCheckResourceAttr("grafana_report.test", "dashboard_uid", "report"),
2930
resource.TestCheckResourceAttr("grafana_report.test", "recipients.0", "[email protected]"),
3031
resource.TestCheckNoResourceAttr("grafana_report.test", "recipients.1"),
3132
resource.TestCheckResourceAttr("grafana_report.test", "schedule.0.frequency", "hourly"),
@@ -47,9 +48,10 @@ func TestAccResourceReport(t *testing.T) {
4748
// This is a custom function to delay the report ID evaluation, because it is generated after the first run
4849
return resource.ComposeTestCheckFunc(
4950
resource.TestCheckResourceAttr("grafana_report.test", "id", strconv.FormatInt(report.ID, 10)),
50-
resource.TestCheckResourceAttr("grafana_report.test", "dashboard_id", strconv.FormatInt(report.DashboardID, 10)),
51+
resource.TestCheckResourceAttr("grafana_report.test", "dashboard_id", strconv.FormatInt(report.Dashboards[0].Dashboard.ID, 10)),
5152
)(s)
5253
},
54+
resource.TestCheckResourceAttr("grafana_report.test", "dashboard_uid", "report"),
5355
resource.TestCheckResourceAttr("grafana_report.test", "name", "my report updated"),
5456
resource.TestCheckResourceAttr("grafana_report.test", "recipients.0", "[email protected]"),
5557
resource.TestCheckResourceAttr("grafana_report.test", "recipients.1", "[email protected]"),
@@ -71,6 +73,7 @@ func TestAccResourceReport(t *testing.T) {
7173
testAccReportCheckExists("grafana_report.test", &report),
7274
resource.TestCheckResourceAttrSet("grafana_report.test", "id"),
7375
resource.TestCheckResourceAttrSet("grafana_report.test", "dashboard_id"),
76+
resource.TestCheckResourceAttr("grafana_report.test", "dashboard_uid", "report"),
7477
resource.TestCheckResourceAttr("grafana_report.test", "name", "my report"),
7578
resource.TestCheckResourceAttr("grafana_report.test", "recipients.0", "[email protected]"),
7679
resource.TestCheckNoResourceAttr("grafana_report.test", "recipients.1"),
@@ -90,6 +93,29 @@ func TestAccResourceReport(t *testing.T) {
9093
})
9194
}
9295

96+
// Testing the deprecated case of using a dashboard ID instead of a dashboard UID
97+
// TODO: Remove in next major version
98+
func TestAccResourceReport_CreateFromDashboardID(t *testing.T) {
99+
CheckCloudInstanceTestsEnabled(t)
100+
101+
var report gapi.Report
102+
103+
resource.Test(t, resource.TestCase{
104+
ProviderFactories: testAccProviderFactories,
105+
CheckDestroy: testAccReportCheckDestroy(&report),
106+
Steps: []resource.TestStep{
107+
{
108+
Config: testAccReportCreateFromID,
109+
Check: resource.ComposeTestCheckFunc(
110+
testAccReportCheckExists("grafana_report.test", &report),
111+
resource.TestCheckResourceAttrSet("grafana_report.test", "dashboard_id"),
112+
resource.TestCheckResourceAttr("grafana_report.test", "dashboard_uid", "report-from-uid"),
113+
),
114+
},
115+
},
116+
})
117+
}
118+
93119
func testAccReportCheckExists(rn string, report *gapi.Report) resource.TestCheckFunc {
94120
return func(s *terraform.State) error {
95121
rs, ok := s.RootModule().Resources[rn]
@@ -131,3 +157,24 @@ func testAccReportCheckDestroy(report *gapi.Report) resource.TestCheckFunc {
131157
return nil
132158
}
133159
}
160+
161+
const testAccReportCreateFromID = `
162+
resource "grafana_dashboard" "test" {
163+
config_json = <<EOD
164+
{
165+
"title": "Dashboard for report from UID",
166+
"uid": "report-from-uid"
167+
}
168+
EOD
169+
message = "initial commit."
170+
}
171+
172+
resource "grafana_report" "test" {
173+
name = "my report"
174+
dashboard_id = grafana_dashboard.test.dashboard_id
175+
recipients = ["[email protected]"]
176+
schedule {
177+
frequency = "hourly"
178+
}
179+
}
180+
`

0 commit comments

Comments
 (0)