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

DigitalTolk / ex / 25397225489

05 May 2026 07:19PM UTC coverage: 92.504% (-0.009%) from 92.513%
25397225489

push

github

web-flow
Review (#73)

2376 of 2637 branches covered (90.1%)

Branch coverage included in aggregate %.

245 of 274 new or added lines in 17 files covered. (89.42%)

9 existing lines in 2 files now uncovered.

13124 of 14119 relevant lines covered (92.95%)

1098.66 hits per line

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

95.08
/internal/handler/handler.go
1
package handler
2

3
import (
4
        "encoding/json"
5
        "errors"
6
        "fmt"
7
        "io"
8
        "net/http"
9
        "strconv"
10

11
        "github.com/DigitalTolk/ex/internal/middleware"
12
        "github.com/DigitalTolk/ex/internal/model"
13
)
14

15
// JSON is a convenience type for building JSON response objects.
16
type JSON map[string]interface{}
17

18
// writeJSON serialises data as JSON and writes it to the response with the
19
// given HTTP status code.
20
func writeJSON(w http.ResponseWriter, status int, data interface{}) {
366✔
21
        w.Header().Set("Content-Type", "application/json; charset=utf-8")
366✔
22
        w.WriteHeader(status)
366✔
23
        _ = json.NewEncoder(w).Encode(data)
366✔
24
}
366✔
25

26
// writeError writes a structured error response.
27
func writeError(w http.ResponseWriter, status int, code, message string) {
252✔
28
        writeJSON(w, status, JSON{
252✔
29
                "error": JSON{
252✔
30
                        "code":    code,
252✔
31
                        "message": message,
252✔
32
                },
252✔
33
        })
252✔
34
}
252✔
35

36
// readJSON decodes the request body (up to 1 MB) into dest.
37
func readJSON(r *http.Request, dest interface{}) error {
164✔
38
        r.Body = http.MaxBytesReader(nil, r.Body, 1<<20) // 1 MB
164✔
39
        dec := json.NewDecoder(r.Body)
164✔
40
        dec.DisallowUnknownFields()
164✔
41
        if err := dec.Decode(dest); err != nil {
203✔
42
                return fmt.Errorf("invalid JSON: %w", err)
39✔
43
        }
39✔
44
        var extra struct{}
125✔
45
        if err := dec.Decode(&extra); err == nil {
125✔
NEW
46
                return errors.New("invalid JSON: trailing data")
×
47
        } else if !errors.Is(err, io.EOF) {
126✔
48
                return fmt.Errorf("invalid JSON: %w", err)
1✔
49
        }
1✔
50
        return nil
124✔
51
}
52

53
// pathParam extracts a named path parameter using Go 1.22+ routing.
54
func pathParam(r *http.Request, name string) string {
270✔
55
        return r.PathValue(name)
270✔
56
}
270✔
57

58
// queryParam returns a query string parameter, or the fallback if absent.
59
func queryParam(r *http.Request, name, fallback string) string {
77✔
60
        v := r.URL.Query().Get(name)
77✔
61
        if v == "" {
128✔
62
                return fallback
51✔
63
        }
51✔
64
        return v
26✔
65
}
66

67
// requireAdmin writes a 403 to w and returns false unless the request is
68
// authenticated as a system admin. Use at the top of admin-only handlers.
69
func requireAdmin(w http.ResponseWriter, r *http.Request) bool {
25✔
70
        claims := middleware.ClaimsFromContext(r.Context())
25✔
71
        if claims == nil || claims.SystemRole != model.SystemRoleAdmin {
31✔
72
                writeError(w, http.StatusForbidden, "forbidden", "admin only")
6✔
73
                return false
6✔
74
        }
6✔
75
        return true
19✔
76
}
77

78
// queryInt returns a query string parameter as an integer, or the fallback on
79
// parse failure or absence.
80
func queryInt(r *http.Request, name string, fallback int) int {
42✔
81
        v := r.URL.Query().Get(name)
42✔
82
        if v == "" {
61✔
83
                return fallback
19✔
84
        }
19✔
85
        n, err := strconv.Atoi(v)
23✔
86
        if err != nil {
28✔
87
                return fallback
5✔
88
        }
5✔
89
        return n
18✔
90
}
91

92
func clampInt(n, min, max int) int {
19✔
93
        if n < min {
19✔
NEW
94
                return min
×
NEW
95
        }
×
96
        if n > max {
23✔
97
                return max
4✔
98
        }
4✔
99
        return n
15✔
100
}
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