Skip to content

Commit

Permalink
Implement sentry filter (#20)
Browse files Browse the repository at this point in the history
  • Loading branch information
fa93hws authored Jan 14, 2021
1 parent f8dd6cf commit eca5867
Show file tree
Hide file tree
Showing 10 changed files with 428 additions and 55 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go 1.13

require (
github.com/dghubble/sling v1.3.0
github.com/google/go-cmp v0.3.1
github.com/hashicorp/terraform-plugin-sdk v1.7.0
github.com/mitchellh/mapstructure v1.1.2
github.com/stretchr/testify v1.5.1
Expand Down
1 change: 1 addition & 0 deletions sentry/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ func Provider() terraform.ResourceProvider {
"sentry_key": resourceSentryKey(),
"sentry_plugin": resourceSentryPlugin(),
"sentry_rule": resourceSentryRule(),
"sentry_filter": resourceSentryFilter(),
},

DataSourcesMap: map[string]*schema.Resource{
Expand Down
117 changes: 117 additions & 0 deletions sentry/resource_sentry_project_filter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package sentry

import (
"errors"
"fmt"
"log"
"strings"

"github.com/canva/terraform-provider-sentry/sentryclient"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
)

func resourceSentryFilter() *schema.Resource {
return &schema.Resource{
Create: resourceSentryFilterUpdate,
Read: resourceSentryFilterRead,
Update: resourceSentryFilterUpdate,
Delete: resourceSentryFilterDelete,
Importer: &schema.ResourceImporter{
State: resourceSentryFilterImporter,
},
Schema: map[string]*schema.Schema{
"organization": {
Type: schema.TypeString,
Required: true,
Description: "The slug of the organization the project belongs to",
},
"project": {
Type: schema.TypeString,
Required: true,
Description: "The slug of the project to create the plugin for",
},
"browser_extension": {
Type: schema.TypeBool,
Required: true,
Description: "Whether to filter out events from browser extension",
},
"legacy_browsers": {
Type: schema.TypeList,
Required: true,
Description: "Events from these legacy browsers will be ignored",
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
},
}
}

func resourceSentryFilterRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*sentryclient.Client)
org := d.Get("organization").(string)
project := d.Get("project").(string)
filterConfig, _, err := client.ProjectFilter.Get(org, project)
if err != nil {
return err
}
d.SetId(fmt.Sprintf("%s-%s_filter", org, project))
d.Set("organization", org)
d.Set("project", project)
d.Set("browser_extension", filterConfig.BrowserExtension)
return nil
}

func resourceSentryFilterUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*sentryclient.Client)
browserExtension := d.Get("browser_extension").(bool)
inputLegacyBrowsers := d.Get("legacy_browsers").([]interface{})
legacyBrowsers := make([]string, len(inputLegacyBrowsers))
for idx, browser := range inputLegacyBrowsers {
legacyBrowsers[idx] = browser.(string)
}
org := d.Get("organization").(string)
project := d.Get("project").(string)
_, err := client.ProjectFilter.UpdateBrowserExtensions(org, project, browserExtension)
if err != nil {
return err
}
_, err = client.ProjectFilter.UpdateLegacyBrowser(org, project, legacyBrowsers)
if err != nil {
return err
}
return resourceSentryFilterRead(d, meta)
}

func resourceSentryFilterDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*sentryclient.Client)
org := d.Get("organization").(string)
project := d.Get("project").(string)
_, err := client.ProjectFilter.UpdateBrowserExtensions(org, project, false)
if err != nil {
return err
}
_, err = client.ProjectFilter.UpdateLegacyBrowser(org, project, []string{})
if err != nil {
return err
}
return resourceSentryFilterRead(d, meta)
}

func resourceSentryFilterImporter(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
addrID := d.Id()

log.Printf("[DEBUG] Importing filter using ADDR ID %s", addrID)

parts := strings.Split(addrID, "/")

if len(parts) != 3 {
return nil, errors.New("Project import requires an ADDR ID of the following schema org-slug/project-slug/rule-id")
}

d.Set("organization", parts[0])
d.Set("project", parts[1])
d.SetId(parts[2])

return []*schema.ResourceData{d}, nil
}
79 changes: 79 additions & 0 deletions sentry/resource_sentry_project_filter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package sentry

import (
"fmt"
"testing"

"github.com/canva/terraform-provider-sentry/sentryclient"
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/terraform"
)

