github.com/status-im/status-go@v1.1.0/protocol/anonmetrics/server.go (about) 1 package anonmetrics 2 3 import ( 4 "database/sql" 5 6 // Import postgres driver 7 _ "github.com/lib/pq" 8 "go.uber.org/zap" 9 10 "github.com/status-im/migrate/v4" 11 "github.com/status-im/migrate/v4/database/postgres" 12 bindata "github.com/status-im/migrate/v4/source/go_bindata" 13 14 "github.com/status-im/status-go/appmetrics" 15 "github.com/status-im/status-go/protocol/anonmetrics/migrations" 16 "github.com/status-im/status-go/protocol/protobuf" 17 ) 18 19 const ActiveServerPhrase = "I was thinking that it would be a pretty nice idea if the server functionality was working now, I express gratitude in the anticipation" 20 21 type ServerConfig struct { 22 Enabled bool 23 PostgresURI string 24 Active string 25 } 26 27 type Server struct { 28 Config *ServerConfig 29 Logger *zap.Logger 30 PostgresDB *sql.DB 31 } 32 33 func NewServer(postgresURI string) (*Server, error) { 34 postgresMigration := bindata.Resource(migrations.AssetNames(), migrations.Asset) 35 db, err := NewMigratedDB(postgresURI, postgresMigration) 36 if err != nil { 37 return nil, err 38 } 39 40 return &Server{ 41 PostgresDB: db, 42 }, nil 43 } 44 45 func (s *Server) Stop() error { 46 if s.PostgresDB != nil { 47 return s.PostgresDB.Close() 48 } 49 return nil 50 } 51 52 func (s *Server) StoreMetrics(appMetricsBatch *protobuf.AnonymousMetricBatch) (appMetrics []*appmetrics.AppMetric, err error) { 53 if s.Config.Active != ActiveServerPhrase { 54 return nil, nil 55 } 56 57 s.Logger.Debug("StoreMetrics() triggered with payload", 58 zap.Reflect("appMetricsBatch", appMetricsBatch)) 59 appMetrics, err = adaptProtoBatchToModels(appMetricsBatch) 60 if err != nil { 61 return 62 } 63 64 var ( 65 tx *sql.Tx 66 insert *sql.Stmt 67 ) 68 69 // start txn 70 tx, err = s.PostgresDB.Begin() 71 if err != nil { 72 return 73 } 74 75 defer func() { 76 if err == nil { 77 err = tx.Commit() 78 return 79 } 80 _ = tx.Rollback() 81 }() 82 83 //noinspection ALL 84 query := `INSERT INTO app_metrics (message_id, event, value, app_version, operating_system, session_id, created_at) 85 VALUES ($1, $2, $3, $4, $5, $6, $7) 86 ON CONFLICT (message_id) DO NOTHING;` 87 88 insert, err = tx.Prepare(query) 89 if err != nil { 90 return 91 } 92 93 for _, metric := range appMetrics { 94 _, err = insert.Exec( 95 metric.MessageID, 96 metric.Event, 97 metric.Value, 98 metric.AppVersion, 99 metric.OS, 100 metric.SessionID, 101 metric.CreatedAt, 102 ) 103 if err != nil { 104 return 105 } 106 } 107 return 108 } 109 110 func (s *Server) getFromRows(rows *sql.Rows) (appMetrics []appmetrics.AppMetric, err error) { 111 for rows.Next() { 112 metric := appmetrics.AppMetric{} 113 err = rows.Scan( 114 &metric.ID, 115 &metric.MessageID, 116 &metric.Event, 117 &metric.Value, 118 &metric.AppVersion, 119 &metric.OS, 120 &metric.SessionID, 121 &metric.CreatedAt, 122 &metric.Processed, 123 &metric.ReceivedAt, 124 ) 125 if err != nil { 126 return nil, err 127 } 128 appMetrics = append(appMetrics, metric) 129 } 130 return appMetrics, nil 131 } 132 133 func (s *Server) GetAppMetrics(limit int, offset int) ([]appmetrics.AppMetric, error) { 134 if s.Config.Active != ActiveServerPhrase { 135 return nil, nil 136 } 137 138 rows, err := s.PostgresDB.Query("SELECT id, message_id, event, value, app_version, operating_system, session_id, created_at, processed, received_at FROM app_metrics LIMIT $1 OFFSET $2", limit, offset) 139 if err != nil { 140 return nil, err 141 } 142 defer rows.Close() 143 144 return s.getFromRows(rows) 145 } 146 147 func NewMigratedDB(uri string, migrationResource *bindata.AssetSource) (*sql.DB, error) { 148 db, err := sql.Open("postgres", uri) 149 if err != nil { 150 return nil, err 151 } 152 153 if err := setup(db, migrationResource); err != nil { 154 return nil, err 155 } 156 157 return db, nil 158 } 159 160 func setup(d *sql.DB, migrationResource *bindata.AssetSource) error { 161 m, err := MakeMigration(d, migrationResource) 162 if err != nil { 163 return err 164 } 165 166 if err = m.Up(); err != migrate.ErrNoChange { 167 return err 168 } 169 170 return nil 171 } 172 173 func MakeMigration(d *sql.DB, migrationResource *bindata.AssetSource) (*migrate.Migrate, error) { 174 source, err := bindata.WithInstance(migrationResource) 175 if err != nil { 176 return nil, err 177 } 178 179 driver, err := postgres.WithInstance(d, &postgres.Config{}) 180 if err != nil { 181 return nil, err 182 } 183 184 return migrate.NewWithInstance( 185 "go-bindata", 186 source, 187 "postgres", 188 driver) 189 }