mirror of
https://github.com/foomo/keel.git
synced 2025-10-16 12:35:34 +00:00
feat: add postgres persistance
This commit is contained in:
parent
8d4e9c6b14
commit
f96f3fab04
55
example/persistence/postgres/main.go
Normal file
55
example/persistence/postgres/main.go
Normal file
@ -0,0 +1,55 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
|
||||
"github.com/foomo/keel"
|
||||
"github.com/foomo/keel/example/persistence/postgres/repository"
|
||||
"github.com/foomo/keel/log"
|
||||
keelpostgres "github.com/foomo/keel/persistence/postgres"
|
||||
)
|
||||
|
||||
// docker run -it --rm -p 5432:5432 -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=postgres postgres:11-alpine
|
||||
func main() {
|
||||
svr := keel.NewServer()
|
||||
|
||||
// get the logger
|
||||
l := svr.Logger()
|
||||
|
||||
// create persistor
|
||||
persistor, err := keelpostgres.New(
|
||||
svr.Context(),
|
||||
"postgres://postgres:postgres@localhost:5432/postgres",
|
||||
keelpostgres.WithInit(`
|
||||
create table if not exists tasks (
|
||||
id serial primary key,
|
||||
description text not null
|
||||
);
|
||||
`),
|
||||
)
|
||||
// use log must helper to exit on error
|
||||
log.Must(l, err, "failed to create persistor")
|
||||
|
||||
// ensure to add the persistor to the closers
|
||||
svr.AddClosers(persistor)
|
||||
|
||||
repo := repository.NewTaskRepository(persistor.Conn())
|
||||
|
||||
if value, err := repo.List(svr.Context()); err != nil {
|
||||
l.Error(err.Error())
|
||||
} else {
|
||||
spew.Dump(value)
|
||||
}
|
||||
|
||||
if err := repo.Insert(svr.Context(), "one"); err != nil {
|
||||
l.Error(err.Error())
|
||||
}
|
||||
|
||||
if value, err := repo.List(svr.Context()); err != nil {
|
||||
l.Error(err.Error())
|
||||
} else {
|
||||
spew.Dump(value)
|
||||
}
|
||||
|
||||
svr.Run()
|
||||
}
|
||||
50
example/persistence/postgres/repository/dummy.go
Normal file
50
example/persistence/postgres/repository/dummy.go
Normal file
@ -0,0 +1,50 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v4"
|
||||
)
|
||||
|
||||
type TaskRepository struct {
|
||||
conn *pgx.Conn
|
||||
}
|
||||
|
||||
// NewTaskRepository constructor
|
||||
func NewTaskRepository(conn *pgx.Conn) *TaskRepository {
|
||||
return &TaskRepository{
|
||||
conn: conn,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *TaskRepository) List(ctx context.Context) (map[int32]string, error) {
|
||||
rows, err := r.conn.Query(ctx, "select * from tasks")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := map[int32]string{}
|
||||
for rows.Next() {
|
||||
var id int32
|
||||
var description string
|
||||
err := rows.Scan(&id, &description)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret[id] = description
|
||||
}
|
||||
|
||||
return ret, rows.Err()
|
||||
}
|
||||
|
||||
func (r *TaskRepository) Insert(ctx context.Context, description string) error {
|
||||
_, err := r.conn.Exec(context.Background(), "insert into tasks(description) values($1)", description)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *TaskRepository) Drop(ctx context.Context) error {
|
||||
if _, err := r.conn.Exec(ctx, `DROP TABLE IF EXISTS order_numbers;`); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
10
persistence/error.go
Normal file
10
persistence/error.go
Normal file
@ -0,0 +1,10 @@
|
||||
package keelpersistence
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNotFound = errors.New("not found error")
|
||||
ErrDirtyWrite = errors.New("dirty write error")
|
||||
)
|
||||
108
persistence/postgres/persistor.go
Normal file
108
persistence/postgres/persistor.go
Normal file
@ -0,0 +1,108 @@
|
||||
package keelpostgres
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v4"
|
||||
"github.com/jackc/pgx/v4/log/zapadapter"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/foomo/keel/log"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Persistor exported to used also for embedding into other types in foreign packages.
|
||||
type (
|
||||
Persistor struct {
|
||||
db *pgx.Conn
|
||||
l *zap.Logger
|
||||
}
|
||||
Options struct {
|
||||
Init string
|
||||
Logger *zap.Logger
|
||||
}
|
||||
Option func(*Options)
|
||||
)
|
||||
|
||||
func WithInit(v string) Option {
|
||||
return func(o *Options) {
|
||||
o.Init = v
|
||||
}
|
||||
}
|
||||
|
||||
func WithLogger(v *zap.Logger) Option {
|
||||
return func(o *Options) {
|
||||
o.Logger = v
|
||||
}
|
||||
}
|
||||
|
||||
func DefaultOptions() Options {
|
||||
return Options{
|
||||
Logger: log.Logger(),
|
||||
}
|
||||
}
|
||||
|
||||
func New(ctx context.Context, uri string, opts ...Option) (*Persistor, error) {
|
||||
// urlExample := "postgres://username:password@localhost:5432/database_name"
|
||||
|
||||
o := DefaultOptions()
|
||||
for _, opt := range opts {
|
||||
opt(&o)
|
||||
}
|
||||
|
||||
config, err := pgx.ParseConfig(uri)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to parse uri")
|
||||
}
|
||||
|
||||
config.Logger = zapadapter.NewLogger(o.Logger)
|
||||
// TODO @franklin performance config
|
||||
|
||||
db, err := pgx.ConnectConfig(ctx, config)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to connect")
|
||||
}
|
||||
|
||||
// TODO @franklin add telemetry
|
||||
|
||||
if err := db.Ping(ctx); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to ping database")
|
||||
}
|
||||
|
||||
p := &Persistor{
|
||||
db: db,
|
||||
l: o.Logger,
|
||||
}
|
||||
|
||||
// initialize
|
||||
if o.Init != "" {
|
||||
if _, err := p.db.Exec(ctx, o.Init); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func (p *Persistor) TableExists(ctx context.Context, name string) (bool, error) {
|
||||
var n int64
|
||||
if err := p.db.QueryRow(ctx, `select 1 from information_schema.tables where table_name=$1`, name).Scan(&n); errors.Is(err, pgx.ErrNoRows) {
|
||||
return false, nil
|
||||
} else if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (p *Persistor) Ping(ctx context.Context) error {
|
||||
return p.db.Ping(ctx)
|
||||
}
|
||||
|
||||
func (p *Persistor) Conn() *pgx.Conn {
|
||||
return p.db
|
||||
}
|
||||
|
||||
func (p *Persistor) Close(ctx context.Context) error {
|
||||
return p.db.Close(ctx)
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user