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

vocdoni / saas-backend / 16141698976

08 Jul 2025 11:15AM UTC coverage: 56.122% (-0.008%) from 56.13%
16141698976

Pull #163

github

altergui
all: fix some renamings left behind (member -> participant, etc)
Pull Request #163: fix/leftover renames

17 of 25 new or added lines in 3 files covered. (68.0%)

1 existing line in 1 file now uncovered.

4854 of 8649 relevant lines covered (56.12%)

24.32 hits per line

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

18.27
/api/process_bundles.go
1
package api
2

3
import (
4
        "encoding/hex"
5
        "encoding/json"
6
        "net/http"
7

8
        "github.com/go-chi/chi/v5"
9
        "github.com/vocdoni/saas-backend/api/apicommon"
10
        "github.com/vocdoni/saas-backend/db"
11
        "github.com/vocdoni/saas-backend/errors"
12
        "github.com/vocdoni/saas-backend/internal"
13
        "go.vocdoni.io/dvote/util"
14
)
15

16
// Using types from apicommon package
17

18
// AddProcessesToBundleRequest represents the request body for adding processes to an existing bundle.
19
// It contains an array of process IDs to add to the bundle.
20
type AddProcessesToBundleRequest struct {
21
        Processes []string `json:"processes"` // Array of process creation requests to add
22
}
23

24
// createProcessBundleHandler godoc
25
//
26
//        @Summary                Create a new process bundle
27
//        @Description        Create a new process bundle with the specified census and optional list of processes. Requires
28
//        @Description        Manager/Admin role for the organization that owns the census. The census root will be the same as the
29
//        @Description        account's public key.
30
//        @Tags                        process
31
//        @Accept                        json
32
//        @Produce                json
33
//        @Security                BearerAuth
34
//        @Param                        request        body                apicommon.CreateProcessBundleRequest        true        "Process bundle creation information"
35
//        @Success                200                {object}        apicommon.CreateProcessBundleResponse
36
//        @Failure                400                {object}        errors.Error        "Invalid input data"
37
//        @Failure                401                {object}        errors.Error        "Unauthorized"
38
//        @Failure                404                {object}        errors.Error        "Census not found"
39
//        @Failure                500                {object}        errors.Error        "Internal server error"
40
//        @Router                        /process/bundle [post]
41
func (a *API) createProcessBundleHandler(w http.ResponseWriter, r *http.Request) {
1✔
42
        var req apicommon.CreateProcessBundleRequest
1✔
43
        if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
1✔
44
                errors.ErrMalformedBody.Write(w)
×
45
                return
×
46
        }
×
47

48
        census, err := a.db.Census(req.CensusID)
1✔
49
        if err != nil {
1✔
50
                if err == db.ErrNotFound {
×
51
                        errors.ErrMalformedURLParam.Withf("census not found").Write(w)
×
52
                        return
×
53
                }
×
54
                errors.ErrGenericInternalServerError.WithErr(err).Write(w)
×
55
                return
×
56
        }
57

58
        // Get the user from the request context
59
        user, ok := apicommon.UserFromContext(r.Context())
1✔
60
        if !ok {
1✔
61
                errors.ErrUnauthorized.Write(w)
×
62
                return
×
63
        }
×
64

65
        // Check if the user has the necessary permissions for the organization
66
        if !user.HasRoleFor(census.OrgAddress, db.ManagerRole) && !user.HasRoleFor(census.OrgAddress, db.AdminRole) {
1✔
67
                errors.ErrUnauthorized.Withf("user is not admin or manager of organization").Write(w)
×
68
                return
×
69
        }
×
70

71
        // generate a new bundle ID
72
        bundleID := a.db.NewBundleID()
1✔
73
        // The cenus root will be the same as the account's public key
1✔
74
        censusRoot, err := a.csp.PubKey()
1✔
75
        if err != nil {
1✔
76
                errors.ErrGenericInternalServerError.Withf("failed to get CSP public key").Write(w)
×
77
                return
×
78
        }
×
79

