Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v3: Add QueryParser for get query using generic #2776

Merged
merged 20 commits into from
Jan 19, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
e793c1f
Add QueryParser method and tests
ryanbekhen Dec 23, 2023
a66024f
Refactor QueryParser and add string support
ryanbekhen Jan 7, 2024
bf9781c
Update example call in method comment
ryanbekhen Jan 7, 2024
5ee498b
Refactor Query function in ctx.go
ryanbekhen Jan 7, 2024
0dba52a
Refactor type handling in Query function
ryanbekhen Jan 7, 2024
6f9305d
Add type assertion check in ctx.go
ryanbekhen Jan 12, 2024
7de975b
Refactor Query function to support more data types
ryanbekhen Jan 13, 2024
136a781
Refactor Query function documentation
ryanbekhen Jan 13, 2024
a2e4483
Add benchmark tests for Query function
ryanbekhen Jan 13, 2024
e2518aa
Update generic Query function signature
ryanbekhen Jan 15, 2024
54f1066
Merge branch 'main' into v3-beta
ryanbekhen Jan 15, 2024
0fb6230
Merge remote-tracking branch 'origin/v3-beta' into v3-beta
ryanbekhen Jan 15, 2024
08b6fb9
Modify `ctx.Query()` calls in documentation
ryanbekhen Jan 15, 2024
0c6bd7e
Refactored assertValueType function and improved query parameter docu…
ryanbekhen Jan 15, 2024
6b4e517
Update Query method calls to use new fiber.Query syntax
ryanbekhen Jan 15, 2024
1c16cef
Add Query method to get query string parameters
ryanbekhen Jan 15, 2024
2fa8b46
Replace 'utils.UnsafeBytes' with 'ctx.app.getBytes'
ryanbekhen Jan 15, 2024
0b63d06
Refactor parsing functions in query handlers
ryanbekhen Jan 18, 2024
722b9b6
Refactor parsing functions in ctx.go
ryanbekhen Jan 18, 2024
2faf484
Refactor code to centralize parsing functions
ryanbekhen Jan 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions ctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -1029,6 +1029,90 @@ func (c *DefaultCtx) QueryFloat(key string, defaultValue ...float64) float64 {
return value
}

// Query parses a query parameter value from the given context and returns it as the specified type.
// The function supports parsing integers, booleans, floats, and strings.
//
// Default to empty or invalid key is 0.
//
// GET /?name=alex&wanna_cake=2&id=
// Query[int]("wanna_cake", 1) == 2
// Query[int]("name", 1) == 1
// Query[int]("id", 1) == 1
// Query[int]("id") == 0
//
// Default to empty or invalid key is true.
//
// Get /?name=alex&want_pizza=false&id=
// Query[bool]("want_pizza") == false
// Query[bool]("want_pizza", true) == false
// Query[bool]("name") == false
// Query[bool]("name", true) == true
// Query[bool]("id") == false
// Query[bool]("id", true) == true
//
// Default to empty or invalid key is 0.
//
// GET /?name=alex&amount=32.23&id=
// Query[float64]("amount") = 32.23
// Query[float64]("amount", 3) = 32.23
// Query[float64]("name", 1) = 1
// Query[float64]("name") = 0
// Query[float64]("id", 3) = 3
//
// If the generic type is not written explicitly, the function will try to infer the type from the default value.
// If the default value is not provided, the function will return the zero value of the type.
//
// GET /?name=alex&wanna_cake=2&id=
// Query("wanna_cake", 1) == 2
func Query[V string | bool | float64 | int](c Ctx, key string, defaultValue ...V) V {
ReneWerner87 marked this conversation as resolved.
Show resolved Hide resolved
ctx, ok := c.(*DefaultCtx)
if !ok {
panic("invalid context")
}
var v V
q := ctx.app.getString(ctx.fasthttp.QueryArgs().Peek(key))

switch any(v).(type) {
case int:
result, err := strconv.Atoi(q)
if err != nil {
if len(defaultValue) > 0 {
return defaultValue[0]
}
result = 0
}
return any(result).(V)
case float64:
result, err := strconv.ParseFloat(q, 64)
if err != nil {
if len(defaultValue) > 0 {
return defaultValue[0]
}
result = 0
}
return any(result).(V)
case bool:
result, err := strconv.ParseBool(q)
if err != nil {
if len(defaultValue) > 0 {
return defaultValue[0]
}
result = false
}
return any(result).(V)
case string:
if q == "" && len(defaultValue) > 0 {
return defaultValue[0]
}
return any(q).(V)
default:
if len(defaultValue) > 0 {
return defaultValue[0]
}
return v
}
}

