Skip to content

Commit f14dcad

Browse files
grafana_library_panels datasource (#1651)
Closes #376 Just doing this one to have a first plugin framework datasource
1 parent 8ab0a2c commit f14dcad

File tree

11 files changed

+302
-6
lines changed

11 files changed

+302
-6
lines changed

docs/data-sources/library_panels.md

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
---
2+
# generated by https://github.com/hashicorp/terraform-plugin-docs
3+
page_title: "grafana_library_panels Data Source - terraform-provider-grafana"
4+
subcategory: "Grafana OSS"
5+
description: |-
6+
7+
---
8+
9+
# grafana_library_panels (Data Source)
10+
11+
12+
13+
## Example Usage
14+
15+
```terraform
16+
resource "grafana_library_panel" "test" {
17+
name = "panelname"
18+
model_json = jsonencode({
19+
title = "test name"
20+
type = "text"
21+
version = 0
22+
description = "test description"
23+
})
24+
}
25+
26+
resource "grafana_folder" "test" {
27+
title = "Panel Folder"
28+
uid = "panelname-folder"
29+
}
30+
31+
resource "grafana_library_panel" "folder" {
32+
name = "panelname In Folder"
33+
folder_uid = grafana_folder.test.uid
34+
model_json = jsonencode({
35+
gridPos = {
36+
x = 0
37+
y = 0
38+
h = 10
39+
w = 10
40+
}
41+
title = "panel"
42+
type = "text"
43+
version = 0
44+
})
45+
}
46+
47+
data "grafana_library_panels" "all" {
48+
depends_on = [grafana_library_panel.folder, grafana_library_panel.test]
49+
}
50+
```
51+
52+
<!-- schema generated by tfplugindocs -->
53+
## Schema
54+
55+
### Optional
56+
57+
- `org_id` (String) The Organization ID. If not set, the default organization is used for basic authentication, or the one that owns your service account for token authentication.
58+
59+
### Read-Only
60+
61+
- `id` (String) The ID of this resource.
62+
- `panels` (Set of Object) (see [below for nested schema](#nestedatt--panels))
63+
64+
<a id="nestedatt--panels"></a>
65+
### Nested Schema for `panels`
66+
67+
Read-Only:
68+
69+
- `description` (String)
70+
- `folder_uid` (String)
71+
- `model_json` (String)
72+
- `name` (String)
73+
- `uid` (String)

docs/resources/dashboard_permission_item.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ resource "grafana_dashboard_permission_item" "team" {
5959

6060
### Optional
6161

62-
- `org_id` (String) The Organization ID. If not set, the Org ID defined in the provider block will be used.
62+
- `org_id` (String) The Organization ID. If not set, the default organization is used for basic authentication, or the one that owns your service account for token authentication.
6363
- `role` (String) the role onto which the permission is to be assigned
6464
- `team` (String) the team onto which the permission is to be assigned
6565
- `user` (String) the user or service account onto which the permission is to be assigned

docs/resources/data_source_permission_item.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ resource "grafana_data_source_permission_item" "service_account" {
7979

8080
### Optional
8181

82-
- `org_id` (String) The Organization ID. If not set, the Org ID defined in the provider block will be used.
82+
- `org_id` (String) The Organization ID. If not set, the default organization is used for basic authentication, or the one that owns your service account for token authentication.
8383
- `role` (String) the role onto which the permission is to be assigned
8484
- `team` (String) the team onto which the permission is to be assigned
8585
- `user` (String) the user or service account onto which the permission is to be assigned

docs/resources/folder_permission_item.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ resource "grafana_folder_permission_item" "on_user" {
6060

6161
### Optional
6262

63-
- `org_id` (String) The Organization ID. If not set, the Org ID defined in the provider block will be used.
63+
- `org_id` (String) The Organization ID. If not set, the default organization is used for basic authentication, or the one that owns your service account for token authentication.
6464
- `role` (String) the role onto which the permission is to be assigned
6565
- `team` (String) the team onto which the permission is to be assigned
6666
- `user` (String) the user or service account onto which the permission is to be assigned

docs/resources/role_assignment_item.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ resource "grafana_role_assignment_item" "service_account" {
6565

6666
### Optional
6767

68-
- `org_id` (String) The Organization ID. If not set, the Org ID defined in the provider block will be used.
68+
- `org_id` (String) The Organization ID. If not set, the default organization is used for basic authentication, or the one that owns your service account for token authentication.
6969
- `service_account_id` (String) the service account onto which the role is to be assigned
7070
- `team_id` (String) the team onto which the role is to be assigned
7171
- `user_id` (String) the user onto which the role is to be assigned

docs/resources/service_account_permission_item.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ resource "grafana_service_account_permission_item" "on_user" {
5454

5555
### Optional
5656

57-
- `org_id` (String) The Organization ID. If not set, the Org ID defined in the provider block will be used.
57+
- `org_id` (String) The Organization ID. If not set, the default organization is used for basic authentication, or the one that owns your service account for token authentication.
5858
- `team` (String) the team onto which the permission is to be assigned
5959
- `user` (String) the user or service account onto which the permission is to be assigned
6060

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
resource "grafana_library_panel" "test" {
2+
name = "panelname"
3+
model_json = jsonencode({
4+
title = "test name"
5+
type = "text"
6+
version = 0
7+
description = "test description"
8+
})
9+
}
10+
11+
resource "grafana_folder" "test" {
12+
title = "Panel Folder"
13+
uid = "panelname-folder"
14+
}
15+
16+
resource "grafana_library_panel" "folder" {
17+
name = "panelname In Folder"
18+
folder_uid = grafana_folder.test.uid
19+
model_json = jsonencode({
20+
gridPos = {
21+
x = 0
22+
y = 0
23+
h = 10
24+
w = 10
25+
}
26+
title = "panel"
27+
type = "text"
28+
version = 0
29+
})
30+
}
31+
32+
data "grafana_library_panels" "all" {
33+
depends_on = [grafana_library_panel.folder, grafana_library_panel.test]
34+
}

internal/resources/grafana/common_plugin_framework.go

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77

88
goapi "github.com/grafana/grafana-openapi-client-go/client"
99
"github.com/grafana/terraform-provider-grafana/v3/internal/common"
10+
"github.com/hashicorp/terraform-plugin-framework/datasource"
1011
"github.com/hashicorp/terraform-plugin-framework/path"
1112
"github.com/hashicorp/terraform-plugin-framework/resource"
1213
frameworkSchema "github.com/hashicorp/terraform-plugin-framework/resource/schema"
@@ -15,6 +16,49 @@ import (
1516
"github.com/hashicorp/terraform-plugin-framework/types"
1617
)
1718

19+
type basePluginFrameworkDataSource struct {
20+
client *goapi.GrafanaHTTPAPI
21+
config *goapi.TransportConfig
22+
}
23+
24+
func (r *basePluginFrameworkDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
25+
// Configure is called multiple times (sometimes when ProviderData is not yet available), we only want to configure once
26+
if req.ProviderData == nil || r.client != nil {
27+
return
28+
}
29+
30+
client, ok := req.ProviderData.(*common.Client)
31+
32+
if !ok {
33+
resp.Diagnostics.AddError(
34+
"Unexpected Resource Configure Type",
35+
fmt.Sprintf("Expected *common.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData),
36+
)
37+
38+
return
39+
}
40+
41+
r.client = client.GrafanaAPI
42+
r.config = client.GrafanaAPIConfig
43+
}
44+
45+
// clientFromNewOrgResource creates an OpenAPI client from the `org_id` attribute of a resource
46+
// This client is meant to be used in `Create` functions when the ID hasn't already been baked into the resource ID
47+
func (r *basePluginFrameworkDataSource) clientFromNewOrgResource(orgIDStr string) (*goapi.GrafanaHTTPAPI, int64, error) {
48+
if r.client == nil {
49+
return nil, 0, fmt.Errorf("client not configured")
50+
}
51+
52+
client := r.client.Clone()
53+
orgID, _ := strconv.ParseInt(orgIDStr, 10, 64)
54+
if orgID == 0 {
55+
orgID = client.OrgID()
56+
} else if orgID > 0 {
57+
client = client.WithOrgID(orgID)
58+
}
59+
return client, orgID, nil
60+
}
61+
1862
type basePluginFrameworkResource struct {
1963
client *goapi.GrafanaHTTPAPI
2064
config *goapi.TransportConfig
@@ -98,7 +142,7 @@ func pluginFrameworkOrgIDAttribute() frameworkSchema.Attribute {
98142
return frameworkSchema.StringAttribute{
99143
Optional: true,
100144
Computed: true,
101-
Description: "The Organization ID. If not set, the Org ID defined in the provider block will be used.",
145+
Description: "The Organization ID. If not set, the default organization is used for basic authentication, or the one that owns your service account for token authentication.",
102146
PlanModifiers: []planmodifier.String{
103147
stringplanmodifier.RequiresReplace(),
104148
&orgIDAttributePlanModifier{},
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package grafana
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
7+
"github.com/grafana/grafana-openapi-client-go/client/library_elements"
8+
"github.com/grafana/terraform-provider-grafana/v3/internal/common"
9+
"github.com/hashicorp/terraform-plugin-framework/attr"
10+
"github.com/hashicorp/terraform-plugin-framework/datasource"
11+
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
12+
"github.com/hashicorp/terraform-plugin-framework/diag"
13+
"github.com/hashicorp/terraform-plugin-framework/types"
14+
)
15+
16+
var dataSourceLibraryPanelsName = "grafana_library_panels"
17+
18+
func datasourceLibraryPanels() *common.DataSource {
19+
return common.NewDataSource(
20+
common.CategoryGrafanaOSS,
21+
dataSourceLibraryPanelsName,
22+
&libraryPanelsDataSource{},
23+
)
24+
}
25+
26+
type libraryPanelsDataSource struct {
27+
basePluginFrameworkDataSource
28+
}
29+
30+
func (r *libraryPanelsDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
31+
resp.TypeName = dataSourceLibraryPanelsName
32+
}
33+
34+
func (r *libraryPanelsDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
35+
resp.Schema = schema.Schema{
36+
Attributes: map[string]schema.Attribute{
37+
"id": schema.StringAttribute{
38+
Computed: true,
39+
},
40+
"org_id": pluginFrameworkOrgIDAttribute(),
41+
"panels": schema.SetAttribute{
42+
Computed: true,
43+
ElementType: types.ObjectType{
44+
AttrTypes: map[string]attr.Type{
45+
"name": types.StringType,
46+
"uid": types.StringType,
47+
"description": types.StringType,
48+
"folder_uid": types.StringType,
49+
"model_json": types.StringType,
50+
},
51+
},
52+
},
53+
},
54+
}
55+
}
56+
57+
type libraryPanelsDataSourcePanelModel struct {
58+
Name types.String `tfsdk:"name"`
59+
UID types.String `tfsdk:"uid"`
60+
Description types.String `tfsdk:"description"`
61+
FolderUID types.String `tfsdk:"folder_uid"`
62+
ModelJSON types.String `tfsdk:"model_json"`
63+
}
64+
65+
type libraryPanelsDataSourceModel struct {
66+
ID types.String `tfsdk:"id"`
67+
OrgID types.String `tfsdk:"org_id"`
68+
Panels []libraryPanelsDataSourcePanelModel `tfsdk:"panels"`
69+
}
70+
71+
func (r *libraryPanelsDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
72+
// Read Terraform state data into the model
73+
var data libraryPanelsDataSourceModel
74+
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
75+
76+
// Read from API
77+
client, _, err := r.clientFromNewOrgResource(data.OrgID.ValueString())
78+
if err != nil {
79+
resp.Diagnostics = diag.Diagnostics{diag.NewErrorDiagnostic("Failed to create client", err.Error())}
80+
return
81+
}
82+
params := library_elements.NewGetLibraryElementsParams().WithKind(common.Ref(libraryPanelKind))
83+
apiResp, err := client.LibraryElements.GetLibraryElements(params)
84+
if err != nil {
85+
resp.Diagnostics = diag.Diagnostics{diag.NewErrorDiagnostic("Failed to get library panels", err.Error())}
86+
return
87+
}
88+
for _, panel := range apiResp.Payload.Result.Elements {
89+
modelJSONBytes, err := json.Marshal(panel.Model)
90+
if err != nil {
91+
resp.Diagnostics = diag.Diagnostics{diag.NewErrorDiagnostic("Failed to get library panel JSON", err.Error())}
92+
return
93+
}
94+
data.Panels = append(data.Panels, libraryPanelsDataSourcePanelModel{
95+
Name: types.StringValue(panel.Name),
96+
UID: types.StringValue(panel.UID),
97+
Description: types.StringValue(panel.Description),
98+
FolderUID: types.StringValue(panel.Meta.FolderUID),
99+
ModelJSON: types.StringValue(string(modelJSONBytes)),
100+
})
101+
}
102+
data.ID = types.StringValue(data.OrgID.ValueString())
103+
104+
// Save data into Terraform state
105+
resp.Diagnostics.Append(resp.State.Set(ctx, data)...)
106+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package grafana_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/grafana/terraform-provider-grafana/v3/internal/testutils"
7+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
8+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
9+
)
10+
11+
func TestAccDatasourceLibraryPanels_basic(t *testing.T) {
12+
testutils.CheckOSSTestsEnabled(t, ">=8.0.0")
13+
14+
randomName := acctest.RandString(10)
15+
16+
resource.ParallelTest(t, resource.TestCase{
17+
ProtoV5ProviderFactories: testutils.ProtoV5ProviderFactories,
18+
Steps: []resource.TestStep{
19+
{
20+
Config: testutils.TestAccExampleWithReplace(t, "data-sources/grafana_library_panels/data-source.tf", map[string]string{
21+
"panelname": randomName,
22+
}),
23+
Check: resource.ComposeTestCheckFunc(
24+
resource.TestCheckTypeSetElemNestedAttrs("data.grafana_library_panels.all", "panels.*", map[string]string{
25+
"description": "test description",
26+
"folder_uid": "",
27+
"panels.0.name": randomName,
28+
}),
29+
resource.TestCheckTypeSetElemNestedAttrs("data.grafana_library_panels.all", "panels.*", map[string]string{
30+
"description": "",
31+
"folder_uid": randomName + "-folder",
32+
"panels.0.name": randomName + " In Folder",
33+
}),
34+
),
35+
},
36+
},
37+
})
38+
}

internal/resources/grafana/resources.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ var DataSources = addValidationToDataSources(
9191
datasourceFolder(),
9292
datasourceFolders(),
9393
datasourceLibraryPanel(),
94+
datasourceLibraryPanels(),
9495
datasourceUser(),
9596
datasourceUsers(),
9697
datasourceRole(),

0 commit comments

Comments
 (0)