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

fix slack dm status updates #3060

Merged
merged 4 commits into from
Jun 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
20 changes: 0 additions & 20 deletions engine/message/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ type DB struct {

sentByCMType *sql.Stmt

updateCMStatusUpdate *sql.Stmt
cleanupStatusUpdateOptOut *sql.Stmt

tempFail *sql.Stmt
Expand Down Expand Up @@ -174,20 +173,6 @@ func NewDB(ctx context.Context, db *sql.DB, a *alertlog.Store, pausable lifecycl
where msg.sent_at > $1 and cm.type = $2
`),

updateCMStatusUpdate: p.P(`
update outgoing_messages msg
set contact_method_id = usr.alert_status_log_contact_method_id
from users usr
where
msg.message_type = 'alert_status_update' and
(
msg.last_status = 'pending' or
(msg.last_status = 'failed' and msg.next_retry_at notnull)
) and
msg.contact_method_id != usr.alert_status_log_contact_method_id and
msg.user_id = usr.id and
usr.alert_status_log_contact_method_id notnull
`),
cleanupStatusUpdateOptOut: p.P(`
delete from outgoing_messages msg
using user_contact_methods cm
Expand Down Expand Up @@ -617,11 +602,6 @@ func (db *DB) _SendMessages(ctx context.Context, send SendFunc, status StatusFun
return errors.Wrap(err, "get current time")
}

_, err = tx.Stmt(db.updateCMStatusUpdate).ExecContext(execCtx)
if err != nil {
return errors.Wrap(err, "update status update CM preferences")
}

_, err = tx.Stmt(db.cleanupStatusUpdateOptOut).ExecContext(execCtx)
if err != nil {
return errors.Wrap(err, "clear disabled status updates")
Expand Down
4 changes: 1 addition & 3 deletions graphql2/graphqlapp/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,9 +143,7 @@ func (a *Mutation) UpdateUser(ctx context.Context, input graphql2.UpdateUserInpu
if input.Email != nil {
usr.Email = *input.Email
}
if input.StatusUpdateContactMethodID != nil {
usr.AlertStatusCMID = *input.StatusUpdateContactMethodID
}

return a.UserStore.UpdateTx(ctx, tx, usr)
})
return err == nil, err
Expand Down
57 changes: 57 additions & 0 deletions test/smoke/slackdm2889_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package smoke

import (
"testing"

"github.com/target/goalert/expflag"
"github.com/target/goalert/test/smoke/harness"
)

// TestSlackDM2889 tests that slack DMs update correctly, even when the old status log config is present (issue #2889).
func TestSlackDM2889(t *testing.T) {
t.Parallel()

sql := `
insert into users (id, name, email, role)
values
({{uuid "user"}}, 'bob', 'joe', 'user');
insert into user_contact_methods (id, user_id, name, type, value)
values
({{uuid "cm1"}}, {{uuid "user"}}, 'personal', 'SLACK_DM', {{slackUserID "bob"}}),
({{uuid "cm2"}}, {{uuid "user"}}, 'personal', 'SMS', {{phone "bob"}});

update users set alert_status_log_contact_method_id = {{uuid "cm2"}} where id = {{uuid "user"}};

insert into user_notification_rules (user_id, contact_method_id, delay_minutes)
values
({{uuid "user"}}, {{uuid "cm1"}}, 0),
({{uuid "user"}}, {{uuid "cm1"}}, 30);

insert into escalation_policies (id, name)
values
({{uuid "eid"}}, 'esc policy');
insert into escalation_policy_steps (id, escalation_policy_id)
values
({{uuid "esid"}}, {{uuid "eid"}});
insert into escalation_policy_actions (escalation_policy_step_id, user_id)
values
({{uuid "esid"}}, {{uuid "user"}});

insert into services (id, escalation_policy_id, name)
values
({{uuid "sid"}}, {{uuid "eid"}}, 'service');

`
h := harness.NewHarnessWithFlags(t, sql, "slack-dm-cm-type", expflag.FlagSet{expflag.SlackDM})
defer h.Close()
h.SetConfigValue("Slack.InteractiveMessages", "true")
h.Trigger() // the user's account should get "linked" via compat mgr

h.CreateAlert(h.UUID("sid"), "testing")
msg := h.Slack().User("bob").ExpectMessage("testing")
msg.Action("Close").Click()

updated := msg.ExpectUpdate()
updated.AssertText("Closed", "testing")
updated.AssertActions()
}
6 changes: 5 additions & 1 deletion test/smoke/statusupdatescancel_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,11 @@ func TestStatusUpdatesCancel(t *testing.T) {
d2.ExpectSMS("first")
h.Trigger() // cleanup subscription to cm2, since only cm1 is configured

h.GraphQLQueryUserT(t, h.UUID("user"), fmt.Sprintf(`mutation{updateUser(input:{id:"%s",statusUpdateContactMethodID:"%s"})}`, h.UUID("user"), h.UUID("cm2")))
h.GraphQLQueryUserT(t, h.UUID("user"), fmt.Sprintf(`
mutation{
updateUserContactMethod(input:{id:"%s",enableStatusUpdates: false})
updateUserContactMethod(input:{id:"%s",enableStatusUpdates: true})
}`, h.UUID("cm1"), h.UUID("cm2")))

h.Trigger() // cleanup subscription to cm1, now that cm2 is the only one configured
doClose("first")
Expand Down
21 changes: 10 additions & 11 deletions user/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,17 +74,16 @@ func NewStore(ctx context.Context, db *sql.DB) (*Store, error) {

insert: p.P(`
INSERT INTO users (
id, name, email, avatar_url, role, alert_status_log_contact_method_id
id, name, email, avatar_url, role
)
VALUES ($1, $2, $3, $4, $5, $6)
VALUES ($1, $2, $3, $4, $5)
`),

update: p.P(`
UPDATE users
SET
name = $2,
email = $3,
alert_status_log_contact_method_id = $4
email = $3
WHERE id = $1
`),

Expand All @@ -103,7 +102,7 @@ func NewStore(ctx context.Context, db *sql.DB) (*Store, error) {

usersMissingProvider: p.P(`
SELECT
id, name, email, avatar_url, role, alert_status_log_contact_method_id, false
id, name, email, avatar_url, role, false
FROM users
WHERE id not in (select user_id from auth_subjects where provider_id = $1)
`),
Expand All @@ -116,7 +115,7 @@ func NewStore(ctx context.Context, db *sql.DB) (*Store, error) {

findMany: p.P(`
SELECT
u.id, u.name, u.email, u.avatar_url, u.role, u.alert_status_log_contact_method_id, fav is distinct from null
u.id, u.name, u.email, u.avatar_url, u.role, fav is distinct from null
FROM users u
LEFT JOIN user_favorites fav ON
fav.tgt_user_id = u.id AND fav.user_id = $2
Expand All @@ -131,23 +130,23 @@ func NewStore(ctx context.Context, db *sql.DB) (*Store, error) {

findOneBySubject: p.P(`
SELECT
u.id, u.name, u.email, u.avatar_url, u.role, u.alert_status_log_contact_method_id, false
u.id, u.name, u.email, u.avatar_url, u.role, false
FROM auth_subjects s
JOIN users u ON u.id = s.user_id
WHERE s.provider_id = $1 AND s.subject_id = $2
`),

findOne: p.P(`
SELECT
u.id, u.name, u.email, u.avatar_url, u.role, u.alert_status_log_contact_method_id, fav is distinct from null
u.id, u.name, u.email, u.avatar_url, u.role, fav is distinct from null
FROM users u
LEFT JOIN user_favorites fav ON
fav.tgt_user_id = u.id AND fav.user_id = $2
WHERE u.id = $1
`),
findOneForUpdate: p.P(`
SELECT
id, name, email, avatar_url, role, alert_status_log_contact_method_id, false
id, name, email, avatar_url, role, false
FROM users
WHERE id = $1
FOR UPDATE
Expand All @@ -161,7 +160,7 @@ func NewStore(ctx context.Context, db *sql.DB) (*Store, error) {

findAll: p.P(`
SELECT
id, name, email, avatar_url, role, alert_status_log_contact_method_id, false
id, name, email, avatar_url, role, false
FROM users
`),

Expand Down Expand Up @@ -501,7 +500,7 @@ func (s *Store) removeUserFromRotation(ctx context.Context, tx *sql.Tx, userID,
// Update id equivalent to calling UpdateTx(ctx, nil, u).
func (s *Store) Update(ctx context.Context, u *User) error { return s.UpdateTx(ctx, nil, u) }

// UpdateTx allows updating a user name, email, and status update preference.
// UpdateTx allows updating a user name and email.
func (s *Store) UpdateTx(ctx context.Context, tx *sql.Tx, u *User) error {
err := permission.LimitCheckAny(ctx, permission.System, permission.Admin, permission.MatchUser(u.ID))
if err != nil {
Expand Down
27 changes: 3 additions & 24 deletions user/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package user

import (
"crypto/md5"
"database/sql"
"encoding/hex"
"fmt"

Expand All @@ -28,7 +27,9 @@ type User struct {
AvatarURL string

// AlertStatusCMID defines a contact method ID for alert status updates.
AlertStatusCMID string `gorm:"column:alert_status_log_contact_method_id"`
//
// Deprecated: No longer used.
AlertStatusCMID string

// The Role of the user
Role permission.Role
Expand All @@ -55,47 +56,32 @@ func (u User) ResolveAvatarURL(fullSize bool) string {
type scanFn func(...interface{}) error

func (u *User) scanFrom(fn scanFn) error {
var statusCM sql.NullString
err := fn(
&u.ID,
&u.Name,
&u.Email,
&u.AvatarURL,
&u.Role,
&statusCM,
&u.isUserFavorite,
)
u.AlertStatusCMID = statusCM.String
return err
}

func (u *User) userUpdateFields() []interface{} {
var statusCM sql.NullString
if u.AlertStatusCMID != "" {
statusCM.Valid = true
statusCM.String = u.AlertStatusCMID
}
return []interface{}{
u.ID,
u.Name,
u.Email,
statusCM,
}
}

func (u *User) fields() []interface{} {
var statusCM sql.NullString
if u.AlertStatusCMID != "" {
statusCM.Valid = true
statusCM.String = u.AlertStatusCMID
}
return []interface{}{
u.ID,
u.Name,
u.Email,
u.AvatarURL,
u.Role,
statusCM,
}
}

Expand All @@ -118,13 +104,6 @@ func (u User) Normalize() (*User, error) {
)
}

if u.AlertStatusCMID != "" {
err = validate.Many(
err,
validate.UUID("AlertStatusCMID", u.AlertStatusCMID),
)
}

err = validate.Many(
err,
validate.Name("Name", u.Name),
Expand Down