80
        if len(req.Processes) == 0 {
1✔
81
                // Create the process bundle
×
82
                bundle := &db.ProcessesBundle{
×
83
                        ID:         bundleID,
×
84
                        CensusRoot: censusRoot.String(),
×
85
                        OrgAddress: census.OrgAddress,
×
86
                        Census:     *census,
×
87
                }
×
88
                _, err = a.db.SetProcessBundle(bundle)
×
89
                if err != nil {
×
90
                        errors.ErrGenericInternalServerError.WithErr(err).Write(w)
×
91
                        return
×
92
                }
×
93

94
                var rootHex internal.HexBytes
×
95
                if err := rootHex.ParseString(censusRoot.String()); err != nil {
×
96
                        errors.ErrGenericInternalServerError.WithErr(err).Write(w)
×
97
                        return
×
98
                }
×
99
                apicommon.HTTPWriteJSON(w, apicommon.CreateProcessBundleResponse{
×
100
                        URI:  a.serverURL + "/process/bundle/" + bundleID.Hex(),
×
101
                        Root: rootHex,
×
102
                })
×
103
                return
×
104
        }
105

106
        // Collect all processes
107
        var processes []internal.HexBytes
1✔
108

1✔
109
        for _, processReq := range req.Processes {
2✔
110
                if len(processReq) == 0 {
1✔
111
                        errors.ErrMalformedBody.Withf("missing process ID").Write(w)
×
112
                        return
×
113
                }
×
114
                processID, err := hex.DecodeString(util.TrimHex(processReq))
1✔
115
                if err != nil {
1✔
116
                        errors.ErrMalformedBody.Withf("invalid process ID").Write(w)
×
117
                        return
×
118
                }
×
119

120
                processes = append(processes, processID)
1✔
121
        }
122

123
        // Create the process bundle
124
        cspPubKey, err := a.csp.PubKey()
1✔
125
        if err != nil {
1✔
126
                errors.ErrGenericInternalServerError.Withf("failed to get CSP public key").Write(w)
×
127
                return
×
128
        }
×
129

130
        bundle := &db.ProcessesBundle{
1✔
131
                ID:         bundleID,
1✔
132
                Processes:  processes,
1✔
133
                CensusRoot: cspPubKey.String(),
1✔
134
                OrgAddress: census.OrgAddress,
1✔
135
                Census:     *census,
1✔
136
        }
1✔
137

1✔
138
        _, err = a.db.SetProcessBundle(bundle)
1✔
139
        if err != nil {
1✔
140
                errors.ErrGenericInternalServerError.WithErr(err).Write(w)
×
141
                return
×
142
        }
×
143

144
        var rootHex internal.HexBytes
1✔
145
        if err := rootHex.ParseString(cspPubKey.String()); err != nil {
1✔
146
                errors.ErrGenericInternalServerError.WithErr(err).Write(w)
×
147
                return
×
148
        }
×
149
        apicommon.HTTPWriteJSON(w, apicommon.CreateProcessBundleResponse{
1✔
150
                URI:  a.serverURL + "/process/bundle/" + bundleID.Hex(),
1✔
151
                Root: rootHex,
1✔
152
        })
1✔
153
}
154

155
// updateProcessBundleHandler godoc
156
//
157
//        @Summary                Add processes to an existing bundle
158
//        @Description        Add additional processes to an existing bundle. Requires Manager/Admin role for the organization
159
//        @Description        that owns the bundle.
160
//        @Tags                        process
161
//        @Accept                        json
162
//        @Produce                json
163
//        @Security                BearerAuth
164
//        @Param                        bundleId        path                string                                                true        "Bundle ID"
165
//        @Param                        request                body                AddProcessesToBundleRequest        true        "Processes to add"
166
//        @Success                200                        {object}        apicommon.CreateProcessBundleResponse
167
//        @Failure                400                        {object}        errors.Error        "Invalid input data"
168
//        @Failure                401                        {object}        errors.Error        "Unauthorized"
169
//        @Failure                404                        {object}        errors.Error        "Bundle or census not found"
170
//        @Failure                500                        {object}        errors.Error        "Internal server error"
171
//        @Router                        /process/bundle/{bundleId} [put]
172
func (a *API) updateProcessBundleHandler(w http.ResponseWriter, r *http.Request) {
×
173
        bundleIDStr := chi.URLParam(r, "bundleId")
×
174
        if bundleIDStr == "" {
×
175
                errors.ErrMalformedURLParam.Withf("missing bundle ID").Write(w)
×
176
                return
×
177
        }
×
178

179
        var bundleID internal.HexBytes
×
180
        if err := bundleID.ParseString(bundleIDStr); err != nil {
×
181
                errors.ErrMalformedURLParam.Withf("invalid bundle ID").Write(w)
×
182
                return
×
183
        }
×
184

185
        var req AddProcessesToBundleRequest
×
186
        if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
×
187
                errors.ErrMalformedBody.Write(w)
×
188
                return
×
189
        }
