status-go/centralizedmetrics/sqlite_persistence.go

202 lines
4.4 KiB
Go

package centralizedmetrics
import (
"context"
"database/sql"
"encoding/json"
"fmt"
"time"
"github.com/google/uuid"
"github.com/status-im/status-go/centralizedmetrics/common"
)
type SQLiteMetricRepository struct {
db *sql.DB
}
func NewSQLiteMetricRepository(db *sql.DB) *SQLiteMetricRepository {
return &SQLiteMetricRepository{db: db}
}
func (r *SQLiteMetricRepository) Poll() ([]common.Metric, error) {
tx, err := r.db.BeginTx(context.Background(), &sql.TxOptions{})
if err != nil {
return nil, err
}
defer func() {
if err == nil {
err = tx.Commit()
return
}
// don't shadow original error
_ = tx.Rollback()
}()
userID, err := r.UserID(tx)
if err != nil {
return nil, err
}
rows, err := tx.Query("SELECT id, event_name, event_value, platform, app_version, timestamp FROM centralizedmetrics_metrics limit 10")
if err != nil {
return nil, err
}
defer rows.Close()
var metrics []common.Metric
for rows.Next() {
var metric common.Metric
var eventValue string
if err := rows.Scan(&metric.ID, &metric.EventName, &eventValue, &metric.Platform, &metric.AppVersion, &metric.Timestamp); err != nil {
return nil, err
}
// Deserialize eventValue
if err := json.Unmarshal([]byte(eventValue), &metric.EventValue); err != nil {
return nil, err
}
metric.UserID = userID
metrics = append(metrics, metric)
}
return metrics, rows.Err()
}
func (r *SQLiteMetricRepository) Delete(metrics []common.Metric) error {
tx, err := r.db.BeginTx(context.Background(), nil)
if err != nil {
return err
}
defer func() {
if err == nil {
err = tx.Commit()
return
}
// don't shadow original error
_ = tx.Rollback()
}()
stmt, err := tx.Prepare("DELETE FROM centralizedmetrics_metrics WHERE id = ?")
if err != nil {
return err
}
defer stmt.Close()
for _, metric := range metrics {
if _, err := stmt.Exec(metric.ID); err != nil {
return err
}
}
return tx.Commit()
}
func (r *SQLiteMetricRepository) Add(metric common.Metric) error {
eventValue, err := json.Marshal(metric.EventValue)
if err != nil {
return err
}
_, err = r.db.Exec("INSERT INTO centralizedmetrics_metrics (id, event_name, event_value, platform, app_version, timestamp) VALUES (?, ?, ?, ?, ?, ?)",
metric.ID, metric.EventName, string(eventValue), metric.Platform, metric.AppVersion, time.Now().UnixNano()/int64(time.Millisecond))
return err
}
func (r *SQLiteMetricRepository) UserID(tx *sql.Tx) (string, error) {
var err error
if tx == nil {
tx, err = r.db.BeginTx(context.Background(), &sql.TxOptions{})
if err != nil {
return "", err
}
defer func() {
if err == nil {
err = tx.Commit()
return
}
// don't shadow original error
_ = tx.Rollback()
}()
}
var userID string
// Check if a UUID already exists in the table
err = tx.QueryRow("SELECT uuid FROM centralizedmetrics_uuid LIMIT 1").Scan(&userID)
if err != nil {
if err == sql.ErrNoRows {
// clean up err
err = nil
// Generate a new UUID
newUUID := uuid.New().String()
// Insert the new UUID into the table
_, err := tx.Exec("INSERT INTO centralizedmetrics_uuid (uuid) VALUES (?)", newUUID)
if err != nil {
return "", fmt.Errorf("failed to insert new UUID: %v", err)
}
return newUUID, nil
}
return "", fmt.Errorf("failed to query for existing UUID: %v", err)
}
return userID, nil
}
func (r *SQLiteMetricRepository) ToggleEnabled(enabled bool) error {
tx, err := r.db.BeginTx(context.Background(), &sql.TxOptions{})
if err != nil {
return err
}
defer func() {
if err == nil {
err = tx.Commit()
return
}
// don't shadow original error
_ = tx.Rollback()
}()
// make sure row is present
userID, err := r.UserID(tx)
if err != nil {
return err
}
_, err = tx.Exec("UPDATE centralizedmetrics_uuid SET enabled = ?, user_confirmed = 1 WHERE uuid = ?", enabled, userID)
if err != nil {
return err
}
// if we are enabling them, nothing else to do
if enabled {
return nil
}
// otherwise clean up metrics that might have been collected in the meantime
_, err = tx.Exec("DELETE FROM centralizedmetrics_metrics")
return err
}
func (r *SQLiteMetricRepository) Info() (*MetricsInfo, error) {
info := MetricsInfo{}
err := r.db.QueryRow("SELECT enabled,user_confirmed FROM centralizedmetrics_uuid LIMIT 1").Scan(&info.Enabled, &info.UserConfirmed)
if err == sql.ErrNoRows {
return &info, nil
}
if err != nil {
return nil, err
}
return &info, nil
}