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  }