// Range returns a struct containing the type and a slice of ranges.
func (c *DefaultCtx) Range(size int) (Range, error) {
var rangeData Range
Expand Down
59 changes: 59 additions & 0 deletions ctx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2092,6 +2092,20 @@ func Test_Ctx_QueryInt(t *testing.T) {
require.Equal(t, 2, c.QueryInt("id", 2))
}

func Test_Ctx_QueryParserInt(t *testing.T) {
ryanbekhen marked this conversation as resolved.
Show resolved Hide resolved
t.Parallel()
app := New()
c := app.NewCtx(&fasthttp.RequestCtx{})

c.Request().URI().SetQueryString("search=john&age=20")
require.Equal(t, 0, Query[int](c, "foo"))
require.Equal(t, 20, Query[int](c, "age", 12))
require.Equal(t, 0, Query[int](c, "search"))
require.Equal(t, 1, Query[int](c, "search", 1))
require.Equal(t, 0, Query[int](c, "id"))
require.Equal(t, 2, Query[int](c, "id", 2))
}

func Test_Ctx_QueryBool(t *testing.T) {
t.Parallel()
app := New()
Expand All @@ -2107,6 +2121,21 @@ func Test_Ctx_QueryBool(t *testing.T) {
require.Equal(t, true, c.QueryBool("id", true))
}

func Test_Ctx_QueryParserBool(t *testing.T) {
t.Parallel()
app := New()
c := app.NewCtx(&fasthttp.RequestCtx{})

c.Request().URI().SetQueryString("name=alex&want_pizza=false&id=")

require.Equal(t, false, Query[bool](c, "want_pizza"))
require.Equal(t, false, Query[bool](c, "want_pizza", true))
require.Equal(t, false, Query[bool](c, "name"))
require.Equal(t, true, Query[bool](c, "name", true))
require.Equal(t, false, Query[bool](c, "id"))
require.Equal(t, true, Query[bool](c, "id", true))
}

func Test_Ctx_QueryFloat(t *testing.T) {
t.Parallel()
app := New()
Expand All @@ -2122,6 +2151,36 @@ func Test_Ctx_QueryFloat(t *testing.T) {
require.Equal(t, float64(0), c.QueryFloat("id"))
}

func Test_Ctx_QueryParserFloat(t *testing.T) {
t.Parallel()
app := New()
c := app.NewCtx(&fasthttp.RequestCtx{})

c.Request().URI().SetQueryString("name=alex&amount=32.23&id=")

require.Equal(t, 32.23, Query[float64](c, "amount"))
require.Equal(t, 32.23, Query[float64](c, "amount", 3.123))
require.Equal(t, 87.123, Query[float64](c, "name", 87.123))
require.Equal(t, float64(0), Query[float64](c, "name"))
require.Equal(t, 12.87, Query[float64](c, "id", 12.87))
require.Equal(t, float64(0), Query[float64](c, "id"))
}

func Test_Ctx_QueryParserString(t *testing.T) {
t.Parallel()
app := New()
c := app.NewCtx(&fasthttp.RequestCtx{})

c.Request().URI().SetQueryString("name=alex&amount=32.23&id=")

require.Equal(t, "alex", Query[string](c, "name"))
require.Equal(t, "alex", Query[string](c, "name", "john"))
require.Equal(t, "32.23", Query[string](c, "amount"))
require.Equal(t, "32.23", Query[string](c, "amount", "3.123"))
require.Equal(t, "", Query[string](c, "id"))
require.Equal(t, "12.87", Query[string](c, "id", "12.87"))
}

// go test -run Test_Ctx_Range
func Test_Ctx_Range(t *testing.T) {
t.Parallel()
Expand Down
Loading