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

vocdoni / saas-backend / 16023543007

02 Jul 2025 11:10AM UTC coverage: 56.13% (-0.05%) from 56.178%
16023543007

Pull #166

github

emmdim
api: Extends errors module to support arbtitrary data
Pull Request #166: api: Extends errors module to support arbtitrary data

2 of 11 new or added lines in 1 file covered. (18.18%)

4853 of 8646 relevant lines covered (56.13%)

24.32 hits per line

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

74.74
/errors/errors.go
1
package errors
2

3
import (
4
        "encoding/json"
5
        "fmt"
6
        "net/http"
7
        "runtime"
8

9
        "go.vocdoni.io/dvote/log"
10
)
11

12
// Error is used by handler functions to wrap errors, assigning a unique error code
13
// and also specifying which HTTP Status should be used.
14
type Error struct {
15
        Err        error  // Original error
16
        Code       int    // Error code
17
        HTTPstatus int    // HTTP status code to return
18
        LogLevel   string // Log level for this error (defaults to "debug")
19
        Data       any    // Optional data to include in the error response
20
}
21

22
// MarshalJSON returns a JSON containing Err.Error() and Code. Field HTTPstatus is ignored.
23
//
24
// Example output: {"error":"account not found","code":4003}
25
func (e Error) MarshalJSON() ([]byte, error) {
83✔
26
        // This anon struct is needed to actually include the error string,
83✔
27
        // since it wouldn't be marshaled otherwise. (json.Marshal doesn't call Err.Error())
83✔
28
        return json.Marshal(
83✔
29
                struct {
83✔
30
                        Err  string `json:"error"`
83✔
31
                        Code int    `json:"code"`
83✔
32
                        Data any    `json:"data,omitempty"`
83✔
33
                }{
83✔
34
                        Err:  e.Err.Error(),
83✔
35
                        Code: e.Code,
83✔
36
                        Data: e.Data,
83✔
37
                })
83✔
38
}
83✔
39

40
// Error returns the Message contained inside the APIerror
41
func (e Error) Error() string {
79✔
42
        return e.Err.Error()
79✔
43
}
79✔
44

45
// Write serializes a JSON msg using Error.Err and Error.Code
46
// and passes that to http.Error(). It also logs the error with appropriate level.
47
func (e Error) Write(w http.ResponseWriter) {
79✔
48
        msg, err := json.Marshal(e)
79✔
49
        if err != nil {
79✔
50
                log.Warn(err)
×
51
                http.Error(w, "marshal failed", http.StatusInternalServerError)
×
52
                return
×
53
        }
×
54

55
        // Get caller information for better logging
56
        pc, file, line, _ := runtime.Caller(1)
79✔
57
        caller := runtime.FuncForPC(pc).Name()
79✔
58

79✔
59
        // Log the error with appropriate level
79✔
60
        logLevel := e.LogLevel
79✔
61
        if logLevel == "" {
110✔
62
                // Default log level based on HTTP status
31✔
63
                if e.HTTPstatus >= 500 {
31✔
64
                        logLevel = "error"
×
65
                } else {
31✔
66
                        logLevel = "debug"
31✔
67
                }
31✔
68
        }
69

70
        // For 5xx errors, always log with Error level and include internal error details
71
        if e.HTTPstatus >= 500 {
86✔
72
                // For internal errors, log the full error details
7✔
73
                log.Errorw(e.Err, fmt.Sprintf("API error response [%d]: %s (code: %d, caller: %s, file: %s:%d)",
7✔
74
                        e.HTTPstatus, e.Error(), e.Code, caller, file, line))
7✔
75
        } else if log.Level() == log.LogLevelDebug {
151✔
76
                // For 4xx errors, log with debug level
72✔
77
                errMsg := fmt.Sprintf("API error response [%d]: %s (code: %d, caller: %s)",
72✔
78
                        e.HTTPstatus, e.Error(), e.Code, caller)
72✔
79

72✔
80
                switch logLevel {
72✔
81
                case "debug":
31✔
82
                        log.Debugw(errMsg)
31✔
83
                case "info":
41✔
84
                        log.Infow(errMsg)
41✔
85
                case "warn":
×
86
                        log.Warnw(errMsg)
×
87
                }
88
        }
89

90
        // Set the content type to JSON
91
        w.Header().Set("Content-Type", "application/json")
79✔
92
        http.Error(w, string(msg), e.HTTPstatus)
79✔
93
}
94

95
// Withf returns a copy of Error with the Sprintf formatted string appended at the end of e.Err
96
func (e Error) Withf(format string, args ...any) Error {
29✔
97
        return Error{
29✔
98
                Err:        fmt.Errorf("%w: %v", e.Err, fmt.Sprintf(format, args...)),
29✔
99
                Code:       e.Code,
29✔
100
                HTTPstatus: e.HTTPstatus,
29✔
101
                LogLevel:   e.LogLevel,
29✔
102
        }
29✔
103
}
29✔
104

105
// With returns a copy of Error with the string appended at the end of e.Err
106
func (e Error) With(s string) Error {
12✔
107
        return Error{
12✔
108
                Err:        fmt.Errorf("%w: %v", e.Err, s),
12✔
109
                Code:       e.Code,
12✔
110
                HTTPstatus: e.HTTPstatus,
12✔
111
                LogLevel:   e.LogLevel,
12✔
112
        }
12✔
113
}
12✔
114

115
// WithErr returns a copy of Error with err.Error() appended at the end of e.Err
116
// The original error is preserved for logging purposes
117
func (e Error) WithErr(err error) Error {
5✔
118
        return Error{
5✔
119
                Err:        fmt.Errorf("%w: %v", e.Err, err.Error()),
5✔
120
                Code:       e.Code,
5✔
121
                HTTPstatus: e.HTTPstatus,
5✔
122
                LogLevel:   e.LogLevel,
5✔
123
        }
5✔
124
}
5✔
125

126
// WithLogLevel returns a copy of Error with the specified log level
127
func (e Error) WithLogLevel(level string) Error {
×
128
        return Error{
×
129
                Err:        e.Err,
×
130
                Code:       e.Code,
×
131
                HTTPstatus: e.HTTPstatus,
×
132
                LogLevel:   level,
×
133
        }
×
134
}
×
135

NEW
136
func (e Error) WithData(data any) Error {
×
NEW
137
        return Error{
×
NEW
138
                Err:        e.Err,
×
NEW
139
                Code:       e.Code,
×
NEW
140
                HTTPstatus: e.HTTPstatus,
×
NEW
141
                LogLevel:   e.LogLevel,
×
NEW
142
                Data:       data,
×
NEW
143
        }
×
NEW
144
}
×
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