mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: add notifications inbox db (#16599)
This PR is linked [to the following issue](https://github.com/coder/internal/issues/334). The objective is to create the DB layer and migration for the new `Coder Inbox`.
This commit is contained in:
@@ -281,6 +281,7 @@ var (
|
||||
DisplayName: "Notifier",
|
||||
Site: rbac.Permissions(map[string][]policy.Action{
|
||||
rbac.ResourceNotificationMessage.Type: {policy.ActionCreate, policy.ActionRead, policy.ActionUpdate, policy.ActionDelete},
|
||||
rbac.ResourceInboxNotification.Type: {policy.ActionCreate},
|
||||
}),
|
||||
Org: map[string][]rbac.Permission{},
|
||||
User: []rbac.Permission{},
|
||||
@@ -1126,6 +1127,14 @@ func (q *querier) CleanTailnetTunnels(ctx context.Context) error {
|
||||
return q.db.CleanTailnetTunnels(ctx)
|
||||
}
|
||||
|
||||
func (q *querier) CountUnreadInboxNotificationsByUserID(ctx context.Context, userID uuid.UUID) (int64, error) {
|
||||
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceInboxNotification.WithOwner(userID.String())); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return q.db.CountUnreadInboxNotificationsByUserID(ctx, userID)
|
||||
}
|
||||
|
||||
// TODO: Handle org scoped lookups
|
||||
func (q *querier) CustomRoles(ctx context.Context, arg database.CustomRolesParams) ([]database.CustomRole, error) {
|
||||
roleObject := rbac.ResourceAssignRole
|
||||
if arg.OrganizationID != uuid.Nil {
|
||||
@@ -1689,6 +1698,10 @@ func (q *querier) GetFileTemplates(ctx context.Context, fileID uuid.UUID) ([]dat
|
||||
return q.db.GetFileTemplates(ctx, fileID)
|
||||
}
|
||||
|
||||
func (q *querier) GetFilteredInboxNotificationsByUserID(ctx context.Context, arg database.GetFilteredInboxNotificationsByUserIDParams) ([]database.InboxNotification, error) {
|
||||
return fetchWithPostFilter(q.auth, policy.ActionRead, q.db.GetFilteredInboxNotificationsByUserID)(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) GetGitSSHKey(ctx context.Context, userID uuid.UUID) (database.GitSSHKey, error) {
|
||||
return fetchWithAction(q.log, q.auth, policy.ActionReadPersonal, q.db.GetGitSSHKey)(ctx, userID)
|
||||
}
|
||||
@@ -1748,6 +1761,14 @@ func (q *querier) GetHungProvisionerJobs(ctx context.Context, hungSince time.Tim
|
||||
return q.db.GetHungProvisionerJobs(ctx, hungSince)
|
||||
}
|
||||
|
||||
func (q *querier) GetInboxNotificationByID(ctx context.Context, id uuid.UUID) (database.InboxNotification, error) {
|
||||
return fetchWithAction(q.log, q.auth, policy.ActionRead, q.db.GetInboxNotificationByID)(ctx, id)
|
||||
}
|
||||
|
||||
func (q *querier) GetInboxNotificationsByUserID(ctx context.Context, userID database.GetInboxNotificationsByUserIDParams) ([]database.InboxNotification, error) {
|
||||
return fetchWithPostFilter(q.auth, policy.ActionRead, q.db.GetInboxNotificationsByUserID)(ctx, userID)
|
||||
}
|
||||
|
||||
func (q *querier) GetJFrogXrayScanByWorkspaceAndAgentID(ctx context.Context, arg database.GetJFrogXrayScanByWorkspaceAndAgentIDParams) (database.JfrogXrayScan, error) {
|
||||
if _, err := fetch(q.log, q.auth, q.db.GetWorkspaceByID)(ctx, arg.WorkspaceID); err != nil {
|
||||
return database.JfrogXrayScan{}, err
|
||||
@@ -3079,6 +3100,10 @@ func (q *querier) InsertGroupMember(ctx context.Context, arg database.InsertGrou
|
||||
return update(q.log, q.auth, fetch, q.db.InsertGroupMember)(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) InsertInboxNotification(ctx context.Context, arg database.InsertInboxNotificationParams) (database.InboxNotification, error) {
|
||||
return insert(q.log, q.auth, rbac.ResourceInboxNotification.WithOwner(arg.UserID.String()), q.db.InsertInboxNotification)(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) InsertLicense(ctx context.Context, arg database.InsertLicenseParams) (database.License, error) {
|
||||
if err := q.authorizeContext(ctx, policy.ActionCreate, rbac.ResourceLicense); err != nil {
|
||||
return database.License{}, err
|
||||
@@ -3666,6 +3691,14 @@ func (q *querier) UpdateInactiveUsersToDormant(ctx context.Context, lastSeenAfte
|
||||
return q.db.UpdateInactiveUsersToDormant(ctx, lastSeenAfter)
|
||||
}
|
||||
|
||||
func (q *querier) UpdateInboxNotificationReadStatus(ctx context.Context, args database.UpdateInboxNotificationReadStatusParams) error {
|
||||
fetchFunc := func(ctx context.Context, args database.UpdateInboxNotificationReadStatusParams) (database.InboxNotification, error) {
|
||||
return q.db.GetInboxNotificationByID(ctx, args.ID)
|
||||
}
|
||||
|
||||
return update(q.log, q.auth, fetchFunc, q.db.UpdateInboxNotificationReadStatus)(ctx, args)
|
||||
}
|
||||
|
||||
func (q *querier) UpdateMemberRoles(ctx context.Context, arg database.UpdateMemberRolesParams) (database.OrganizationMember, error) {
|
||||
// Authorized fetch will check that the actor has read access to the org member since the org member is returned.
|
||||
member, err := database.ExpectOne(q.OrganizationMembers(ctx, database.OrganizationMembersParams{
|
||||
|
||||
@@ -4466,6 +4466,141 @@ func (s *MethodTestSuite) TestNotifications() {
|
||||
Disableds: []bool{true, false},
|
||||
}).Asserts(rbac.ResourceNotificationPreference.WithOwner(user.ID.String()), policy.ActionUpdate)
|
||||
}))
|
||||
|
||||
s.Run("GetInboxNotificationsByUserID", s.Subtest(func(db database.Store, check *expects) {
|
||||
u := dbgen.User(s.T(), db, database.User{})
|
||||
|
||||
notifID := uuid.New()
|
||||
|
||||
notif := dbgen.NotificationInbox(s.T(), db, database.InsertInboxNotificationParams{
|
||||
ID: notifID,
|
||||
UserID: u.ID,
|
||||
TemplateID: notifications.TemplateWorkspaceAutoUpdated,
|
||||
Title: "test title",
|
||||
Content: "test content notification",
|
||||
Icon: "https://coder.com/favicon.ico",
|
||||
Actions: json.RawMessage("{}"),
|
||||
})
|
||||
|
||||
check.Args(database.GetInboxNotificationsByUserIDParams{
|
||||
UserID: u.ID,
|
||||
ReadStatus: database.InboxNotificationReadStatusAll,
|
||||
}).Asserts(rbac.ResourceInboxNotification.WithID(notifID).WithOwner(u.ID.String()), policy.ActionRead).Returns([]database.InboxNotification{notif})
|
||||
}))
|
||||
|
||||
s.Run("GetFilteredInboxNotificationsByUserID", s.Subtest(func(db database.Store, check *expects) {
|
||||
u := dbgen.User(s.T(), db, database.User{})
|
||||
|
||||
notifID := uuid.New()
|
||||
|
||||
targets := []uuid.UUID{u.ID, notifications.TemplateWorkspaceAutoUpdated}
|
||||
|
||||
notif := dbgen.NotificationInbox(s.T(), db, database.InsertInboxNotificationParams{
|
||||
ID: notifID,
|
||||
UserID: u.ID,
|
||||
TemplateID: notifications.TemplateWorkspaceAutoUpdated,
|
||||
Targets: targets,
|
||||
Title: "test title",
|
||||
Content: "test content notification",
|
||||
Icon: "https://coder.com/favicon.ico",
|
||||
Actions: json.RawMessage("{}"),
|
||||
})
|
||||
|
||||
check.Args(database.GetFilteredInboxNotificationsByUserIDParams{
|
||||
UserID: u.ID,
|
||||
Templates: []uuid.UUID{notifications.TemplateWorkspaceAutoUpdated},
|
||||
Targets: []uuid.UUID{u.ID},
|
||||
ReadStatus: database.InboxNotificationReadStatusAll,
|
||||
}).Asserts(rbac.ResourceInboxNotification.WithID(notifID).WithOwner(u.ID.String()), policy.ActionRead).Returns([]database.InboxNotification{notif})
|
||||
}))
|
||||
|
||||
s.Run("GetInboxNotificationByID", s.Subtest(func(db database.Store, check *expects) {
|
||||
u := dbgen.User(s.T(), db, database.User{})
|
||||
|
||||
notifID := uuid.New()
|
||||
|
||||
targets := []uuid.UUID{u.ID, notifications.TemplateWorkspaceAutoUpdated}
|
||||
|
||||
notif := dbgen.NotificationInbox(s.T(), db, database.InsertInboxNotificationParams{
|
||||
ID: notifID,
|
||||
UserID: u.ID,
|
||||
TemplateID: notifications.TemplateWorkspaceAutoUpdated,
|
||||
Targets: targets,
|
||||
Title: "test title",
|
||||
Content: "test content notification",
|
||||
Icon: "https://coder.com/favicon.ico",
|
||||
Actions: json.RawMessage("{}"),
|
||||
})
|
||||
|
||||
check.Args(notifID).Asserts(rbac.ResourceInboxNotification.WithID(notifID).WithOwner(u.ID.String()), policy.ActionRead).Returns(notif)
|
||||
}))
|
||||
|
||||
s.Run("CountUnreadInboxNotificationsByUserID", s.Subtest(func(db database.Store, check *expects) {
|
||||
u := dbgen.User(s.T(), db, database.User{})
|
||||
|
||||
notifID := uuid.New()
|
||||
|
||||
targets := []uuid.UUID{u.ID, notifications.TemplateWorkspaceAutoUpdated}
|
||||
|
||||
_ = dbgen.NotificationInbox(s.T(), db, database.InsertInboxNotificationParams{
|
||||
ID: notifID,
|
||||
UserID: u.ID,
|
||||
TemplateID: notifications.TemplateWorkspaceAutoUpdated,
|
||||
Targets: targets,
|
||||
Title: "test title",
|
||||
Content: "test content notification",
|
||||
Icon: "https://coder.com/favicon.ico",
|
||||
Actions: json.RawMessage("{}"),
|
||||
})
|
||||
|
||||
check.Args(u.ID).Asserts(rbac.ResourceInboxNotification.WithOwner(u.ID.String()), policy.ActionRead).Returns(int64(1))
|
||||
}))
|
||||
|
||||
s.Run("InsertInboxNotification", s.Subtest(func(db database.Store, check *expects) {
|
||||
u := dbgen.User(s.T(), db, database.User{})
|
||||
|
||||
notifID := uuid.New()
|
||||
|
||||
targets := []uuid.UUID{u.ID, notifications.TemplateWorkspaceAutoUpdated}
|
||||
|
||||
check.Args(database.InsertInboxNotificationParams{
|
||||
ID: notifID,
|
||||
UserID: u.ID,
|
||||
TemplateID: notifications.TemplateWorkspaceAutoUpdated,
|
||||
Targets: targets,
|
||||
Title: "test title",
|
||||
Content: "test content notification",
|
||||
Icon: "https://coder.com/favicon.ico",
|
||||
Actions: json.RawMessage("{}"),
|
||||
}).Asserts(rbac.ResourceInboxNotification.WithOwner(u.ID.String()), policy.ActionCreate)
|
||||
}))
|
||||
|
||||
s.Run("UpdateInboxNotificationReadStatus", s.Subtest(func(db database.Store, check *expects) {
|
||||
u := dbgen.User(s.T(), db, database.User{})
|
||||
|
||||
notifID := uuid.New()
|
||||
|
||||
targets := []uuid.UUID{u.ID, notifications.TemplateWorkspaceAutoUpdated}
|
||||
readAt := dbtestutil.NowInDefaultTimezone()
|
||||
|
||||
notif := dbgen.NotificationInbox(s.T(), db, database.InsertInboxNotificationParams{
|
||||
ID: notifID,
|
||||
UserID: u.ID,
|
||||
TemplateID: notifications.TemplateWorkspaceAutoUpdated,
|
||||
Targets: targets,
|
||||
Title: "test title",
|
||||
Content: "test content notification",
|
||||
Icon: "https://coder.com/favicon.ico",
|
||||
Actions: json.RawMessage("{}"),
|
||||
})
|
||||
|
||||
notif.ReadAt = sql.NullTime{Time: readAt, Valid: true}
|
||||
|
||||
check.Args(database.UpdateInboxNotificationReadStatusParams{
|
||||
ID: notifID,
|
||||
ReadAt: sql.NullTime{Time: readAt, Valid: true},
|
||||
}).Asserts(rbac.ResourceInboxNotification.WithID(notifID).WithOwner(u.ID.String()), policy.ActionUpdate)
|
||||
}))
|
||||
}
|
||||
|
||||
func (s *MethodTestSuite) TestOAuth2ProviderApps() {
|
||||
|
||||
Reference in New Issue
Block a user