diff --git a/README.md b/README.md index eeb3476..ae64654 100644 --- a/README.md +++ b/README.md @@ -9,39 +9,41 @@ Most session libraries are highly opinionated and hard-wired to work with `net/h ## Features 1. Framework/network library agnostic. 2. Simple API and with support for primitive data types. Complex types can be stored using own encoding/decoding. -3. Pre-built redis/postgres/in-memory stores that can be separately installed. +3. Pre-built redis/postgres/in-memory/securecookie stores that can be separately installed. 4. Multiple session instances with custom handlers and different backend stores. ## Installation Install `simplesessions` and all [available stores](/stores). ```shell -go get -u github.com/vividvilla/simplesessions +go get -u github.com/vividvilla/simplesessions/v3 -# Install the requrired store: memory|goredis|redis|postgres -go get -u github.com/vividvilla/simplesessions/stores/goredis +# Install the requrired store: memory|redis|postgres|securecookie +go get -u github.com/vividvilla/simplesessions/v3/stores/redis +go get -u github.com/vividvilla/simplesessions/v3/stores/postgres ``` # Stores Sessions can be stored to any backend by implementing the [store](/store.go) interface. The following stores are bundled. -* [in-memory](/stores/memory) * [redis](/stores/redis) +* [postgres](/stores/postgres) +* [in-memory](/stores/memory) * [secure cookie](/stores/securecookie) # Usage Check the [examples](/examples) directory for complete examples. ## Connecting a store -Stores can be registered to a session instance by using `Use` method. +Stores can be registered to a session instance by using `Use` method. Check individual [Stores](#stores) docs for more details. ```go sess := simplesessions.New(simplesessions.Options{}) -sess.UseStore(memory.New()) +sess.UseStore(store.New()) ``` ## Connecting an HTTP handler -Any HTTP library can be connected to simplesessions by registering the `RegisterGetCookie()` and `RegisterSetCookie()` callbacks. The below example shows a simple `net/http` usecase. Another example showing `fasthttp` can be found [here](/examples). +Any HTTP library can be connected to simplesessions by registering the get and set cookie hooks using `SetCookieHooks()`. The below example shows a simple `net/http` usecase. Another example showing `fasthttp` can be found [here](/examples). ```go var sessMan *simplesessions.Manager @@ -81,44 +83,58 @@ func setCookie(cookie *http.Cookie, w interface{}) error { func handler(w http.ResponseWriter, r *http.Request) { // Use method `Acquire` to acquire a session before you access the session. // Acquire takes read, write interface and context respectively. - // Read interface sent to callback registered with `RegisterGetCookie` - // and write interface is sent to callback registered with `RegisterWriteCookie` + // Read interface sent to callback registered with get cookie hook + // and write interface is sent to callback registered with write cookie hook + // set using `SetCookieHooks()` method. + // // Optionally `context` can be sent which is usually request context where acquire // session will get previously loaded session. This is useful if you have multiple // middlewares accessing sessions. New sessions will be created in first middleware which // does `Acquire` and will be reused in other places. - sess, err := sessMan.Acquire(r, w, nil) - - // Use 'Set` and `Commit` to set a field for session. - // 'Set` ideally doesn't persist the value to store unless method `Commit` is called. - // But note that its up to the store you are using to decide to - // persist data only on `commit` or persist on `Set` itself. - // Stores like redis, db etc should persist on `Commit` while in-memory does on `Set`. - // No matter what store you use its better to explicitly - // call `Commit` method when you set all the values. + // + // If `Options.EnableAutoCreate` is set to True then if session doesn't exist it will + // be immediately created and returned. Bydefault its set to False so if session doesn't + // exist then `ErrInvalidSession` error is returned. + sess, err := sessMan.Acquire(nil, r, w) + + // If session doesn't exist then create new session. + // In a traditional login flow you can create a new session once user completes the login flow. + if err == simplesessions.ErrInvalidSession { + sess, err = sessMan.NewSession(r, w) + } + + // Use 'Set` or `SetMulti` to set a field for session. err = sess.Set("somekey", "somevalue") - err = sess.Set("someotherkey", 10) - err = sess.Commit() + err = sess.SetMulti(map[string]interface{}{ + "k1": "v1", + "k2": "v2", + }) // Use `Get` method to get a field from current session. The result will be an interface // so you can use helper methods like // `String', `Int`, `Int64`, `UInt64`, `Float64`, `Bytes`, `Bool`. val, err := sess.String(sess.Get("somekey")) + fmt.Println("val=", val) // Use `GetAll` to get map of all fields from session. // The result is map of string and interface you can use helper methods to type cast it. - val, err := sess.GetAll() + all, err := sess.GetAll() + fmt.Println("all=", all) // Use `GetMulti` to get values for given fields from session. // The result is map of string and interface you can use helper methods to type cast it. // If key is not there then store should ideally send `nil` value for given key. - val, err := sess.GetMulti("somekey", "someotherkey") + vals, err := sess.GetMulti("somekey", "someotherkey") + fmt.Println("vals=", vals) // Use `Delete` to delete a field from session. - err := sess.Delete("somekey") + err = sess.Delete("somekey") + + // Use `Clear` to empty the session but to keep the session alive. + err = sess.Clear() - // Use `Clear` to clear session from store. - err := sess.Clear() + // Use `Destroy` to clear session from store and cookie. + err = sess.Destroy() fmt.Fprintf(w, "success") } @@ -126,16 +142,45 @@ func handler(w http.ResponseWriter, r *http.Request) { func main() { // Create a session manager with custom options like cookie name, // cookie domain, is secure cookie etc. Check `Options` struct for more options. - sessMan := simplesessions.New(simplesessions.Options{}) + sessMan := simplesessions.New(simplesessions.Options{ + // If set to true then `Acquire()` method will create new session instead of throwing + // `ErrInvalidSession` when the session doesn't exist. By default its set to false. + EnableAutoCreate: false, + Cookie: simplesessions.CookieOptions{ + // Name sets http cookie name. This is also sent as cookie name in `GetCookie` callback. + Name: "session", + // Domain sets hostname for the cookie. Domain specifies allowed hosts to receive the cookie. + Domain: "example.com", + // Path sets path for the cookie. Path indicates a URL path that must exist in the requested URL in order to send the cookie header. + Path: "/", + // IsSecure marks the cookie as secure cookie (only sent in HTTPS). + IsSecure: true, + // IsHTTPOnly marks the cookie as http only cookie. JS won't be able to access the cookie so prevents XSS attacks. + IsHTTPOnly: true, + // SameSite sets allows you to declare if your cookie should be restricted to a first-party or same-site context. + SameSite: http.SameSiteDefaultMode, + // Expires sets absolute expiration date and time for the cookie. + // If both Expires and MaxAge are sent then MaxAge takes precedence over Expires. + // Cookies without a Max-age or Expires attribute – are deleted when the current session ends + // and some browsers use session restoring when restarting. This can cause session cookies to last indefinitely. + Expires: time.Now().Add(time.Hour * 24), + // Sets the cookie's expiration in seconds from the current time, internally its rounder off to nearest seconds. + // If both Expires and MaxAge are sent then MaxAge takes precedence over Expires. + // Cookies without a Max-age or Expires attribute – are deleted when the current session ends + // and some browsers use session restoring when restarting. This can cause session cookies to last indefinitely. + MaxAge: time.Hour * 24, + }, + }) + // Create a new store instance and attach to session manager sessMan.UseStore(memory.New()) - // Register callbacks for read and write cookie + // Register callbacks for read and write cookie. // Get cookie callback should get cookie based on cookie name and // sent back in net/http cookie format. - sessMan.RegisterGetCookie(getCookie) // Set cookie callback should set cookie it received for received cookie name. - sessMan.RegisterSetCookie(setCookie) + sessMan.SetCookieHooks(getCookie, setCookie) - http.HandleFunc("/set", handler) + // Initialize the handler. + http.HandleFunc("/", handler) } ``` diff --git a/TODO b/TODO deleted file mode 100644 index 8a93ec3..0000000 --- a/TODO +++ /dev/null @@ -1,14 +0,0 @@ -[x] Redis store -[x] Helper methods for type assertion -[x] Provision in store to call `SetCookie` -[x] Tests for session manager -[x] Tests for session -[x] Tests for Redis store -[x] In-memory store -[x] Tests for in-memory store -[x] Net http example -[x] Delete method for deleting individual field in session -[x] Fasthttp examples -[x] Secure cookie store with optional encoding and decoding -[x] Tests for Secure cookie store -[ ] Benchmark comparing with gosessions, gorilla sessions and scs diff --git a/conv/conv.go b/conv/conv.go deleted file mode 100644 index 6d46369..0000000 --- a/conv/conv.go +++ /dev/null @@ -1,200 +0,0 @@ -// Package conv to help type assertions and conversions. -package conv - -import ( - "strconv" -) - -var ( - // Error codes for store errors. This should match the codes - // defined in the /simplesessions package exactly. - ErrInvalidSession = &Err{code: 1, msg: "invalid session"} - ErrNil = &Err{code: 2, msg: "nil returned"} - ErrAssertType = &Err{code: 3, msg: "assertion failed"} -) - -type Err struct { - code int - msg string -} - -func (e *Err) Error() string { - return e.msg -} - -func (e *Err) Code() int { - return e.code -} - -// Int converts interface to integer. -func Int(r interface{}, err error) (int, error) { - if err != nil { - return 0, err - } - - switch r := r.(type) { - case int: - return r, nil - case int64: - x := int(r) - if int64(x) != r { - return 0, strconv.ErrRange - } - return x, nil - case []byte: - n, err := strconv.ParseInt(string(r), 10, 0) - return int(n), err - case string: - n, err := strconv.ParseInt(r, 10, 0) - return int(n), err - case nil: - return 0, ErrNil - case error: - return 0, r - } - - return 0, ErrAssertType -} - -// Int64 converts interface to Int64. -func Int64(r interface{}, err error) (int64, error) { - if err != nil { - return 0, err - } - - switch r := r.(type) { - case int: - return int64(r), nil - case int64: - return r, nil - case []byte: - n, err := strconv.ParseInt(string(r), 10, 64) - return n, err - case string: - n, err := strconv.ParseInt(r, 10, 64) - return n, err - case nil: - return 0, ErrNil - case error: - return 0, r - } - - return 0, ErrAssertType -} - -// UInt64 converts interface to UInt64. -func UInt64(r interface{}, err error) (uint64, error) { - if err != nil { - return 0, err - } - - switch r := r.(type) { - case uint64: - return r, err - case int: - if r < 0 { - return 0, ErrAssertType - } - return uint64(r), nil - case int64: - if r < 0 { - return 0, ErrAssertType - } - return uint64(r), nil - case []byte: - n, err := strconv.ParseUint(string(r), 10, 64) - return n, err - case string: - n, err := strconv.ParseUint(r, 10, 64) - return n, err - case nil: - return 0, ErrNil - case error: - return 0, r - } - - return 0, ErrAssertType -} - -// Float64 converts interface to Float64. -func Float64(r interface{}, err error) (float64, error) { - if err != nil { - return 0, err - } - switch r := r.(type) { - case float64: - return r, err - case []byte: - n, err := strconv.ParseFloat(string(r), 64) - return n, err - case string: - n, err := strconv.ParseFloat(r, 64) - return n, err - case nil: - return 0, ErrNil - case error: - return 0, r - } - return 0, ErrAssertType -} - -// String converts interface to String. -func String(r interface{}, err error) (string, error) { - if err != nil { - return "", err - } - switch r := r.(type) { - case []byte: - return string(r), nil - case string: - return r, nil - case nil: - return "", ErrNil - case error: - return "", r - } - return "", ErrAssertType -} - -// Bytes converts interface to Bytes. -func Bytes(r interface{}, err error) ([]byte, error) { - if err != nil { - return nil, err - } - switch r := r.(type) { - case []byte: - return r, nil - case string: - return []byte(r), nil - case nil: - return nil, ErrNil - case error: - return nil, r - } - return nil, ErrAssertType -} - -// Bool converts interface to Bool. -func Bool(r interface{}, err error) (bool, error) { - if err != nil { - return false, err - } - switch r := r.(type) { - case bool: - return r, err - // Very common in redis to reply int64 with 0 for bool flag. - case int: - return r != 0, nil - case int64: - return r != 0, nil - case []byte: - return strconv.ParseBool(string(r)) - case string: - return strconv.ParseBool(r) - case nil: - return false, ErrNil - case error: - return false, r - } - return false, ErrAssertType -} diff --git a/conv/conv_test.go b/conv/conv_test.go deleted file mode 100644 index 2ce0a9b..0000000 --- a/conv/conv_test.go +++ /dev/null @@ -1,284 +0,0 @@ -package conv - -import ( - "errors" - "testing" - - "github.com/stretchr/testify/assert" -) - -var ( - errTest = errors.New("test error") -) - -func TestInt(t *testing.T) { - assert := assert.New(t) - - v, err := Int(1, nil) - assert.NoError(err) - assert.Equal(1, v) - - v, err = Int("1", nil) - assert.NoError(err) - assert.Equal(1, v) - - v, err = Int([]byte("1"), nil) - assert.NoError(err) - assert.Equal(1, v) - - var tVal int64 = 1 - v, err = Int(tVal, nil) - assert.NoError(err) - assert.Equal(1, v) - - var tVal1 interface{} = 1 - v, err = Int(tVal1, nil) - assert.NoError(err) - assert.Equal(1, v) - - // Test if ErrNil is returned if value is nil. - v, err = Int(nil, nil) - assert.Error(err, ErrNil) - assert.Equal(0, v) - - // Test if custom error sent is returned. - v, err = Int(nil, errTest) - assert.Error(err, errTest) - assert.Equal(0, v) - - // Test invalid assert error. - v, err = Int(10.1112, nil) - assert.Error(err, ErrAssertType) - assert.Equal(0, v) -} - -func TestInt64(t *testing.T) { - assert := assert.New(t) - - v, err := Int64(int64(1), nil) - assert.NoError(err) - assert.Equal(int64(1), v) - - v, err = Int64("1", nil) - assert.NoError(err) - assert.Equal(int64(1), v) - - v, err = Int64([]byte("1"), nil) - assert.NoError(err) - assert.Equal(int64(1), v) - - var tVal interface{} = 1 - v, err = Int64(tVal, nil) - assert.NoError(err) - assert.Equal(int64(1), v) - - // Test if ErrNil is returned if value is nil. - v, err = Int64(nil, nil) - assert.Error(err, ErrNil) - assert.Equal(int64(0), v) - - // Test if custom error sent is returned. - v, err = Int64(nil, errTest) - assert.Error(err, errTest) - assert.Equal(int64(0), v) - - // Test invalid assert error. - v, err = Int64(10.1112, nil) - assert.Error(err, ErrAssertType) - assert.Equal(int64(0), v) -} - -func TestUInt64(t *testing.T) { - assert := assert.New(t) - - v, err := UInt64(uint64(1), nil) - assert.NoError(err) - assert.Equal(uint64(1), v) - - v, err = UInt64("1", nil) - assert.NoError(err) - assert.Equal(uint64(1), v) - - v, err = UInt64([]byte("1"), nil) - assert.NoError(err) - assert.Equal(uint64(1), v) - - var tVal interface{} = 1 - v, err = UInt64(tVal, nil) - assert.NoError(err) - assert.Equal(uint64(1), v) - - // Test if ErrNil is returned if value is nil. - v, err = UInt64(nil, nil) - assert.Error(err, ErrNil) - assert.Equal(uint64(0), v) - - // Test if custom error sent is returned. - v, err = UInt64(nil, errTest) - assert.Error(err, errTest) - assert.Equal(uint64(0), v) - - // Test invalid assert error. - v, err = UInt64(10.1112, nil) - assert.Error(err, ErrAssertType) - assert.Equal(uint64(0), v) -} - -func TestFloat64(t *testing.T) { - assert := assert.New(t) - - v, err := Float64(float64(1.11), nil) - assert.NoError(err) - assert.Equal(float64(1.11), v) - - v, err = Float64("1.11", nil) - assert.NoError(err) - assert.Equal(float64(1.11), v) - - v, err = Float64([]byte("1.11"), nil) - assert.NoError(err) - assert.Equal(float64(1.11), v) - - var tVal float64 = 1.11 - v, err = Float64(tVal, nil) - assert.NoError(err) - assert.Equal(float64(1.11), v) - - // Test if ErrNil is returned if value is nil. - v, err = Float64(nil, nil) - assert.Error(err, ErrNil) - assert.Equal(float64(0), v) - - // Test if custom error sent is returned. - v, err = Float64(nil, errTest) - assert.Error(err, errTest) - assert.Equal(float64(0), v) - - // Test invalid assert error. - v, err = Float64("abc", nil) - assert.Error(err, ErrAssertType) - assert.Equal(float64(0), v) -} - -func TestString(t *testing.T) { - assert := assert.New(t) - - v, err := String("abc", nil) - assert.NoError(err) - assert.Equal("abc", v) - - v, err = String([]byte("abc"), nil) - assert.NoError(err) - assert.Equal("abc", v) - - var tVal interface{} = "abc" - v, err = String(tVal, nil) - assert.NoError(err) - assert.Equal("abc", v) - - // Test if ErrNil is returned if value is nil. - v, err = String(nil, nil) - assert.Error(err, ErrNil) - assert.Equal("", v) - - // Test if custom error sent is returned. - v, err = String(nil, errTest) - assert.Error(err, errTest) - assert.Equal("", v) - - // Test invalid assert error. - v, err = String(10.1112, nil) - assert.Error(err, ErrAssertType) - assert.Equal("", v) -} - -func TestBytes(t *testing.T) { - assert := assert.New(t) - - v, err := Bytes("abc", nil) - assert.NoError(err) - assert.Equal([]byte("abc"), v) - - v, err = Bytes([]byte("abc"), nil) - assert.NoError(err) - assert.Equal([]byte("abc"), v) - - var tVal interface{} = "abc" - v, err = Bytes(tVal, nil) - assert.NoError(err) - assert.Equal([]byte("abc"), v) - - // Test if ErrNil is returned if value is nil. - v, err = Bytes(nil, nil) - assert.Error(err, ErrNil) - assert.Equal([]byte(nil), v) - - // Test if custom error sent is returned. - v, err = Bytes(nil, errTest) - assert.Error(err, errTest) - assert.Equal([]byte(nil), v) - - // Test invalid assert error. - v, err = Bytes(10.1112, nil) - assert.Error(err, ErrAssertType) - assert.Equal([]byte(nil), v) -} - -func TestBool(t *testing.T) { - assert := assert.New(t) - - v, err := Bool(true, nil) - assert.NoError(err) - assert.Equal(true, v) - - v, err = Bool(false, nil) - assert.NoError(err) - assert.Equal(false, v) - - v, err = Bool(0, nil) - assert.NoError(err) - assert.Equal(false, v) - - v, err = Bool(1, nil) - assert.NoError(err) - assert.Equal(true, v) - - v, err = Bool(int64(0), nil) - assert.NoError(err) - assert.Equal(false, v) - - v, err = Bool(int64(1), nil) - assert.NoError(err) - assert.Equal(true, v) - - v, err = Bool([]byte("true"), nil) - assert.NoError(err) - assert.Equal(true, v) - - v, err = Bool([]byte("false"), nil) - assert.NoError(err) - assert.Equal(false, v) - - v, err = Bool("true", nil) - assert.NoError(err) - assert.Equal(true, v) - - v, err = Bool("false", nil) - assert.NoError(err) - assert.Equal(false, v) - - // Test if ErrNil is returned if value is nil. - v, err = Bool(nil, nil) - assert.Error(err, ErrNil) - assert.Equal(false, v) - - // Test if custom error sent is returned. - v, err = Bool(nil, errTest) - assert.Error(err, errTest) - assert.Equal(false, v) - - // Test invalid assert error. - v, err = Bool(10.1112, nil) - assert.Error(err, ErrAssertType) - assert.Equal(false, v) -} diff --git a/conv/go.mod b/conv/go.mod deleted file mode 100644 index cab6c20..0000000 --- a/conv/go.mod +++ /dev/null @@ -1,5 +0,0 @@ -module github.com/vividvilla/simplesessions/conv - -go 1.14 - -require github.com/stretchr/testify v1.9.0 diff --git a/go.work b/go.work index fbc2fb4..e220ee0 100644 --- a/go.work +++ b/go.work @@ -2,7 +2,6 @@ go 1.14 use ( . - ./conv ./stores/memory ./stores/redis ./stores/securecookie diff --git a/manager.go b/manager.go index a6bf335..dd18f24 100644 --- a/manager.go +++ b/manager.go @@ -9,6 +9,7 @@ import ( "unicode" ) +// Context name type. type ctxNameType string const ( @@ -46,7 +47,7 @@ type Manager struct { validateID func(string) bool } -// Options are available options to configure Manager. +// Options to configure manager and cookie. type Options struct { // If enabled, Acquire() will always create and return a new session if one doesn't already exist. // If disabled then new session can only be created using NewSession() method. @@ -187,8 +188,10 @@ func (m *Manager) NewSession(r, w interface{}) (*Session, error) { } // Acquire retrieves a `Session` from the store using the current session cookie. -// If not found and `opt.EnableAutoCreate` is true, a new session is created and stored. -// If not found and `opt.EnableAutoCreate` is false which is the default, it returns ErrInvalidSession. +// +// If session not found and `opt.EnableAutoCreate` is true, a new session is created and returned. +// If session not found and `opt.EnableAutoCreate` is false which is the default, it returns `ErrInvalidSession`. +// // `r` and `w` are request and response interfaces which is passed back in in GetCookie and SetCookie callbacks. // Optionally, a context can be passed to get an already loaded session, useful in middleware chains. func (m *Manager) Acquire(c context.Context, r, w interface{}) (*Session, error) { diff --git a/session.go b/session.go index c5e899f..d6decfb 100644 --- a/session.go +++ b/session.go @@ -7,10 +7,11 @@ import ( "time" ) -// Session provides the object to get, set, or clear session data. +// Session represents a session object used for retrieving/setting session data and cookies. type Session struct { // Map to store session data, loaded using `CacheAll` method. - // All `Get` methods checks here before fetching from the store. + // All `Get` methods tries to retrive cached value before fetching from the store. + // If its nil then cache is not set and `Get` methods directly fetch from the store. cache map[string]interface{} cacheMux sync.RWMutex @@ -20,8 +21,7 @@ type Session struct { // Session ID. id string - // HTTP reader and writer interfaces which are passed on to - // `GetCookie`` and `SetCookie`` callbacks. + // HTTP reader and writer interfaces which are passed on to `GetCookie`` and `SetCookie`` callbacks. reader interface{} writer interface{} } @@ -45,8 +45,8 @@ type errCode interface { Code() int } -// WriteCookie creates a cookie with the given session ID and parameters, -// then calls the `SetCookie` callback. This can be used to update the cookie externally. +// WriteCookie writes the cookie for the given session ID. +// Uses all the cookie options set in Manager. func (s *Session) WriteCookie(id string) error { ck := &http.Cookie{ Value: id, @@ -64,8 +64,8 @@ func (s *Session) WriteCookie(id string) error { return s.manager.setCookieHook(ck, s.writer) } -// clearCookie sets the cookie's expiry to one day prior to clear it. -func (s *Session) clearCookie() error { +// ClearCookie sets the cookie's expiry to one day prior to clear it. +func (s *Session) ClearCookie() error { ck := &http.Cookie{ Name: s.manager.opts.Cookie.Name, Value: "", @@ -100,7 +100,7 @@ func (s *Session) getCacheAll() map[string]interface{} { } // getCacheAll returns a map of values for the given list of keys. -// If key doesn't exist then ErrFieldNotFound is returned. +// If key doesn't exist then Nil is returned. func (s *Session) getCache(key ...string) map[string]interface{} { s.cacheMux.RLock() defer s.cacheMux.RUnlock() @@ -241,7 +241,7 @@ func (s *Session) SetMulti(data map[string]interface{}) error { return errAs(err) } -// Delete deletes a field from session. +// Delete deletes a given list of fields from the session. func (s *Session) Delete(key ...string) error { err := s.manager.store.Delete(s.id, key...) if err == nil { @@ -250,8 +250,8 @@ func (s *Session) Delete(key ...string) error { return errAs(err) } -// Clear empties the data for the given session id. -// Use `Destroy()` to delete entire session from store and clear the cookie. +// Clear empties the data for the given session id but doesn't clear the cookie. +// Use `Destroy()` to delete entire session from the store and clear the cookie. func (s *Session) Clear() error { err := s.manager.store.Clear(s.id) if err != nil { @@ -261,53 +261,60 @@ func (s *Session) Clear() error { return nil } -// Clear deletes the entire session from store and clears the cookie. +// Destroy deletes the session from backend and clears the cookie. func (s *Session) Destroy() error { err := s.manager.store.Destroy(s.id) if err != nil { return errAs(err) } s.ResetCache() - return s.clearCookie() + return s.ClearCookie() } // Int is a helper to get values as integer. +// If the value is Nil, ErrNil is returned, which means key doesn't exist. func (s *Session) Int(r interface{}, err error) (int, error) { out, err := s.manager.store.Int(r, err) return out, errAs(err) } // Int64 is a helper to get values as Int64. +// If the value is Nil, ErrNil is returned, which means key doesn't exist. func (s *Session) Int64(r interface{}, err error) (int64, error) { out, err := s.manager.store.Int64(r, err) return out, errAs(err) } // UInt64 is a helper to get values as UInt64. +// If the value is Nil, ErrNil is returned, which means key doesn't exist. func (s *Session) UInt64(r interface{}, err error) (uint64, error) { out, err := s.manager.store.UInt64(r, err) return out, errAs(err) } // Float64 is a helper to get values as Float64. +// If the value is Nil, ErrNil is returned, which means key doesn't exist. func (s *Session) Float64(r interface{}, err error) (float64, error) { out, err := s.manager.store.Float64(r, err) return out, errAs(err) } // String is a helper to get values as String. +// If the value is Nil, ErrNil is returned, which means key doesn't exist. func (s *Session) String(r interface{}, err error) (string, error) { out, err := s.manager.store.String(r, err) return out, errAs(err) } // Bytes is a helper to get values as Bytes. +// If the value is Nil, ErrNil is returned, which means key doesn't exist. func (s *Session) Bytes(r interface{}, err error) ([]byte, error) { out, err := s.manager.store.Bytes(r, err) return out, errAs(err) } // Bool is a helper to get values as Bool. +// If the value is Nil, ErrNil is returned, which means key doesn't exist. func (s *Session) Bool(r interface{}, err error) (bool, error) { out, err := s.manager.store.Bool(r, err) return out, errAs(err) diff --git a/session_test.go b/session_test.go index fac7442..119d387 100644 --- a/session_test.go +++ b/session_test.go @@ -218,7 +218,7 @@ func TestClearCookie(t *testing.T) { sess, err := mgr.NewSession(nil, nil) assert.NoError(t, err) - err = sess.clearCookie() + err = sess.ClearCookie() assert.NoError(t, err) assert.True(t, isCb) diff --git a/store.go b/store.go index c45adc7..6f66ce5 100644 --- a/store.go +++ b/store.go @@ -3,14 +3,14 @@ package simplesessions // Store represents store interface. This interface can be // implemented to create various backend stores for session. type Store interface { - // Create creates new session in the store and returns the session ID. + // Create creates new session in the store for the given session ID. Create(id string) (err error) - // Get gets a value for given key from session. + // Get a value for the given key from session. Get(id, key string) (value interface{}, err error) - // GetMulti gets a maps of multiple values for given keys. - // If some fields are not found then return ErrFieldNotFound for that field. + // GetMulti gets a maps of multiple values for given keys from session. + // If some fields are not found then return nil for that field. GetMulti(id string, keys ...string) (data map[string]interface{}, err error) // GetAll gets all key and value from session. @@ -25,13 +25,19 @@ type Store interface { // Delete a given list of keys from session. Delete(id string, key ...string) error - // Clear empties the session. + // Clear empties the session but doesn't delete it. Clear(id string) error // Destroy deletes the entire session. Destroy(id string) error // Helper method for typecasting/asserting. + // Supposed to be used as a chain. + // For example: sess.Int(sess.Get("id", "key")) + // Take `error` and returns that if its not nil. + // Take `interface{}` value and type assert or convert. + // If its nil then return ErrNil. + // If it can't type asserted/converted then return ErrAssertType. Int(interface{}, error) (int, error) Int64(interface{}, error) (int64, error) UInt64(interface{}, error) (uint64, error)