func TestAccSentryFilterUpdate(t *testing.T) {
var filterConfig sentryclient.FilterConfig
testAccSentryFilterUpdateConfig := fmt.Sprintf(`
resource "sentry_filter" "test_filter" {
organization = "%s"
project = "%s"
browser_extension = true
legacy_browsers = ["ie_pre_9","ie10"]
}
`, testOrganization, projectSlug)

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckSentryFilterDestroy,
Steps: []resource.TestStep{
{
Config: testAccSentryFilterUpdateConfig,
Check: testFilterConfig(&filterConfig, true, []string{"ie_pre_9", "ie10"}),
},
},
})
}

func contains(array []string, element string) bool {
for _, a := range array {
if a == element {
return true
}
}
return false
}

func testFilterConfig(filterConfig *sentryclient.FilterConfig, browserExtension bool, legacyBrowsers []string) resource.TestCheckFunc {
return func(s *terraform.State) error {
client := testAccProvider.Meta().(*sentryclient.Client)
filterConfig, _, err := client.ProjectFilter.Get(testOrganization, projectSlug)
if err != nil {
return err
}
if filterConfig.BrowserExtension != browserExtension {
return fmt.Errorf("got browser_extension %t; want %t", filterConfig.BrowserExtension, browserExtension)
}

for _, browser := range legacyBrowsers {
if !contains(filterConfig.LegacyBrowsers, browser) {
return fmt.Errorf("got legacy_browser %v; want %v", filterConfig.LegacyBrowsers, legacyBrowsers)
}
}

return nil
}
}

func testAccCheckSentryFilterDestroy(s *terraform.State) error {
client := testAccProvider.Meta().(*sentryclient.Client)
filterConfig, _, err := client.ProjectFilter.Get(testOrganization, projectSlug)
if err != nil {
return err
}
if filterConfig.BrowserExtension != false {
return fmt.Errorf("got browser_extension %t; want false", filterConfig.BrowserExtension)
}
if len(filterConfig.LegacyBrowsers) != 0 {
return fmt.Errorf("got legacy_browser %v; want []", filterConfig.LegacyBrowsers)
}
return nil
}
69 changes: 32 additions & 37 deletions sentry/resource_sentry_project_rule_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,16 @@ import (
"testing"

"github.com/canva/terraform-provider-sentry/sentryclient"
"github.com/google/go-cmp/cmp"
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/terraform"
"github.com/google/go-cmp/cmp"
)

const projectSlug = "test-project"

