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

escalation policy: add user favorite support #1567

Merged
merged 27 commits into from
Jun 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
ab9f7ec
added migration for escalation polices to user_favorites and react co…
May 13, 2021
2bc1a4e
Merge branch 'master' of https://github.com/target/goalert into esc-p…
May 17, 2021
2a546d1
debug for set user favorite
May 17, 2021
52ffa39
Merge branch 'master' of https://github.com/target/goalert into esc-p…
May 18, 2021
745b28a
added favoritesFirst to esc policy schema and associated files
May 18, 2021
c04c7f4
Merge branch 'master' of https://github.com/target/goalert into esc-p…
May 19, 2021
2ce24f0
removed comments for debug
May 19, 2021
fca4b9a
restoring spacing for previous migration
May 19, 2021
53a720c
restoring file to origin/master
May 19, 2021
0332c73
comments clean-up
May 19, 2021
2c25d15
pr fixes
May 19, 2021
8aff55a
pr fixes
May 19, 2021
6fe41f8
pr fixes
May 20, 2021
6818918
build fix
May 20, 2021
576de30
pr fixes
May 21, 2021
d66754f
Merge branch 'master' of https://github.com/target/goalert into esc-p…
May 21, 2021
123fb6f
build fix
May 21, 2021
36894cd
fix cypress tests
KatieMSB Jun 1, 2021
91c8abb
cypress test fix
pnengchu Jun 1, 2021
0ec8ad6
fix auto-format
pnengchu Jun 1, 2021
9ee4f44
ep favorites in regendb
mastercactapus Jun 1, 2021
14f1f8e
Merge branch 'master' of https://github.com/target/goalert into esc-p…
pnengchu Jun 2, 2021
8562bfb
applied pr fixes
pnengchu Jun 2, 2021
7dd99f0
Merge branch 'esc-policies-fav' of https://github.com/target/goalert …
pnengchu Jun 2, 2021
c9fc4b6
Merge branch 'master' of https://github.com/target/goalert into esc-p…
pnengchu Jun 2, 2021
ada3d0c
applied pr fixes
pnengchu Jun 2, 2021
1f68afb
fix for ep pagination.ts
pnengchu Jun 3, 2021
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: 3 additions & 1 deletion devtools/resetdb/datagen.go
Original file line number Diff line number Diff line change
Expand Up @@ -343,13 +343,15 @@ func (d *datagen) NewAlert(status alert.Status) {
// NewFavorite will generate a new favorite for the provided user ID.
func (d *datagen) NewFavorite(userID string) {
var tgt assignment.Target
switch rand.Intn(3) {
switch rand.Intn(4) {
case 0:
tgt = assignment.ServiceTarget(d.ids.Gen(func() string { return d.Services[rand.Intn(len(d.Services))].ID }, "favSvc", userID))
case 1:
tgt = assignment.RotationTarget(d.ids.Gen(func() string { return d.Rotations[rand.Intn(len(d.Rotations))].ID }, "favRot", userID))
case 2:
tgt = assignment.ScheduleTarget(d.ids.Gen(func() string { return d.Schedules[rand.Intn(len(d.Schedules))].ID }, "favSched", userID))
case 3:
tgt = assignment.EscalationPolicyTarget(d.ids.Gen(func() string { return d.EscalationPolicies[rand.Intn(len(d.EscalationPolicies))].ID }, "favEP", userID))
}

d.Favorites = append(d.Favorites, userFavorite{
Expand Down
2 changes: 1 addition & 1 deletion devtools/resetdb/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ func fillDB(ctx context.Context, url string) error {
rot = &id
}
return []interface{}{asUUID(fav.UserID), svc, sched, rot}
}, "users", "services", "schedules", "rotations")
}, "users", "services", "schedules", "rotations", "escalation_policies")

_, err = pool.Exec(ctx, "alter table alerts disable trigger trg_enforce_alert_limit")
must(err)
Expand Down
14 changes: 10 additions & 4 deletions escalation/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ import (
)

type Policy struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Repeat int `json:"repeat"`
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Repeat int `json:"repeat"`
isUserFavorite bool
}

