Merge pull request #22 from wchrisjohnson/remove-gorp
Remove the Gorp dependency
This commit is contained in:
commit
68b36d681a
|
@ -15,7 +15,7 @@ script:
|
||||||
- make check
|
- make check
|
||||||
|
|
||||||
env:
|
env:
|
||||||
- PGSTORE_TEST_CONN="postgres://postgres@127.0.0.1/test?sslmode=disable"
|
- GO15VENDOREXPERIMENT=1 PGSTORE_TEST_CONN="postgres://postgres@127.0.0.1/test?sslmode=disable"
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- psql -c 'create database test;' -U postgres
|
- psql -c 'create database test;' -U postgres
|
||||||
|
|
13
Makefile
13
Makefile
|
@ -1,3 +1,10 @@
|
||||||
|
UNAME := $(shell uname)
|
||||||
|
ifeq ($(UNAME), Darwin)
|
||||||
|
DHOST := $(shell echo $$(docker-machine ip))
|
||||||
|
else
|
||||||
|
DHOST := 127.0.0.1
|
||||||
|
endif
|
||||||
|
|
||||||
all: get-deps build
|
all: get-deps build
|
||||||
|
|
||||||
.PHONY: build
|
.PHONY: build
|
||||||
|
@ -27,9 +34,11 @@ fmt:
|
||||||
.PHONY: docker-test
|
.PHONY: docker-test
|
||||||
docker-test:
|
docker-test:
|
||||||
docker run -d -p 5432:5432 --name=pgstore_test_1 postgres:9.4
|
docker run -d -p 5432:5432 --name=pgstore_test_1 postgres:9.4
|
||||||
sleep 5
|
@echo "Ugly hack: Sleeping for 75 secs to give the Postgres container time to come up..."
|
||||||
|
sleep 75
|
||||||
|
@echo "Waking up - let's do this!"
|
||||||
docker run --rm --link pgstore_test_1:postgres postgres:9.4 psql -c 'create database test;' -U postgres -h postgres
|
docker run --rm --link pgstore_test_1:postgres postgres:9.4 psql -c 'create database test;' -U postgres -h postgres
|
||||||
PGSTORE_TEST_CONN="postgres://postgres@127.0.0.1:5432/test?sslmode=disable" make test
|
PGSTORE_TEST_CONN="postgres://postgres@$(DHOST):5432/test?sslmode=disable" make test
|
||||||
docker kill pgstore_test_1
|
docker kill pgstore_test_1
|
||||||
docker rm pgstore_test_1
|
docker rm pgstore_test_1
|
||||||
|
|
||||||
|
|
|
@ -74,8 +74,6 @@ I've stolen, borrowed and gotten inspiration from the other backends available:
|
||||||
|
|
||||||
Thank you all for sharing your code!
|
Thank you all for sharing your code!
|
||||||
|
|
||||||
What makes this backend different is that it's for Postgresql and uses the fine
|
What makes this backend different is that it's for PostgreSQL.
|
||||||
datamapper [Gorp](https://github.com/coopernurse/gorp).
|
|
||||||
Make sure you use a somewhat new codebase of Gorp as it now defaults to text for
|
We've recently refactored this backend to use the standard database/sql driver instead of Gorp. This removes a dependency and makes this package very lightweight and makes database interactions very transparent. Lastly, from the standpoint of unit testing where you want to mock the database layer instead of requiring a real database, you can now easily use a package like [go-SQLMock](https://github.com/DATA-DOG/go-sqlmock) to do just that.
|
||||||
strings when it used to default to varchar 255. Varchar 255 is unfortunately too
|
|
||||||
small.
|
|
||||||
|
|
|
@ -53,6 +53,6 @@ func (db *PGStore) cleanup(interval time.Duration, quit <-chan struct{}, done ch
|
||||||
|
|
||||||
// deleteExpired deletes expired sessions from the database.
|
// deleteExpired deletes expired sessions from the database.
|
||||||
func (db *PGStore) deleteExpired() error {
|
func (db *PGStore) deleteExpired() error {
|
||||||
_, err := db.DbMap.Exec("DELETE FROM http_sessions WHERE expires_on < now()")
|
_, err := db.DbPool.Exec("DELETE FROM http_sessions WHERE expires_on < now()")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,14 +43,14 @@ func TestCleanup(t *testing.T) {
|
||||||
// Give the ticker a moment to run.
|
// Give the ticker a moment to run.
|
||||||
time.Sleep(time.Millisecond * 1500)
|
time.Sleep(time.Millisecond * 1500)
|
||||||
|
|
||||||
// SELECT expired sessions. We should get a zero-length result slice back.
|
// SELECT expired sessions. We should get a count of zero back.
|
||||||
var results []int64
|
var count int
|
||||||
_, err = ss.DbMap.Select(&results, "SELECT id FROM http_sessions WHERE expires_on < now()")
|
err = ss.DbPool.QueryRow("SELECT count(*) FROM http_sessions WHERE expires_on < now()").Scan(&count)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to select expired sessions from DB: %v", err)
|
t.Fatalf("failed to select expired sessions from DB: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(results) > 0 {
|
if count > 0 {
|
||||||
t.Fatalf("ticker did not delete expired sessions: want 0 got %v", len(results))
|
t.Fatalf("ticker did not delete expired sessions: want 0 got %v", count)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
96
pgstore.go
96
pgstore.go
|
@ -3,11 +3,12 @@ package pgstore
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"encoding/base32"
|
"encoding/base32"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/coopernurse/gorp"
|
|
||||||
"github.com/gorilla/securecookie"
|
"github.com/gorilla/securecookie"
|
||||||
"github.com/gorilla/sessions"
|
"github.com/gorilla/sessions"
|
||||||
|
|
||||||
|
@ -19,18 +20,18 @@ import (
|
||||||
type PGStore struct {
|
type PGStore struct {
|
||||||
Codecs []securecookie.Codec
|
Codecs []securecookie.Codec
|
||||||
Options *sessions.Options
|
Options *sessions.Options
|
||||||
path string
|
Path string
|
||||||
DbMap *gorp.DbMap
|
DbPool *sql.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
// Session type
|
// PGSession type
|
||||||
type Session struct {
|
type PGSession struct {
|
||||||
Id int64 `db:"id"`
|
ID int64
|
||||||
Key string `db:"key"`
|
Key string
|
||||||
Data string `db:"data"`
|
Data string
|
||||||
CreatedOn time.Time `db:"created_on"`
|
CreatedOn time.Time
|
||||||
ModifiedOn time.Time `db:"modified_on"`
|
ModifiedOn time.Time
|
||||||
ExpiresOn time.Time `db:"expires_on"`
|
ExpiresOn time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPGStore creates a new PGStore instance and a new database/sql pool.
|
// NewPGStore creates a new PGStore instance and a new database/sql pool.
|
||||||
|
@ -38,7 +39,7 @@ type Session struct {
|
||||||
func NewPGStore(dbURL string, keyPairs ...[]byte) (*PGStore, error) {
|
func NewPGStore(dbURL string, keyPairs ...[]byte) (*PGStore, error) {
|
||||||
db, err := sql.Open("postgres", dbURL)
|
db, err := sql.Open("postgres", dbURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Ignore PGStore value and return nil.
|
// Ignore and return nil.
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return NewPGStoreFromPool(db, keyPairs...)
|
return NewPGStoreFromPool(db, keyPairs...)
|
||||||
|
@ -46,25 +47,20 @@ func NewPGStore(dbURL string, keyPairs ...[]byte) (*PGStore, error) {
|
||||||
|
|
||||||
// NewPGStoreFromPool creates a new PGStore instance from an existing
|
// NewPGStoreFromPool creates a new PGStore instance from an existing
|
||||||
// database/sql pool.
|
// database/sql pool.
|
||||||
// This will also create in the database the schema needed by pgstore.
|
// This will also create the database schema needed by pgstore.
|
||||||
func NewPGStoreFromPool(db *sql.DB, keyPairs ...[]byte) (*PGStore, error) {
|
func NewPGStoreFromPool(db *sql.DB, keyPairs ...[]byte) (*PGStore, error) {
|
||||||
dbmap := &gorp.DbMap{Db: db, Dialect: gorp.PostgresDialect{}}
|
|
||||||
|
|
||||||
dbStore := &PGStore{
|
dbStore := &PGStore{
|
||||||
Codecs: securecookie.CodecsFromPairs(keyPairs...),
|
Codecs: securecookie.CodecsFromPairs(keyPairs...),
|
||||||
Options: &sessions.Options{
|
Options: &sessions.Options{
|
||||||
Path: "/",
|
Path: "/",
|
||||||
MaxAge: 86400 * 30,
|
MaxAge: 86400 * 30,
|
||||||
},
|
},
|
||||||
DbMap: dbmap,
|
DbPool: db,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create table if it doesn't exist
|
// Create table if it doesn't exist
|
||||||
dbmap.AddTableWithName(Session{}, "http_sessions").SetKeys(true, "Id")
|
err := dbStore.createSessionsTable()
|
||||||
err := dbmap.CreateTablesIfNotExists()
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Ignore PGStore value and return nil.
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,7 +69,7 @@ func NewPGStoreFromPool(db *sql.DB, keyPairs ...[]byte) (*PGStore, error) {
|
||||||
|
|
||||||
// Close closes the database connection.
|
// Close closes the database connection.
|
||||||
func (db *PGStore) Close() {
|
func (db *PGStore) Close() {
|
||||||
db.DbMap.Db.Close()
|
db.DbPool.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get Fetches a session for a given name after it has been added to the
|
// Get Fetches a session for a given name after it has been added to the
|
||||||
|
@ -171,8 +167,9 @@ func (db *PGStore) MaxAge(age int) {
|
||||||
// load fetches a session by ID from the database and decodes its content
|
// load fetches a session by ID from the database and decodes its content
|
||||||
// into session.Values.
|
// into session.Values.
|
||||||
func (db *PGStore) load(session *sessions.Session) error {
|
func (db *PGStore) load(session *sessions.Session) error {
|
||||||
var s Session
|
var s PGSession
|
||||||
err := db.DbMap.SelectOne(&s, "SELECT * FROM http_sessions WHERE key = $1", session.ID)
|
|
||||||
|
err := db.selectOne(&s, session.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -207,7 +204,7 @@ func (db *PGStore) save(session *sessions.Session) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s := Session{
|
s := PGSession{
|
||||||
Key: session.ID,
|
Key: session.ID,
|
||||||
Data: encoded,
|
Data: encoded,
|
||||||
CreatedOn: createdOn,
|
CreatedOn: createdOn,
|
||||||
|
@ -216,15 +213,58 @@ func (db *PGStore) save(session *sessions.Session) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if session.IsNew {
|
if session.IsNew {
|
||||||
return db.DbMap.Insert(&s)
|
return db.insert(&s)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = db.DbMap.Exec("update http_sessions set data=$1, modified_on=$2, expires_on=$3 where key=$4", s.Data, s.ModifiedOn, s.ExpiresOn, s.Key)
|
return db.update(&s)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete session
|
// Delete session
|
||||||
func (db *PGStore) destroy(session *sessions.Session) error {
|
func (db *PGStore) destroy(session *sessions.Session) error {
|
||||||
_, err := db.DbMap.Db.Exec("DELETE FROM http_sessions WHERE key = $1", session.ID)
|
_, err := db.DbPool.Exec("DELETE FROM http_sessions WHERE key = $1", session.ID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *PGStore) createSessionsTable() error {
|
||||||
|
stmt := `CREATE TABLE IF NOT EXISTS http_sessions (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
key BYTEA,
|
||||||
|
data BYTEA,
|
||||||
|
created_on TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
modified_on TIMESTAMPTZ,
|
||||||
|
expires_on TIMESTAMPTZ);`
|
||||||
|
|
||||||
|
_, err := db.DbPool.Exec(stmt)
|
||||||
|
if err != nil {
|
||||||
|
msg := fmt.Sprintf("Unable to create http_sessions table in the database: %s\n", err.Error())
|
||||||
|
return errors.New(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *PGStore) selectOne(s *PGSession, key string) error {
|
||||||
|
stmt := "SELECT id, key, data, created_on, modified_on, expires_on FROM http_sessions WHERE key = $1"
|
||||||
|
err := db.DbPool.QueryRow(stmt, key).Scan(&s.ID, &s.Key, &s.Data, &s.CreatedOn, &s.ModifiedOn, &s.ExpiresOn)
|
||||||
|
if err != nil {
|
||||||
|
msg := fmt.Sprintf("Unable to find session in the database: %s\n", err.Error())
|
||||||
|
return errors.New(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *PGStore) insert(s *PGSession) error {
|
||||||
|
stmt := `INSERT INTO http_sessions (key, data, created_on, modified_on, expires_on)
|
||||||
|
VALUES ($1, $2, $3, $4, $5)`
|
||||||
|
_, err := db.DbPool.Exec(stmt, s.Key, s.Data, s.CreatedOn, s.ModifiedOn, s.ExpiresOn)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *PGStore) update(s *PGSession) error {
|
||||||
|
stmt := `UPDATE http_sessions SET data=$1, modified_on=$2, expires_on=$3 WHERE key=$4`
|
||||||
|
_, err := db.DbPool.Exec(stmt, s.Data, s.ModifiedOn, s.ExpiresOn, s.Key)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue