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 53dfba5
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 3 deletions.
3 changes: 3 additions & 0 deletions .changelog/1290.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
resource/cloudflare_filter: move `expression` validation into schema
```
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 TestAccCloudflareFilter_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 TestAccCloudflareFilter_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: testFilterInvalidExpression(rnd, zoneID),
ExpectError: regexp.MustCompile("filter expression is invalid"),
},
},
})
}

func TestAccCloudflareFilter_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: testFilterInvalidExpression(rnd, zoneID),
ExpectError: regexp.MustCompile("filter expression is invalid"),
},
},
})
}

func testFilterInvalidExpression(resourceID, zoneID string) string {
return fmt.Sprintf(`
resource "cloudflare_filter" "%[1]s" {
zone_id = "%[2]s"
paused = "true"
description = "example"
expression = "(foo eq \"bar\")"
}
`, resourceID, zoneID)
}

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 TestAccCloudflareFilter_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 TestAccCloudflareFilter_HTMLEntity(t *testing.T) {
rnd := generateRandomResourceName()
name := "cloudflare_filter." + rnd
zoneID := os.Getenv("CLOUDFLARE_ZONE_ID")
Expand Down

0 comments on commit 53dfba5

Please sign in to comment.