Fix unit-tests. Update documentation

state-management
Juan Calderon-Perez 2025-03-30 17:39:32 -04:00
parent 4ff6a2ecc0
commit 4c48576768
2 changed files with 312 additions and 85 deletions

View File

@ -1,18 +1,18 @@
# State Management
This document details the state management functionality provided by Fiber, a thread-safe global key-value store used to store application dependencies and runtime data. The implementation is based on Go's `sync.Map` ensuring concurrency safety.
This document details the state management functionality provided by Fiber, a thread-safe global keyvalue store used to store application dependencies and runtime data. The implementation is based on Go's `sync.Map`, ensuring concurrency safety.
Below is the detailed description of all public methods and usage examples.
## State Type
`State` is a key-value store built on top of `sync.Map`. It allows storage and retrieval of dependencies and configurations in a Fiber application as well as thread-safe access to runtime data.
`State` is a keyvalue store built on top of `sync.Map`. It allows storage and retrieval of dependencies and configurations in a Fiber application as well as threadsafe access to runtime data.
### Definition
```go
// State is a key-value store for Fiber's app in order to be used as a global storage for the app's dependencies.
// It's a thread-safe implementation of a map[string]any, using sync.Map.
// State is a keyvalue store for Fiber's app, used as a global storage for the app's dependencies.
// It is a threadsafe implementation of a map[string]any, using sync.Map.
type State struct {
dependencies sync.Map
}
@ -22,9 +22,10 @@ type State struct {
### Set
Set adds or updates a key-value pair in the State.
Set adds or updates a keyvalue pair in the State.
```go title="Signature"
```go
// Set adds or updates a keyvalue pair in the State.
func (s *State) Set(key string, value any)
```
@ -66,75 +67,12 @@ appName := app.State().MustGet("appName")
fmt.Println("App Name:", appName)
```
### GetString
GetString retrieves a string value from the State. It returns the string and a boolean indicating successful type assertion.
```go title="Signature"
func (s *State) GetString(key string) (string, bool)
```
**Usage Example:**
```go
if appName, ok := app.State().GetString("appName"); ok {
fmt.Println("App Name:", appName)
}
```
### GetInt
GetInt retrieves an integer value from the State. It returns the int and a boolean indicating successful type assertion.
```go title="Signature"
func (s *State) GetInt(key string) (int, bool)
```
**Usage Example:**
```go
if count, ok := app.State().GetInt("userCount"); ok {
fmt.Printf("User Count: %d\n", count)
}
```
### GetBool
GetBool retrieves a boolean value from the State. It returns the bool and a boolean indicating successful type assertion.
```go title="Signature"
func (s *State) GetBool(key string) (value, ok bool)
```
**Usage Example:**
```go
if debug, ok := app.State().GetBool("debugMode"); ok {
fmt.Printf("Debug Mode: %v\n", debug)
}
```
### GetFloat64
GetFloat64 retrieves a float64 value from the State. It returns the float64 and a boolean indicating successful type assertion.
```go title="Signature"
func (s *State) GetFloat64(key string) (float64, bool)
```
**Usage Example:**
```go title="Signature"
if ratio, ok := app.State().GetFloat64("scalingFactor"); ok {
fmt.Printf("Scaling Factor: %f\n", ratio)
}
```
### Has
Has checks if a key exists in the State.
```go title="Signature"
```go title="Signature"s
func (s *State) Has(key string) bool
```
@ -148,7 +86,7 @@ if app.State().Has("appName") {
### Delete
Delete removes a key-value pair from the State.
Delete removes a keyvalue pair from the State.
```go title="Signature"
func (s *State) Delete(key string)
@ -162,7 +100,7 @@ app.State().Delete("obsoleteKey")
### Reset
Reset resets the State by removing all keys.
Reset removes all keys from the State.
```go title="Signature"
func (s *State) Reset()
@ -193,7 +131,8 @@ fmt.Println("State Keys:", keys)
Len returns the number of keys in the State.
```go title="Signature"
```go
// Len returns the number of keys in the State.
func (s *State) Len() int
```
@ -203,13 +142,285 @@ func (s *State) Len() int
fmt.Printf("Total State Entries: %d\n", app.State().Len())
```
### GetString
GetString retrieves a string value from the State. It returns the string and a boolean indicating a successful type assertion.
```go title="Signature"
func (s *State) GetString(key string) (string, bool)
```
**Usage Example:**
```go
if appName, ok := app.State().GetString("appName"); ok {
fmt.Println("App Name:", appName)
}
```
### GetInt
GetInt retrieves an integer value from the State. It returns the int and a boolean indicating a successful type assertion.
```go title="Signature"
func (s *State) GetInt(key string) (int, bool)
```
**Usage Example:**
```go
if count, ok := app.State().GetInt("userCount"); ok {
fmt.Printf("User Count: %d\n", count)
}
```
### GetBool
GetBool retrieves a boolean value from the State. It returns the bool and a boolean indicating a successful type assertion.
```go title="Signature"
func (s *State) GetBool(key string) (value, bool)
```
**Usage Example:**
```go
if debug, ok := app.State().GetBool("debugMode"); ok {
fmt.Printf("Debug Mode: %v\n", debug)
}
```
### GetFloat64
GetFloat64 retrieves a float64 value from the State. It returns the float64 and a boolean indicating a successful type assertion.
```go title="Signature"
func (s *State) GetFloat64(key string) (float64, bool)
```
**Usage Example:**
```go title="Signature"
if ratio, ok := app.State().GetFloat64("scalingFactor"); ok {
fmt.Printf("Scaling Factor: %f\n", ratio)
}
```
### GetUint
GetUint retrieves a `uint` value from the State.
```go title="Signature"
func (s *State) GetUint(key string) (uint, bool)
```
**Usage Example:**
```go
if val, ok := app.State().GetUint("maxConnections"); ok {
fmt.Printf("Max Connections: %d\n", val)
}
```
### GetInt8
GetInt8 retrieves an `int8` value from the State.
```go title="Signature"
func (s *State) GetInt8(key string) (int8, bool)
```
**Usage Example:**
```go
if val, ok := app.State().GetInt8("threshold"); ok {
fmt.Printf("Threshold: %d\n", val)
}
```
### GetInt16
GetInt16 retrieves an `int16` value from the State.
```go title="Signature"
func (s *State) GetInt16(key string) (int16, bool)
```
**Usage Example:**
```go
if val, ok := app.State().GetInt16("minValue"); ok {
fmt.Printf("Minimum Value: %d\n", val)
}
```
### GetInt32
GetInt32 retrieves an `int32` value from the State.
```go title="Signature"
func (s *State) GetInt32(key string) (int32, bool)
```
**Usage Example:**
```go
if val, ok := app.State().GetInt32("portNumber"); ok {
fmt.Printf("Port Number: %d\n", val)
}
```
### GetInt64
GetInt64 retrieves an `int64` value from the State.
```go title="Signature"
func (s *State) GetInt64(key string) (int64, bool)
```
**Usage Example:**
```go
if val, ok := app.State().GetInt64("fileSize"); ok {
fmt.Printf("File Size: %d\n", val)
}
```
### GetUint8
GetUint8 retrieves a `uint8` value from the State.
```go title="Signature"
func (s *State) GetUint8(key string) (uint8, bool)
```
**Usage Example:**
```go
if val, ok := app.State().GetUint8("byteValue"); ok {
fmt.Printf("Byte Value: %d\n", val)
}
```
### GetUint16
GetUint16 retrieves a `uint16` value from the State.
```go title="Signature"
func (s *State) GetUint16(key string) (uint16, bool)
```
**Usage Example:**
```go
if val, ok := app.State().GetUint16("limit"); ok {
fmt.Printf("Limit: %d\n", val)
}
```
### GetUint32
GetUint32 retrieves a `uint32` value from the State.
```go title="Signature"
func (s *State) GetUint32(key string) (uint32, bool)
```
**Usage Example:**
```go
if val, ok := app.State().GetUint32("timeout"); ok {
fmt.Printf("Timeout: %d\n", val)
}
```
### GetUint64
GetUint64 retrieves a `uint64` value from the State.
```go title="Signature"
func (s *State) GetUint64(key string) (uint64, bool)
```
**Usage Example:**
```go
if val, ok := app.State().GetUint64("maxSize"); ok {
fmt.Printf("Max Size: %d\n", val)
}
```
### GetUintptr
GetUintptr retrieves a `uintptr` value from the State.
```go title="Signature"
func (s *State) GetUintptr(key string) (uintptr, bool)
```
**Usage Example:**
```go
if val, ok := app.State().GetUintptr("pointerValue"); ok {
fmt.Printf("Pointer Value: %d\n", val)
}
```
### GetFloat32
GetFloat32 retrieves a `float32` value from the State.
```go title="Signature"
func (s *State) GetFloat32(key string) (float32, bool)
```
**Usage Example:**
```go
if val, ok := app.State().GetFloat32("scalingFactor32"); ok {
fmt.Printf("Scaling Factor (float32): %f\n", val)
}
```
### GetComplex64
GetComplex64 retrieves a `complex64` value from the State.
```go title="Signature"
func (s *State) GetComplex64(key string) (complex64, bool)
```
**Usage Example:**
```go
if val, ok := app.State().GetComplex64("complexVal"); ok {
fmt.Printf("Complex Value (complex64): %v\n", val)
}
```
### GetComplex128
GetComplex128 retrieves a `complex128` value from the State.
```go title="Signature"
func (s *State) GetComplex128(key string) (complex128, bool)
```
**Usage Example:**
```go
if val, ok := app.State().GetComplex128("complexVal128"); ok {
fmt.Printf("Complex Value (complex128): %v\n", val)
}
```
## Generic Functions
Fiber provides generic functions to retrieve state values with type-safety and fallback options.
Fiber provides generic functions to retrieve state values with type safety and fallback options.
### GetState
GetState retrieves a value from the State and casts it to the desired type. It returns the casted value and a boolean indicating if the cast was successful.
GetState retrieves a value from the State and casts it to the desired type. It returns the cast value and a boolean indicating if the cast was successful.
```go title="Signature"
func GetState[T any](s *State, key string) (T, bool)
@ -217,7 +428,7 @@ func GetState[T any](s *State, key string) (T, bool)
**Usage Example:**
```go title="Signature"
```go
// Retrieve an integer value safely.
userCount, ok := GetState[int](app.State(), "userCount")
if ok {
@ -235,7 +446,7 @@ func MustGetState[T any](s *State, key string) T
**Usage Example:**
```go title="Signature"
```go
// Retrieve the value or panic if it is not present.
config := MustGetState[string](app.State(), "configFile")
fmt.Println("Config File:", config)
@ -279,7 +490,7 @@ func main() {
// Middleware: Increase counter for every request.
app.Use(func(c fiber.Ctx) error {
count := fiber.GetStateWithDefault(app.State(), "requestCount", 0)
count := c.App().GetStateWithDefault(app.State(), "requestCount", 0)
app.State().Set("requestCount", count+1)
return c.Next()
})
@ -289,7 +500,7 @@ func main() {
})
app.Get("/stats", func(c fiber.Ctx) error {
count := fiber.GetStateWithDefault(c.App().State(), "requestCount", 0)
count := c.App().GetStateWithDefault(c.App().State(), "requestCount", 0)
return c.SendString(fmt.Sprintf("Total requests: %d", count))
})
@ -297,7 +508,7 @@ func main() {
}
```
### Example: Environment-Specific Configuration
### Example: EnvironmentSpecific Configuration
This example shows how to configure different settings based on the environment.
@ -332,8 +543,8 @@ func main() {
app.Get("/config", func(c fiber.Ctx) error {
config := map[string]any{
"environment": environment,
"apiUrl": fiber.GetStateWithDefault(app.State(), "apiUrl", ""),
"debug": fiber.GetStateWithDefault(app.State(), "debug", false),
"apiUrl": c.App().GetStateWithDefault(app.State(), "apiUrl", ""),
"debug": c.App().GetStateWithDefault(app.State(), "debug", false),
}
return c.JSON(config)
})
@ -390,7 +601,7 @@ func main() {
}
// Save the user to the database.
rdb, ok := fiber.GetState[*redis.Client](app.State(), "redis")
rdb, ok := c.App().GetState[*redis.Client](app.State(), "redis")
if !ok {
return c.Status(fiber.StatusInternalServerError).SendString("Redis client not found")
}
@ -408,7 +619,7 @@ func main() {
app.Get("/user/:id", func(c fiber.Ctx) error {
id := c.Params("id")
rdb, ok := fiber.GetState[*redis.Client](app.State(), "redis")
rdb, ok := c.App().GetState[*redis.Client](app.State(), "redis")
if !ok {
return c.Status(fiber.StatusInternalServerError).SendString("Redis client not found")
}

View File

@ -7,6 +7,22 @@ import (
"github.com/stretchr/testify/require"
)
func TestState_SetAndGet_WithApp(t *testing.T) {
t.Parallel()
// Create app
app := New()
// test setting and getting a value
app.state.Set("foo", "bar")
val, ok := app.state.Get("foo")
require.True(t, ok)
require.Equal(t, "bar", val)
// test key not found
_, ok = app.state.Get("unknown")
require.False(t, ok)
}
func TestState_SetAndGet(t *testing.T) {
t.Parallel()
st := newState()
@ -320,7 +336,7 @@ func TestState_GetComplex64(t *testing.T) {
t.Parallel()
st := newState()
var c = complex(2, 3)
var c complex64 = complex(2, 3)
st.Set("complex64", c)
cRes, ok := st.GetComplex64("complex64")
require.True(t, ok)