PiwigoDirectoryUploader/internal/app/datastore.go

250 lines
6.7 KiB
Go
Raw Normal View History

package app
2019-03-21 22:37:16 +01:00
//go:generate mockgen -destination=../pkg/mocks/mock_app_datastore.go -package=mocks git.haefelfinger.net/piwigo/PiwigoDirectoryUploader/internal/app ImageMetadataProvider
import (
2019-03-15 00:35:49 +01:00
"database/sql"
"errors"
"fmt"
2019-03-15 00:35:49 +01:00
_ "github.com/mattn/go-sqlite3"
"github.com/sirupsen/logrus"
"time"
)
var ErrorRecordNotFound = errors.New("Record not found")
type ImageMetaData struct {
ImageId int
PiwigoId int
FullImagePath string
Filename string
Md5Sum string
LastChange time.Time
CategoryPath string
CategoryId int
UploadRequired bool
}
func (img *ImageMetaData) String() string {
return fmt.Sprintf("ImageMetaData{ImageId:%d, PiwigoId:%d, CategoryId:%d, RelPath:%s, File:%s, Md5:%s, Change:%sS, catpath:%s, UploadRequired: %t}", img.ImageId, img.PiwigoId, img.CategoryId, img.FullImagePath, img.Filename, img.Md5Sum, img.LastChange.String(), img.CategoryPath, img.UploadRequired)
}
type ImageMetadataProvider interface {
ImageMetadata(fullImagePath string) (ImageMetaData, error)
ImageMetadataToUpload() ([]ImageMetaData, error)
SaveImageMetadata(m ImageMetaData) error
2019-03-19 23:07:55 +01:00
SavePiwigoIdAndUpdateUploadFlag(md5Sum string, piwigoId int) error
}
type localDataStore struct {
connectionString string
}
2019-03-15 00:35:49 +01:00
func (d *localDataStore) Initialize(connectionString string) error {
if connectionString == "" {
return errors.New("connection string could not be empty.")
}
d.connectionString = connectionString
2019-03-15 00:35:49 +01:00
db, err := d.openDatabase()
if err != nil {
return err
}
defer db.Close()
err = d.createTablesIfNeeded(db)
return err
}
func (d *localDataStore) ImageMetadata(fullImagePath string) (ImageMetaData, error) {
logrus.Tracef("Query image metadata for file %s", fullImagePath)
img := ImageMetaData{}
2019-03-15 00:35:49 +01:00
db, err := d.openDatabase()
if err != nil {
return img, err
2019-03-15 00:35:49 +01:00
}
defer db.Close()
stmt, err := db.Prepare("SELECT imageId, piwigoId, fullImagePath, fileName, md5sum, lastChanged, categoryPath, categoryId, uploadRequired FROM image WHERE fullImagePath = ?")
2019-03-15 00:35:49 +01:00
if err != nil {
return img, err
2019-03-15 00:35:49 +01:00
}
rows, err := stmt.Query(fullImagePath)
2019-03-15 00:35:49 +01:00
if err != nil {
return img, err
2019-03-15 00:35:49 +01:00
}
defer rows.Close()
2019-03-15 00:35:49 +01:00
if rows.Next() {
err = ReadImageMetadataFromRow(rows, &img)
if err != nil {
return img, err
}
} else {
return img, ErrorRecordNotFound
}
err = rows.Err()
return img, err
}
func (d *localDataStore) ImageMetadataToUpload() ([]ImageMetaData, error) {
logrus.Tracef("Query all image metadata that represent files queued to upload")
db, err := d.openDatabase()
if err != nil {
return nil, err
}
defer db.Close()
rows, err := db.Query("SELECT imageId, piwigoId, fullImagePath, fileName, md5sum, lastChanged, categoryPath, categoryId, uploadRequired FROM image WHERE uploadRequired = 1")
if err != nil {
return nil, err
}
defer rows.Close()
images := []ImageMetaData{}
for rows.Next() {
img := &ImageMetaData{}
err = ReadImageMetadataFromRow(rows, img)
if err != nil {
return nil, err
}
images = append(images, *img)
}
err = rows.Err()
return images, err
}
func ReadImageMetadataFromRow(rows *sql.Rows, img *ImageMetaData) error {
err := rows.Scan(&img.ImageId, &img.PiwigoId, &img.FullImagePath, &img.Filename, &img.Md5Sum, &img.LastChange, &img.CategoryPath, &img.CategoryId, &img.UploadRequired)
return err
}
func (d *localDataStore) SaveImageMetadata(img ImageMetaData) error {
logrus.Tracef("Saving imagemetadata: %s", img.String())
2019-03-15 00:35:49 +01:00
db, err := d.openDatabase()
if err != nil {
return err
}
defer db.Close()
tx, err := db.Begin()
if err != nil {
return err
}
if img.ImageId <= 0 {
err = d.insertImageMetaData(tx, img)
2019-03-15 00:35:49 +01:00
} else {
err = d.updateImageMetaData(tx, img)
2019-03-15 00:35:49 +01:00
}
if err != nil {
logrus.Errorf("Rolling back transaction for metadata of %s", img.FullImagePath)
errTx := tx.Rollback()
if errTx != nil {
logrus.Errorf("Rollback of transaction for metadata of %s failed!", img.FullImagePath)
}
return err
}
logrus.Tracef("Commiting metadata for image %s", img.String())
return tx.Commit()
2019-03-15 00:35:49 +01:00
}
2019-03-19 23:07:55 +01:00
func (d *localDataStore) SavePiwigoIdAndUpdateUploadFlag(md5Sum string, piwigoId int) error {
logrus.Tracef("Saving piwigo id %d for file with md5sum %s", piwigoId, md5Sum)
db, err := d.openDatabase()
if err != nil {
return err
}
defer db.Close()
tx, err := db.Begin()
if err != nil {
return err
}
2019-03-20 00:31:09 +01:00
uploadRequired := 1
if piwigoId > 0 {
uploadRequired = 0
}
stmt, err := tx.Prepare("UPDATE image SET piwigoId = ?, uploadRequired = ? WHERE md5sum = ?")
2019-03-19 23:07:55 +01:00
if err != nil {
return err
}
2019-03-20 00:31:09 +01:00
_, err = stmt.Exec(piwigoId, uploadRequired, md5Sum)
2019-03-19 23:07:55 +01:00
if err != nil {
return err
}
if err != nil {
logrus.Errorf("Rolling back transaction for piwigo id update of file %s", md5Sum)
errTx := tx.Rollback()
if errTx != nil {
logrus.Errorf("Rollback of transaction for piwigo id update of file %s failed!", md5Sum)
}
return err
}
logrus.Tracef("Commiting piwigo id %d for file with md5sum %s", piwigoId, md5Sum)
return tx.Commit()
}
func (d *localDataStore) insertImageMetaData(tx *sql.Tx, data ImageMetaData) error {
stmt, err := tx.Prepare("INSERT INTO image (piwigoId, fullImagePath, fileName, md5sum, lastChanged, categoryPath, categoryId, uploadRequired) VALUES (?,?,?,?,?,?,?,?)")
2019-03-15 00:35:49 +01:00
if err != nil {
return err
}
_, err = stmt.Exec(data.PiwigoId, data.FullImagePath, data.Filename, data.Md5Sum, data.LastChange, data.CategoryPath, data.CategoryId, data.UploadRequired)
2019-03-15 00:35:49 +01:00
return err
}
func (d *localDataStore) openDatabase() (*sql.DB, error) {
db, err := sql.Open("sqlite3", d.connectionString)
if err != nil {
logrus.Warnf("Could not open database %s", d.connectionString)
2019-03-15 00:35:49 +01:00
return nil, err
}
db.SetMaxOpenConns(1)
return db, err
}
func (d *localDataStore) createTablesIfNeeded(db *sql.DB) error {
_, err := db.Exec("CREATE TABLE IF NOT EXISTS image (" +
"imageId INTEGER PRIMARY KEY," +
2019-03-15 00:35:49 +01:00
"piwigoId INTEGER NULL," +
"fullImagePath NVARCHAR(1000) NOT NULL," +
2019-03-15 00:35:49 +01:00
"fileName NVARCHAR(255) NOT NULL," +
"md5sum NVARCHAR(50) NOT NULL," +
"lastChanged DATETIME NOT NULL," +
"categoryPath NVARCHAR(1000) NOT NULL," +
"categoryId INTEGER NULL," +
"uploadRequired BIT NOT NULL" +
2019-03-15 00:35:49 +01:00
");")
if err != nil {
return err
}
_, err = db.Exec("CREATE UNIQUE INDEX IF NOT EXISTS UX_ImageFullImagePath ON image (fullImagePath);")
return err
}
func (d *localDataStore) updateImageMetaData(tx *sql.Tx, data ImageMetaData) error {
stmt, err := tx.Prepare("UPDATE image SET piwigoId = ?, fullImagePath = ?, fileName = ?, md5sum = ?, lastChanged = ?, categoryPath = ?, categoryId = ?, uploadRequired = ? WHERE imageId = ?")
if err != nil {
return err
}
_, err = stmt.Exec(data.PiwigoId, data.FullImagePath, data.Filename, data.Md5Sum, data.LastChange, data.CategoryPath, data.CategoryId, data.UploadRequired, data.ImageId)
2019-03-15 00:35:49 +01:00
return err
}