-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #159 from calvinmclean/feature/notifications
Add Watering Notifications
- Loading branch information
Showing
15 changed files
with
814 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
package notifications | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"net/http" | ||
"time" | ||
|
||
"github.com/calvinmclean/automated-garden/garden-app/pkg/notifications/fake" | ||
"github.com/calvinmclean/automated-garden/garden-app/pkg/notifications/pushover" | ||
"github.com/calvinmclean/babyapi" | ||
) | ||
|
||
// Client is used to interact with an external notification API. It has generic options to allow multiple Client implementations | ||
type Client struct { | ||
ID babyapi.ID `json:"id" yaml:"id"` | ||
Name string `json:"name" yaml:"name"` | ||
Type string `json:"type" yaml:"type"` | ||
Options map[string]any `json:"options" yaml:"options"` | ||
} | ||
|
||
// TestCreate will call the Client implementation's initialization function to make sure it is valid and able to connect | ||
func (nc *Client) TestCreate() error { | ||
_, err := newClient(nc) | ||
return err | ||
} | ||
|
||
func (nc *Client) GetID() string { | ||
return nc.ID.String() | ||
} | ||
|
||
func (nc *Client) Render(_ http.ResponseWriter, _ *http.Request) error { | ||
return nil | ||
} | ||
|
||
func (nc *Client) Bind(r *http.Request) error { | ||
if nc == nil { | ||
return errors.New("missing required NotificationClient fields") | ||
} | ||
|
||
err := nc.ID.Bind(r) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
switch r.Method { | ||
case http.MethodPut, http.MethodPost: | ||
if nc.Name == "" { | ||
return errors.New("missing required name field") | ||
} | ||
if nc.Type == "" { | ||
return errors.New("missing required type field") | ||
} | ||
if nc.Options == nil { | ||
return errors.New("missing required options field") | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// Patch allows modifying an existing Config with fields from a new one | ||
func (nc *Client) Patch(newConfig *Client) *babyapi.ErrResponse { | ||
if newConfig.Name != "" { | ||
nc.Name = newConfig.Name | ||
} | ||
if newConfig.Type != "" { | ||
nc.Type = newConfig.Type | ||
} | ||
|
||
if nc.Options == nil && newConfig.Options != nil { | ||
nc.Options = map[string]any{} | ||
} | ||
for k, v := range newConfig.Options { | ||
nc.Options[k] = v | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// EndDated allows this to satisfy an interface even though the resources does not have end-dates | ||
func (*Client) EndDated() bool { | ||
return false | ||
} | ||
|
||
func (*Client) SetEndDate(_ time.Time) {} | ||
|
||
// SendMessage will send a notification using the client created from this config | ||
func (nc *Client) SendMessage(title, message string) error { | ||
client, err := newClient(nc) | ||
if err != nil { | ||
return fmt.Errorf("error initializing client: %w", err) | ||
} | ||
|
||
return client.SendMessage(title, message) | ||
} | ||
|
||
// client is an interface defining the possible methods used to interact with the notification APIs | ||
type client interface { | ||
SendMessage(title, message string) error | ||
} | ||
|
||
// newClient will use the config to create and return the correct type of notification client | ||
func newClient(c *Client) (client, error) { | ||
var client client | ||
var err error | ||
switch c.Type { | ||
case "pushover": | ||
client, err = pushover.NewClient(c.Options) | ||
case "fake": | ||
client, err = fake.NewClient(c.Options) | ||
default: | ||
err = fmt.Errorf("invalid type '%s'", c.Type) | ||
} | ||
|
||
return client, err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package notifications | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestConfigPatch(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
newConfig *Client | ||
}{ | ||
{ | ||
"PatchType", | ||
&Client{Type: "other_type"}, | ||
}, | ||
{ | ||
"PatchName", | ||
&Client{Name: "NewName"}, | ||
}, | ||
{ | ||
"PatchOptions", | ||
&Client{Options: map[string]interface{}{ | ||
"key": "value", | ||
}}, | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
c := &Client{} | ||
err := c.Patch(tt.newConfig) | ||
require.Nil(t, err) | ||
assert.Equal(t, tt.newConfig, c) | ||
}) | ||
} | ||
} | ||
|
||
func TestNewClientInvalidType(t *testing.T) { | ||
_, err := newClient(&Client{Type: "DNE"}) | ||
assert.Error(t, err) | ||
assert.Equal(t, "invalid type 'DNE'", err.Error()) | ||
} | ||
|
||
func TestEndDated(t *testing.T) { | ||
assert.False(t, (&Client{}).EndDated()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package fake | ||
|
||
import ( | ||
"errors" | ||
|
||
"github.com/mitchellh/mapstructure" | ||
) | ||
|
||
type Config struct { | ||
CreateError string `mapstructure:"create_error"` | ||
SendMessageError string `mapstructure:"send_message_error"` | ||
} | ||
|
||
type Client struct { | ||
*Config | ||
} | ||
|
||
func NewClient(options map[string]interface{}) (*Client, error) { | ||
client := &Client{} | ||
|
||
err := mapstructure.Decode(options, &client.Config) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
if client.Config.CreateError != "" { | ||
return nil, errors.New(client.CreateError) | ||
} | ||
|
||
return client, nil | ||
} | ||
|
||
func (c *Client) SendMessage(string, string) error { | ||
if c.SendMessageError != "" { | ||
return errors.New(c.SendMessageError) | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package pushover | ||
|
||
import ( | ||
"errors" | ||
|
||
"github.com/gregdel/pushover" | ||
"github.com/mitchellh/mapstructure" | ||
) | ||
|
||
type Config struct { | ||
AppToken string `json:"app_token,omitempty" yaml:"app_token,omitempty" mapstructure:"app_token,omitempty"` | ||
RecipientToken string `json:"recipient_token,omitempty" yaml:"recipient_token,omitempty" mapstructure:"recipient_token,omitempty"` | ||
} | ||
|
||
type Client struct { | ||
*Config | ||
app *pushover.Pushover | ||
recipient *pushover.Recipient | ||
} | ||
|
||
func NewClient(options map[string]interface{}) (*Client, error) { | ||
client := &Client{} | ||
|
||
err := mapstructure.Decode(options, &client.Config) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
if client.AppToken == "" { | ||
return nil, errors.New("missing required app_token") | ||
} | ||
if client.RecipientToken == "" { | ||
return nil, errors.New("missing required recipient_token") | ||
} | ||
|
||
client.app = pushover.New(client.AppToken) | ||
client.recipient = pushover.NewRecipient(client.RecipientToken) | ||
|
||
return client, nil | ||
} | ||
|
||
func (c *Client) SendMessage(title, message string) error { | ||
msg := pushover.NewMessageWithTitle(message, title) | ||
_, err := c.app.SendMessage(msg, c.recipient) | ||
return err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.