Markus Pesch
fb8d4dd5eb
changes: - Remove cli Some cli commands are not complete tested and are deprecated. - Daemon - Old version has a very bad implementation of how to verify, if the device or the sensors are in the database insert. The current implementation can be improved but this one is betten then the old one. - Remove complete the cache store implementation. Use a normal array and query the length and capacity to determine how the array cache must be cleaned. - Type Remove unused types and functions
241 lines
6.9 KiB
Go
241 lines
6.9 KiB
Go
package storage
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"fmt"
|
|
"net/url"
|
|
"path/filepath"
|
|
|
|
_ "github.com/lib/pq"
|
|
"github.com/volker-raschek/flucky/pkg/types"
|
|
"github.com/volker-raschek/go-logger/pkg/logger"
|
|
)
|
|
|
|
// Storage is a general interface for a storage endpoint
|
|
type Storage interface {
|
|
InsertDevice(ctx context.Context, device *types.Device) error
|
|
InsertMeasuredValues(ctx context.Context, measuredValues []*types.MeasuredValue) error
|
|
InsertSensor(ctx context.Context, sensor *types.Sensor) error
|
|
SelectDevice(ctx context.Context, id string) (*types.Device, error)
|
|
SelectSensor(ctx context.Context, id string) (*types.Sensor, error)
|
|
}
|
|
|
|
var (
|
|
postgresAssetPath = "pkg/storage/postgres"
|
|
)
|
|
|
|
// Postgres implementation
|
|
type Postgres struct {
|
|
dbo *sql.DB
|
|
flogger logger.Logger
|
|
}
|
|
|
|
// InsertDevice into the database
|
|
func (postgres *Postgres) InsertDevice(ctx context.Context, device *types.Device) error {
|
|
asset := filepath.Join(postgresAssetPath, "insertDevice.sql")
|
|
queryBytes, err := Asset(asset)
|
|
if err != nil {
|
|
return fmt.Errorf("Failed to load asset %v: %v", asset, err)
|
|
}
|
|
query := string(queryBytes)
|
|
|
|
tx, err := postgres.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false})
|
|
if err != nil {
|
|
return fmt.Errorf("Failed to begin new transaction: %v", err)
|
|
}
|
|
|
|
stmt, err := tx.Prepare(query)
|
|
if err != nil {
|
|
return fmt.Errorf("Failed to prepare statement: %v", err)
|
|
}
|
|
defer stmt.Close()
|
|
|
|
_, err = stmt.Exec(&device.ID, &device.Name, &device.Location, &device.CreationDate)
|
|
if err != nil {
|
|
tx.Rollback()
|
|
return fmt.Errorf("Failed to execute statement: %v", err)
|
|
}
|
|
|
|
return tx.Commit()
|
|
}
|
|
|
|
// InsertMeasuredValues into the database
|
|
func (postgres *Postgres) InsertMeasuredValues(ctx context.Context, measuredValues []*types.MeasuredValue) error {
|
|
splittedMeasuredValues := make(map[string][]*types.MeasuredValue, 0)
|
|
|
|
for _, measuredValue := range measuredValues {
|
|
if _, ok := splittedMeasuredValues[measuredValue.ValueType]; !ok {
|
|
splittedMeasuredValues[measuredValue.ValueType] = make([]*types.MeasuredValue, 0)
|
|
}
|
|
splittedMeasuredValues[measuredValue.ValueType] = append(splittedMeasuredValues[measuredValue.ValueType], measuredValue)
|
|
}
|
|
|
|
tx, err := postgres.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false})
|
|
if err != nil {
|
|
return fmt.Errorf("Failed to begin new transaction: %v", err)
|
|
}
|
|
|
|
// General insert function
|
|
insert := func(tx *sql.Tx, asset string, measuredValues []*types.MeasuredValue) error {
|
|
queryBytes, err := Asset(asset)
|
|
if err != nil {
|
|
return fmt.Errorf("Failed to load asset %v: %v", asset, err)
|
|
}
|
|
query := string(queryBytes)
|
|
|
|
stmt, err := tx.Prepare(query)
|
|
if err != nil {
|
|
return fmt.Errorf("Failed to prepare statement: %v", err)
|
|
}
|
|
defer stmt.Close()
|
|
|
|
for _, measuredValue := range measuredValues {
|
|
_, err := stmt.Exec(&measuredValue.ID, &measuredValue.Value, &measuredValue.FromDate, &measuredValue.TillDate, &measuredValue.SensorID, &measuredValue.CreationDate, &measuredValue.UpdateDate)
|
|
if err != nil {
|
|
return fmt.Errorf("Failed to execute statement: %v", err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
for measuredValueType, measuredValues := range splittedMeasuredValues {
|
|
var asset string
|
|
|
|
switch measuredValueType {
|
|
case "humidity":
|
|
asset = filepath.Join(postgresAssetPath, "insertHumidity.sql")
|
|
case "pressure":
|
|
asset = filepath.Join(postgresAssetPath, "insertPressure.sql")
|
|
case "temperature":
|
|
asset = filepath.Join(postgresAssetPath, "insertTemperature.sql")
|
|
default:
|
|
tx.Rollback()
|
|
return fmt.Errorf("Measured value type %v not supported", measuredValueType)
|
|
}
|
|
|
|
err := insert(tx, asset, measuredValues)
|
|
if err != nil {
|
|
tx.Rollback()
|
|
return err
|
|
}
|
|
}
|
|
|
|
return tx.Commit()
|
|
}
|
|
|
|
// InsertSensor into the database
|
|
func (postgres *Postgres) InsertSensor(ctx context.Context, sensor *types.Sensor) error {
|
|
asset := filepath.Join(postgresAssetPath, "insertSensor.sql")
|
|
queryBytes, err := Asset(asset)
|
|
if err != nil {
|
|
return fmt.Errorf("Failed to load asset %v: %v", asset, err)
|
|
}
|
|
query := string(queryBytes)
|
|
|
|
tx, err := postgres.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: false})
|
|
if err != nil {
|
|
return fmt.Errorf("Failed to begin new transaction: %v", err)
|
|
}
|
|
|
|
stmt, err := tx.Prepare(query)
|
|
if err != nil {
|
|
return fmt.Errorf("Failed to prepare statement: %v", err)
|
|
}
|
|
defer stmt.Close()
|
|
|
|
_, err = stmt.Exec(&sensor.ID, &sensor.Name, &sensor.Location, &sensor.WireID, &sensor.I2CBus, &sensor.I2CAddress, &sensor.GPIONumber, &sensor.Model, &sensor.Enabled, &sensor.DeviceID, &sensor.CreationDate)
|
|
if err != nil {
|
|
tx.Rollback()
|
|
return fmt.Errorf("Failed to execute statement: %v", err)
|
|
}
|
|
|
|
return tx.Commit()
|
|
}
|
|
|
|
// SelectDevice from database
|
|
func (postgres *Postgres) SelectDevice(ctx context.Context, id string) (*types.Device, error) {
|
|
asset := filepath.Join(postgresAssetPath, "selectDeviceByID.sql")
|
|
queryBytes, err := Asset(asset)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Failed to load asset %v: %v", asset, err)
|
|
}
|
|
query := string(queryBytes)
|
|
|
|
tx, err := postgres.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Failed to begin new transaction: %v", err)
|
|
}
|
|
|
|
stmt, err := tx.Prepare(query)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Failed to prepare statement: %v", err)
|
|
}
|
|
defer stmt.Close()
|
|
|
|
row := stmt.QueryRow(id)
|
|
|
|
device := new(types.Device)
|
|
err = row.Scan(&device.ID, &device.Name, &device.Location, &device.CreationDate)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Failed to scan row: %v", err)
|
|
}
|
|
|
|
return device, nil
|
|
}
|
|
|
|
// SelectSensor from database
|
|
func (postgres *Postgres) SelectSensor(ctx context.Context, id string) (*types.Sensor, error) {
|
|
asset := filepath.Join(postgresAssetPath, "selectSensorByID.sql")
|
|
queryBytes, err := Asset(asset)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Failed to load asset %v: %v", asset, err)
|
|
}
|
|
query := string(queryBytes)
|
|
|
|
tx, err := postgres.dbo.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Failed to begin new transaction: %v", err)
|
|
}
|
|
|
|
stmt, err := tx.Prepare(query)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Failed to prepare statement: %v", err)
|
|
}
|
|
defer stmt.Close()
|
|
|
|
row := stmt.QueryRow(id)
|
|
|
|
sensor := new(types.Sensor)
|
|
err = row.Scan(&sensor.ID, &sensor.Name, &sensor.Location, &sensor.WireID, &sensor.I2CBus, &sensor.I2CAddress, &sensor.GPIONumber, &sensor.Model, &sensor.Enabled, &sensor.DeviceID, &sensor.CreationDate)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Failed to scan row: %v", err)
|
|
}
|
|
|
|
return sensor, nil
|
|
}
|
|
|
|
// New returns a new storage provider
|
|
func New(storageEndpoint string, flogger logger.Logger) (Storage, error) {
|
|
storageEndpointURL, err := url.Parse(storageEndpoint)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
switch storageEndpointURL.Scheme {
|
|
case "postgres":
|
|
newDBO, err := sql.Open(storageEndpointURL.Scheme, storageEndpointURL.String())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &Postgres{
|
|
dbo: newDBO,
|
|
flogger: flogger,
|
|
}, nil
|
|
default:
|
|
return nil, fmt.Errorf("Unsupported database scheme: %v", storageEndpointURL.Scheme)
|
|
}
|
|
}
|