blob: 6693000832bc8dc6045a7c78358d0014258c26cc [file] [log] [blame]
Adrià Vilanova Martínez25e12112021-08-25 13:48:06 +02001package db
2
3import (
4 "context"
5 "database/sql"
6 "fmt"
7
8 "github.com/Masterminds/semver/v3"
9
10 pb "gomodules.avm99963.com/twpt-server/api_proto"
11)
12
13func isValidSemVersion(v string) bool {
14 _, err := semver.NewVersion(v)
15 return err == nil
16}
17
18func fillRawKillSwitch(db *sql.DB, ctx context.Context, k *pb.KillSwitch, featureId int32) error {
19 f, err := GetFeatureById(db, ctx, featureId)
20 if err != nil {
21 return fmt.Errorf("fillRawKillSwitch: %v", err)
22 }
23 if f == nil {
24 return fmt.Errorf("fillRawKillSwitch: linked feature doesn't exist.")
25 }
26 k.Feature = f
27
28 rows, err := db.QueryContext(ctx, "SELECT browser FROM KillSwitch2Browser WHERE kswitch_id = ?", k.Id)
29 if err != nil {
30 return fmt.Errorf("fillRawKillSwitch: %v", err)
31 }
32 defer rows.Close()
33
34 var browsers []pb.Environment_Browser
35 for rows.Next() {
36 var b pb.Environment_Browser
37 if err := rows.Scan(&b); err != nil {
38 return fmt.Errorf("fillRawKillSwitch: %v", err)
39 }
40 browsers = append(browsers, b)
41 }
42 k.Browsers = browsers
43
44 return nil
45}
46
47func GetKillSwitchById(db *sql.DB, ctx context.Context, id int32) (*pb.KillSwitch, error) {
48 query := db.QueryRowContext(ctx, "SELECT kswitch_id, feat_id, min_version, max_version, active FROM KillSwitch WHERE kswitch_id = ?", id)
49 var featureId int32
50 var k pb.KillSwitch
51 if err := query.Scan(&k.Id, &featureId, &k.MinVersion, &k.MaxVersion, &k.Active); err != nil {
52 if err == sql.ErrNoRows {
53 return nil, nil
54 }
55 return nil, fmt.Errorf("GetKillSwitchById: %v.", err)
56 }
57
58 err := fillRawKillSwitch(db, ctx, &k, featureId)
59 if err != nil {
60 return nil, fmt.Errorf("GetKillSwitchById: $v.", err)
61 }
62
63 return &k, nil
64}
65
Adrià Vilanova Martínez86103142021-09-03 15:13:20 +020066func ListKillSwitches(db *sql.DB, ctx context.Context, withNonactiveKillSwitches bool) ([]*pb.KillSwitch, error) {
Adrià Vilanova Martínez25e12112021-08-25 13:48:06 +020067 var rows *sql.Rows
68 var err error
Adrià Vilanova Martínez86103142021-09-03 15:13:20 +020069 if withNonactiveKillSwitches {
70 rows, err = db.QueryContext(ctx, "SELECT kswitch_id, feat_id, min_version, max_version, active FROM KillSwitch")
71 } else {
72 rows, err = db.QueryContext(ctx, "SELECT kswitch_id, feat_id, min_version, max_version, active FROM KillSwitch WHERE active <> 0")
73 }
Adrià Vilanova Martínez25e12112021-08-25 13:48:06 +020074 if err != nil {
75 return nil, fmt.Errorf("ListKillSwitches: ", err)
76 }
77 defer rows.Close()
78
79 var killSwitches []*pb.KillSwitch
80 for rows.Next() {
81 var featureId int32
82 var k pb.KillSwitch
83 if err := rows.Scan(&k.Id, &featureId, &k.MinVersion, &k.MaxVersion, &k.Active); err != nil {
84 return nil, fmt.Errorf("ListKillSwitches: %v.", err)
85 }
86 err := fillRawKillSwitch(db, ctx, &k, featureId)
87 if err != nil {
88 return nil, fmt.Errorf("ListKillSwitches: $v.", err)
89 }
90 killSwitches = append(killSwitches, &k)
91 }
92 if err := rows.Err(); err != nil {
93 return nil, fmt.Errorf("ListKillSwitches: ", err)
94 }
95 return killSwitches, nil
96}
97
Adrià Vilanova Martínezc147b6a2021-09-01 17:25:38 +020098func EnableKillSwitch(db *sql.DB, ctx context.Context, k *pb.KillSwitch, currentUser *pb.KillSwitchAuthorizedUser) error {
Adrià Vilanova Martínez25e12112021-08-25 13:48:06 +020099 if k.MinVersion != "" && !isValidSemVersion(k.MinVersion) {
100 return fmt.Errorf("min_version is not a valid semantic version.")
101 }
102 if k.MaxVersion != "" && !isValidSemVersion(k.MaxVersion) {
103 return fmt.Errorf("max_version is not a valid semantic version.")
104 }
105 if k.MinVersion != "" && k.MaxVersion != "" {
106 minVersion, _ := semver.NewVersion(k.MinVersion)
107 maxVersion, _ := semver.NewVersion(k.MaxVersion)
108 if minVersion.GreaterThan(maxVersion) {
109 return fmt.Errorf("min_version must be less than max_version.")
110 }
111 }
112 for _, b := range k.GetBrowsers() {
113 if b == pb.Environment_BROWSER_UNKNOWN {
114 return fmt.Errorf("browsers cannot contain BROWSER_UNKNOWN.")
115 }
116 }
117
118 k.Active = true
119
120 f, err := GetFeatureById(db, ctx, k.Feature.Id)
121 if err != nil {
122 return fmt.Errorf("EnableKillSwitch: %v", err)
123 }
124 if f == nil {
125 return fmt.Errorf("EnableKillSwitch: this feature doesn't exist.")
126 }
127 k.Feature = f
128
129 tx, err := db.BeginTx(ctx, nil)
130 if err != nil {
131 return err
132 }
133
134 result, err := tx.ExecContext(ctx, "INSERT INTO KillSwitch (feat_id, min_version, max_version, active) VALUES (?, ?, ?, ?)", k.Feature.Id, k.MinVersion, k.MaxVersion, k.Active)
135 if err != nil {
136 tx.Rollback()
137 return err
138 }
139
140 id, err := result.LastInsertId()
141 if err != nil {
142 tx.Rollback()
143 return err
144 }
145 k.Id = int32(id)
146
147 for _, b := range k.GetBrowsers() {
148 if _, err := tx.ExecContext(ctx, "INSERT INTO KillSwitch2Browser (kswitch_id, browser) VALUES (?, ?)", k.Id, b); err != nil {
149 tx.Rollback()
150 return err
151 }
152 }
153
154 logEntry := &pb.KillSwitchAuditLogEntry{
Adrià Vilanova Martínezc147b6a2021-09-01 17:25:38 +0200155 User: currentUser,
Adrià Vilanova Martínez25e12112021-08-25 13:48:06 +0200156 Description: &pb.KillSwitchAuditLogEntry_KillSwitchEnabled_{
157 &pb.KillSwitchAuditLogEntry_KillSwitchEnabled{
158 KillSwitch: k,
159 },
160 },
161 }
162 if err := AddKillSwitchAuditLogEntry(tx, ctx, logEntry); err != nil {
163 tx.Rollback()
164 return err
165 }
166
167 return tx.Commit()
168}
169
Adrià Vilanova Martínezc147b6a2021-09-01 17:25:38 +0200170func DisableKillSwitch(db *sql.DB, ctx context.Context, id int32, currentUser *pb.KillSwitchAuthorizedUser) error {
Adrià Vilanova Martínez25e12112021-08-25 13:48:06 +0200171 oldKillSwitch, err := GetKillSwitchById(db, ctx, id)
172 if err != nil {
173 return err
174 }
175 if oldKillSwitch == nil {
176 return fmt.Errorf("Such kill switch doesn't exist")
177 }
178 if oldKillSwitch.GetActive() != true {
179 return fmt.Errorf("The kill switch is not active")
180 }
181
182 tx, err := db.BeginTx(ctx, nil)
183 if err != nil {
184 return err
185 }
186
187 newKillSwitch := *oldKillSwitch
188 newKillSwitch.Active = false
189
190 if _, err := tx.ExecContext(ctx, "UPDATE KillSwitch SET active = ? WHERE kswitch_id = ?", newKillSwitch.Active, id); err != nil {
191 tx.Rollback()
192 return err
193 }
194
195 logEntry := &pb.KillSwitchAuditLogEntry{
Adrià Vilanova Martínezc147b6a2021-09-01 17:25:38 +0200196 User: currentUser,
Adrià Vilanova Martínez25e12112021-08-25 13:48:06 +0200197 Description: &pb.KillSwitchAuditLogEntry_KillSwitchDisabled_{
198 &pb.KillSwitchAuditLogEntry_KillSwitchDisabled{
199 Transformation: &pb.KillSwitchTransformation{
200 Old: oldKillSwitch,
201 New: &newKillSwitch,
202 },
203 },
204 },
205 }
206 if err := AddKillSwitchAuditLogEntry(tx, ctx, logEntry); err != nil {
207 tx.Rollback()
208 return err
209 }
210
211 return tx.Commit()
212}