• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

DigitalTolk / ex / 28823234688

06 Jul 2026 09:05PM UTC coverage: 97.667% (-0.04%) from 97.704%
28823234688

Pull #163

github

web-flow
Merge db4b362d8 into f9238247c
Pull Request #163: Add sentry

2769 of 2796 branches covered (99.03%)

Branch coverage included in aggregate %.

126 of 140 new or added lines in 10 files covered. (90.0%)

346 existing lines in 36 files now uncovered.

18834 of 19323 relevant lines covered (97.47%)

70.4 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.0
/internal/handler/thread.go
1
package handler
2

3
import (
4
        "net/http"
5

6
        "github.com/DigitalTolk/ex/internal/middleware"
7
        "github.com/DigitalTolk/ex/internal/service"
8
)
9

10
// ThreadHandler exposes endpoints that span all parents (channels and
11
// conversations) for the authenticated user.
12
type ThreadHandler struct {
13
        messageSvc *service.MessageService
14
}
15

16
// NewThreadHandler creates a ThreadHandler.
17
func NewThreadHandler(messageSvc *service.MessageService) *ThreadHandler {
7✔
18
        return &ThreadHandler{messageSvc: messageSvc}
7✔
19
}
7✔
20

21
// List returns thread summaries the authenticated user has participated in,
22
// sorted newest-activity first.
23
func (h *ThreadHandler) List(w http.ResponseWriter, r *http.Request) {
4✔
24
        userID := middleware.UserIDFromContext(r.Context())
4✔
25
        if userID == "" {
5✔
26
                writeError(w, http.StatusUnauthorized, "unauthorized", "authentication required")
1✔
27
                return
1✔
28
        }
1✔
29
        summaries, err := h.messageSvc.ListUserThreads(r.Context(), userID)
3✔
30
        if err != nil {
4✔
31
                writeInternalError(w, r, "list_error", err)
1✔
32
                return
1✔
33
        }
1✔
34
        if summaries == nil { // coverage-ignore: MessageService.ListUserThreads always returns a make()-initialized (non-nil) slice; this nil-coercion is defensive against a future contract change.
2✔
35
                summaries = []*service.ThreadSummary{}
×
UNCOV
36
        }
×
37
        writeJSON(w, http.StatusOK, summaries)
2✔
38
}
39

40
func (h *ThreadHandler) Follow(w http.ResponseWriter, r *http.Request) {
5✔
41
        h.setFollow(w, r, true)
5✔
42
}
5✔
43

44
func (h *ThreadHandler) Unfollow(w http.ResponseWriter, r *http.Request) {
1✔
45
        h.setFollow(w, r, false)
1✔
46
}
1✔
47

48
func (h *ThreadHandler) setFollow(w http.ResponseWriter, r *http.Request, following bool) {
6✔
49
        userID := middleware.UserIDFromContext(r.Context())
6✔
50
        if userID == "" {
7✔
51
                writeError(w, http.StatusUnauthorized, "unauthorized", "authentication required")
1✔
52
                return
1✔
53
        }
1✔
54
        parentID := r.PathValue("parentID")
5✔
55
        threadRootID := r.PathValue("threadRootID")
5✔
56
        parentType, ok := normalizeThreadParentType(r.PathValue("parentType"))
5✔
57
        if parentID == "" || threadRootID == "" || !ok {
7✔
58
                writeError(w, http.StatusBadRequest, "bad_request", "invalid thread follow target")
2✔
59
                return
2✔
60
        }
2✔
61
        if err := h.messageSvc.SetThreadFollow(r.Context(), userID, parentID, parentType, threadRootID, following); err != nil {
4✔
62
                writeError(w, http.StatusBadRequest, "follow_error", err.Error())
1✔
63
                return
1✔
64
        }
1✔
65
        w.WriteHeader(http.StatusNoContent)
2✔
66
}
67

68
func normalizeThreadParentType(raw string) (string, bool) {
19✔
69
        switch raw {
19✔
70
        case "channel", "channels":
13✔
71
                return service.ParentChannel, true
13✔
72
        case "conversation", "conversations":
3✔
73
                return service.ParentConversation, true
3✔
74
        default:
3✔
75
                return "", false
3✔
76
        }
77
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc