Skip to content

feat: add notifications inbox db #16599

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

Merged
merged 40 commits into from
Mar 3, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
90c7b64
work on db queries
defelmnq Feb 18, 2025
e205049
work on dbauthz
defelmnq Feb 19, 2025
ef91969
work on dbauthz
defelmnq Feb 19, 2025
72e6de4
fmt and lint
defelmnq Feb 19, 2025
9edb384
change queries to match targets
defelmnq Feb 20, 2025
dc74ac4
test dbauthz
defelmnq Feb 20, 2025
6c9e41f
move notification migration
defelmnq Feb 20, 2025
1f18eca
make gen
defelmnq Feb 20, 2025
d02609f
Merge remote-tracking branch 'origin/main' into notif-inbox/int-334
defelmnq Feb 20, 2025
3bb9c57
rename inbox notifications
defelmnq Feb 20, 2025
27c1592
rename inbox notifications
defelmnq Feb 20, 2025
08c55c3
Improve authz
defelmnq Feb 21, 2025
b441448
add references in db
defelmnq Feb 21, 2025
1814c58
fix test icon
defelmnq Feb 21, 2025
9f46f51
improve foreign keys
defelmnq Feb 21, 2025
ddc65e6
add back querier file
defelmnq Feb 21, 2025
d1ce11e
improve uuid in seeding
defelmnq Feb 21, 2025
1f2fa24
improve dbauthz testing
defelmnq Feb 21, 2025
791ed76
format migration
defelmnq Feb 21, 2025
e141354
format migration
defelmnq Feb 21, 2025
a14723b
format migration
defelmnq Feb 21, 2025
e413ab9
rename functions
defelmnq Feb 21, 2025
452583e
add back index
defelmnq Feb 21, 2025
5aa54e1
fix query name missing
defelmnq Feb 21, 2025
da56e8f
regen file
defelmnq Feb 21, 2025
30b5ac4
add count queries
defelmnq Feb 23, 2025
8230ef3
improve queries to add pagination
defelmnq Feb 23, 2025
783bfe0
fix dbauthz tests
defelmnq Feb 23, 2025
c5aa917
fix sqlc queries
defelmnq Feb 23, 2025
da50947
improve dbauthz testing
defelmnq Feb 23, 2025
c1da7f8
improve sql queries
defelmnq Feb 24, 2025
f03976c
add type for read status
defelmnq Feb 24, 2025
94763e8
iterate on testing dbauthz
defelmnq Feb 24, 2025
0c5d322
change zero value of timestamp
defelmnq Feb 24, 2025
66d6fd6
add returns check in tests
defelmnq Feb 25, 2025
c45dfce
Merge remote-tracking branch 'origin/main' into notif-inbox/int-334
defelmnq Feb 25, 2025
b54dbef
rename migration
defelmnq Feb 25, 2025
e2e895b
improve queries comments
defelmnq Feb 25, 2025
e6abcbd
cange type for read status
defelmnq Feb 25, 2025
f691cf5
merge
defelmnq Mar 3, 2025
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
Prev Previous commit
Next Next commit
fix query name missing
  • Loading branch information
defelmnq committed Feb 21, 2025
commit 5aa54e12de7b8cbe40d11a8eb42557a4083dd1a0
48 changes: 24 additions & 24 deletions coderd/database/dbauthz/dbauthz.go
Original file line number Diff line number Diff line change
Expand Up @@ -1029,6 +1029,14 @@ func (q *querier) customRoleCheck(ctx context.Context, role database.CustomRole)
return nil
}

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) AcquireLock(ctx context.Context, id int64) error {
return q.db.AcquireLock(ctx, id)
}
Expand Down Expand Up @@ -1418,14 +1426,6 @@ func (q *querier) FavoriteWorkspace(ctx context.Context, id uuid.UUID) error {
return update(q.log, q.auth, fetch, q.db.FavoriteWorkspace)(ctx, id)
}

