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

vocdoni / saas-backend / 17557469823

08 Sep 2025 04:25PM UTC coverage: 58.777% (-0.06%) from 58.841%
17557469823

Pull #213

github

altergui
fix
Pull Request #213: api: standardize parameters ProcessID, CensusID, GroupID, JobID, UserID, BundleID

254 of 345 new or added lines in 22 files covered. (73.62%)

19 existing lines in 7 files now uncovered.

5652 of 9616 relevant lines covered (58.78%)

32.01 hits per line

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

0.0
/cmd/census-workflow/main.go
1
// Package main provides a command-line tool for census workflow operations.
2
// It handles the creation and management of census data for voting processes
3
// and demonstrates the API usage for the saas-backend service.
4
package main
5

6
import (
7
        "bytes"
8
        "encoding/json"
9
        "flag"
10
        "fmt"
11
        "io"
12
        "net/http"
13
        "time"
14

15
        "github.com/vocdoni/saas-backend/api/apicommon"
16
        "github.com/vocdoni/saas-backend/db"
17
        "github.com/vocdoni/saas-backend/internal"
18
)
19

20
// Config holds the script configuration
21
type Config struct {
22
        APIEndpoint string
23
        Email       string
24
        Password    string
25
}
26

27
// LoginRequest matches the expected login request format
28
type LoginRequest struct {
29
        Email    string `json:"email"`
30
        Password string `json:"password"`
31
}
32

33
// Client handles HTTP requests with authentication
34
type Client struct {
35
        http    *http.Client
36
        baseURL string
37
        token   string
38
}
39

40
func newClient(baseURL string) *Client {
×
41
        return &Client{
×
42
                http:    &http.Client{},
×
43
                baseURL: baseURL,
×
44
        }
×
45
}
×
46

47
func (c *Client) makeRequest(method, path string, body any, target any) error {
×
48
        var bodyReader io.Reader
×
49
        if body != nil {
×
50
                bodyBytes, err := json.Marshal(body)
×
51
                if err != nil {
×
52
                        return fmt.Errorf("error marshaling request body: %v", err)
×
53
                }
×
54
                bodyReader = bytes.NewReader(bodyBytes)
×
55
        }
56

57
        req, err := http.NewRequest(method, c.baseURL+path, bodyReader)
×
58
        if err != nil {
×
59
                return fmt.Errorf("error creating request: %v", err)
×
60
        }
×
61

62
        if c.token != "" {
×
63
                req.Header.Set("Authorization", "Bearer "+c.token)
×
64
        }
×
65
        req.Header.Set("Content-Type", "application/json")
×
66

×
67
        resp, err := c.http.Do(req)
×
68
        if err != nil {
×
69
                return fmt.Errorf("error making request: %v", err)
×
70
        }
×
71
        defer func() {
×
72
                if err := resp.Body.Close(); err != nil {
×
73
                        fmt.Printf("Error closing response body: %v", err)
×
74
                }
×
75
        }()
76

77
        if resp.StatusCode < 200 || resp.StatusCode >= 300 {
×
78
                bodyBytes, _ := io.ReadAll(resp.Body)
×
79
                return fmt.Errorf("request failed with status %d: %s", resp.StatusCode, string(bodyBytes))
×
80
        }
×
81

82
        if target != nil {
×
83
                if err := json.NewDecoder(resp.Body).Decode(target); err != nil {
×
84
                        return fmt.Errorf("error decoding response: %v", err)
×
85
                }
×
86
        }
87

88
        return nil
×
89
}
90