func TestAccSentryRule_basic(t *testing.T) {
var rule sentryclient.Rule


testAccSentryRuleUpdateConfig := fmt.Sprintf(`
resource "sentry_rule" "test_rule" {
name = "Important Issue"
Expand All @@ -34,13 +33,13 @@ func TestAccSentryRule_basic(t *testing.T) {
{
id = "sentry.rules.conditions.event_frequency.EventFrequencyCondition"
value = 101
name = "An issue is seen more than 101 times in 1h"
name = "The issue is seen more than 101 times in 1h"
interval = "1h"
},
{
id = "sentry.rules.conditions.event_frequency.EventUniqueUserFrequencyCondition"
interval = "1m"
name = "An issue is seen by more than 30 users in 1 minute"
name = "The issue is seen by more than 30 users in 1 minute"
value = 30
}
]
Expand All @@ -57,28 +56,28 @@ func TestAccSentryRule_basic(t *testing.T) {
Check: resource.ComposeTestCheckFunc(
testAccCheckSentryRuleExists("sentry_rule.test_rule", &rule, testOrganization, projectSlug),
testAccCheckSentryRuleAttributes(&rule, &testAccSentryRuleExpectedAttributes{
Name: "Important Issue",
Name: "Important Issue",
ActionMatch: "all",
Frequency: 1440,
Frequency: 1440,
Environment: "prod",
Actions: []sentryclient.RuleAction{
{
ID: "sentry.rules.actions.notify_event.NotifyEventAction",
Name: "Send a notification (for all legacy integrations)", // Default name added by Sentry
ID: "sentry.rules.actions.notify_event.NotifyEventAction",
Name: "Send a notification (for all legacy integrations)", // Default name added by Sentry
},
},
Conditions: []sentryclient.RuleCondition{
{
ID: "sentry.rules.conditions.event_frequency.EventFrequencyCondition",
Value: 100,
Name: "An issue is seen more than 100 times in 1m",
ID: "sentry.rules.conditions.event_frequency.EventFrequencyCondition",
Value: 100,
Name: "The issue is seen more than 100 times in 1m",
Interval: "1m",
},
{
ID: "sentry.rules.conditions.event_frequency.EventUniqueUserFrequencyCondition",
ID: "sentry.rules.conditions.event_frequency.EventUniqueUserFrequencyCondition",
Interval: "1m",
Name: "An issue is seen by more than 25 users in 1m",
Value: 25,
Name: "The issue is seen by more than 25 users in 1m",
Value: 25,
},
},
}),
Expand All @@ -89,28 +88,28 @@ func TestAccSentryRule_basic(t *testing.T) {
Check: resource.ComposeTestCheckFunc(
testAccCheckSentryRuleExists("sentry_rule.test_rule", &rule, testOrganization, projectSlug),
testAccCheckSentryRuleAttributes(&rule, &testAccSentryRuleExpectedAttributes{
Name: "Important Issue",
Name: "Important Issue",
ActionMatch: "all",
Frequency: 1300,
Frequency: 1300,
Environment: "prod",
Actions: []sentryclient.RuleAction{
{
ID: "sentry.rules.actions.notify_event.NotifyEventAction",
Name: "Send a notification (for all legacy integrations)", // Default name added by Sentry
ID: "sentry.rules.actions.notify_event.NotifyEventAction",
Name: "Send a notification (for all legacy integrations)", // Default name added by Sentry
},
},
Conditions: []sentryclient.RuleCondition{
{
ID: "sentry.rules.conditions.event_frequency.EventFrequencyCondition",
Value: 101,
Name: "An issue is seen more than 101 times in 1h",
ID: "sentry.rules.conditions.event_frequency.EventFrequencyCondition",
Value: 101,
Name: "The issue is seen more than 101 times in 1h",
Interval: "1h",
},
{
ID: "sentry.rules.conditions.event_frequency.EventUniqueUserFrequencyCondition",
ID: "sentry.rules.conditions.event_frequency.EventUniqueUserFrequencyCondition",
Interval: "1m",
Name: "An issue is seen by more than 30 users in 1m",
Value: 30,
Name: "The issue is seen by more than 30 users in 1m",
Value: 30,
},
},
}),
Expand All @@ -128,7 +127,6 @@ func testAccCheckSentryRuleDestroy(s *terraform.State) error {
continue
}


rules, resp, err := client.Rules.List(rs.Primary.Attributes["organization"], projectSlug)
var rule *sentryclient.Rule
for _, r := range rules {
Expand Down Expand Up @@ -191,14 +189,12 @@ type testAccSentryRuleExpectedAttributes struct {
// Organization string
// Project string
ActionMatch string
Frequency int
Frequency int
Environment string
Actions []sentryclient.RuleAction
Conditions []sentryclient.RuleCondition
Actions []sentryclient.RuleAction
Conditions []sentryclient.RuleCondition
}



func testAccCheckSentryRuleAttributes(rule *sentryclient.Rule, want *testAccSentryRuleExpectedAttributes) resource.TestCheckFunc {
return func(s *terraform.State) error {
if rule.Name != want.Name {
Expand All @@ -217,19 +213,18 @@ func testAccCheckSentryRuleAttributes(rule *sentryclient.Rule, want *testAccSent
return fmt.Errorf("got environment %s; want %s", rule.Environment, want.Environment)
}

if !cmp.Equal(rule.Actions, want.Actions){
return fmt.Errorf("got actions: %+v\n; want %+v\n", rule.Actions, want.Actions)
if !cmp.Equal(rule.Actions, want.Actions) {
return fmt.Errorf("got actions: %+v\n; want %+v\n", rule.Actions, want.Actions)
}

if !cmp.Equal(rule.Conditions, want.Conditions){
return fmt.Errorf("got conditions: %+v\n; want %+v\n", rule.Conditions, want.Conditions)
if !cmp.Equal(rule.Conditions, want.Conditions) {
return fmt.Errorf("got conditions: %+v\n; want %+v\n", rule.Conditions, want.Conditions)
}

return nil
}
}


var testAccSentryRuleConfig = fmt.Sprintf(`
resource "sentry_rule" "test_rule" {
name = "Important Issue"
Expand All @@ -247,13 +242,13 @@ resource "sentry_rule" "test_rule" {
{
id = "sentry.rules.conditions.event_frequency.EventFrequencyCondition"
value = 100
name = "An issue is seen more than 100 times in 1m"
name = "The issue is seen more than 100 times in 1m"
interval = "1m"
},
{
id = "sentry.rules.conditions.event_frequency.EventUniqueUserFrequencyCondition"
interval = "1m"
name = "An issue is seen by more than 25 users in 1m"
name = "The issue is seen by more than 25 users in 1m"
value = 25
}
]
Expand Down
Loading

0 comments on commit eca5867

Please sign in to comment.