×
190

191
        // Get the user from the request context
192
        user, ok := apicommon.UserFromContext(r.Context())
×
193
        if !ok {
×
194
                errors.ErrUnauthorized.Write(w)
×
195
                return
×
196
        }
×
197

198
        // Get the existing bundle
199
        bundle, err := a.db.ProcessBundle(bundleID)
×
200
        if err != nil {
×
201
                if err == db.ErrNotFound {
×
202
                        errors.ErrMalformedURLParam.Withf("bundle not found").Write(w)
×
203
                        return
×
204
                }
×
205
                errors.ErrGenericInternalServerError.WithErr(err).Write(w)
×
206
                return
×
207
        }
208

209
        if len(req.Processes) == 0 {
×
210
                var rootHex internal.HexBytes
×
211
                if err := rootHex.ParseString(bundle.CensusRoot); err != nil {
×
212
                        errors.ErrGenericInternalServerError.WithErr(err).Write(w)
×
213
                        return
×
214
                }
×
215
                apicommon.HTTPWriteJSON(w, apicommon.CreateProcessBundleResponse{
×
216
                        URI:  "/process/bundle/" + bundleIDStr,
×
217
                        Root: rootHex,
×
218
                })
×
219
                return
×
220
        }
221

222
        // Check if the user has the necessary permissions for the organization
223
        if !user.HasRoleFor(bundle.OrgAddress, db.ManagerRole) && !user.HasRoleFor(bundle.OrgAddress, db.AdminRole) {
×
224
                errors.ErrUnauthorized.Withf("user is not admin or manager of organization").Write(w)
×
225
                return
×
226
        }
×
227

228
        // Collect all processes to add
229
        var processesToAdd []internal.HexBytes
×
230

×
231
        for _, processReq := range req.Processes {
×
232
                if len(processReq) == 0 {
×
233
                        errors.ErrMalformedBody.Withf("missing process ID").Write(w)
×
234
                        return
×
235
                }
×
236
                processID, err := hex.DecodeString(util.TrimHex(processReq))
×
237
                if err != nil {
×
238
                        errors.ErrMalformedBody.Withf("invalid process ID").Write(w)
×
239
                        return
×
240
                }
×
241

242
                processesToAdd = append(processesToAdd, processID)
×
243
        }
244

245
        // Add processes to the bundle
246
        if err := a.db.AddProcessesToBundle(bundleID, processesToAdd); err != nil {
×
247
                errors.ErrGenericInternalServerError.WithErr(err).Write(w)
×
248
                return
×
249
        }
×
250

251
        var rootHex internal.HexBytes
×
252
        if err := rootHex.ParseString(bundle.CensusRoot); err != nil {
×
253
                errors.ErrGenericInternalServerError.WithErr(err).Write(w)
×
254
                return
×
255
        }
×
256
        apicommon.HTTPWriteJSON(w, apicommon.CreateProcessBundleResponse{
×
257
                URI:  "/process/bundle/" + bundleIDStr,
×
258
                Root: rootHex,
×
259
        })
×
260
}
261

