blob: 3c85f2b6a41d6f7527621289d0bb7a65cf672094 [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
66func ListKillSwitches(db *sql.DB, ctx context.Context) ([]*pb.KillSwitch, error) {
67 var rows *sql.Rows
68 var err error
69 rows, err = db.QueryContext(ctx, "SELECT kswitch_id, feat_id, min_version, max_version, active FROM KillSwitch")
70 if err != nil {
71 return nil, fmt.Errorf("ListKillSwitches: ", err)
72 }
73 defer rows.Close()
74
75 var killSwitches []*pb.KillSwitch
76 for rows.Next() {
77 var featureId int32
78 var k pb.KillSwitch
79 if err := rows.Scan(&k.Id, &featureId, &k.MinVersion, &k.MaxVersion, &k.Active); err != nil {
80 return nil, fmt.Errorf("ListKillSwitches: %v.", err)
81 }
82 err := fillRawKillSwitch(db, ctx, &k, featureId)
83 if err != nil {
84 return nil, fmt.Errorf("ListKillSwitches: $v.", err)
85 }
86 killSwitches = append(killSwitches, &k)
87 }
88 if err := rows.Err(); err != nil {
89 return nil, fmt.Errorf("ListKillSwitches: ", err)
90 }
91 return killSwitches, nil
92}
93
Adrià Vilanova Martínezc147b6a2021-09-01 17:25:38 +020094func EnableKillSwitch(db *sql.DB, ctx context.Context, k *pb.KillSwitch, currentUser *pb.KillSwitchAuthorizedUser) error {
Adrià Vilanova Martínez25e12112021-08-25 13:48:06 +020095 if k.MinVersion != "" && !isValidSemVersion(k.MinVersion) {
96 return fmt.Errorf("min_version is not a valid semantic version.")
97 }
98 if k.MaxVersion != "" && !isValidSemVersion(k.MaxVersion) {
99 return fmt.Errorf("max_version is not a valid semantic version.")
100 }
101 if k.MinVersion != "" && k.MaxVersion != "" {
102 minVersion, _ := semver.NewVersion(k.MinVersion)
103 maxVersion, _ := semver.NewVersion(k.MaxVersion)
104 if minVersion.GreaterThan(maxVersion) {
105 return fmt.Errorf("min_version must be less than max_version.")
106 }
107 }
108 for _, b := range k.GetBrowsers() {
109 if b == pb.Environment_BROWSER_UNKNOWN {
110 return fmt.Errorf("browsers cannot contain BROWSER_UNKNOWN.")
111 }
112 }
113
114 k.Active = true
115
116 f, err := GetFeatureById(db, ctx, k.Feature.Id)
117 if err != nil {
118 return fmt.Errorf("EnableKillSwitch: %v", err)
119 }
120 if f == nil {
121 return fmt.Errorf("EnableKillSwitch: this feature doesn't exist.")
122 }
123 k.Feature = f
124
125 tx, err := db.BeginTx(ctx, nil)
126 if err != nil {
127 return err
128 }
129
130 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)
131 if err != nil {
132 tx.Rollback()
133 return err
134 }
135
136 id, err := result.LastInsertId()
137 if err != nil {
138 tx.Rollback()
139 return err
140 }
141 k.Id = int32(id)
142
143 for _, b := range k.GetBrowsers() {
144 if _, err := tx.ExecContext(ctx, "INSERT INTO KillSwitch2Browser (kswitch_id, browser) VALUES (?, ?)", k.Id, b); err != nil {
145 tx.Rollback()
146 return err
147 }
148 }
149
150 logEntry := &pb.KillSwitchAuditLogEntry{
Adrià Vilanova Martínezc147b6a2021-09-01 17:25:38 +0200151 User: currentUser,
Adrià Vilanova Martínez25e12112021-08-25 13:48:06 +0200152 Description: &pb.KillSwitchAuditLogEntry_KillSwitchEnabled_{
153 &pb.KillSwitchAuditLogEntry_KillSwitchEnabled{
154 KillSwitch: k,
155 },
156 },
157 }
158 if err := AddKillSwitchAuditLogEntry(tx, ctx, logEntry); err != nil {
159 tx.Rollback()
160 return err
161 }
162
163 return tx.Commit()
164}
165
Adrià Vilanova Martínezc147b6a2021-09-01 17:25:38 +0200166func DisableKillSwitch(db *sql.DB, ctx context.Context, id int32, currentUser *pb.KillSwitchAuthorizedUser) error {
Adrià Vilanova Martínez25e12112021-08-25 13:48:06 +0200167 oldKillSwitch, err := GetKillSwitchById(db, ctx, id)
168 if err != nil {
169 return err
170 }
171 if oldKillSwitch == nil {
172 return fmt.Errorf("Such kill switch doesn't exist")
173 }
174 if oldKillSwitch.GetActive() != true {
175 return fmt.Errorf("The kill switch is not active")
176 }
177
178 tx, err := db.BeginTx(ctx, nil)
179 if err != nil {
180 return err
181 }
182
183 newKillSwitch := *oldKillSwitch
184 newKillSwitch.Active = false
185
186 if _, err := tx.ExecContext(ctx, "UPDATE KillSwitch SET active = ? WHERE kswitch_id = ?", newKillSwitch.Active, id); err != nil {
187 tx.Rollback()
188 return err
189 }
190
191 logEntry := &pb.KillSwitchAuditLogEntry{
Adrià Vilanova Martínezc147b6a2021-09-01 17:25:38 +0200192 User: currentUser,
Adrià Vilanova Martínez25e12112021-08-25 13:48:06 +0200193 Description: &pb.KillSwitchAuditLogEntry_KillSwitchDisabled_{
194 &pb.KillSwitchAuditLogEntry_KillSwitchDisabled{
195 Transformation: &pb.KillSwitchTransformation{
196 Old: oldKillSwitch,
197 New: &newKillSwitch,
198 },
199 },
200 },
201 }
202 if err := AddKillSwitchAuditLogEntry(tx, ctx, logEntry); err != nil {
203 tx.Rollback()
204 return err
205 }
206
207 return tx.Commit()
208}