Skip to content

Commit

Permalink
Use the swagger-generated OpenAPI client for the Folder service (#1013
Browse files Browse the repository at this point in the history
)

* Replace manual Grafana client with OAPI-generated client for Folder service

* Fetch orgID in Folder service

* Add method for fetching OrgID with manual client to tests

* Add folderUID to UpdateFolder cmd

* Change 'access denied' test exp error to 403

* Add OrgID to client in dashboard test

* Add validation for force deletion rule

* Add retries to OAPI Client

* Update OAPI client dependency to use retries

* Update OAPI client dependency to use retries

* Feature parity with old client

* Oops. Upper case

---------

Co-authored-by: Julien Duchesne <[email protected]>
  • Loading branch information
nikimanoledaki and julienduchesne authored Oct 25, 2023
1 parent 2988bb3 commit 2ab68d4
Show file tree
Hide file tree
Showing 9 changed files with 114 additions and 62 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ require (
github.com/go-openapi/strfmt v0.21.7
github.com/grafana/amixr-api-go-client v0.0.10
github.com/grafana/grafana-api-golang-client v0.24.0
github.com/grafana/grafana-openapi-client-go v0.0.0-20230918131703-659d2cff09a7
github.com/grafana/grafana-openapi-client-go v0.0.0-20231025152211-949fda3a17a0
github.com/grafana/machine-learning-go-client v0.5.0
github.com/grafana/synthetic-monitoring-agent v0.18.1
github.com/grafana/synthetic-monitoring-api-go-client v0.7.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,8 @@ github.com/grafana/amixr-api-go-client v0.0.10 h1:L2Wc1aETiG7ORqmB+XSCBJdncHM/V0
github.com/grafana/amixr-api-go-client v0.0.10/go.mod h1:N6x26XUrM5zGtK5zL5vNJnAn2JFMxLFPPLTw/6pDkFE=
github.com/grafana/grafana-api-golang-client v0.24.0 h1:9cUvft7xCMnnL/Uscwy7eoldn16Gz5TH4T1MymuVs8E=
github.com/grafana/grafana-api-golang-client v0.24.0/go.mod h1:24W29gPe9yl0/3A9X624TPkAOR8DpHno490cPwnkv8E=
github.com/grafana/grafana-openapi-client-go v0.0.0-20230918131703-659d2cff09a7 h1:LPPNA/l6jCMRheMWvMPdni3WlOGL6Wjw3d/A1IbWCVg=
github.com/grafana/grafana-openapi-client-go v0.0.0-20230918131703-659d2cff09a7/go.mod h1:2vJ8YEgriYoHaNg5eijRU/q7eJTxT078VrGRSTTLeRk=
github.com/grafana/grafana-openapi-client-go v0.0.0-20231025152211-949fda3a17a0 h1:DeH1i5ORbYQnhlB2vpB4H5iSHrALPfDgr3qGx0uBApM=
github.com/grafana/grafana-openapi-client-go v0.0.0-20231025152211-949fda3a17a0/go.mod h1:2vJ8YEgriYoHaNg5eijRU/q7eJTxT078VrGRSTTLeRk=
github.com/grafana/machine-learning-go-client v0.5.0 h1:Q1K+MPSy8vfMm2jsk3WQ7O77cGr2fM5hxwtPSoPc5NU=
github.com/grafana/machine-learning-go-client v0.5.0/go.mod h1:QFfZz8NkqVF8++skjkKQXJEZfpCYd8S0yTWJUpsLLTA=
github.com/grafana/synthetic-monitoring-agent v0.18.1 h1:qQoxf4h+cUNxCtwgajehPp+4fGmRvoZubciIou1Ydw0=
Expand Down
24 changes: 17 additions & 7 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -433,13 +433,23 @@ func createGrafanaOAPIClient(apiURL string, d *schema.ResourceData) (*goapi.Graf
}

cfg := goapi.TransportConfig{
Host: u.Host,
BasePath: "/api",
Schemes: []string{u.Scheme},
TLSConfig: tlsClientConfig,
BasicAuth: userInfo,
OrgID: orgID,
APIKey: APIKey,
Host: u.Host,
BasePath: "/api",
Schemes: []string{u.Scheme},
NumRetries: d.Get("retries").(int),
RetryTimeout: time.Second * time.Duration(d.Get("retry_wait").(int)),
TLSConfig: tlsClientConfig,
BasicAuth: userInfo,
OrgID: orgID,
APIKey: APIKey,
}

if v, ok := d.GetOk("retry_status_codes"); ok {
cfg.RetryStatusCodes = common.SetToStringSlice(v.(*schema.Set))
}

if cfg.HTTPHeaders, err = getHTTPHeadersMap(d); err != nil {
return nil, err
}

return goapi.NewHTTPClientWithConfig(strfmt.Default, &cfg), nil
Expand Down
4 changes: 2 additions & 2 deletions internal/resources/grafana/data_source_folder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"strings"
"testing"

gapi "github.com/grafana/grafana-api-golang-client"
goapi "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/resource"
Expand All @@ -14,7 +14,7 @@ import (
func TestAccDatasourceFolder(t *testing.T) {
testutils.CheckOSSTestsEnabled(t)

var folder gapi.Folder
var folder goapi.Folder
checks := []resource.TestCheckFunc{
testAccFolderCheckExists("grafana_folder.test", &folder),
resource.TestCheckResourceAttr(
Expand Down
6 changes: 3 additions & 3 deletions internal/resources/grafana/data_source_folders_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@ package grafana_test
import (
"testing"

gapi "github.com/grafana/grafana-api-golang-client"
goapi "github.com/grafana/grafana-openapi-client-go/models"
"github.com/grafana/terraform-provider-grafana/internal/testutils"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

func TestAccDatasourceFolders(t *testing.T) {
testutils.CheckOSSTestsEnabled(t)

var folderA gapi.Folder
var folderB gapi.Folder
var folderA goapi.Folder
var folderB goapi.Folder
titleBase := "test-folder-"
uidBase := "test-ds-folder-uid-"
checks := []resource.TestCheckFunc{
Expand Down
19 changes: 13 additions & 6 deletions internal/resources/grafana/resource_dashboard_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"testing"

gapi "github.com/grafana/grafana-api-golang-client"
goapi "github.com/grafana/grafana-openapi-client-go/models"
"github.com/grafana/terraform-provider-grafana/internal/common"
"github.com/grafana/terraform-provider-grafana/internal/resources/grafana"
"github.com/grafana/terraform-provider-grafana/internal/testutils"
Expand Down Expand Up @@ -163,7 +164,7 @@ func TestAccDashboard_folder(t *testing.T) {
testutils.CheckOSSTestsEnabled(t)

var dashboard gapi.Dashboard
var folder gapi.Folder
var folder goapi.Folder

resource.ParallelTest(t, resource.TestCase{
ProviderFactories: testutils.ProviderFactories,
Expand All @@ -189,7 +190,7 @@ func TestAccDashboard_folder_uid(t *testing.T) {
testutils.CheckOSSTestsSemver(t, ">=8.0.0") // UID in folders were added in v8

var dashboard gapi.Dashboard
var folder gapi.Folder
var folder goapi.Folder

resource.ParallelTest(t, resource.TestCase{
ProviderFactories: testutils.ProviderFactories,
Expand Down Expand Up @@ -219,7 +220,7 @@ func TestAccDashboard_inOrg(t *testing.T) {
testutils.CheckOSSTestsEnabled(t)

var dashboard gapi.Dashboard
var folder gapi.Folder
var folder goapi.Folder
var org gapi.Org

orgName := acctest.RandString(10)
Expand Down Expand Up @@ -285,7 +286,7 @@ func testAccDashboardCheckExists(rn string, dashboard *gapi.Dashboard) resource.
}
}

func testAccDashboardCheckExistsInFolder(dashboard *gapi.Dashboard, folder *gapi.Folder) resource.TestCheckFunc {
func testAccDashboardCheckExistsInFolder(dashboard *gapi.Dashboard, folder *goapi.Folder) resource.TestCheckFunc {
return func(s *terraform.State) error {
if dashboard.FolderID != folder.ID && folder.ID != 0 {
return fmt.Errorf("dashboard.Folder(%d) does not match folder.ID(%d)", dashboard.FolderID, folder.ID)
Expand All @@ -298,6 +299,9 @@ func testAccDashboardCheckDestroy(dashboard *gapi.Dashboard, orgID int64) resour
return func(s *terraform.State) error {
// Check that the dashboard was deleted from the default organization
client := testutils.Provider.Meta().(*common.Client).GrafanaAPI
if dashboard.Model["uid"] == nil {
return fmt.Errorf("dashboard UID should be string, not nil")
}
dashboard, err := client.DashboardByUID(dashboard.Model["uid"].(string))
if dashboard != nil || err == nil {
return fmt.Errorf("dashboard still exists")
Expand All @@ -316,14 +320,17 @@ func testAccDashboardCheckDestroy(dashboard *gapi.Dashboard, orgID int64) resour
}
}

func testAccDashboardFolderCheckDestroy(dashboard *gapi.Dashboard, folder *gapi.Folder) resource.TestCheckFunc {
func testAccDashboardFolderCheckDestroy(dashboard *gapi.Dashboard, folder *goapi.Folder) resource.TestCheckFunc {
return func(s *terraform.State) error {
client := testutils.Provider.Meta().(*common.Client).GrafanaAPI
_, err := client.DashboardByUID(dashboard.Model["uid"].(string))
if err == nil {
return fmt.Errorf("dashboard still exists")
}
folder, err = grafana.GetFolderByIDorUID(client, folder.UID)

orgID := testutils.Provider.Meta().(*common.Client).GrafanaAPIConfig.OrgID
OAPIclient := testutils.Provider.Meta().(*common.Client).GrafanaOAPI.WithOrgID(orgID)
folder, err = grafana.GetFolderByIDorUID(OAPIclient.Folders, folder.UID)
if err == nil {
return fmt.Errorf("the following folder still exists: %s", folder.Title)
}
Expand Down
74 changes: 52 additions & 22 deletions internal/resources/grafana/resource_folder.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import (
"strings"

gapi "github.com/grafana/grafana-api-golang-client"
goapi "github.com/grafana/grafana-openapi-client-go/client/folders"
"github.com/grafana/grafana-openapi-client-go/models"

"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 Down Expand Up @@ -58,36 +61,51 @@ func ResourceFolder() *schema.Resource {
}

func CreateFolder(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client, orgID := ClientFromNewOrgResource(meta, d)
client, orgID := OAPIClientFromNewOrgResource(meta, d)

var body models.CreateFolderCommand
if title := d.Get("title").(string); title != "" {
body.Title = title
}

var resp gapi.Folder
var err error
title := d.Get("title").(string)
if uid, ok := d.GetOk("uid"); ok {
resp, err = client.NewFolder(title, uid.(string))
} else {
resp, err = client.NewFolder(title)
body.UID = uid.(string)
}

params := goapi.NewCreateFolderParams().WithBody(&body)
resp, err := client.Folders.CreateFolder(params, nil)
if err != nil {
return diag.Errorf("failed to create folder: %s", err)
}

d.SetId(MakeOrgResourceID(orgID, resp.ID))
d.Set("uid", resp.UID)
d.Set("title", resp.Title)
folder := resp.GetPayload()
d.SetId(MakeOrgResourceID(orgID, folder.ID))
d.Set("uid", folder.UID)
d.Set("title", folder.Title)

return ReadFolder(ctx, d, meta)
}

func UpdateFolder(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client, _, idStr := ClientFromExistingOrgResource(meta, d.Id())
client, _, idStr := OAPIClientFromExistingOrgResource(meta, d.Id())

folder, err := GetFolderByIDorUID(client, idStr)
folder, err := GetFolderByIDorUID(client.Folders, idStr)
if err != nil {
return diag.Errorf("failed to get folder %s: %s", idStr, err)
}

if err := client.UpdateFolder(folder.UID, d.Get("title").(string), d.Get("uid").(string)); err != nil {
params := goapi.NewUpdateFolderParams().
WithBody(&models.UpdateFolderCommand{
Overwrite: true,
Title: d.Get("title").(string),
}).
WithFolderUID(folder.UID)

if newUID := d.Get("uid").(string); newUID != "" {
params.Body.UID = newUID
}

if _, err := client.Folders.UpdateFolder(params, nil); err != nil {
return diag.FromErr(err)
}

Expand All @@ -96,9 +114,9 @@ func UpdateFolder(ctx context.Context, d *schema.ResourceData, meta interface{})

func ReadFolder(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
gapiURL := meta.(*common.Client).GrafanaAPIURL
client, orgID, idStr := ClientFromExistingOrgResource(meta, d.Id())
client, orgID, idStr := OAPIClientFromExistingOrgResource(meta, d.Id())

folder, err := GetFolderByIDorUID(client, idStr)
folder, err := GetFolderByIDorUID(client.Folders, idStr)
if err, shouldReturn := common.CheckReadError("folder", d, err); shouldReturn {
return err
}
Expand All @@ -113,12 +131,11 @@ func ReadFolder(ctx context.Context, d *schema.ResourceData, meta interface{}) d
}

func DeleteFolder(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client, _, idStr := ClientFromExistingOrgResource(meta, d.Id())

deleteParams := []url.Values{}
if d.Get("prevent_destroy_if_not_empty").(bool) {
// Search for dashboards and fail if any are found
dashboards, err := client.FolderDashboardSearch(url.Values{
GAPIClient, _, idStr := ClientFromExistingOrgResource(meta, d.Id())
dashboards, err := GAPIClient.FolderDashboardSearch(url.Values{
"type": []string{"dash-db"},
"folderIds": []string{idStr},
})
Expand All @@ -137,7 +154,14 @@ func DeleteFolder(ctx context.Context, d *schema.ResourceData, meta interface{})
deleteParams = append(deleteParams, gapi.ForceDeleteFolderRules())
}

if err := client.DeleteFolder(d.Get("uid").(string), deleteParams...); err != nil {
var force bool
if len(deleteParams) > 0 {
force, _ = strconv.ParseBool(deleteParams[0].Get("forceDeleteRules"))
}

client, _, _ := OAPIClientFromExistingOrgResource(meta, d.Id())
params := goapi.NewDeleteFolderParams().WithForceDeleteRules(&force).WithFolderUID(d.Get("uid").(string))
if _, err := client.Folders.DeleteFolder(params, nil); err != nil {
return diag.Errorf("failed to delete folder: %s", err)
}

Expand Down Expand Up @@ -178,16 +202,17 @@ func NormalizeFolderConfigJSON(configI interface{}) string {
return string(ret)
}

func GetFolderByIDorUID(client *gapi.Client, id string) (*gapi.Folder, error) {
func GetFolderByIDorUID(client goapi.ClientService, id string) (*models.Folder, error) {
// If the ID is a number, find the folder UID
// Getting the folder by ID is broken in some versions, but getting by UID works in all versions
// We need to use two API calls in the numerical ID case, because the "list" call doesn't have all the info
uid := id
if numericalID, err := strconv.ParseInt(id, 10, 64); err == nil {
folders, err := client.Folders()
resp, err := client.GetFolders(goapi.NewGetFoldersParams(), nil)
if err != nil {
return nil, err
}
folders := resp.GetPayload()
for _, folder := range folders {
if folder.ID == numericalID {
uid = folder.UID
Expand All @@ -196,5 +221,10 @@ func GetFolderByIDorUID(client *gapi.Client, id string) (*gapi.Folder, error) {
}
}

return client.FolderByUID(uid)
params := goapi.NewGetFolderByUIDParams().WithFolderUID(uid)
resp, err := client.GetFolderByUID(params, nil)
if err != nil {
return nil, err
}
return resp.GetPayload(), nil
}
Loading

0 comments on commit 2ab68d4

Please sign in to comment.