diff --git a/docker-compose.yml b/docker-compose.yml index 1f9a616aa..18a454747 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -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 diff --git a/docs/data-sources/folder.md b/docs/data-sources/folder.md index 17e3b3990..77ba6e6a6 100644 --- a/docs/data-sources/folder.md +++ b/docs/data-sources/folder.md @@ -29,7 +29,7 @@ data "grafana_folder" "from_title" { ### Required -- `title` (String) The name of the Grafana folder. +- `title` (String) The title of the folder. ### Optional @@ -37,6 +37,7 @@ data "grafana_folder" "from_title" { ### 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. diff --git a/internal/resources/grafana/data_source_folder.go b/internal/resources/grafana/data_source_folder.go index 720b17e3f..8a7659f07 100644 --- a/internal/resources/grafana/data_source_folder.go +++ b/internal/resources/grafana/data_source_folder.go @@ -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" @@ -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 } } @@ -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) } diff --git a/internal/resources/grafana/data_source_folder_test.go b/internal/resources/grafana/data_source_folder_test.go index 384c46730..158b43ca2 100644 --- a/internal/resources/grafana/data_source_folder_test.go +++ b/internal/resources/grafana/data_source_folder_test.go @@ -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" ) @@ -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{ @@ -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) +} diff --git a/internal/resources/grafana/resource_folder_test.go b/internal/resources/grafana/resource_folder_test.go index 6a7f6ab8e..46234798f 100644 --- a/internal/resources/grafana/resource_folder_test.go +++ b/internal/resources/grafana/resource_folder_test.go @@ -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