Skip to content

Commit

Permalink
Add inbound email resources (#165)
Browse files Browse the repository at this point in the history
  • Loading branch information
bobbytables authored Oct 17, 2024
1 parent fc848dd commit 6c348e9
Show file tree
Hide file tree
Showing 10 changed files with 617 additions and 52 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## 0.13.0 (Unreleased)
## 0.13.0

ENHANCEMENTS:
* **New Resource**: `firehydrant_inbound_email` ([#165](https://github.com/firehydrant/terraform-provider-firehydrant/pull/165/))
* Adds the attribute for notification priority override to the `firehydrant_signal_rule` resource ([#166](https://github.com/firehydrant/terraform-provider-firehydrant/pull/166))

## 0.12.1
Expand Down
62 changes: 62 additions & 0 deletions docs/resources/inbound_email.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
---
page_title: "FireHydrant Resource: firehydrant_inbound_email"
subcategory: "Signals"
---

# firehydrant_inbound_email Resource

FireHydrant inbound email resources allow you to configure email-based alerts by sending emails to the FireHydrant platform.

## Example Usage

```hcl
data "firehydrant_team" "example_team" {
name = "Example Team"
}
resource "firehydrant_inbound_email" "example" {
name = "Inbound Email Alert"
slug = "inbound-email-alert"
description = "Inbound email alert for critical issues"
status_cel = "email.body.contains('has recovered') ? 'CLOSED' : 'OPEN'"
level_cel = "email.body.contains('panic') ? 'ERROR' : 'INFO'"
allowed_senders = ["@firehydrant.com", "@example.com"]
target {
type = "Team"
id = data.firehydrant_team.example_team.id
}
rules = ["email.body.contains(\"critical\")"]
rule_matching_strategy = "all"
}
```

## Argument Reference

The following arguments are supported:

* `name` - (Required) The name of the inbound email resource.
* `slug` - (Required) The slug for the inbound email resource.
* `description` - (Optional) A description of the inbound email resource.
* `status_cel` - (Required) A Common Expression Language (CEL) expression to determine the status of the alert based on the email content.
* `level_cel` - (Required) A CEL expression to determine the severity level of the alert based on the email content.
* `allowed_senders` - (Required) A list of email domains or addresses allowed to send alerts.
* `target` - (Required) A block to specify the target for the alert. The block supports:
* `type` - (Required) The type of the target. Valid values are "Team", "User", or "EscalationPolicy".
* `id` - (Required) The ID of the target resource.
* `rules` - (Required) A list of CEL expressions that define when an alert should be triggered.
* `rule_matching_strategy` - (Required) The strategy for matching rules. Valid values are "all" or "any".

## Attributes Reference

In addition to all arguments above, the following attributes are exported:

* `id` - The ID of the inbound email resource.
* `email` - The email address to send alerts to.

## Import

Inbound email resources can be imported using the resource ID, e.g.,

```
$ terraform import firehydrant_inbound_email.example 12345678-90ab-cdef-1234-567890abcdef
```
14 changes: 14 additions & 0 deletions examples/inbound_emails.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
resource "firehydrant_inbound_email" "example" {
name = "Inbound Email"
slug = "inbound-email"
description = "Description here"
status_cel = "email.body.contains('has recovered') ? 'CLOSED' : 'OPEN'"
level_cel = "email.body.contains('panic') ? 'ERROR' : 'INFO'"
allowed_senders = ["@firehydrant.com"]
target {
type = "Team"
id = "30715262-7fa9-4328-95e2-7cd45433d518"
}
rules = ["email.body.contains(\"hello\")"]
rule_matching_strategy = "all"
}
1 change: 1 addition & 0 deletions firehydrant/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ type Client interface {
EscalationPolicies() EscalationPolicies
IngestURL() IngestURLClient
Transposers() TransposersClient
InboundEmails() InboundEmailsClient
}

// OptFunc is a function that sets a setting on a client
Expand Down
148 changes: 148 additions & 0 deletions firehydrant/inbound_emails.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package firehydrant

import (
"context"

"github.com/dghubble/sling"
"github.com/pkg/errors"
)

// InboundEmailClient is an interface for interacting with inbound emails on FireHydrant
type InboundEmailsClient interface {
Create(ctx context.Context, createReq CreateInboundEmailRequest) (*InboundEmailResponse, error)
Get(ctx context.Context, id string) (*InboundEmailResponse, error)
Update(ctx context.Context, id string, updateReq UpdateInboundEmailRequest) (*InboundEmailResponse, error)
Delete(ctx context.Context, id string) error
}

// RESTInboundEmailClient implements the InboundEmailClient interface
type RESTInboundEmailClient struct {
client *APIClient
}

var _ InboundEmailsClient = &RESTInboundEmailClient{}

func (c *RESTInboundEmailClient) restClient() *sling.Sling {
return c.client.client()
}

// CreateInboundEmailRequest is the payload for creating an inbound email
type CreateInboundEmailRequest struct {
Name string `json:"name"`
Slug string `json:"slug"`
Description string `json:"description"`
StatusCEL string `json:"status_cel"`
LevelCEL string `json:"level_cel"`
AllowedSenders []string `json:"allowed_senders"`
Target Target `json:"target"`
Rules []string `json:"rules"`
RuleMatchingStrategy string `json:"rule_matching_strategy"`
}

// Target represents the target for the inbound email
type Target struct {
Type string `json:"type"`
ID string `json:"id"`
}

// InboundEmailResponse is the response for an inbound email
type InboundEmailResponse struct {
ID string `json:"id"`
Name string `json:"name"`
Slug string `json:"slug"`
Description string `json:"description"`
StatusCEL string `json:"status_cel"`
LevelCEL string `json:"level_cel"`
AllowedSenders []string `json:"allowed_senders"`
Target Target `json:"target"`
Rules []string `json:"rules"`
RuleMatchingStrategy string `json:"rule_matching_strategy"`

// Calculated fields
Email string `json:"email"`
}

// UpdateInboundEmailRequest is the payload for updating an inbound email
type UpdateInboundEmailRequest struct {
Name string `json:"name"`
Slug string `json:"slug"`
Description string `json:"description"`
StatusCEL string `json:"status_cel"`
LevelCEL string `json:"level_cel"`
AllowedSenders []string `json:"allowed_senders"`
Target Target `json:"target"`
Rules []string `json:"rules"`
RuleMatchingStrategy string `json:"rule_matching_strategy"`
}

// Create creates a new inbound email in FireHydrant
func (c *RESTInboundEmailClient) Create(ctx context.Context, createReq CreateInboundEmailRequest) (*InboundEmailResponse, error) {
inboundEmailResponse := &InboundEmailResponse{}
apiError := &APIError{}
response, err := c.restClient().Post("signals/email_targets").BodyJSON(&createReq).Receive(inboundEmailResponse, apiError)
if err != nil {
return nil, errors.Wrap(err, "could not create inbound email")
}

err = checkResponseStatusCode(response, apiError)
if err != nil {
return nil, err
}

return inboundEmailResponse, nil
}

// Get retrieves an inbound email from FireHydrant
func (c *RESTInboundEmailClient) Get(ctx context.Context, id string) (*InboundEmailResponse, error) {
inboundEmailResponse := &InboundEmailResponse{}
apiError := &APIError{}
response, err := c.restClient().Get("signals/email_targets/"+id).Receive(inboundEmailResponse, apiError)
if err != nil {
return nil, errors.Wrap(err, "could not get inbound email")
}

err = checkResponseStatusCode(response, apiError)
if err != nil {
return nil, err
}

return inboundEmailResponse, nil
}

// Update updates an inbound email in FireHydrant
func (c *RESTInboundEmailClient) Update(ctx context.Context, id string, updateReq UpdateInboundEmailRequest) (*InboundEmailResponse, error) {
inboundEmailResponse := &InboundEmailResponse{}
apiError := &APIError{}
response, err := c.restClient().Patch("signals/email_targets/"+id).BodyJSON(&updateReq).Receive(inboundEmailResponse, apiError)
if err != nil {
return nil, errors.Wrap(err, "could not update inbound email")
}

err = checkResponseStatusCode(response, apiError)
if err != nil {
return nil, err
}

return inboundEmailResponse, nil
}

// Delete deletes an inbound email from FireHydrant
func (c *RESTInboundEmailClient) Delete(ctx context.Context, id string) error {
apiError := &APIError{}
response, err := c.restClient().Delete("signals/email_targets/"+id).Receive(nil, apiError)
if err != nil {
return errors.Wrap(err, "could not delete inbound email")
}

err = checkResponseStatusCode(response, apiError)
if err != nil {
return err
}

return nil
}

// Add this method to the APIClient struct in client.go
func (c *APIClient) InboundEmails() InboundEmailsClient {
return &RESTInboundEmailClient{client: c}
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go 1.22.1

require (
github.com/bxcodec/faker/v3 v3.5.0
github.com/davecgh/go-spew v1.1.1
github.com/dghubble/sling v1.4.0
github.com/google/go-querystring v1.1.0
github.com/hashicorp/terraform-plugin-log v0.7.0
Expand All @@ -17,7 +18,6 @@ require (
github.com/agext/levenshtein v1.2.3 // indirect
github.com/apparentlymart/go-cidr v1.1.0 // indirect
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fatih/color v1.13.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/go-cmp v0.5.9 // indirect
Expand Down
Loading

0 comments on commit 6c348e9

Please sign in to comment.