Skip to content
Open
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
308 changes: 308 additions & 0 deletions graphql2/generated.go

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions graphql2/graph/_Mutation.graphqls
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ type Mutation {
reEncryptKeyringsAndConfig: Boolean!

setTemporarySchedule(input: SetTemporaryScheduleInput!): Boolean!
setTemporarySchedulePickOrder(input: SetTempSchedPickOrderInput!): Boolean!
clearTemporarySchedules(input: ClearTemporarySchedulesInput!): Boolean!

setScheduleOnCallNotificationRules(
Expand Down
29 changes: 29 additions & 0 deletions graphql2/graphqlapp/schedule.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"github.com/target/goalert/schedule"
"github.com/target/goalert/schedule/rule"
"github.com/target/goalert/search"
"github.com/target/goalert/user"
"github.com/target/goalert/util"
"github.com/target/goalert/validation"
"github.com/target/goalert/validation/validate"
Expand Down Expand Up @@ -172,6 +173,34 @@
return result, nil
}

func (s *Schedule) AssociatedUsers(ctx context.Context, raw *schedule.Schedule) ([]user.User, error) {
userIDs, err := s.ScheduleStore.FindAssociatedUserIDs(ctx, raw.ID)
if err != nil {
return nil, err
}
users, err := s.UserStore.FindMany(ctx, userIDs)
if err != nil {
return nil, err
}
return users, nil
}

func (s *Schedule) LastTempSchedPickOrder(ctx context.Context, raw *schedule.Schedule) ([]string, error) {
userIDs, err := s.ScheduleStore.FindLastTempSchedPickOrder(ctx, raw.ID)
if err != nil {
return nil, err
}
return userIDs, nil
}

func (m *Mutation) SetTemporarySchedulePickOrder(ctx context.Context, input graphql2.SetTempSchedPickOrderInput) (ok bool, err error) {
ok, err = m.ScheduleStore.SetTempSchedPickOrder(ctx, input.ScheduleID, input.UserIDs)

Check failure on line 197 in graphql2/graphqlapp/schedule.go

View workflow job for this annotation

GitHub Actions / lint

ineffectual assignment to ok (ineffassign)
if err != nil {
return false, err
}
return true, nil
}

func (s *Schedule) AssignedTo(ctx context.Context, raw *schedule.Schedule) ([]assignment.RawTarget, error) {
pols, err := s.PolicyStore.FindAllPoliciesBySchedule(ctx, raw.ID)
if err != nil {
Expand Down
5 changes: 5 additions & 0 deletions graphql2/models_gen.go

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

8 changes: 8 additions & 0 deletions graphql2/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -692,6 +692,9 @@ type Schedule {
userIDs: [ID!]
): [OnCallShift!]!

associatedUsers: [User!]!
lastTempSchedPickOrder: [ID!]!

targets: [ScheduleTarget!]!
target(input: TargetInput!): ScheduleTarget
isFavorite: Boolean!
Expand All @@ -700,6 +703,11 @@ type Schedule {
onCallNotificationRules: [OnCallNotificationRule!]!
}

input SetTempSchedPickOrderInput {
scheduleID: ID!
userIDs: [ID!]!
}

input SetScheduleOnCallNotificationRulesInput {
scheduleID: ID!
rules: [OnCallNotificationRuleInput!]!
Expand Down
125 changes: 125 additions & 0 deletions schedule/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package schedule
import (
"context"
"database/sql"
"encoding/json"

"github.com/google/uuid"
"github.com/pkg/errors"
Expand All @@ -16,12 +17,61 @@ import (
type Store struct {
db *sql.DB
usr *user.Store

findAssociatedUserIDs *sql.Stmt
findLastTempSchedPickOrder *sql.Stmt
setTempSchedPickOrder *sql.Stmt
}

func NewStore(ctx context.Context, db *sql.DB, usr *user.Store) (*Store, error) {
p := &util.Prepare{
DB: db,
Ctx: ctx,
}

return &Store{
db: db,
usr: usr,

findAssociatedUserIDs: p.P(`
SELECT DISTINCT COALESCE(s.tgt_user_id, r.user_id)
FROM schedule_rules s
LEFT JOIN rotation_participants r ON r.rotation_id = s.tgt_rotation_id
WHERE s.schedule_id = $1
`),
findLastTempSchedPickOrder: p.P(`
SELECT data -> 'user_ids' AS user_ids
FROM schedule_data
WHERE schedule_id = $1;
`),
setTempSchedPickOrder: p.P(`
INSERT INTO schedule_data (schedule_id, data)
VALUES (
$1,
jsonb_build_object('user_ids', to_jsonb($2::text[]))
)
ON CONFLICT (schedule_id)
DO UPDATE SET data = jsonb_set(
COALESCE(schedule_data.data, '{}'),
'{user_ids}',
to_jsonb((
SELECT ARRAY(
SELECT * FROM (
SELECT unnest($2::text[]) AS user_id
UNION ALL
SELECT user_id
FROM (
SELECT jsonb_array_elements_text(sd.data->'user_ids') AS user_id
FROM schedule_data sd
WHERE sd.schedule_id = $1
) existing
WHERE user_id <> ALL($2::text[])
) merged
)
)),
true
);
`),
}, nil
}

Expand Down Expand Up @@ -345,3 +395,78 @@ func (store *Store) DeleteManyTx(ctx context.Context, tx *sql.Tx, ids []string)
err = db.SchedDeleteMany(ctx, uuids)
return err
}

func (store *Store) FindAssociatedUserIDs(ctx context.Context, id string) ([]string, error) {
err := validate.UUID("ScheduleID", id)
if err != nil {
return nil, err
}
err = permission.LimitCheckAny(ctx, permission.All)
if err != nil {
return nil, err
}

rows, err := store.findAssociatedUserIDs.QueryContext(ctx, id)
if err != nil {
return nil, err
}
defer rows.Close()

var userIDs []string
for rows.Next() {
var userID string
if err := rows.Scan(&userID); err != nil {
return nil, err
}
userIDs = append(userIDs, userID)
}
if err := rows.Err(); err != nil {
return nil, err
}

return userIDs, nil
}

func (store *Store) FindLastTempSchedPickOrder(ctx context.Context, scheduleID string) ([]string, error) {
err := validate.UUID("ScheduleID", scheduleID)
if err != nil {
return nil, err
}

var userIDs []string
var rawUserIDs []byte
row := store.findLastTempSchedPickOrder.QueryRowContext(ctx, scheduleID)
err = row.Scan(&rawUserIDs)
if errors.Is(err, sql.ErrNoRows) {
return userIDs, nil
}
if err != nil {
return nil, err
}

err = json.Unmarshal(rawUserIDs, &userIDs)
if err != nil {
return nil, err
}

return userIDs, nil
}

func (store *Store) SetTempSchedPickOrder(ctx context.Context, scheduleID string, userIDs []string) (bool, error) {
err := validate.UUID("ScheduleID", scheduleID)
if err != nil {
return false, err
}

err = validate.ManyUUID("UserID", userIDs, 200)
if err != nil {
return false, err
}

_, err = store.setTempSchedPickOrder.ExecContext(ctx, scheduleID, userIDs)
if err != nil {
return false, err
}

return true, nil
}
1 change: 1 addition & 0 deletions web/src/app/dialogs/components/DialogTitleWrapper.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const useStyles = makeStyles((theme) => {
subtitle: {
overflowY: 'unset',
flexGrow: 0,
paddingBottom: 0,
},
topRightActions,
}
Expand Down
Loading
Loading