diff --git a/server/cmd/server/notification_listeners_test.go b/server/cmd/server/notification_listeners_test.go index 79aa3f0a..441f11f9 100644 --- a/server/cmd/server/notification_listeners_test.go +++ b/server/cmd/server/notification_listeners_test.go @@ -18,6 +18,7 @@ import ( func inboxItemsForRecipient(t *testing.T, queries *db.Queries, recipientID string) []db.ListInboxItemsRow { t.Helper() items, err := queries.ListInboxItems(context.Background(), db.ListInboxItemsParams{ + WorkspaceID: util.ParseUUID(testWorkspaceID), RecipientType: "member", RecipientID: util.ParseUUID(recipientID), Limit: 100, diff --git a/server/internal/handler/inbox.go b/server/internal/handler/inbox.go index fbd01423..e826172d 100644 --- a/server/internal/handler/inbox.go +++ b/server/internal/handler/inbox.go @@ -91,6 +91,7 @@ func (h *Handler) ListInbox(w http.ResponseWriter, r *http.Request) { if !ok { return } + workspaceID := r.Header.Get("X-Workspace-ID") limit := 50 offset := 0 @@ -106,6 +107,7 @@ func (h *Handler) ListInbox(w http.ResponseWriter, r *http.Request) { } items, err := h.Queries.ListInboxItems(r.Context(), db.ListInboxItemsParams{ + WorkspaceID: parseUUID(workspaceID), RecipientType: "member", RecipientID: parseUUID(userID), Limit: int32(limit), @@ -173,8 +175,10 @@ func (h *Handler) CountUnreadInbox(w http.ResponseWriter, r *http.Request) { if !ok { return } + workspaceID := r.Header.Get("X-Workspace-ID") count, err := h.Queries.CountUnreadInbox(r.Context(), db.CountUnreadInboxParams{ + WorkspaceID: parseUUID(workspaceID), RecipientType: "member", RecipientID: parseUUID(userID), }) @@ -191,15 +195,18 @@ func (h *Handler) MarkAllInboxRead(w http.ResponseWriter, r *http.Request) { if !ok { return } + workspaceID := r.Header.Get("X-Workspace-ID") - count, err := h.Queries.MarkAllInboxRead(r.Context(), parseUUID(userID)) + count, err := h.Queries.MarkAllInboxRead(r.Context(), db.MarkAllInboxReadParams{ + WorkspaceID: parseUUID(workspaceID), + RecipientID: parseUUID(userID), + }) if err != nil { writeError(w, http.StatusInternalServerError, "failed to mark all inbox read") return } slog.Info("inbox: mark all read", append(logger.RequestAttrs(r), "user_id", userID, "count", count)...) - workspaceID := r.Header.Get("X-Workspace-ID") h.publish(protocol.EventInboxBatchRead, workspaceID, "member", userID, map[string]any{ "recipient_id": userID, "count": count, @@ -213,15 +220,18 @@ func (h *Handler) ArchiveAllInbox(w http.ResponseWriter, r *http.Request) { if !ok { return } + workspaceID := r.Header.Get("X-Workspace-ID") - count, err := h.Queries.ArchiveAllInbox(r.Context(), parseUUID(userID)) + count, err := h.Queries.ArchiveAllInbox(r.Context(), db.ArchiveAllInboxParams{ + WorkspaceID: parseUUID(workspaceID), + RecipientID: parseUUID(userID), + }) if err != nil { writeError(w, http.StatusInternalServerError, "failed to archive all inbox") return } slog.Info("inbox: archive all", append(logger.RequestAttrs(r), "user_id", userID, "count", count)...) - workspaceID := r.Header.Get("X-Workspace-ID") h.publish(protocol.EventInboxBatchArchived, workspaceID, "member", userID, map[string]any{ "recipient_id": userID, "count": count, @@ -235,15 +245,18 @@ func (h *Handler) ArchiveAllReadInbox(w http.ResponseWriter, r *http.Request) { if !ok { return } + workspaceID := r.Header.Get("X-Workspace-ID") - count, err := h.Queries.ArchiveAllReadInbox(r.Context(), parseUUID(userID)) + count, err := h.Queries.ArchiveAllReadInbox(r.Context(), db.ArchiveAllReadInboxParams{ + WorkspaceID: parseUUID(workspaceID), + RecipientID: parseUUID(userID), + }) if err != nil { writeError(w, http.StatusInternalServerError, "failed to archive all read inbox") return } slog.Info("inbox: archive all read", append(logger.RequestAttrs(r), "user_id", userID, "count", count)...) - workspaceID := r.Header.Get("X-Workspace-ID") h.publish(protocol.EventInboxBatchArchived, workspaceID, "member", userID, map[string]any{ "recipient_id": userID, "count": count, @@ -257,15 +270,18 @@ func (h *Handler) ArchiveCompletedInbox(w http.ResponseWriter, r *http.Request) if !ok { return } + workspaceID := r.Header.Get("X-Workspace-ID") - count, err := h.Queries.ArchiveCompletedInbox(r.Context(), parseUUID(userID)) + count, err := h.Queries.ArchiveCompletedInbox(r.Context(), db.ArchiveCompletedInboxParams{ + WorkspaceID: parseUUID(workspaceID), + RecipientID: parseUUID(userID), + }) if err != nil { writeError(w, http.StatusInternalServerError, "failed to archive completed inbox") return } slog.Info("inbox: archive completed", append(logger.RequestAttrs(r), "user_id", userID, "count", count)...) - workspaceID := r.Header.Get("X-Workspace-ID") h.publish(protocol.EventInboxBatchArchived, workspaceID, "member", userID, map[string]any{ "recipient_id": userID, "count": count, diff --git a/server/pkg/db/generated/inbox.sql.go b/server/pkg/db/generated/inbox.sql.go index 9dc7e3b5..b63e2ded 100644 --- a/server/pkg/db/generated/inbox.sql.go +++ b/server/pkg/db/generated/inbox.sql.go @@ -13,11 +13,16 @@ import ( const archiveAllInbox = `-- name: ArchiveAllInbox :execrows UPDATE inbox_item SET archived = true -WHERE recipient_type = 'member' AND recipient_id = $1 AND archived = false +WHERE workspace_id = $1 AND recipient_type = 'member' AND recipient_id = $2 AND archived = false ` -func (q *Queries) ArchiveAllInbox(ctx context.Context, recipientID pgtype.UUID) (int64, error) { - result, err := q.db.Exec(ctx, archiveAllInbox, recipientID) +type ArchiveAllInboxParams struct { + WorkspaceID pgtype.UUID `json:"workspace_id"` + RecipientID pgtype.UUID `json:"recipient_id"` +} + +func (q *Queries) ArchiveAllInbox(ctx context.Context, arg ArchiveAllInboxParams) (int64, error) { + result, err := q.db.Exec(ctx, archiveAllInbox, arg.WorkspaceID, arg.RecipientID) if err != nil { return 0, err } @@ -26,11 +31,16 @@ func (q *Queries) ArchiveAllInbox(ctx context.Context, recipientID pgtype.UUID) const archiveAllReadInbox = `-- name: ArchiveAllReadInbox :execrows UPDATE inbox_item SET archived = true -WHERE recipient_type = 'member' AND recipient_id = $1 AND read = true AND archived = false +WHERE workspace_id = $1 AND recipient_type = 'member' AND recipient_id = $2 AND read = true AND archived = false ` -func (q *Queries) ArchiveAllReadInbox(ctx context.Context, recipientID pgtype.UUID) (int64, error) { - result, err := q.db.Exec(ctx, archiveAllReadInbox, recipientID) +type ArchiveAllReadInboxParams struct { + WorkspaceID pgtype.UUID `json:"workspace_id"` + RecipientID pgtype.UUID `json:"recipient_id"` +} + +func (q *Queries) ArchiveAllReadInbox(ctx context.Context, arg ArchiveAllReadInboxParams) (int64, error) { + result, err := q.db.Exec(ctx, archiveAllReadInbox, arg.WorkspaceID, arg.RecipientID) if err != nil { return 0, err } @@ -38,13 +48,18 @@ func (q *Queries) ArchiveAllReadInbox(ctx context.Context, recipientID pgtype.UU } const archiveCompletedInbox = `-- name: ArchiveCompletedInbox :execrows -UPDATE inbox_item SET archived = true -WHERE recipient_type = 'member' AND recipient_id = $1 AND archived = false - AND issue_id IN (SELECT id FROM issue WHERE status IN ('done', 'cancelled')) +UPDATE inbox_item i SET archived = true +WHERE i.workspace_id = $1 AND i.recipient_type = 'member' AND i.recipient_id = $2 AND i.archived = false + AND i.issue_id IN (SELECT id FROM issue WHERE status IN ('done', 'cancelled')) ` -func (q *Queries) ArchiveCompletedInbox(ctx context.Context, recipientID pgtype.UUID) (int64, error) { - result, err := q.db.Exec(ctx, archiveCompletedInbox, recipientID) +type ArchiveCompletedInboxParams struct { + WorkspaceID pgtype.UUID `json:"workspace_id"` + RecipientID pgtype.UUID `json:"recipient_id"` +} + +func (q *Queries) ArchiveCompletedInbox(ctx context.Context, arg ArchiveCompletedInboxParams) (int64, error) { + result, err := q.db.Exec(ctx, archiveCompletedInbox, arg.WorkspaceID, arg.RecipientID) if err != nil { return 0, err } @@ -82,16 +97,17 @@ func (q *Queries) ArchiveInboxItem(ctx context.Context, id pgtype.UUID) (InboxIt const countUnreadInbox = `-- name: CountUnreadInbox :one SELECT count(*) FROM inbox_item -WHERE recipient_type = $1 AND recipient_id = $2 AND read = false AND archived = false +WHERE workspace_id = $1 AND recipient_type = $2 AND recipient_id = $3 AND read = false AND archived = false ` type CountUnreadInboxParams struct { + WorkspaceID pgtype.UUID `json:"workspace_id"` RecipientType string `json:"recipient_type"` RecipientID pgtype.UUID `json:"recipient_id"` } func (q *Queries) CountUnreadInbox(ctx context.Context, arg CountUnreadInboxParams) (int64, error) { - row := q.db.QueryRow(ctx, countUnreadInbox, arg.RecipientType, arg.RecipientID) + row := q.db.QueryRow(ctx, countUnreadInbox, arg.WorkspaceID, arg.RecipientType, arg.RecipientID) var count int64 err := row.Scan(&count) return count, err @@ -188,12 +204,13 @@ SELECT i.id, i.workspace_id, i.recipient_type, i.recipient_id, i.type, i.severit iss.status as issue_status FROM inbox_item i LEFT JOIN issue iss ON iss.id = i.issue_id -WHERE i.recipient_type = $1 AND i.recipient_id = $2 AND i.archived = false +WHERE i.workspace_id = $1 AND i.recipient_type = $2 AND i.recipient_id = $3 AND i.archived = false ORDER BY i.created_at DESC -LIMIT $3 OFFSET $4 +LIMIT $4 OFFSET $5 ` type ListInboxItemsParams struct { + WorkspaceID pgtype.UUID `json:"workspace_id"` RecipientType string `json:"recipient_type"` RecipientID pgtype.UUID `json:"recipient_id"` Limit int32 `json:"limit"` @@ -221,6 +238,7 @@ type ListInboxItemsRow struct { func (q *Queries) ListInboxItems(ctx context.Context, arg ListInboxItemsParams) ([]ListInboxItemsRow, error) { rows, err := q.db.Query(ctx, listInboxItems, + arg.WorkspaceID, arg.RecipientType, arg.RecipientID, arg.Limit, @@ -263,11 +281,16 @@ func (q *Queries) ListInboxItems(ctx context.Context, arg ListInboxItemsParams) const markAllInboxRead = `-- name: MarkAllInboxRead :execrows UPDATE inbox_item SET read = true -WHERE recipient_type = 'member' AND recipient_id = $1 AND archived = false AND read = false +WHERE workspace_id = $1 AND recipient_type = 'member' AND recipient_id = $2 AND archived = false AND read = false ` -func (q *Queries) MarkAllInboxRead(ctx context.Context, recipientID pgtype.UUID) (int64, error) { - result, err := q.db.Exec(ctx, markAllInboxRead, recipientID) +type MarkAllInboxReadParams struct { + WorkspaceID pgtype.UUID `json:"workspace_id"` + RecipientID pgtype.UUID `json:"recipient_id"` +} + +func (q *Queries) MarkAllInboxRead(ctx context.Context, arg MarkAllInboxReadParams) (int64, error) { + result, err := q.db.Exec(ctx, markAllInboxRead, arg.WorkspaceID, arg.RecipientID) if err != nil { return 0, err } diff --git a/server/pkg/db/queries/inbox.sql b/server/pkg/db/queries/inbox.sql index 2a144293..bff47a78 100644 --- a/server/pkg/db/queries/inbox.sql +++ b/server/pkg/db/queries/inbox.sql @@ -3,9 +3,9 @@ SELECT i.*, iss.status as issue_status FROM inbox_item i LEFT JOIN issue iss ON iss.id = i.issue_id -WHERE i.recipient_type = $1 AND i.recipient_id = $2 AND i.archived = false +WHERE i.workspace_id = $1 AND i.recipient_type = $2 AND i.recipient_id = $3 AND i.archived = false ORDER BY i.created_at DESC -LIMIT $3 OFFSET $4; +LIMIT $4 OFFSET $5; -- name: GetInboxItem :one SELECT * FROM inbox_item @@ -31,21 +31,21 @@ RETURNING *; -- name: CountUnreadInbox :one SELECT count(*) FROM inbox_item -WHERE recipient_type = $1 AND recipient_id = $2 AND read = false AND archived = false; +WHERE workspace_id = $1 AND recipient_type = $2 AND recipient_id = $3 AND read = false AND archived = false; -- name: MarkAllInboxRead :execrows UPDATE inbox_item SET read = true -WHERE recipient_type = 'member' AND recipient_id = $1 AND archived = false AND read = false; +WHERE workspace_id = $1 AND recipient_type = 'member' AND recipient_id = $2 AND archived = false AND read = false; -- name: ArchiveAllInbox :execrows UPDATE inbox_item SET archived = true -WHERE recipient_type = 'member' AND recipient_id = $1 AND archived = false; +WHERE workspace_id = $1 AND recipient_type = 'member' AND recipient_id = $2 AND archived = false; -- name: ArchiveAllReadInbox :execrows UPDATE inbox_item SET archived = true -WHERE recipient_type = 'member' AND recipient_id = $1 AND read = true AND archived = false; +WHERE workspace_id = $1 AND recipient_type = 'member' AND recipient_id = $2 AND read = true AND archived = false; -- name: ArchiveCompletedInbox :execrows -UPDATE inbox_item SET archived = true -WHERE recipient_type = 'member' AND recipient_id = $1 AND archived = false - AND issue_id IN (SELECT id FROM issue WHERE status IN ('done', 'cancelled')); +UPDATE inbox_item i SET archived = true +WHERE i.workspace_id = $1 AND i.recipient_type = 'member' AND i.recipient_id = $2 AND i.archived = false + AND i.issue_id IN (SELECT id FROM issue WHERE status IN ('done', 'cancelled'));