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

vocdoni / saas-backend / 16301338771

15 Jul 2025 06:29PM UTC coverage: 56.793% (+0.7%) from 56.082%
16301338771

Pull #165

github

emmdim
refactor:  census creation

- Removed the PublishedCensus type and added it as Census parameter.
- Introduced new OrgMemberAuthFields defining the data options for member authentication.
- Added the `CheckOrgMemberAuthFields` function that checks a set of members and given auth fields empties an
d duplicates
- Add the option to create a census through the api based on a given group
Pull Request #165: Implements group based census creation

250 of 399 new or added lines in 9 files covered. (62.66%)

4 existing lines in 3 files now uncovered.

5104 of 8987 relevant lines covered (56.79%)

25.32 hits per line

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

76.92
/db/census.go
1
// Package db provides database operations for the Vocdoni SaaS backend,
2
// handling storage and retrieval of censuses, organizations, users, and
3
// other data structures required for the voting platform.
4
package db
5

6
import (
7
        "context"
8
        "fmt"
9
        "time"
10

11
        "github.com/vocdoni/saas-backend/internal"
12
        "go.mongodb.org/mongo-driver/bson"
13
        "go.mongodb.org/mongo-driver/bson/primitive"
14
        "go.mongodb.org/mongo-driver/mongo/options"
15
        "go.vocdoni.io/dvote/log"
16
)
17

18
// SetCensus creates a new census for an organization
19
// Returns the hex representation of the census
20
func (ms *MongoStorage) SetCensus(census *Census) (string, error) {
28✔
21
        // create a context with a timeout
28✔
22
        ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
28✔
23
        defer cancel()
28✔
24

28✔
25
        if census.OrgAddress == "" {
29✔
26
                return "", ErrInvalidData
1✔
27
        }
1✔
28
        // check that the org exists
29
        _, err := ms.Organization(census.OrgAddress)
27✔
30
        if err != nil {
28✔
31
                if err == ErrNotFound {
2✔
32
                        return "", ErrInvalidData
1✔
33
                }
1✔
34
                return "", fmt.Errorf("organization not found: %w", err)
×
35
        }
36

37
        if census.ID != primitive.NilObjectID {
30✔
38
                // if the census exists, update it with the new data
4✔
39
                census.UpdatedAt = time.Now()
4✔
40
        } else {
26✔
41
                // if the census doesn't exist, create its id
22✔
42
                census.ID = primitive.NewObjectID()
22✔
43
                census.CreatedAt = time.Now()
22✔
44
        }
22✔
45

46
        updateDoc, err := dynamicUpdateDocument(census, nil)
26✔
47
        if err != nil {
26✔
48
                return "", err
×
49
        }
×
50
        ms.keysLock.Lock()
26✔
51
        defer ms.keysLock.Unlock()
26✔
52
        filter := bson.M{"_id": census.ID}
26✔
53
        opts := options.Update().SetUpsert(true)
26✔
54
        _, err = ms.censuses.UpdateOne(ctx, filter, updateDoc, opts)
26✔
55
        if err != nil {
26✔
56
                return "", err
×
57
        }
×
58

59
        return census.ID.Hex(), nil
26✔
60
}
61

62
// SetPublished census updates the PublishedCensus field of a census
NEW
63
func (ms *MongoStorage) SetPublishedCensus(censusID, uri string, root internal.HexBytes) (string, error) {
×
NEW
64
        if len(censusID) == 0 || len(uri) == 0 || len(root) == 0 {
×
NEW
65
                return "", ErrInvalidData
×
NEW
66
        }
×
67

NEW
68
        censusOID, err := primitive.ObjectIDFromHex(censusID)
×
NEW
69
        if err != nil {
×
NEW
70
                return "", ErrInvalidData
×
NEW
71
        }
×
NEW
72
        census := &Census{
×
NEW
73
                ID: censusOID,
×
NEW
74
                Published: PublishedCensus{
×
NEW
75
                        Root: root,
×
NEW
76
                        URI:  uri,
×
NEW
77
                },
×
NEW
78
        }
×
NEW
79

×
NEW
80
        return ms.SetCensus(census)
×
81
}
82

