github.com/nais/knorten@v0.0.0-20240104110906-55926958e361/pkg/database/repo.go (about)

     1  package database
     2  
     3  import (
     4  	"context"
     5  	"database/sql"
     6  	"embed"
     7  	"fmt"
     8  	"time"
     9  
    10  	"github.com/gin-contrib/sessions"
    11  	"github.com/gin-contrib/sessions/postgres"
    12  	"github.com/gin-gonic/gin"
    13  	"github.com/google/uuid"
    14  	_ "github.com/lib/pq"
    15  	"github.com/nais/knorten/pkg/database/crypto"
    16  	"github.com/nais/knorten/pkg/database/gensql"
    17  	"github.com/pressly/goose/v3"
    18  	"github.com/sirupsen/logrus"
    19  
    20  	// Pin version of sqlc cli
    21  	_ "github.com/kyleconroy/sqlc"
    22  )
    23  
    24  //go:embed migrations/*.sql
    25  var embedMigrations embed.FS
    26  
    27  type Repository interface {
    28  	EventSetStatus(context.Context, uuid.UUID, EventStatus) error
    29  	EventSetPendingStatus(context.Context, uuid.UUID) error
    30  	DispatchableEventsGet(context.Context) ([]gensql.Event, error)
    31  	EventLogCreate(context.Context, uuid.UUID, string, LogType) error
    32  }
    33  
    34  type Repo struct {
    35  	querier     Querier
    36  	db          *sql.DB
    37  	cryptClient *crypto.EncrypterDecrypter
    38  	log         *logrus.Entry
    39  }
    40  
    41  type Querier interface {
    42  	gensql.Querier
    43  	WithTx(tx *sql.Tx) *gensql.Queries
    44  }
    45  
    46  func New(dbConnDSN, cryptoKey string, log *logrus.Entry) (*Repo, error) {
    47  	db, err := sql.Open("postgres", dbConnDSN)
    48  	if err != nil {
    49  		return nil, fmt.Errorf("open sql connection: %w", err)
    50  	}
    51  
    52  	err = gooseMigrationWithRetries(log, db)
    53  	if err != nil {
    54  		return nil, fmt.Errorf("goose up: %w", err)
    55  	}
    56  
    57  	return &Repo{
    58  		querier:     gensql.New(db),
    59  		db:          db,
    60  		cryptClient: crypto.New(cryptoKey),
    61  		log:         log,
    62  	}, nil
    63  }
    64  
    65  func gooseMigrationWithRetries(log *logrus.Entry, db *sql.DB) error {
    66  	goose.SetLogger(log)
    67  	goose.SetBaseFS(embedMigrations)
    68  
    69  	err := goose.Up(db, "migrations")
    70  	if err != nil {
    71  		backoffSchedule := []time.Duration{
    72  			5 * time.Second,
    73  			15 * time.Second,
    74  			30 * time.Second,
    75  			60 * time.Second,
    76  		}
    77  
    78  		for _, duration := range backoffSchedule {
    79  			time.Sleep(duration)
    80  			err = goose.Up(db, "migrations")
    81  			if err == nil {
    82  				return nil
    83  			}
    84  			log.Infof("goose up failed: %v", err)
    85  		}
    86  	}
    87  
    88  	return err
    89  }
    90  
    91  func (r *Repo) NewSessionStore(key string) (gin.HandlerFunc, error) {
    92  	store, err := postgres.NewStore(r.db, []byte(key))
    93  	if err != nil {
    94  		return nil, err
    95  	}
    96  
    97  	return sessions.Sessions("session", store), nil
    98  }
    99  
   100  func (r *Repo) DecryptValue(encValue string) (string, error) {
   101  	return r.cryptClient.DecryptValue(encValue)
   102  }
   103  
   104  func (r *Repo) EncryptValue(encValue string) (string, error) {
   105  	return r.cryptClient.EncryptValue(encValue)
   106  }