github.com/readium/readium-lcp-server@v0.0.0-20240101192032-6e95190e99f1/transactions/transactions.go (about)

     1  // Copyright 2020 Readium Foundation. All rights reserved.
     2  // Use of this source code is governed by a BSD-style license
     3  // that can be found in the LICENSE file exposed on Github (readium) in the project repository.
     4  
     5  package transactions
     6  
     7  import (
     8  	"database/sql"
     9  	"errors"
    10  	"log"
    11  	"time"
    12  
    13  	"github.com/readium/readium-lcp-server/config"
    14  	"github.com/readium/readium-lcp-server/status"
    15  )
    16  
    17  var ErrNotFound = errors.New("Event not found")
    18  
    19  type Transactions interface {
    20  	Get(id int) (Event, error)
    21  	Add(e Event, eventType int) error
    22  	GetByLicenseStatusId(licenseStatusFk int) func() (Event, error)
    23  	CheckDeviceStatus(licenseStatusFk int, deviceId string) (string, error)
    24  	ListRegisteredDevices(licenseStatusFk int) func() (Device, error)
    25  }
    26  
    27  type RegisteredDevicesList struct {
    28  	ID      string   `json:"id"`
    29  	Devices []Device `json:"devices"`
    30  }
    31  
    32  type Device struct {
    33  	DeviceId   string    `json:"id"`
    34  	DeviceName string    `json:"name"`
    35  	Timestamp  time.Time `json:"timestamp"`
    36  }
    37  
    38  type Event struct {
    39  	ID              int       `json:"-"`
    40  	DeviceName      string    `json:"name"`
    41  	Timestamp       time.Time `json:"timestamp"`
    42  	Type            string    `json:"type"`
    43  	DeviceId        string    `json:"id"`
    44  	LicenseStatusFk int       `json:"-"`
    45  }
    46  
    47  type dbTransactions struct {
    48  	db                      *sql.DB
    49  	dbGet                   *sql.Stmt
    50  	dbGetByStatusID         *sql.Stmt
    51  	dbCheckDeviceStatus     *sql.Stmt
    52  	dbListRegisteredDevices *sql.Stmt
    53  }
    54  
    55  // Get returns an event by its id
    56  func (i dbTransactions) Get(id int) (Event, error) {
    57  
    58  	row := i.dbGet.QueryRow(id)
    59  	var e Event
    60  	var typeInt int
    61  	err := row.Scan(&e.ID, &e.DeviceName, &e.Timestamp, &typeInt, &e.DeviceId, &e.LicenseStatusFk)
    62  	if err != nil {
    63  		return Event{}, err
    64  	}
    65  	e.Type = status.EventTypes[typeInt]
    66  	return e, err
    67  }
    68  
    69  // Add adds an event in the database,
    70  // The parameter eventType corresponds to the field 'type' in table 'event'
    71  func (i dbTransactions) Add(e Event, eventType int) error {
    72  
    73  	_, err := i.db.Exec("INSERT INTO event (device_name, timestamp, type, device_id, license_status_fk) VALUES (?, ?, ?, ?, ?)",
    74  		e.DeviceName, e.Timestamp, eventType, e.DeviceId, e.LicenseStatusFk)
    75  	return err
    76  }
    77  
    78  // GetByLicenseStatusId returns all events by license status id
    79  func (i dbTransactions) GetByLicenseStatusId(licenseStatusFk int) func() (Event, error) {
    80  	rows, err := i.dbGetByStatusID.Query(licenseStatusFk)
    81  	if err != nil {
    82  		return func() (Event, error) { return Event{}, err }
    83  	}
    84  	return func() (Event, error) {
    85  		var e Event
    86  		var err error
    87  		var typeInt int
    88  
    89  		if rows.Next() {
    90  			err = rows.Scan(&e.ID, &e.DeviceName, &e.Timestamp, &typeInt, &e.DeviceId, &e.LicenseStatusFk)
    91  			if err == nil {
    92  				e.Type = status.EventTypes[typeInt]
    93  			}
    94  		} else {
    95  			rows.Close()
    96  			err = ErrNotFound
    97  		}
    98  		return e, err
    99  	}
   100  }
   101  
   102  // ListRegisteredDevices returns all devices which have an 'active' status by licensestatus id
   103  func (i dbTransactions) ListRegisteredDevices(licenseStatusFk int) func() (Device, error) {
   104  
   105  	rows, err := i.dbListRegisteredDevices.Query(licenseStatusFk)
   106  	if err != nil {
   107  		return func() (Device, error) { return Device{}, err }
   108  	}
   109  	return func() (Device, error) {
   110  		var d Device
   111  		var err error
   112  		if rows.Next() {
   113  			err = rows.Scan(&d.DeviceId, &d.DeviceName, &d.Timestamp)
   114  		} else {
   115  			rows.Close()
   116  			err = ErrNotFound
   117  		}
   118  		return d, err
   119  	}
   120  }
   121  
   122  // CheckDeviceStatus gets the current status of a device as a string
   123  // if the device has not been recorded in the 'event' table, returns an empty string.
   124  func (i dbTransactions) CheckDeviceStatus(licenseStatusFk int, deviceId string) (string, error) {
   125  	var typeString string
   126  	var typeInt int
   127  
   128  	row := i.dbCheckDeviceStatus.QueryRow(licenseStatusFk, deviceId)
   129  	err := row.Scan(&typeInt)
   130  	if err != nil {
   131  		if err == sql.ErrNoRows {
   132  			return typeString, nil
   133  		}
   134  	}
   135  	typeString = status.EventTypes[typeInt]
   136  	return typeString, err
   137  }
   138  
   139  // Open defines scripts for queries & create the 'event' table if it does not exist
   140  func Open(db *sql.DB) (t Transactions, err error) {
   141  
   142  	driver, _ := config.GetDatabase(config.Config.LsdServer.Database)
   143  
   144  	// if sqlite, create the event table in the lsd db if it does not exist
   145  	if driver == "sqlite3" {
   146  		_, err = db.Exec(tableDef)
   147  		if err != nil {
   148  			log.Println("Error creating sqlite event table")
   149  			return
   150  		}
   151  	}
   152  
   153  	// select an event by its id
   154  	dbGet, err := db.Prepare("SELECT * FROM event WHERE id = ?")
   155  	if err != nil {
   156  		return
   157  	}
   158  
   159  	dbGetByStatusID, err := db.Prepare("SELECT * FROM event WHERE license_status_fk = ?")
   160  	if err != nil {
   161  		return
   162  	}
   163  
   164  	// the status of a device corresponds to the latest event stored in the db.
   165  	var dbCheckDeviceStatus *sql.Stmt
   166  	if driver == "mssql" {
   167  		dbCheckDeviceStatus, err = db.Prepare(`SELECT TOP 1 type FROM event WHERE license_status_fk = ?
   168  		AND device_id = ? ORDER BY timestamp DESC`)
   169  	} else {
   170  		dbCheckDeviceStatus, err = db.Prepare(`SELECT type FROM event WHERE license_status_fk = ?
   171  		AND device_id = ? ORDER BY timestamp DESC LIMIT 1`)
   172  	}
   173  	if err != nil {
   174  		return
   175  	}
   176  
   177  	dbListRegisteredDevices, err := db.Prepare(`SELECT device_id,
   178  	device_name, timestamp  FROM event  WHERE license_status_fk = ? AND type = 1`)
   179  	if err != nil {
   180  		return
   181  	}
   182  
   183  	t = dbTransactions{db, dbGet, dbGetByStatusID, dbCheckDeviceStatus, dbListRegisteredDevices}
   184  	return
   185  }
   186  
   187  const tableDef = "CREATE TABLE IF NOT EXISTS event (" +
   188  	"id integer PRIMARY KEY," +
   189  	"device_name varchar(255) DEFAULT NULL," +
   190  	"timestamp datetime NOT NULL," +
   191  	"type int NOT NULL," +
   192  	"device_id varchar(255) DEFAULT NULL," +
   193  	"license_status_fk int NOT NULL," +
   194  	"FOREIGN KEY(license_status_fk) REFERENCES license_status(id)" +
   195  	");" +
   196  	"CREATE INDEX IF NOT EXISTS license_status_fk_index on event (license_status_fk);"