91
func generateMembers(n int) []apicommon.OrgMember {
×
92
        // members := make([]apicommon.OrgMember, n+1)
×
93
        members := make([]apicommon.OrgMember, n)
×
94
        for i := 0; i < n; i++ {
×
95
                members[i] = apicommon.OrgMember{
×
96
                        Email:        fmt.Sprintf("user%d@example.com", i+1),
×
97
                        MemberNumber: fmt.Sprintf("member_%d", i+1),
×
98
                        Name:         fmt.Sprintf("User %d", i+1),
×
99
                }
×
100
        }
×
101
        // Add a member with an empty value for email
102
        // members = append(members, apicommon.OrgMember{
103
        //         Email:        "",
104
        //         MemberNumber: fmt.Sprintf("member_%d", n),
105
        //         Name:         fmt.Sprintf("User %d", n),
106
        // })
107
        // // Add a member with a duplicate value for email
108
        // members = append(members, apicommon.OrgMember{
109
        //         Email:        fmt.Sprintf("user%d@example.com", n-1),
110
        //         MemberNumber: fmt.Sprintf("member_%d", n-1),
111
        //         Name:         fmt.Sprintf("User %d", n-1),
112
        // })
113

114
        return members
×
115
}
116

117
// func generateElectionMetadata() []byte {
118
//         metadata := map[string]any{
119
//                 "title":       "Test Voting Process",
120
//                 "description": "This is a test voting process created by the workflow script",
121
//                 "startDate":   time.Now().Add(24 * time.Hour).Format(time.RFC3339),
122
//                 "endDate":     time.Now().Add(72 * time.Hour).Format(time.RFC3339),
123
//                 "votingType":  "single-choice",
124
//                 "questions": []map[string]any{
125
//                         {
126
//                                 "title":   "Test Question",
127
//                                 "choices": []string{"Option A", "Option B", "Option C"},
128
//                         },
129
//                 },
130
//         }
131

132
//         metadataBytes, _ := json.Marshal(metadata)
133
//         return metadataBytes
134
// }
135

