Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Folder datasource fixes #1416

Merged
merged 1 commit into from
Mar 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ services:
- GF_SERVER_ROOT_URL=${GRAFANA_URL}
- GF_ENTERPRISE_LICENSE_TEXT=${GF_ENTERPRISE_LICENSE_TEXT:-}
- GF_SERVER_SERVE_FROM_SUB_PATH=${GF_SERVER_SERVE_FROM_SUB_PATH:-}
- GF_FEATURE_TOGGLES_ENABLE=nestedFolders
healthcheck:
test: wget --no-verbose --tries=1 --spider http://0.0.0.0:3000/api/health || exit 1 # Use wget because older versions of Grafana don't have curl
interval: 10s
Expand Down
7 changes: 4 additions & 3 deletions docs/data-sources/folder.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,15 @@ data "grafana_folder" "from_title" {

### Required

- `title` (String) The name of the Grafana folder.
- `title` (String) The title of the folder.

### Optional

- `org_id` (String) The Organization ID. If not set, the Org ID defined in the provider block will be used.

### Read-Only

- `id` (Number) The numerical ID of the Grafana folder.
- `uid` (String) The uid of the Grafana folder.
- `id` (String) The ID of this resource.
- `parent_folder_uid` (String) The uid of the parent folder. If set, the folder will be nested. If not set, the folder will be created in the root folder. Note: This requires the nestedFolders feature flag to be enabled on your Grafana instance.
- `uid` (String) Unique identifier.
- `url` (String) The full URL of the folder.
56 changes: 14 additions & 42 deletions internal/resources/grafana/data_source_folder.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@ package grafana
import (
"context"
"fmt"
"strconv"

goapi "github.com/grafana/grafana-openapi-client-go/client"
"github.com/grafana/grafana-openapi-client-go/client/folders"
"github.com/grafana/grafana-openapi-client-go/models"
"github.com/grafana/grafana-openapi-client-go/client/search"
"github.com/grafana/terraform-provider-grafana/internal/common"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
Expand All @@ -20,53 +18,35 @@ func DatasourceFolder() *schema.Resource {
* [HTTP API](https://grafana.com/docs/grafana/latest/developers/http_api/folder/)
`,
ReadContext: dataSourceFolderRead,
Schema: map[string]*schema.Schema{
Schema: common.CloneResourceSchemaForDatasource(ResourceFolder(), map[string]*schema.Schema{
"org_id": orgIDAttribute(),
"title": {
Type: schema.TypeString,
Required: true,
Description: "The name of the Grafana folder.",
Description: "The title of the folder.",
},
"id": {
Type: schema.TypeInt,
Computed: true,
Description: "The numerical ID of the Grafana folder.",
},
"uid": {
Type: schema.TypeString,
Computed: true,
Description: "The uid of the Grafana folder.",
},
"url": {
Type: schema.TypeString,
Computed: true,
Description: "The full URL of the folder.",
},
},
"prevent_destroy_if_not_empty": nil,
}),
}
}

func findFolderWithTitle(client *goapi.GrafanaHTTPAPI, title string) (*models.Folder, error) {
func findFolderWithTitle(client *goapi.GrafanaHTTPAPI, title string) (string, error) {
var page int64 = 1

for {
params := folders.NewGetFoldersParams().WithPage(&page)
resp, err := client.Folders.GetFolders(params)
params := search.NewSearchParams().WithType(common.Ref("dash-folder")).WithPage(&page)
resp, err := client.Search.Search(params)
if err != nil {
return nil, err
return "", err
}

if len(resp.Payload) == 0 {
return nil, fmt.Errorf("folder with title %s not found", title)
return "", fmt.Errorf("folder with title %s not found", title)
}

for _, folder := range resp.Payload {
if folder.Title == title {
resp, err := client.Folders.GetFolderByUID(folder.UID)
if err != nil {
return nil, err
}
return resp.Payload, nil
return folder.UID, nil
}
}

Expand All @@ -75,19 +55,11 @@ func findFolderWithTitle(client *goapi.GrafanaHTTPAPI, title string) (*models.Fo
}

func dataSourceFolderRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
metaClient := meta.(*common.Client)
client, orgID := OAPIClientFromNewOrgResource(meta, d)

folder, err := findFolderWithTitle(client, d.Get("title").(string))
uid, err := findFolderWithTitle(client, d.Get("title").(string))
if err != nil {
return diag.FromErr(err)
}

d.SetId(strconv.FormatInt(folder.ID, 10))
d.Set("org_id", strconv.FormatInt(orgID, 10))
d.Set("uid", folder.UID)
d.Set("title", folder.Title)
d.Set("url", metaClient.GrafanaSubpath(folder.URL))

return nil
d.SetId(MakeOrgResourceID(orgID, uid))
return ReadFolder(ctx, d, meta)
}
76 changes: 63 additions & 13 deletions internal/resources/grafana/data_source_folder_test.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package grafana_test

import (
"fmt"
"os"
"strings"
"testing"

"github.com/grafana/grafana-openapi-client-go/models"
"github.com/grafana/terraform-provider-grafana/internal/common"
"github.com/grafana/terraform-provider-grafana/internal/testutils"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

Expand All @@ -17,18 +18,10 @@ func TestAccDatasourceFolder_basic(t *testing.T) {
var folder models.Folder
checks := []resource.TestCheckFunc{
folderCheckExists.exists("grafana_folder.test", &folder),
resource.TestCheckResourceAttr(
"data.grafana_folder.from_title", "title", "test-folder",
),
resource.TestMatchResourceAttr(
"data.grafana_folder.from_title", "id", common.IDRegexp,
),
resource.TestCheckResourceAttr(
"data.grafana_folder.from_title", "uid", "test-ds-folder-uid",
),
resource.TestCheckResourceAttr(
"data.grafana_folder.from_title", "url", strings.TrimRight(os.Getenv("GRAFANA_URL"), "/")+"/dashboards/f/test-ds-folder-uid/test-folder",
),
resource.TestCheckResourceAttr("data.grafana_folder.from_title", "title", "test-folder"),
resource.TestMatchResourceAttr("data.grafana_folder.from_title", "id", defaultOrgIDRegexp),
resource.TestCheckResourceAttr("data.grafana_folder.from_title", "uid", "test-ds-folder-uid"),
resource.TestCheckResourceAttr("data.grafana_folder.from_title", "url", strings.TrimRight(os.Getenv("GRAFANA_URL"), "/")+"/dashboards/f/test-ds-folder-uid/test-folder"),
}

resource.ParallelTest(t, resource.TestCase{
Expand All @@ -42,3 +35,60 @@ func TestAccDatasourceFolder_basic(t *testing.T) {
},
})
}

func TestAccDatasourceFolder_nested(t *testing.T) {
testutils.CheckOSSTestsEnabled(t, ">=10.3.0")

var parent models.Folder
var child models.Folder
randomName := acctest.RandStringFromCharSet(6, acctest.CharSetAlpha)

resource.ParallelTest(t, resource.TestCase{
ProviderFactories: testutils.ProviderFactories,
CheckDestroy: resource.ComposeTestCheckFunc(
folderCheckExists.destroyed(&parent, nil),
folderCheckExists.destroyed(&child, nil),
),
Steps: []resource.TestStep{
{
Config: testNestedFolderData(randomName),
Check: resource.ComposeTestCheckFunc(
folderCheckExists.exists("grafana_folder.parent", &parent),
folderCheckExists.exists("grafana_folder.child", &child),
resource.TestCheckResourceAttr("data.grafana_folder.parent", "title", randomName),
resource.TestCheckResourceAttr("data.grafana_folder.parent", "uid", randomName),
resource.TestMatchResourceAttr("data.grafana_folder.parent", "id", defaultOrgIDRegexp),
resource.TestCheckResourceAttr("data.grafana_folder.parent", "parent_folder_uid", ""),

resource.TestCheckResourceAttr("data.grafana_folder.child", "title", randomName+"-child"),
resource.TestCheckResourceAttr("data.grafana_folder.child", "uid", randomName+"-child"),
resource.TestMatchResourceAttr("data.grafana_folder.child", "id", defaultOrgIDRegexp),
resource.TestCheckResourceAttr("data.grafana_folder.child", "parent_folder_uid", randomName),
),
},
},
})
}

func testNestedFolderData(name string) string {
return fmt.Sprintf(`
resource "grafana_folder" "parent" {
title = "%[1]s"
uid = "%[1]s"
}

resource "grafana_folder" "child" {
title = "%[1]s-child"
uid = "%[1]s-child"
parent_folder_uid = grafana_folder.parent.uid
}

data "grafana_folder" "parent" {
title = grafana_folder.parent.title
}

data "grafana_folder" "child" {
title = grafana_folder.child.title
}
`, name)
}
2 changes: 1 addition & 1 deletion internal/resources/grafana/resource_folder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ func TestAccFolder_basic(t *testing.T) {
}

func TestAccFolder_nested(t *testing.T) {
testutils.CheckCloudInstanceTestsEnabled(t) // TODO: Switch to OSS once nested folders are enabled by default
testutils.CheckOSSTestsEnabled(t, ">=10.3.0")

var parentFolder models.Folder
var childFolder1 models.Folder
Expand Down
Loading