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

vocdoni / saas-backend / 20601603207

30 Dec 2025 02:15PM UTC coverage: 62.59% (-0.5%) from 63.071%
20601603207

Pull #381

github

web-flow
hotfix weight pointer (#380)

* api: correctly handle OrgMember.Weight=0
* Always updates the weight field based on the given value in the `UpsertOrgMemberAndCensusParticipants`

---------

Co-authored-by: emmdim <emmdim@users.noreply.github.com>
Pull Request #381: v2.2.1 dev to stage

281 of 364 new or added lines in 11 files covered. (77.2%)

2 existing lines in 2 files now uncovered.

6766 of 10810 relevant lines covered (62.59%)

37.87 hits per line

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

79.34
/errors/errors.go
1
package errors
2

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

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

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

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

41
// UnmarshalJSON parses a JSON containing error, code and optionally data.
42
//
43
// Example input: {"error":"account not found","code":4003}
44
func (e *Error) UnmarshalJSON(data []byte) error {
22✔
45
        // This anon struct is needed to actually set the error string,
22✔
46
        // since it wouldn't be unmarshaled otherwise. (cannot json.Unmarshal string into type error)
22✔
47
        parsed := struct {
22✔
48
                Error string `json:"error"`
22✔
49
                Code  int    `json:"code"`
22✔
50
                Data  any    `json:"data,omitempty"`
22✔
51
        }{}
22✔
52
        if err := json.Unmarshal(data, &parsed); err != nil {
22✔
NEW
53
                return err
×
NEW
54
        }
×
55
        e.Err = fmt.Errorf("%s", parsed.Error)
22✔
56
        e.Code = parsed.Code
22✔
57
        e.Data = parsed.Data
22✔
58
        return nil
22✔
59
}
60

61
// Error returns the Message contained inside the APIerror
62
func (e Error) Error() string {
181✔
63
        return e.Err.Error()
181✔
64
}
181✔
65

66
// Unwrap returns the error contained inside
67
func (e Error) Unwrap() error {
×
68
        return e.Err
×
69
}
×
70

71
// Write serializes a JSON msg using Error.Err and Error.Code
72
// and passes that to http.Error(). It also logs the error with appropriate level.
73
func (e Error) Write(w http.ResponseWriter) {
162✔
74
        msg, err := json.Marshal(e)
162✔
75
        if err != nil {
162✔
76
                log.Warn(err)
×
77
                http.Error(w, "marshal failed", http.StatusInternalServerError)
×
78
                return
×
79
        }
×
80

81
        // Get caller information for better logging
82
        pc, file, line, _ := runtime.Caller(1)
162✔
83
        caller := runtime.FuncForPC(pc).Name()
162✔
84

162✔
85
        // Log the error with appropriate level
162✔
86
        logLevel := e.LogLevel
162✔
87
        if logLevel == "" {
236✔
88
                // Default log level based on HTTP status
74✔
89
                if e.HTTPstatus >= 500 {
74✔
90
                        logLevel = "error"
×
91
                } else {
74✔
92
                        logLevel = "debug"
74✔
93
                }
74✔
94
        }
95

96
        // For 5xx errors, always log with Error level and include internal error details
97
        if e.HTTPstatus >= 500 {
180✔
98
                // For internal errors, log the full error details
18✔
99
                log.Errorw(e.Err, fmt.Sprintf("API error response [%d]: %s (code: %d, caller: %s, file: %s:%d)",
18✔
100
                        e.HTTPstatus, e.Error(), e.Code, caller, file, line))
18✔
101
        } else if log.Level() == log.LogLevelDebug {
306✔
102
                // For 4xx errors, log with debug level
144✔
103
                errMsg := fmt.Sprintf("API error response [%d]: %s (code: %d, caller: %s)",
144✔
104
                        e.HTTPstatus, e.Error(), e.Code, caller)
144✔
105

144✔
106
                switch logLevel {
144✔
107
                case "debug":
74✔
108
                        log.Debugw(errMsg)
74✔
109
                case "info":
70✔
110
                        log.Infow(errMsg)
70✔
111
                case "warn":
×
112
                        log.Warnw(errMsg)
×
113
                default:
×
114
                        log.Debugw(errMsg) // Default to debug level for unknown log levels
×
115
                }
116
        }
117

118
        // Set the content type to JSON
119
        w.Header().Set("Content-Type", "application/json")
162✔
120
        http.Error(w, string(msg), e.HTTPstatus)
162✔
121
}
122

123
// Withf returns a copy of Error with the Sprintf formatted string appended at the end of e.Err
124
func (e Error) Withf(format string, args ...any) Error {
75✔
125
        return Error{
75✔
126
                Err:        fmt.Errorf("%w: %v", e.Err, fmt.Sprintf(format, args...)),
75✔
127
                Code:       e.Code,
75✔
128
                HTTPstatus: e.HTTPstatus,
75✔
129
                LogLevel:   e.LogLevel,
75✔
130
        }
75✔
131
}
75✔
132

133
// With returns a copy of Error with the string appended at the end of e.Err
134
func (e Error) With(s string) Error {
17✔
135
        return Error{
17✔
136
                Err:        fmt.Errorf("%w: %v", e.Err, s),
17✔
137
                Code:       e.Code,
17✔
138
                HTTPstatus: e.HTTPstatus,
17✔
139
                LogLevel:   e.LogLevel,
17✔
140
        }
17✔
141
}
17✔
142

143
// WithErr returns a copy of Error with err.Error() appended at the end of e.Err
144
// The original error is preserved for logging purposes
145
func (e Error) WithErr(err error) Error {
10✔
146
        return Error{
10✔
147
                Err:        fmt.Errorf("%w: %v", e.Err, err.Error()),
10✔
148
                Code:       e.Code,
10✔
149
                HTTPstatus: e.HTTPstatus,
10✔
150
                LogLevel:   e.LogLevel,
10✔
151
        }
10✔
152
}
10✔
153

154
// WithLogLevel returns a copy of Error with the specified log level
155
func (e Error) WithLogLevel(level string) Error {
×
156
        return Error{
×
157
                Err:        e.Err,
×
158
                Code:       e.Code,
×
159
                HTTPstatus: e.HTTPstatus,
×
160
                LogLevel:   level,
×
161
        }
×
162
}
×
163

164
func (e Error) WithData(data any) Error {
4✔
165
        return Error{
4✔
166
                Err:        e.Err,
4✔
167
                Code:       e.Code,
4✔
168
                HTTPstatus: e.HTTPstatus,
4✔
169
                LogLevel:   e.LogLevel,
4✔
170
                Data:       data,
4✔
171
        }
4✔
172
}
4✔
173

174
// As finds the first error in err's tree that matches target, and if one is found, sets
175
// target to that error value and returns true. Otherwise, it returns false.
176
//
177
// The tree consists of err itself, followed by the errors obtained by repeatedly
178
// calling its Unwrap() error or Unwrap() []error method. When err wraps multiple
179
// errors, As examines err followed by a depth-first traversal of its children.
180
//
181
// An error matches target if the error's concrete value is assignable to the value
182
// pointed to by target, or if the error has a method As(any) bool such that
183
// As(target) returns true. In the latter case, the As method is responsible for
184
// setting target.
185
//
186
// An error type might provide an As method so it can be treated as if it were a
187
// different error type.
188
//
189
// As panics if target is not a non-nil pointer to either a type that implements
190
// error, or to any interface type.
NEW
191
func As(err error, target any) bool {
×
NEW
192
        return stderrors.As(err, target)
×
NEW
193
}
×
194

195
// Is reports whether any error in err's tree matches target.
196
//
197
// The tree consists of err itself, followed by the errors obtained by repeatedly
198
// calling its Unwrap() error or Unwrap() []error method. When err wraps multiple
199
// errors, Is examines err followed by a depth-first traversal of its children.
200
//
201
// An error is considered to match a target if it is equal to that target or if
202
// it implements a method Is(error) bool such that Is(target) returns true.
203
//
204
// An error type might provide an Is method so it can be treated as equivalent
205
// to an existing error. For example, if MyError defines
206
//
207
//        func (m MyError) Is(target error) bool { return target == fs.ErrExist }
208
//
209
// then Is(MyError{}, fs.ErrExist) returns true. See [syscall.Errno.Is] for
210
// an example in the standard library. An Is method should only shallowly
211
// compare err and the target and not call [Unwrap] on either.
212
func Is(err error, target error) bool {
15✔
213
        return stderrors.Is(err, target)
15✔
214
}
15✔
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