func (q *querier) GetInboxNotificationsByUserID(ctx context.Context, userID uuid.UUID) ([]database.InboxNotification, error) {
return fetchWithPostFilter(q.auth, policy.ActionRead, q.db.GetInboxNotificationsByUserID)(ctx, userID)
}

func (q *querier) GetInboxNotificationsByUserIDFilteredByTemplatesAndTargets(ctx context.Context, arg database.GetInboxNotificationsByUserIDFilteredByTemplatesAndTargetsParams) ([]database.InboxNotification, error) {
return fetchWithPostFilter(q.auth, policy.ActionRead, q.db.GetInboxNotificationsByUserIDFilteredByTemplatesAndTargets)(ctx, arg)
}

func (q *querier) FetchMemoryResourceMonitorsByAgentID(ctx context.Context, agentID uuid.UUID) (database.WorkspaceAgentMemoryResourceMonitor, error) {
workspace, err := q.db.GetWorkspaceByAgentID(ctx, agentID)
if err != nil {
Expand All @@ -1447,14 +1447,6 @@ func (q *querier) FetchNewMessageMetadata(ctx context.Context, arg database.Fetc
return q.db.FetchNewMessageMetadata(ctx, arg)
}

func (q *querier) GetUnreadInboxNotificationsByUserID(ctx context.Context, userID uuid.UUID) ([]database.InboxNotification, error) {
return fetchWithPostFilter(q.auth, policy.ActionRead, q.db.GetUnreadInboxNotificationsByUserID)(ctx, userID)
}

func (q *querier) GetUnreadInboxNotificationsByUserIDFilteredByTemplatesAndTargets(ctx context.Context, arg database.GetUnreadInboxNotificationsByUserIDFilteredByTemplatesAndTargetsParams) ([]database.InboxNotification, error) {
return fetchWithPostFilter(q.auth, policy.ActionRead, q.db.GetUnreadInboxNotificationsByUserIDFilteredByTemplatesAndTargets)(ctx, arg)
}

func (q *querier) FetchVolumesResourceMonitorsByAgentID(ctx context.Context, agentID uuid.UUID) ([]database.WorkspaceAgentVolumeResourceMonitor, error) {
workspace, err := q.db.GetWorkspaceByAgentID(ctx, agentID)
if err != nil {
Expand Down Expand Up @@ -1771,6 +1763,14 @@ func (q *querier) GetInboxNotificationByID(ctx context.Context, id uuid.UUID) (d
return fetchWithAction(q.log, q.auth, policy.ActionRead, q.db.GetInboxNotificationByID)(ctx, id)
}

func (q *querier) GetInboxNotificationsByUserID(ctx context.Context, userID uuid.UUID) ([]database.InboxNotification, error) {
return fetchWithPostFilter(q.auth, policy.ActionRead, q.db.GetInboxNotificationsByUserID)(ctx, userID)
}

func (q *querier) GetInboxNotificationsByUserIDFilteredByTemplatesAndTargets(ctx context.Context, arg database.GetInboxNotificationsByUserIDFilteredByTemplatesAndTargetsParams) ([]database.InboxNotification, error) {
return fetchWithPostFilter(q.auth, policy.ActionRead, q.db.GetInboxNotificationsByUserIDFilteredByTemplatesAndTargets)(ctx, arg)
}

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
Expand Down Expand Up @@ -2461,6 +2461,14 @@ func (q *querier) GetUnexpiredLicenses(ctx context.Context) ([]database.License,
return q.db.GetUnexpiredLicenses(ctx)
}

func (q *querier) GetUnreadInboxNotificationsByUserID(ctx context.Context, userID uuid.UUID) ([]database.InboxNotification, error) {
return fetchWithPostFilter(q.auth, policy.ActionRead, q.db.GetUnreadInboxNotificationsByUserID)(ctx, userID)
}

func (q *querier) GetUnreadInboxNotificationsByUserIDFilteredByTemplatesAndTargets(ctx context.Context, arg database.GetUnreadInboxNotificationsByUserIDFilteredByTemplatesAndTargetsParams) ([]database.InboxNotification, error) {
return fetchWithPostFilter(q.auth, policy.ActionRead, q.db.GetUnreadInboxNotificationsByUserIDFilteredByTemplatesAndTargets)(ctx, arg)
}

func (q *querier) GetUserActivityInsights(ctx context.Context, arg database.GetUserActivityInsightsParams) ([]database.GetUserActivityInsightsRow, error) {
// Used by insights endpoints. Need to check both for auditors and for regular users with template acl perms.
if err := q.authorizeContext(ctx, policy.ActionViewInsights, rbac.ResourceTemplate); err != nil {
Expand Down Expand Up @@ -3589,14 +3597,6 @@ func (q *querier) RevokeDBCryptKey(ctx context.Context, activeKeyDigest string)
return q.db.RevokeDBCryptKey(ctx, activeKeyDigest)
}

func (q *querier) UpdateInboxNotificationAsRead(ctx context.Context, args database.UpdateInboxNotificationAsReadParams) error {
fetchFunc := func(ctx context.Context, args database.UpdateInboxNotificationAsReadParams) (database.InboxNotification, error) {
return q.db.GetInboxNotificationByID(ctx, args.ID)
}

return update(q.log, q.auth, fetchFunc, q.db.UpdateInboxNotificationAsRead)(ctx, args)
}

func (q *querier) TryAcquireLock(ctx context.Context, id int64) (bool, error) {
return q.db.TryAcquireLock(ctx, id)
}
Expand Down
4 changes: 2 additions & 2 deletions coderd/database/dbauthz/dbauthz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4604,7 +4604,7 @@ func (s *MethodTestSuite) TestNotifications() {
}).Asserts(rbac.ResourceInboxNotification.WithOwner(u.ID.String()), policy.ActionCreate)
}))

s.Run("UpdateInboxNotificationAsRead", s.Subtest(func(db database.Store, check *expects) {
s.Run("UpdateInboxNotificationReadStatus", s.Subtest(func(db database.Store, check *expects) {
u := dbgen.User(s.T(), db, database.User{})

notifID := uuid.New()
Expand All @@ -4625,7 +4625,7 @@ func (s *MethodTestSuite) TestNotifications() {

notif.ReadAt = sql.NullTime{Time: readAt, Valid: true}

check.Args(database.UpdateInboxNotificationAsReadParams{
check.Args(database.UpdateInboxNotificationReadStatusParams{
ID: notifID,
ReadAt: sql.NullTime{Time: readAt, Valid: true},
}).Asserts(rbac.ResourceInboxNotification.WithID(notifID).WithOwner(u.ID.String()), policy.ActionUpdate)
Expand Down
248 changes: 124 additions & 124 deletions coderd/database/dbmem/dbmem.go
Original file line number Diff line number Diff line change
Expand Up @@ -1230,6 +1230,24 @@ func (q *FakeQuerier) getProvisionerJobsByIDsWithQueuePositionLocked(_ context.C
return jobs, nil
}

func (q *FakeQuerier) UpdateInboxNotificationReadStatus(_ context.Context, arg database.UpdateInboxNotificationReadStatusParams) error {
err := validateDatabaseType(arg)
if err != nil {
return err
}

q.mutex.Lock()
defer q.mutex.Unlock()

for i := range q.InboxNotification {
if q.InboxNotification[i].ID == arg.ID {
q.InboxNotification[i].ReadAt = arg.ReadAt
}
}

return nil
}

func (*FakeQuerier) AcquireLock(_ context.Context, _ int64) error {
return xerrors.New("AcquireLock must only be called within a transaction")
}
Expand Down Expand Up @@ -2365,59 +2383,6 @@ func (q *FakeQuerier) FavoriteWorkspace(_ context.Context, arg uuid.UUID) error
return nil
}

func (q *FakeQuerier) GetInboxNotificationsByUserID(_ context.Context, userID uuid.UUID) ([]database.InboxNotification, error) {
q.mutex.RLock()
defer q.mutex.RUnlock()

notifications := make([]database.InboxNotification, 0)
for _, notification := range q.InboxNotification {
if notification.UserID == userID {
notifications = append(notifications, notification)
}
}

return notifications, nil
}

func (q *FakeQuerier) GetInboxNotificationsByUserIDFilteredByTemplatesAndTargets(_ context.Context, arg database.GetInboxNotificationsByUserIDFilteredByTemplatesAndTargetsParams) ([]database.InboxNotification, error) {
q.mutex.RLock()
defer q.mutex.RUnlock()

notifications := make([]database.InboxNotification, 0)
for _, notification := range q.InboxNotification {
if notification.UserID == arg.UserID {
for _, template := range arg.Templates {
templateFound := false
if notification.TemplateID == template {
templateFound = true
}

if !templateFound {
continue
}
}

for _, target := range arg.Targets {
isFound := false
for _, insertedTarget := range notification.Targets {
if insertedTarget == target {
isFound = true
break
}
}

if !isFound {
continue
}

notifications = append(notifications, notification)
}
}
}

return notifications, nil
}

func (q *FakeQuerier) FetchMemoryResourceMonitorsByAgentID(_ context.Context, agentID uuid.UUID) (database.WorkspaceAgentMemoryResourceMonitor, error) {
for _, monitor := range q.workspaceAgentMemoryResourceMonitors {
if monitor.AgentID == agentID {
Expand Down Expand Up @@ -2460,59 +2425,6 @@ func (q *FakeQuerier) FetchNewMessageMetadata(_ context.Context, arg database.Fe
}, nil
}

func (q *FakeQuerier) GetUnreadInboxNotificationsByUserID(_ context.Context, userID uuid.UUID) ([]database.InboxNotification, error) {
q.mutex.RLock()
defer q.mutex.RUnlock()

notifications := make([]database.InboxNotification, 0)
for _, notification := range q.InboxNotification {
if notification.UserID == userID && !notification.ReadAt.Valid {
notifications = append(notifications, notification)
}
}

return notifications, nil
}

func (q *FakeQuerier) GetUnreadInboxNotificationsByUserIDFilteredByTemplatesAndTargets(_ context.Context, arg database.GetUnreadInboxNotificationsByUserIDFilteredByTemplatesAndTargetsParams) ([]database.InboxNotification, error) {
q.mutex.RLock()
defer q.mutex.RUnlock()

notifications := make([]database.InboxNotification, 0)
for _, notification := range q.InboxNotification {
if notification.UserID == arg.UserID && !notification.ReadAt.Valid {
for _, template := range arg.Templates {
templateFound := false
if notification.TemplateID == template {
templateFound = true
}

if !templateFound {
continue
}
}

for _, target := range arg.Targets {
isFound := false
for _, insertedTarget := range notification.Targets {
if insertedTarget == target {
isFound = true
break
}
}

if !isFound {
continue
}

notifications = append(notifications, notification)
}
}
}

return notifications, nil
}

func (q *FakeQuerier) FetchVolumesResourceMonitorsByAgentID(_ context.Context, agentID uuid.UUID) ([]database.WorkspaceAgentVolumeResourceMonitor, error) {
monitors := []database.WorkspaceAgentVolumeResourceMonitor{}

Expand Down Expand Up @@ -3453,6 +3365,59 @@ func (q *FakeQuerier) GetInboxNotificationByID(_ context.Context, id uuid.UUID)
return database.InboxNotification{}, sql.ErrNoRows
}

func (q *FakeQuerier) GetInboxNotificationsByUserID(_ context.Context, userID uuid.UUID) ([]database.InboxNotification, error) {
q.mutex.RLock()
defer q.mutex.RUnlock()

notifications := make([]database.InboxNotification, 0)
for _, notification := range q.InboxNotification {
if notification.UserID == userID {
notifications = append(notifications, notification)
}
}

return notifications, nil
}

func (q *FakeQuerier) GetInboxNotificationsByUserIDFilteredByTemplatesAndTargets(_ context.Context, arg database.GetInboxNotificationsByUserIDFilteredByTemplatesAndTargetsParams) ([]database.InboxNotification, error) {
q.mutex.RLock()
defer q.mutex.RUnlock()

notifications := make([]database.InboxNotification, 0)
for _, notification := range q.InboxNotification {
if notification.UserID == arg.UserID {
for _, template := range arg.Templates {
templateFound := false
if notification.TemplateID == template {
templateFound = true
}

if !templateFound {
continue
}
}

for _, target := range arg.Targets {
isFound := false
for _, insertedTarget := range notification.Targets {
if insertedTarget == target {
isFound = true
break
}
}

if !isFound {
continue
}

notifications = append(notifications, notification)
}
}
}

return notifications, nil
}

func (q *FakeQuerier) GetJFrogXrayScanByWorkspaceAndAgentID(_ context.Context, arg database.GetJFrogXrayScanByWorkspaceAndAgentIDParams) (database.JfrogXrayScan, error) {
err := validateDatabaseType(arg)
if err != nil {
Expand Down Expand Up @@ -5905,6 +5870,59 @@ func (q *FakeQuerier) GetUnexpiredLicenses(_ context.Context) ([]database.Licens
return results, nil
}

func (q *FakeQuerier) GetUnreadInboxNotificationsByUserID(_ context.Context, userID uuid.UUID) ([]database.InboxNotification, error) {
q.mutex.RLock()
defer q.mutex.RUnlock()

notifications := make([]database.InboxNotification, 0)
for _, notification := range q.InboxNotification {
if notification.UserID == userID && !notification.ReadAt.Valid {
notifications = append(notifications, notification)
}
}

return notifications, nil
}

func (q *FakeQuerier) GetUnreadInboxNotificationsByUserIDFilteredByTemplatesAndTargets(_ context.Context, arg database.GetUnreadInboxNotificationsByUserIDFilteredByTemplatesAndTargetsParams) ([]database.InboxNotification, error) {
q.mutex.RLock()
defer q.mutex.RUnlock()

notifications := make([]database.InboxNotification, 0)
for _, notification := range q.InboxNotification {
if notification.UserID == arg.UserID && !notification.ReadAt.Valid {
for _, template := range arg.Templates {
templateFound := false
if notification.TemplateID == template {
templateFound = true
}

if !templateFound {
continue
}
}

for _, target := range arg.Targets {
isFound := false
for _, insertedTarget := range notification.Targets {
if insertedTarget == target {
isFound = true
break
}
}

if !isFound {
continue
}

notifications = append(notifications, notification)
}
}
}

return notifications, nil
}

func (q *FakeQuerier) GetUserActivityInsights(_ context.Context, arg database.GetUserActivityInsightsParams) ([]database.GetUserActivityInsightsRow, error) {
err := validateDatabaseType(arg)
if err != nil {
Expand Down Expand Up @@ -9588,24 +9606,6 @@ func (q *FakeQuerier) RevokeDBCryptKey(_ context.Context, activeKeyDigest string
return sql.ErrNoRows
}

func (q *FakeQuerier) UpdateInboxNotificationAsRead(_ context.Context, arg database.UpdateInboxNotificationAsReadParams) error {
err := validateDatabaseType(arg)
if err != nil {
return err
}

q.mutex.Lock()
defer q.mutex.Unlock()

for i := range q.InboxNotification {
if q.InboxNotification[i].ID == arg.ID {
q.InboxNotification[i].ReadAt = arg.ReadAt
}
}

return nil
}

func (*FakeQuerier) TryAcquireLock(_ context.Context, _ int64) (bool, error) {
return false, xerrors.New("TryAcquireLock must only be called within a transaction")
}
Expand Down
Loading
Loading