Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: adding webhooks as a notification channel type #2809

Merged
merged 49 commits into from
Feb 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
d52d375
initial ts conversions
nataliekorzh Nov 29, 2022
92e746a
removing functions
nataliekorzh Nov 30, 2022
55c063a
updating UI to handle webhooks and building out backend
nataliekorzh Dec 7, 2022
5152a6b
backend working, adding to UI
nataliekorzh Dec 9, 2022
3501747
removing name text field
nataliekorzh Dec 12, 2022
03e8918
adding selector to UI
nataliekorzh Dec 13, 2022
3f601e7
adding gql queries for webhook select
nataliekorzh Dec 14, 2022
6c77e32
removing webhook store experiment
nataliekorzh Dec 27, 2022
5dd2197
adding webhook sender
nataliekorzh Dec 28, 2022
e773df6
query search for webhooks working
nataliekorzh Dec 28, 2022
24d5992
updating UI for new webhookselector
nataliekorzh Jan 3, 2023
9223290
get onCreate working for new webhooks
KatieMSB Jan 3, 2023
21de48e
clean up some print statements
KatieMSB Jan 5, 2023
a359bc3
fix webhook query
KatieMSB Jan 5, 2023
9c2bcef
webhook url to id
KatieMSB Jan 5, 2023
d6fbd29
update original step targets on create
KatieMSB Jan 5, 2023
b011160
fixing chips to display hostnames for webhooks
nataliekorzh Jan 5, 2023
7c8bd58
register webhook sender
KatieMSB Jan 5, 2023
8a170a5
fix alert details step text
KatieMSB Jan 5, 2023
952ea17
fix unique key error
KatieMSB Jan 5, 2023
727ec10
adding query for escalation policy id
nataliekorzh Jan 9, 2023
6c4f169
pass epID for filtering
KatieMSB Jan 10, 2023
cc2a2c4
hide webhook step if disabled
KatieMSB Jan 10, 2023
9266d35
add webhook whitelist vadliation
KatieMSB Jan 10, 2023
92cb8c8
add tooltip on hover for webhook chips
KatieMSB Jan 10, 2023
3b09eb3
merge latest
KatieMSB Jan 10, 2023
f27fc8b
adding cypress testing and removing print statements
nataliekorzh Jan 10, 2023
3cc77a8
cleaning up
nataliekorzh Jan 10, 2023
f70a15c
separating PR into webhook as notification channel
nataliekorzh Feb 1, 2023
0835ebd
fixing make check errors
nataliekorzh Feb 2, 2023
ecf9369
removing webhook store code
nataliekorzh Feb 2, 2023
a39f8f8
fixing make check error
nataliekorzh Feb 2, 2023
1b94f71
Update app/app.go
nataliekorzh Feb 2, 2023
81b7f54
Merge branch 'master' of https://github.com/target/goalert into feat/…
nataliekorzh Feb 3, 2023
effb94f
adding feature flags for register sender
nataliekorzh Feb 3, 2023
55ac746
Merge branch 'feat/webhook-notification-channel' of https://github.co…
nataliekorzh Feb 3, 2023
1373305
Address implicit break
nataliekorzh Feb 6, 2023
e0adf37
fixing merge issue and tidying feature flags
nataliekorzh Feb 6, 2023
a3288c7
adding webhook qualification
nataliekorzh Feb 9, 2023
bfece12
Update startup.go
nataliekorzh Feb 9, 2023
5dee5d8
Update startup.go
nataliekorzh Feb 9, 2023
ae5c450
Update graphql2/schema.graphql
nataliekorzh Feb 10, 2023
e0f8839
Update assignment/targettype.go
nataliekorzh Feb 10, 2023
7dcc13a
Update assignment/targettype.go
nataliekorzh Feb 10, 2023
693c032
Update app/startup.go
nataliekorzh Feb 10, 2023
aea6e20
updating generated files with chanWebhook
nataliekorzh Feb 10, 2023
9c0ff43
fixing merge conflict
nataliekorzh Feb 10, 2023
62e5dd0
adding in API <-> DB connection
nataliekorzh Feb 21, 2023
b36b519
adding feature flag check
nataliekorzh Feb 23, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions alert/alertlog/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,8 @@ func (s *Store) logAny(ctx context.Context, tx *sql.Tx, insertStmt *sql.Stmt, id
switch ncType {
case notificationchannel.TypeSlack:
r.subject.classifier = "Slack"
case notificationchannel.TypeWebhook:
r.subject.classifier = "Webhook"
}
r.subject.channelID.String = src.ID
r.subject.channelID.Valid = true
Expand Down Expand Up @@ -344,6 +346,8 @@ func (s *Store) logAny(ctx context.Context, tx *sql.Tx, insertStmt *sql.Stmt, id
r.subject.classifier = "SMS"
case notification.DestTypeUserEmail:
r.subject.classifier = "Email"
case notification.DestTypeChanWebhook:
fallthrough
case notification.DestTypeUserWebhook:
r.subject.classifier = "Webhook"
case notification.DestTypeSlackChannel:
Expand Down
5 changes: 4 additions & 1 deletion app/startup.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,10 @@ func (app *App) startup(ctx context.Context) error {

app.initStartup(ctx, "Startup.Slack", app.initSlack)
app.notificationManager.RegisterSender(notification.DestTypeUserEmail, "smtp", email.NewSender(ctx))
app.notificationManager.RegisterSender(notification.DestTypeUserWebhook, "webhook", webhook.NewSender(ctx))
app.notificationManager.RegisterSender(notification.DestTypeUserWebhook, "webhook-user", webhook.NewSender(ctx))
if expflag.ContextHas(ctx, expflag.ChanWebhook) {
app.notificationManager.RegisterSender(notification.DestTypeChanWebhook, "webhook-channel", webhook.NewSender(ctx))
}

app.initStartup(ctx, "Startup.Engine", app.initEngine)
app.initStartup(ctx, "Startup.Auth", app.initAuth)
Expand Down
5 changes: 5 additions & 0 deletions assignment/targettype.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const (
TargetTypeUser
TargetTypeNotificationChannel
TargetTypeSlackChannel
TargetTypeChanWebhook
TargetTypeIntegrationKey
TargetTypeUserOverride
TargetTypeNotificationRule
Expand Down Expand Up @@ -61,6 +62,8 @@ func (tt *TargetType) UnmarshalText(data []byte) error {
*tt = TargetTypeNotificationChannel
case "slackChannel":
*tt = TargetTypeSlackChannel
case "chanWebhook":
*tt = TargetTypeChanWebhook
case "userOverride":
*tt = TargetTypeUserOverride
case "contactMethod":
Expand Down Expand Up @@ -111,6 +114,8 @@ func (tt TargetType) MarshalText() ([]byte, error) {
return []byte("notificationChannel"), nil
case TargetTypeSlackChannel:
return []byte("slackChannel"), nil
case TargetTypeChanWebhook:
return []byte("chanWebhook"), nil
case TargetTypeContactMethod:
return []byte("contactMethod"), nil
case TargetTypeNotificationRule:
Expand Down
17 changes: 9 additions & 8 deletions assignment/targettype_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

52 changes: 45 additions & 7 deletions escalation/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@ package escalation
import (
"context"
"database/sql"
"net/url"

"github.com/target/goalert/alert/alertlog"
"github.com/target/goalert/assignment"
"github.com/target/goalert/expflag"
"github.com/target/goalert/notification/slack"
"github.com/target/goalert/notificationchannel"
"github.com/target/goalert/permission"
"github.com/target/goalert/util"
"github.com/target/goalert/util/log"
"github.com/target/goalert/util/sqlutil"
"github.com/target/goalert/validation"
"github.com/target/goalert/validation/validate"

"github.com/google/uuid"
Expand All @@ -31,7 +34,7 @@ type Store struct {
ncStore *notificationchannel.Store
slackFn func(ctx context.Context, channelID string) (*slack.Channel, error)

findSlackChan *sql.Stmt
findNotifChan *sql.Stmt

findOnePolicy *sql.Stmt
findOnePolicyForUpdate *sql.Stmt
Expand Down Expand Up @@ -64,13 +67,13 @@ func NewStore(ctx context.Context, db *sql.DB, cfg Config) (*Store, error) {
slackFn: cfg.SlackLookupFunc,
ncStore: cfg.NCStore,

findSlackChan: p.P(`
findNotifChan: p.P(`
SELECT chan.id
FROM notification_channels chan
JOIN escalation_policy_actions act ON
act.escalation_policy_step_id = $1 AND
act.channel_id = chan.id
WHERE chan.value = $2 and chan.type = 'SLACK'
WHERE chan.value = $2 and chan.type = $3
`),

findOnePolicy: p.P(`
Expand Down Expand Up @@ -286,6 +289,22 @@ func (s *Store) _updateStepTarget(ctx context.Context, stepID string, tgt assign
return err
}

func (s *Store) chanWebhook(ctx context.Context, tx *sql.Tx, webhookTarget assignment.Target) (assignment.Target, error) {
webhookUrl, err := url.Parse(webhookTarget.TargetID())
if err != nil {
return nil, err
}
notifID, err := s.ncStore.MapToID(ctx, tx, &notificationchannel.Channel{
Type: notificationchannel.TypeWebhook,
Name: webhookUrl.Hostname(),
Value: webhookTarget.TargetID(),
})
if err != nil {
return nil, err
}
return assignment.NotificationChannelTarget(notifID.String()), nil
}

func (s *Store) newSlackChannel(ctx context.Context, tx *sql.Tx, slackChanID string) (assignment.Target, error) {
ch, err := s.slackFn(ctx, slackChanID)
if err != nil {
Expand All @@ -304,9 +323,9 @@ func (s *Store) newSlackChannel(ctx context.Context, tx *sql.Tx, slackChanID str
return assignment.NotificationChannelTarget(notifID.String()), nil
}

func (s *Store) lookupSlackChannel(ctx context.Context, tx *sql.Tx, stepID, slackChanID string) (assignment.Target, error) {
func (s *Store) lookupNotifChannel(ctx context.Context, tx *sql.Tx, stepID, chanID, chanType string) (assignment.Target, error) {
var notifChanID string
err := tx.StmtContext(ctx, s.findSlackChan).QueryRowContext(ctx, stepID, slackChanID).Scan(&notifChanID)
err := tx.StmtContext(ctx, s.findNotifChan).QueryRowContext(ctx, stepID, chanID, chanType).Scan(&notifChanID)
if err != nil {
return nil, err
}
Expand All @@ -323,14 +342,31 @@ func (s *Store) AddStepTargetTx(ctx context.Context, tx *sql.Tx, stepID string,
return err
}
}
if tgt.TargetType() == assignment.TargetTypeChanWebhook {
if !expflag.ContextHas(ctx, expflag.ChanWebhook) {
return validation.NewFieldError("type", "Webhook notification channels are not enabled")
}
var err error
tgt, err = s.chanWebhook(ctx, tx, tgt)
if err != nil {
return err
}
}
return s._updateStepTarget(ctx, stepID, tgt, tx.StmtContext(ctx, s.addStepTarget), true)
}

// DeleteStepTargetTx removes the target from the step.
func (s *Store) DeleteStepTargetTx(ctx context.Context, tx *sql.Tx, stepID string, tgt assignment.Target) error {
if tgt.TargetType() == assignment.TargetTypeSlackChannel {
var err error
tgt, err = s.lookupSlackChannel(ctx, tx, stepID, tgt.TargetID())
tgt, err = s.lookupNotifChannel(ctx, tx, stepID, tgt.TargetID(), "SLACK")
if err != nil {
return err
}
}
if tgt.TargetType() == assignment.TargetTypeChanWebhook {
var err error
tgt, err = s.lookupNotifChannel(ctx, tx, stepID, tgt.TargetID(), "WEBHOOK")
if err != nil {
return err
}
Expand Down Expand Up @@ -389,6 +425,9 @@ func (s *Store) FindAllStepTargetsTx(ctx context.Context, tx *sql.Tx, stepID str
case notificationchannel.TypeSlack:
tgt.ID = chValue.String
tgt.Type = assignment.TargetTypeSlackChannel
case notificationchannel.TypeWebhook:
tgt.ID = chValue.String
tgt.Type = assignment.TargetTypeChanWebhook
default:
tgt.ID = ch.String
tgt.Type = assignment.TargetTypeNotificationChannel
Expand Down Expand Up @@ -678,7 +717,6 @@ func (s *Store) CreateStepTx(ctx context.Context, tx *sql.Tx, st *Step) (*Step,
}

s.logChange(ctx, tx, st.PolicyID)

return n, nil
}

Expand Down
10 changes: 6 additions & 4 deletions expflag/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ import "sort"
type Flag string

const (
Example Flag = "example"
SlackDM Flag = "slack-dm"
Example Flag = "example"
SlackDM Flag = "slack-dm"
ChanWebhook Flag = "chan-webhook"
)

var desc = map[Flag]string{
Example: "An example experimental flag to demonstrate usage.",
SlackDM: "Enables sending notifications to Slack DMs as a user contact method.",
Example: "An example experimental flag to demonstrate usage.",
SlackDM: "Enables sending notifications to Slack DMs as a user contact method.",
ChanWebhook: "Enables webhooks as a notification channel type",
}

// AllFlags returns a slice of all experimental flags sorted by name.
Expand Down
1 change: 1 addition & 0 deletions graphql2/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -1176,6 +1176,7 @@ enum TargetType {
service
schedule
user
chanWebhook
integrationKey
userOverride
notificationRule
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-- +migrate Up

ALTER TYPE enum_notif_channel_type ADD VALUE IF NOT EXISTS 'WEBHOOK';

-- +migrate Down
5 changes: 5 additions & 0 deletions notification/desttype.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ const (
DestTypeSlackDM
DestTypeUserEmail
DestTypeUserWebhook
DestTypeChanWebhook
)

func (d Dest) String() string { return fmt.Sprintf("%s(%s)", d.Type.String(), d.ID) }
Expand Down Expand Up @@ -77,6 +78,8 @@ func (t ScannableDestType) DestType() DestType {
switch t.NC {
case notificationchannel.TypeSlack:
return DestTypeSlackChannel
case notificationchannel.TypeWebhook:
return DestTypeChanWebhook
}

return DestTypeUnknown
Expand All @@ -87,6 +90,8 @@ func (t DestType) NCType() notificationchannel.Type {
switch t {
case DestTypeSlackChannel:
return notificationchannel.TypeSlack
case DestTypeChanWebhook:
return notificationchannel.TypeWebhook
}

return notificationchannel.TypeUnknown
Expand Down
5 changes: 3 additions & 2 deletions notification/desttype_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion notificationchannel/channel.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func (c Channel) Normalize() (*Channel, error) {
err := validate.Many(
validate.UUID("ID", c.ID),
validate.Text("Name", c.Name, 1, 255),
validate.OneOf("Type", c.Type, TypeSlack),
validate.OneOf("Type", c.Type, TypeSlack, TypeWebhook),
)

switch c.Type {
Expand Down
1 change: 1 addition & 0 deletions notificationchannel/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ type Type string
const (
TypeUnknown Type = ""
TypeSlack Type = "SLACK"
TypeWebhook Type = "WEBHOOK"
)

// Valid returns true if t is a known Type.
Expand Down
2 changes: 1 addition & 1 deletion web/src/expflag.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
// Code generated by expflag/cmd/tsgen DO NOT EDIT.

type ExpFlag = 'example' | 'slack-dm'
type ExpFlag = 'chan-webhook' | 'example' | 'slack-dm'
1 change: 1 addition & 0 deletions web/src/schema.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -935,6 +935,7 @@ export type TargetType =
| 'service'
| 'schedule'
| 'user'
| 'chanWebhook'
| 'integrationKey'
| 'userOverride'
| 'notificationRule'
Expand Down