83
// SetGroupCensus creates a new census for an organization
84
// Returns the hex representation of the census
85
func (ms *MongoStorage) SetGroupCensus(
86
        census *Census,
87
        groupID string,
88
        participantIDs []primitive.ObjectID,
89
) (string, error) {
12✔
90
        if len(groupID) == 0 {
13✔
91
                return ms.SetCensus(census)
1✔
92
        }
1✔
93

94
        // create a context with a timeout
95
        ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
11✔
96
        defer cancel()
11✔
97

11✔
98
        if census.OrgAddress == "" {
12✔
99
                return "", ErrInvalidData
1✔
100
        }
1✔
101

102
        // check that the org exists
103
        _, err := ms.Organization(census.OrgAddress)
10✔
104
        if err != nil {
11✔
105
                if err == ErrNotFound {
2✔
106
                        return "", ErrInvalidData
1✔
107
                }
1✔
NEW
108
                return "", fmt.Errorf("error retrieving organization: %w", err)
×
109
        }
110

111
        // check that the group exists
112
        group, err := ms.OrganizationMemberGroup(groupID, census.OrgAddress)
9✔
113
        if err != nil {
12✔
114
                if err == ErrNotFound {
5✔
115
                        return "", ErrInvalidData
2✔
116
                }
2✔
117
                return "", fmt.Errorf("error retrieving organization group: %w", err)
1✔
118
        }
119
        census.GroupID = group.ID
6✔
120

6✔
121
        if census.ID != primitive.NilObjectID {
7✔
122
                // if the census exists, update it with the new data
1✔
123
                census.UpdatedAt = time.Now()
1✔
124
        } else {
6✔
125
                // if the census doesn't exist, create its id
5✔
126
                census.ID = primitive.NewObjectID()
5✔
127
                census.CreatedAt = time.Now()
5✔
128
        }
5✔
129

130
        updateDoc, err := dynamicUpdateDocument(census, nil)
6✔
131
        if err != nil {
6✔
NEW
132
                return "", err
×
NEW
133
        }
×
134
        ms.keysLock.Lock()
6✔
135
        defer ms.keysLock.Unlock()
6✔
136
        filter := bson.M{"_id": census.ID}
6✔
137
        opts := options.Update().SetUpsert(true)
6✔
138
        _, err = ms.censuses.UpdateOne(ctx, filter, updateDoc, opts)
6✔
139
        if err != nil {
6✔
NEW
140
                return "", err
×
NEW
141
        }
×
142

143
        // set the participants for the census
144
        if len(participantIDs) > 0 {
8✔
145
                err := ms.setBulkCensusParticipant(ctx, census.ID.Hex(), participantIDs)
2✔
146
                if err != nil {
2✔
NEW
147
                        return "", fmt.Errorf("error setting census participants: %w", err)
×
NEW
148
                }
×
149
        }
150
        return census.ID.Hex(), nil
6✔
151
}
152

153
// DeleteCensus removes a census and all its members
154
func (ms *MongoStorage) DelCensus(censusID string) error {
4✔
155
        objID, err := primitive.ObjectIDFromHex(censusID)
4✔
156
        if err != nil {
6✔
157
                return ErrInvalidData
2✔
158
        }
2✔
159

160
        ms.keysLock.Lock()
2✔
161
        defer ms.keysLock.Unlock()
2✔
162
        // create a context with a timeout
2✔
163
        ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
2✔
164
        defer cancel()
2✔
165

2✔
166
        // delete the census from the database using the ID
2✔
167
        filter := bson.M{"_id": objID}
2✔
168
        _, err = ms.censuses.DeleteOne(ctx, filter)
2✔
169
        return err
2✔
170
}
171

172
// Census retrieves a census from the DB based on its ID
173
func (ms *MongoStorage) Census(censusID string) (*Census, error) {
76✔
174
        objID, err := primitive.ObjectIDFromHex(censusID)
76✔
175
        if err != nil {
78✔
176
                return nil, ErrInvalidData
2✔
177
        }
2✔
178

179
        // create a context with a timeout
180
        ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
74✔
181
        defer cancel()
74✔
182

74✔
183
        census := &Census{}
74✔
184
        err = ms.censuses.FindOne(ctx, bson.M{"_id": objID}).Decode(census)
74✔
185
        if err != nil {
78✔
186
                return nil, fmt.Errorf("failed to get census: %w", err)
4✔
187
        }
4✔
188

189
        return census, nil
70✔
190
}
191

192
// CensusesByOrg retrieves all the censuses for an organization based on its
193
// address. It checks that the organization exists and returns an error if it
194
// doesn't. If the organization exists, it returns the censuses.
195
func (ms *MongoStorage) CensusesByOrg(orgAddress string) ([]*Census, error) {
5✔
196
        ms.keysLock.RLock()
5✔
197
        defer ms.keysLock.RUnlock()
5✔
198
        // create a context with a timeout
5✔
199
        ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
5✔
200
        defer cancel()
5✔
201

5✔
202
        if _, err := ms.fetchOrganizationFromDB(ctx, orgAddress); err != nil {
6✔
203
                if err == ErrNotFound {
2✔
204
                        return nil, ErrInvalidData
1✔
205
                }
1✔
206
                return nil, fmt.Errorf("organization not found: %w", err)
×
207
        }
208
        // find the censuses in the database
209
        censuses := []*Census{}
4✔
210
        cursor, err := ms.censuses.Find(ctx, bson.M{"orgAddress": orgAddress})
4✔
211
        if err != nil {
4✔
212
                return nil, err
×
213
        }
×
214
        defer func() {
8✔
215
                if err := cursor.Close(ctx); err != nil {
4✔
216
                        log.Warnw("error closing cursor", "error", err)
×
217
                }
×
218
        }()
219
        if err := cursor.All(ctx, &censuses); err != nil {
4✔
220
                return nil, err
×
221
        }
×
222
        return censuses, nil
4✔
223
}
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