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

vocdoni / saas-backend / 18531836246

15 Oct 2025 02:12PM UTC coverage: 57.067% (-1.0%) from 58.083%
18531836246

Pull #84

github

altergui
api: new handler listProcessDraftsHandler
Pull Request #84: api: support creating a draft process

26 of 114 new or added lines in 2 files covered. (22.81%)

265 existing lines in 8 files now uncovered.

5931 of 10393 relevant lines covered (57.07%)

33.96 hits per line

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

48.0
/db/process.go
1
package db
2

3
import (
4
        "context"
5
        "fmt"
6
        "math"
7

8
        "github.com/ethereum/go-ethereum/common"
9
        "github.com/vocdoni/saas-backend/internal"
10
        "go.mongodb.org/mongo-driver/bson"
11
        "go.mongodb.org/mongo-driver/mongo"
12
        "go.mongodb.org/mongo-driver/mongo/options"
13
        "go.vocdoni.io/dvote/log"
14
)
15

16
// SetProcess creates a new process or updates an existing one for an organization.
17
// If the process already exists and is in draft mode, it will be updated.
18
func (ms *MongoStorage) SetProcess(process *Process) error {
23✔
19
        if len(process.ID) == 0 || process.OrgAddress.Cmp(common.Address{}) == 0 || len(process.Census.ID) == 0 {
25✔
20
                return ErrInvalidData
2✔
21
        }
2✔
22

23
        // check that the org exists
24
        if _, err := ms.Organization(process.OrgAddress); err != nil {
22✔
25
                return fmt.Errorf("failed to get organization %s: %w", process.OrgAddress, err)
1✔
26
        }
1✔
27
        // check that the census exists
28
        census, err := ms.Census(process.Census.ID.Hex())
20✔
29
        if err != nil {
20✔
30
                return fmt.Errorf("failed to get census: %w", err)
×
31
        }
×
32
        if len(census.Published.Root) == 0 || len(census.Published.URI) == 0 {
21✔
33
                return fmt.Errorf("census %s does not have a published root or URI", census.ID.Hex())
1✔
34
        }
1✔
35

36
        // TODO create the census if not found?
37

38
        ms.keysLock.Lock()
19✔
39
        defer ms.keysLock.Unlock()
19✔
40
        // create a context with a timeout
19✔
41
        ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
19✔
42
        defer cancel()
19✔
43

19✔
44
        // Use ReplaceOne with upsert option to either update an existing process or insert a new one
19✔
45
        filter := bson.M{"_id": process.ID}
19✔
46
        opts := options.Replace().SetUpsert(true)
19✔
47
        if _, err := ms.processes.ReplaceOne(ctx, filter, process, opts); err != nil {
19✔
NEW
48
                return fmt.Errorf("failed to create or update process: %w", err)
×
UNCOV
49
        }
×
50

51
        return nil
19✔
52
}
53

54
// DeleteProcess removes a process
55
func (ms *MongoStorage) DelProcess(processID internal.HexBytes) error {
2✔
56
        if len(processID) == 0 {
3✔
57
                return ErrInvalidData
1✔
58
        }
1✔
59
        ms.keysLock.Lock()
1✔
60
        defer ms.keysLock.Unlock()
1✔
61
        // create a context with a timeout
1✔
62
        ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
1✔
63
        defer cancel()
1✔
64

1✔
65
        // delete the process from the database using the ID
1✔
66
        filter := bson.M{"_id": processID}
1✔
67
        _, err := ms.processes.DeleteOne(ctx, filter)
1✔
68
        return err
1✔
69
}
70

71
// Process retrieves a process from the DB based on its ID
72
func (ms *MongoStorage) Process(processID internal.HexBytes) (*Process, error) {
9✔
73
        if len(processID) == 0 {
9✔
74
                return nil, ErrInvalidData
×
75
        }
×
76

77
        // create a context with a timeout
78
        ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
9✔
79
        defer cancel()
9✔
80

9✔
81
        process := &Process{}
9✔
82
        if err := ms.processes.FindOne(ctx, bson.M{"_id": processID}).Decode(process); err != nil {
13✔
83
                if err == mongo.ErrNoDocuments {
8✔
84
                        return nil, ErrNotFound
4✔
85
                }
4✔
UNCOV
86
                return nil, fmt.Errorf("failed to get process: %w", err)
×
87
        }
88

89
        return process, nil
5✔
90
}
91

92
// ListProcesses retrieves all processes from the DB for an organization
NEW
93
func (ms *MongoStorage) ListProcesses(orgAddress common.Address, page, pageSize int, draft bool) (int, []Process, error) {
×
NEW
94
        if orgAddress.Cmp(common.Address{}) == 0 {
×
NEW
95
                return 0, nil, ErrInvalidData
×
NEW
96
        }
×
97
        // create a context with a timeout
NEW
98
        ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
×
NEW
99
        defer cancel()
×
NEW
100

×
NEW
101
        // Create filter
×
NEW
102
        filter := bson.M{
×
NEW
103
                "orgAddress": orgAddress,
×
NEW
104
                "draft":      draft,
×
NEW
105
        }
×
NEW
106

×
NEW
107
        // Calculate skip value based on page and pageSize
×
NEW
108
        skip := (page - 1) * pageSize
×
NEW
109

×
NEW
110
        // Count total documents
×
NEW
111
        totalCount, err := ms.processes.CountDocuments(ctx, filter)
×
NEW
112
        if err != nil {
×
NEW
113
                return 0, nil, err
×
NEW
114
        }
×
NEW
115
        totalPages := int(math.Ceil(float64(totalCount) / float64(pageSize)))
×
NEW
116
        sort := bson.D{
×
NEW
117
                bson.E{Key: "_id", Value: 1},
×
NEW
118
        }
×
NEW
119
        // Set up options for pagination
×
NEW
120
        findOptions := options.Find().
×
NEW
121
                SetSort(sort). // Sort by _id in descending order
×
NEW
122
                SetSkip(int64(skip)).
×
NEW
123
                SetLimit(int64(pageSize))
×
NEW
124

×
NEW
125
        // Execute the find operation with pagination
×
NEW
126
        cursor, err := ms.processes.Find(ctx, filter, findOptions)
×
NEW
127
        if err != nil {
×
NEW
128
                return 0, nil, fmt.Errorf("failed to get processes: %w", err)
×
NEW
129
        }
×
NEW
130
        defer func() {
×
NEW
131
                if err := cursor.Close(ctx); err != nil {
×
NEW
132
                        log.Warnw("error closing cursor", "error", err)
×
NEW
133
                }
×
134
        }()
135

136
        // Decode results
NEW
137
        var processes []Process
×
NEW
138
        if err = cursor.All(ctx, &processes); err != nil {
×
NEW
139
                return 0, nil, fmt.Errorf("failed to decode processes: %w", err)
×
NEW
140
        }
×
141

NEW
142
        return totalPages, processes, nil
×
143
}
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