func (p Policy) Normalize() (*Policy, error) {
Expand All @@ -23,3 +24,8 @@ func (p Policy) Normalize() (*Policy, error) {

return &p, nil
}

// IsUserFavorite returns true if this policy is a favorite of the current user.
func (p Policy) IsUserFavorite() bool {
return p.isUserFavorite
}
58 changes: 52 additions & 6 deletions escalation/search.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@ type SearchOptions struct {
Search string `json:"s,omitempty"`
After SearchCursor `json:"a,omitempty"`

// FavoritesUserID specifies the UserID whose favorite escalation policies want to be displayed.
FavoritesUserID string `json:"u,omitempty"`

// FavoritesOnly controls filtering the results to those marked as favorites by FavoritesUserID.
FavoritesOnly bool `json:"g,omitempty"`

// FavoritesFirst indicates that escalation policy marked as favorite (by FavoritesUserID) should be returned first (before any non-favorites).
FavoritesFirst bool `json:"f,omitempty"`

// Omit specifies a list of policy IDs to exclude from the results.
Omit []string `json:"o,omitempty"`

Expand All @@ -26,29 +35,51 @@ type SearchOptions struct {

// SearchCursor is used to indicate a position in a paginated list.
type SearchCursor struct {
Name string `json:"n,omitempty"`
Name string `json:"n,omitempty"`
IsFavorite bool `json:"f,omitempty"`
}

var searchTemplate = template.Must(template.New("search").Parse(`
SELECT
id, name, description, repeat
pol.id,
pol.name,
pol.description,
pol.repeat,
fav IS DISTINCT FROM NULL
FROM escalation_policies pol
{{if not .FavoritesOnly }}
LEFT {{end}}JOIN user_favorites fav ON pol.id = fav.tgt_escalation_policy_id
AND {{if .FavoritesUserID}}fav.user_id = :favUserID{{else}}false{{end}}
WHERE true
{{if .Omit}}
AND not id = any(:omit)
AND NOT pol.id = any(:omit)
{{end}}
{{if .SearchStr}}
AND (pol.name ILIKE :search OR pol.description ILIKE :search)
{{end}}
{{if .After.Name}}
AND lower(pol.name) > lower(:afterName)
AND
{{if not .FavoritesFirst}}
lower(pol.name) > lower(:afterName)
{{else if .After.IsFavorite}}
((fav IS DISTINCT FROM NULL AND lower(pol.name) > lower(:afterName)) OR fav isnull)
{{else}}
(fav isnull AND lower(pol.name) > lower(:afterName))
{{end}}
{{end}}
ORDER BY lower(pol.name)
ORDER BY {{ .OrderBy }}
LIMIT {{.Limit}}
`))

type renderData SearchOptions

func (opts renderData) OrderBy() string {
if opts.FavoritesFirst {
return "fav isnull, lower(pol.name)"
}
return "lower(pol.name)"
}

func (opts renderData) SearchStr() string {
if opts.Search == "" {
return ""
Expand All @@ -70,6 +101,12 @@ func (opts renderData) Normalize() (*renderData, error) {
if opts.After.Name != "" {
err = validate.Many(err, validate.IDName("After.Name", opts.After.Name))
}
if opts.FavoritesOnly || opts.FavoritesFirst || opts.FavoritesUserID != "" {
err = validate.Many(err, validate.UUID("FavoritesUserID", opts.FavoritesUserID))
}
if err != nil {
return nil, err
}

return &opts, err
}
Expand All @@ -79,6 +116,7 @@ func (opts renderData) QueryArgs() []sql.NamedArg {
sql.Named("search", opts.SearchStr()),
sql.Named("afterName", opts.After.Name),
sql.Named("omit", sqlutil.UUIDArray(opts.Omit)),
sql.Named("favUserID", opts.FavoritesUserID),
}
}

Expand All @@ -90,6 +128,14 @@ func (db *DB) Search(ctx context.Context, opts *SearchOptions) ([]Policy, error)
if opts == nil {
opts = &SearchOptions{}
}
userCheck := permission.User
if opts.FavoritesUserID != "" {
userCheck = permission.MatchUser(opts.FavoritesUserID)
}
err = permission.LimitCheckAny(ctx, permission.System, userCheck)
if err != nil {
return nil, err
}
data, err := (*renderData)(opts).Normalize()
if err != nil {
return nil, err
Expand All @@ -111,7 +157,7 @@ func (db *DB) Search(ctx context.Context, opts *SearchOptions) ([]Policy, error)
var result []Policy
var p Policy
for rows.Next() {
err = rows.Scan(&p.ID, &p.Name, &p.Description, &p.Repeat)
err = rows.Scan(&p.ID, &p.Name, &p.Description, &p.Repeat, &p.isUserFavorite)
if err != nil {
return nil, err
}
Expand Down
35 changes: 29 additions & 6 deletions escalation/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,10 +151,33 @@ func NewDB(ctx context.Context, db *sql.DB, cfg Config) (*DB, error) {
WHERE chan.value = $2
`),

findOnePolicy: p.P(`SELECT id, name, description, repeat FROM escalation_policies WHERE id = $1`),
findOnePolicy: p.P(`
SELECT
e.id,
e.name,
e.description,
e.repeat,
fav is distinct from null
FROM
escalation_policies e
LEFT JOIN user_favorites fav ON
fav.tgt_escalation_policy_id = e.id AND fav.user_id = $2
WHERE e.id = $1
`),
findOnePolicyForUpdate: p.P(`SELECT id, name, description, repeat FROM escalation_policies WHERE id = $1 FOR UPDATE`),
findManyPolicies: p.P(`SELECT id, name, description, repeat FROM escalation_policies WHERE id = any($1)`),

findManyPolicies: p.P(`
SELECT
e.id,
e.name,
e.description,
e.repeat,
fav is distinct from null
FROM
escalation_policies e
LEFT JOIN user_favorites fav ON
fav.tgt_escalation_policy_id = e.id AND fav.user_id = $2
WHERE e.id = any($1)
`),
findAllPolicies: p.P(`SELECT id, name, description, repeat FROM escalation_policies`),
findAllPoliciesBySchedule: p.P(`
SELECT DISTINCT
Expand Down Expand Up @@ -337,8 +360,8 @@ func (db *DB) FindManyPolicies(ctx context.Context, ids []string) ([]Policy, err
if err != nil {
return nil, err
}

rows, err := db.findManyPolicies.QueryContext(ctx, sqlutil.UUIDArray(ids))
userID := permission.UserID(ctx)
rows, err := db.findManyPolicies.QueryContext(ctx, sqlutil.UUIDArray(ids), userID)
if errors.Is(err, sql.ErrNoRows) {
return nil, nil
}
Expand All @@ -350,7 +373,7 @@ func (db *DB) FindManyPolicies(ctx context.Context, ids []string) ([]Policy, err
var result []Policy
var p Policy
for rows.Next() {
err = rows.Scan(&p.ID, &p.Name, &p.Description, &p.Repeat)
err = rows.Scan(&p.ID, &p.Name, &p.Description, &p.Repeat, &p.isUserFavorite)
if err != nil {
return nil, err
}
Expand Down
91 changes: 91 additions & 0 deletions graphql2/generated.go

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

Loading