Skip to content

Commit

Permalink
filter: move validation to schema
Browse files Browse the repository at this point in the history
While making changes to filters you need to run apply to actually find
out whether the expression is valid. This is a slow feedback cycle for
developers and there is an API endpoint that allows you to validate
expressions before using them so it's a no brainer to improve the
validation in the schema.

This updates the ValidateFunc for expression to call out to the
validation API endpoint and raise those exceptions earlier in the
development cycle.

Take 2 of #848 now that API tokens are supported in the routes.

Closes #846
  • Loading branch information
jacobbednarz committed Nov 1, 2021
1 parent c6a77db commit 3014a32
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 3 deletions.
24 changes: 24 additions & 0 deletions cloudflare/resource_cloudflare_filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"html"
"log"
"os"
"strings"

cloudflare "github.com/cloudflare/cloudflare-go"
Expand Down Expand Up @@ -38,6 +39,29 @@ func resourceCloudflareFilter() *schema.Resource {
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
return strings.TrimSpace(new) == old
},
ValidateFunc: func(val interface{}, key string) (warns []string, errs []error) {
var api *cloudflare.API
var err error

// Establish a bare bones cloudflare client to make the validation call.
if os.Getenv("CLOUDFLARE_API_TOKEN") != "" {
api, err = cloudflare.NewWithAPIToken(os.Getenv("CLOUDFLARE_API_TOKEN"), cloudflare.UserAgent("terraform-provider-cloudflare/unknown"))
if err != nil {
log.Fatal(err)
}
} else {
api, err = cloudflare.New(os.Getenv("CLOUDFLARE_API_KEY"), os.Getenv("CLOUDFLARE_EMAIL"), cloudflare.UserAgent("terraform-provider-cloudflare/unknown"))
if err != nil {
log.Fatal(err)
}
}

expression := val.(string)
if err := api.ValidateFilterExpression(context.Background(), expression); err != nil {
errs = append(errs, fmt.Errorf("filter expression is invalid: %s", err))
}
return
},
},
"description": {
Type: schema.TypeString,
Expand Down
58 changes: 55 additions & 3 deletions cloudflare/resource_cloudflare_filter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"log"
"os"
"regexp"
"testing"

"github.com/cloudflare/cloudflare-go"
Expand Down Expand Up @@ -42,7 +43,7 @@ func testSweepCloudflareFilterSweeper(r string) error {
return nil
}

func TestAccFilterSimple(t *testing.T) {
func TestAccFilter_Simple(t *testing.T) {
rnd := generateRandomResourceName()
name := "cloudflare_filter." + rnd
zoneID := os.Getenv("CLOUDFLARE_ZONE_ID")
Expand All @@ -67,6 +68,57 @@ func TestAccFilterSimple(t *testing.T) {
})
}

func TestAccFilter_InvalidExpressionWithAPIToken(t *testing.T) {
// Temporarily unset CLOUDFLARE_API_TOKEN to confirm users depending on API
// tokens can also work with the filter validation.
if os.Getenv("CLOUDFLARE_API_TOKEN") != "" {
defer func(apiToken string) {
os.Setenv("CLOUDFLARE_API_TOKEN", apiToken)
}(os.Getenv("CLOUDFLARE_API_TOKEN"))
os.Setenv("CLOUDFLARE_API_TOKEN", "")
}
rnd := generateRandomResourceName()
zoneID := os.Getenv("CLOUDFLARE_ZONE_ID")

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testFilterInvalidExpressionWithAPIKey(rnd, zoneID, "true", "this is notes", `(foo eq \"bar\")`),
ExpectError: regexp.MustCompile("filter expression is invalid"),
},
},
})
}

func TestAccFilter_InvalidExpressionWithAPIKey(t *testing.T) {
rnd := generateRandomResourceName()
zoneID := os.Getenv("CLOUDFLARE_ZONE_ID")

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testFilterInvalidExpressionWithAPIKey(rnd, zoneID, "true", "this is notes", `(foo eq \"bar\")`),
ExpectError: regexp.MustCompile("filter expression is invalid"),
},
},
})
}

func testFilterInvalidExpressionWithAPIKey(resourceID, zoneID, paused, description, expression string) string {
return fmt.Sprintf(`
resource "cloudflare_filter" "%[1]s" {
zone_id = "%[2]s"
paused = "%[3]s"
description = "%[4]s"
expression = "%[5]s"
}
`, resourceID, zoneID, paused, description, expression)
}

func testFilterConfig(resourceID, zoneID, paused, description, expression string) string {
return fmt.Sprintf(`
resource "cloudflare_filter" "%[1]s" {
Expand All @@ -89,7 +141,7 @@ EOF
}
`

func TestAccFilterWhitespace(t *testing.T) {
func TestAccFilter_Whitespace(t *testing.T) {
rnd := generateRandomResourceName()
zoneID := os.Getenv("CLOUDFLARE_ZONE_ID")

Expand All @@ -105,7 +157,7 @@ func TestAccFilterWhitespace(t *testing.T) {
})
}

func TestAccFilterHTMLEntity(t *testing.T) {
func TestAccFilter_HTMLEntity(t *testing.T) {
rnd := generateRandomResourceName()
name := "cloudflare_filter." + rnd
zoneID := os.Getenv("CLOUDFLARE_ZONE_ID")
Expand Down

0 comments on commit 3014a32

Please sign in to comment.