136
func main() {
×
137
        config := Config{}
×
138
        flag.StringVar(&config.APIEndpoint, "api", "http://localhost:8080", "API endpoint URL")
×
139
        flag.StringVar(&config.Email, "email", "", "User email")
×
140
        flag.StringVar(&config.Password, "password", "", "User password")
×
141
        flag.Parse()
×
142

×
143
        if config.Email == "" || config.Password == "" {
×
144
                fmt.Println("Error: email, password are required")
×
145
                flag.Usage()
×
146
                return
×
147
        }
×
148

149
        client := newClient(config.APIEndpoint)
×
150

×
151
        // 1. Login
×
152
        fmt.Println("1. Logging in...")
×
153
        var loginResp apicommon.LoginResponse
×
154
        err := client.makeRequest("POST", "/auth/login", LoginRequest{
×
155
                Email:    config.Email,
×
156
                Password: config.Password,
×
157
        }, &loginResp)
×
158
        if err != nil {
×
159
                fmt.Printf("Error logging in: %v", err)
×
160
                return
×
161
        }
×
162
        client.token = loginResp.Token
×
163
        fmt.Println("✓ Login successful")
×
164

×
165
        // 2. Create organization and add members
×
166
        fmt.Println("\n2. Creating organization and adding members...")
×
167
        orgMembers := generateMembers(5) // Generate 5 test members
×
168
        var orgResp apicommon.OrganizationInfo
×
169
        err = client.makeRequest("POST", "/organizations", apicommon.OrganizationInfo{
×
170
                Size:    fmt.Sprintf("%d", len(orgMembers)),
×
171
                Type:    string(db.AssociationType),
×
172
                Country: "ES",
×
173
        }, &orgResp)
×
174
        if err != nil {
×
175
                fmt.Printf("Error creating organization: %v", err)
×
176
                return
×
177
        }
×
178
        fmt.Printf("✓ Organization created with address: %s\n", orgResp.Address)
×
179
        var addMembersResp apicommon.AddMembersResponse
×
180
        err = client.makeRequest(
×
181
                "POST",
×
182
                fmt.Sprintf("/organizations/%s/members", orgResp.Address),
×
183
                apicommon.AddMembersRequest{
×
184
                        Members: orgMembers,
×
185
                },
×
186
                &addMembersResp,
×
187
        )
×
188
        if err != nil {
×
189
                fmt.Printf("Error adding members: %v", err)
×
190
                return
×
191
        }
×
192
        // wait for members to be added
193
        if len(addMembersResp.JobID) > 0 {
×
194
                fmt.Printf("Members are being added asynchronously, job ID: %s\n", addMembersResp.JobID)
×
195
                for {
×
196
                        var jobResp db.BulkOrgMembersJob
×
197
                        err = client.makeRequest("GET", fmt.Sprintf("/jobs/%s", addMembersResp.JobID), nil, &jobResp)
×
198
                        if err != nil {
×
199
                                fmt.Printf("Error checking job status: %v\n", err)
×
200
                                return
×
201
                        }
×
202
                        if jobResp.Progress == 100 {
×
203
                                fmt.Printf("Members added asynchronously, %d members added\n", jobResp.Added)
×
204
                                break
×
205
                        }
206
                        fmt.Printf("Waiting for members to be added... Progress: %d%%\n", jobResp.Progress)
×
207
                        // Sleep for a while before checking again
×
208
                        time.Sleep(5 * time.Second)
×
209
                }
210
        } else {
×
211
                fmt.Printf("Members added synchronously, %d members added\n", addMembersResp.Added)
×
212
        }
×
213

214
        // get orgmembers to verify
215
        var orgMembersResp apicommon.OrganizationMembersResponse
×
216
        err = client.makeRequest("GET", fmt.Sprintf("/organizations/%s/members", orgResp.Address), nil, &orgMembersResp)
×
217
        if err != nil {
×
218
                fmt.Printf("Error getting organization members: %v", err)
×
219
                return
×
220
        }
×
221
        if len(orgMembersResp.Members) != len(orgMembers) {
×
222
                fmt.Printf("Error: expected %d members, got %d\n", len(orgMembers), len(orgMembersResp.Members))
×
223
                return
×
224
        }
×
225
        fmt.Printf("✓ Added %d members to organization %s\n", len(orgMembersResp.Members), orgResp.Address)
×
226

×
227
        // 4 Create a group of members
×
228
        fmt.Println("\n4. Creating group of members...")
×
229
        groupName := "Test Group"
×
NEW
230
        groupMembersIDs := make([]internal.ObjectID, 0, len(orgMembersResp.Members))
×
231
        for _, member := range orgMembersResp.Members {
×
232
                groupMembersIDs = append(groupMembersIDs, member.ID)
×
233
        }
×
234
        var groupResp apicommon.OrganizationMemberGroupInfo
×
235
        // Create group with all members
×
236
        err = client.makeRequest(
×
237
                "POST",
×
238
                fmt.Sprintf("/organizations/%s/groups", orgResp.Address),
×
239
                apicommon.CreateOrganizationMemberGroupRequest{
×
240
                        Title:     groupName,
×
241
                        MemberIDs: groupMembersIDs,
×
242
                },
×
243
                &groupResp,
×
244
        )
×
245
        if err != nil {
×
246
                fmt.Printf("Error creating group: %v", err)
×
247
                return
×
248
        }
×
249

250
        // 4. Create a census for the group
251
        fmt.Printf("\n4. Creating census for the group %s and org %s\n", groupResp.ID, orgResp.Address)
×
252
        var createCensusResp apicommon.CreateCensusResponse
×
253
        err = client.makeRequest(
×
254
                "POST",
×
255
                "/census",
×
256
                apicommon.CreateCensusRequest{
×
257
                        OrgAddress: orgResp.Address,
×
258
                        AuthFields: db.OrgMemberAuthFields{
×
259
                                "email",
×
260
                                "memberNumber",
×
261
                        },
×
262
                }, &createCensusResp,
×
263
        )
×
264
        // Print the census ID and member warnings
×
265
        if err != nil {
×
266
                fmt.Printf("Error creating census: %v", err)
×
267
                return
×
268
        }
×
269

270
        fmt.Printf("✓ Census created with ID: %s\n", createCensusResp.ID)
×
271

272
        // 3. Get census info
273

274
        // 5. Publish census
275

276
        // 6. Create process
277

278
        // 7. Get process info
279
}
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