262
// processBundleInfoHandler godoc
263
//
264
//        @Summary                Get process bundle information
265
//        @Description        Retrieve process bundle information by ID. Returns bundle details including the associated census,
266
//        @Description        census root, organization address, and list of processes.
267
//        @Tags                        process
268
//        @Accept                        json
269
//        @Produce                json
270
//        @Param                        bundleId        path                string        true        "Bundle ID"
271
//        @Success                200                        {object}        db.ProcessesBundle
272
//        @Failure                400                        {object}        errors.Error        "Invalid bundle ID"
273
//        @Failure                404                        {object}        errors.Error        "Bundle not found"
274
//        @Failure                500                        {object}        errors.Error        "Internal server error"
275
//        @Router                        /process/bundle/{bundleId} [get]
276
func (a *API) processBundleInfoHandler(w http.ResponseWriter, r *http.Request) {
×
277
        bundleIDStr := chi.URLParam(r, "bundleId")
×
278
        if bundleIDStr == "" {
×
279
                errors.ErrMalformedURLParam.Withf("missing bundle ID").Write(w)
×
280
                return
×
281
        }
×
282

283
        var bundleID internal.HexBytes
×
284
        if err := bundleID.ParseString(bundleIDStr); err != nil {
×
285
                errors.ErrMalformedURLParam.Withf("invalid bundle ID").Write(w)
×
286
                return
×
287
        }
×
288

289
        bundle, err := a.db.ProcessBundle(bundleID)
×
290
        if err != nil {
×
291
                if err == db.ErrNotFound {
×
292
                        errors.ErrMalformedURLParam.Withf("bundle not found").Write(w)
×
293
                        return
×
294
                }
×
295
                errors.ErrGenericInternalServerError.WithErr(err).Write(w)
×
296
                return
×
297
        }
298

299
        apicommon.HTTPWriteJSON(w, bundle)
×
300
}
301

302
// processBundleParticipantInfoHandler godoc
303
//
304
//        @Summary                Get participant information for a process bundle
305
//        @Description        Retrieve process information for a participant in a process bundle. Returns process details including
306
//        @Description        the census and metadata.
307
//        @Tags                        process
308
//        @Accept                        json
309
//        @Produce                json
310
//        @Param                        bundleId                path                string        true        "Bundle ID"
311
//        @Param                        participantID        path                string        true        "Participant ID"
312
//        @Success                200                                {object}        interface{}
313
//        @Failure                400                                {object}        errors.Error        "Invalid bundle ID or participant ID"
314
//        @Failure                404                                {object}        errors.Error        "Bundle not found"
315
//        @Failure                500                                {object}        errors.Error        "Internal server error"
316
//        @Router                        /process/bundle/{bundleId}/{participantId} [get]
317
func (a *API) processBundleParticipantInfoHandler(w http.ResponseWriter, r *http.Request) {
×
318
        bundleIDStr := chi.URLParam(r, "bundleId")
×
319
        if bundleIDStr == "" {
×
320
                errors.ErrMalformedURLParam.Withf("missing bundle ID").Write(w)
×
321
                return
×
322
        }
×
323

324
        var bundleID internal.HexBytes
×
325
        if err := bundleID.ParseString(bundleIDStr); err != nil {
×
326
                errors.ErrMalformedURLParam.Withf("invalid bundle ID").Write(w)
×
327
                return
×
328
        }
×
329

330
        _, err := a.db.ProcessBundle(bundleID)
×
331
        if err != nil {
×
332
                if err == db.ErrNotFound {
×
333
                        errors.ErrMalformedURLParam.Withf("bundle not found").Write(w)
×
334
                        return
×
335
                }
×
336
                errors.ErrGenericInternalServerError.WithErr(err).Write(w)
×
337
                return
×
338
        }
339

340
        participantID := chi.URLParam(r, "participantId")
×
341
        if participantID == "" {
×
NEW
342
                errors.ErrMalformedURLParam.Withf("missing participant ID").Write(w)
×
343
                return
×
344
        }
×
345

346
        // TODO
347
        /*        elections := a.csp.Indexer(participantID, bundleIDStr, "")
348
                if len(elections) == 0 {
349
                        httpWriteJSON(w, []twofactor.Election{})
350
                        return
351
                }
352
        */
353

354
        apicommon.HTTPWriteJSON(w, nil)
×
355
}
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

© 2025 Coveralls, Inc