Skip to content

Commit

Permalink
πŸš€ FEATURE: add queries function (#2475)
Browse files Browse the repository at this point in the history
* πŸš€ FEATURE: add queries method

* πŸ“š DOCS: add documents for queries method.

* 🩹 Fix: fix wrap error returned from Queries function

* 🚨 tests: add url encoded tests

* πŸ”₯ feature: add url encoded support for queries

* 🩹 Fix: fix wrap error returned from Queries function

* ♻️ Refactor: change error message of url.QueryUnescape

* ♻️ Refactor: refactor of mapping key and value queries

* 🚨 Test: Validate to fail parse queries

* 🚨 Test: Add benchmark test for Queries

* 🩹 Fix: remove parsing for encoded urls

* ♻️ Refactor: change string function to c.app.getString fucntion

Co-authored-by: cmd777 <[email protected]>

* ♻️ Refactor: change name of benchamark function ctx queries

Co-authored-by: leonklingele <[email protected]>

* ♻️ Refactor: remove empty lines

Co-authored-by: leonklingele <[email protected]>

* Revert "♻️ Refactor: change string function to c.app.getString fucntion"

This reverts commit 28febf9.

* πŸ“š Docs: add documents for queries method

* 🚨 Tests: add more tests for queries function

* ♻️ Refactor: change string function to c.app.getString function

* 🚨 Tests: add more test for queries function

* πŸ“š Docs: add more documents to queries function

---------

Co-authored-by: cmd777 <[email protected]>
Co-authored-by: leonklingele <[email protected]>
  • Loading branch information
3 people authored Jun 12, 2023
1 parent f5d2abb commit d87065f
Show file tree
Hide file tree
Showing 3 changed files with 165 additions and 0 deletions.
29 changes: 29 additions & 0 deletions ctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -1054,6 +1054,35 @@ func (c *Ctx) Query(key string, defaultValue ...string) string {
return defaultString(c.app.getString(c.fasthttp.QueryArgs().Peek(key)), defaultValue)
}

// Queries returns a map of query parameters and their values.
//
// GET /?name=alex&wanna_cake=2&id=
// Queries()["name"] == "alex"
// Queries()["wanna_cake"] == "2"
// Queries()["id"] == ""
//
// GET /?field1=value1&field1=value2&field2=value3
// Queries()["field1"] == "value2"
// Queries()["field2"] == "value3"
//
// GET /?list_a=1&list_a=2&list_a=3&list_b[]=1&list_b[]=2&list_b[]=3&list_c=1,2,3
// Queries()["list_a"] == "3"
// Queries()["list_b[]"] == "3"
// Queries()["list_c"] == "1,2,3"
//
// GET /api/search?filters.author.name=John&filters.category.name=Technology&filters[customer][name]=Alice&filters[status]=pending
// Queries()["filters.author.name"] == "John"
// Queries()["filters.category.name"] == "Technology"
// Queries()["filters[customer][name]"] == "Alice"
// Queries()["filters[status]"] == "pending"
func (c *Ctx) Queries() map[string]string {
m := make(map[string]string, c.Context().QueryArgs().Len())
c.Context().QueryArgs().VisitAll(func(key, value []byte) {
m[c.app.getString(key)] = c.app.getString(value)
})
return m
}

// QueryInt returns integer value of key string parameter in the url.
// Default to empty or invalid key is 0.
//
Expand Down
72 changes: 72 additions & 0 deletions ctx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3842,6 +3842,78 @@ func Benchmark_Ctx_SendString_B(b *testing.B) {
utils.AssertEqual(b, []byte("Hello, world!"), c.Response().Body())
}

// go test -run Test_Ctx_Queries -v
func Test_Ctx_Queries(t *testing.T) {
t.Parallel()
app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
defer app.ReleaseCtx(c)

c.Request().SetBody([]byte(``))
c.Request().Header.SetContentType("")
c.Request().URI().SetQueryString("id=1&name=tom&hobby=basketball,football&favouriteDrinks=milo,coke,pepsi&alloc=&no=1&field1=value1&field1=value2&field2=value3&list_a=1&list_a=2&list_a=3&list_b[]=1&list_b[]=2&list_b[]=3&list_c=1,2,3")

queries := c.Queries()
utils.AssertEqual(t, "1", queries["id"])
utils.AssertEqual(t, "tom", queries["name"])
utils.AssertEqual(t, "basketball,football", queries["hobby"])
utils.AssertEqual(t, "milo,coke,pepsi", queries["favouriteDrinks"])
utils.AssertEqual(t, "", queries["alloc"])
utils.AssertEqual(t, "1", queries["no"])
utils.AssertEqual(t, "value2", queries["field1"])
utils.AssertEqual(t, "value3", queries["field2"])
utils.AssertEqual(t, "3", queries["list_a"])
utils.AssertEqual(t, "3", queries["list_b[]"])
utils.AssertEqual(t, "1,2,3", queries["list_c"])

c.Request().URI().SetQueryString("filters.author.name=John&filters.category.name=Technology&filters[customer][name]=Alice&filters[status]=pending")

queries = c.Queries()
utils.AssertEqual(t, "John", queries["filters.author.name"])
utils.AssertEqual(t, "Technology", queries["filters.category.name"])
utils.AssertEqual(t, "Alice", queries["filters[customer][name]"])
utils.AssertEqual(t, "pending", queries["filters[status]"])

c.Request().URI().SetQueryString("tags=apple,orange,banana&filters[tags]=apple,orange,banana&filters[category][name]=fruits&filters.tags=apple,orange,banana&filters.category.name=fruits")

queries = c.Queries()
utils.AssertEqual(t, "apple,orange,banana", queries["tags"])
utils.AssertEqual(t, "apple,orange,banana", queries["filters[tags]"])
utils.AssertEqual(t, "fruits", queries["filters[category][name]"])
utils.AssertEqual(t, "apple,orange,banana", queries["filters.tags"])
utils.AssertEqual(t, "fruits", queries["filters.category.name"])

c.Request().URI().SetQueryString("filters[tags][0]=apple&filters[tags][1]=orange&filters[tags][2]=banana&filters[category][name]=fruits")

queries = c.Queries()
utils.AssertEqual(t, "apple", queries["filters[tags][0]"])
utils.AssertEqual(t, "orange", queries["filters[tags][1]"])
utils.AssertEqual(t, "banana", queries["filters[tags][2]"])
utils.AssertEqual(t, "fruits", queries["filters[category][name]"])
}

// go test -v -run=^$ -bench=Benchmark_Ctx_Queries -benchmem -count=4
func Benchmark_Ctx_Queries(b *testing.B) {
app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
defer app.ReleaseCtx(c)
b.ReportAllocs()
b.ResetTimer()
c.Request().URI().SetQueryString("id=1&name=tom&hobby=basketball,football&favouriteDrinks=milo,coke,pepsi&alloc=&no=1")

var queries map[string]string
for n := 0; n < b.N; n++ {
queries = c.Queries()
}

utils.AssertEqual(b, "1", queries["id"])
utils.AssertEqual(b, "tom", queries["name"])
utils.AssertEqual(b, "basketball,football", queries["hobby"])
utils.AssertEqual(b, "milo,coke,pepsi", queries["favouriteDrinks"])
utils.AssertEqual(b, "", queries["alloc"])
utils.AssertEqual(b, "1", queries["no"])
}

// go test -run Test_Ctx_QueryParser -v
func Test_Ctx_QueryParser(t *testing.T) {
t.Parallel()
Expand Down
64 changes: 64 additions & 0 deletions docs/api/ctx.md
Original file line number Diff line number Diff line change
Expand Up @@ -1094,6 +1094,70 @@ app.Get("/", func(c *fiber.Ctx) error {
> _Returned value is only valid within the handler. Do not store any references.
> Make copies or use the_ [_**`Immutable`**_](ctx.md) _setting instead._ [_Read more..._](../#zero-allocation)
## Queries
Queries is a function that returns an object containing a property for each query string parameter in the route.
```go title="Signature"
func (c *Ctx) Queries() (map[string]string, error)
```
```go title="Example"
// GET http://example.com/?name=alex&want_pizza=false&id=

app.Get("/", func(c *fiber.Ctx) error {
m := c.Queries()
m["name"] // "alex"
m["want_pizza"] // "false"
m["id"] // ""
// ...
})
```
```go title="Example"
// GET http://example.com/?field1=value1&field1=value2&field2=value3

app.Get("/", func (c *fiber.Ctx) error {
m := c.Queries()
m["field1"] // "value2"
m["field2"] // value3
})
```
```go title="Example"
// GET http://example.com/?list_a=1&list_a=2&list_a=3&list_b[]=1&list_b[]=2&list_b[]=3&list_c=1,2,3

app.Get("/", func(c *fiber.Ctx) error {
m := c.Queries()
m["list_a"] // "3"
m["list_b[]"] // "3"
m["list_c"] // "1,2,3"
})
```
```go title="Example"
// GET /api/posts?filters.author.name=John&filters.category.name=Technology

app.Get("/", func(c *fiber.Ctx) error {
m := c.Queies()
m["filters.author.name"] // John
m["filters.category.name"] // Technology
})
```
```go title="Example"
// GET /api/posts?tags=apple,orange,banana&filters[tags]=apple,orange,banana&filters[category][name]=fruits&filters.tags=apple,orange,banana&filters.category.name=fruits

app.Get("/", func(c *fiber.Ctx) error {
m := c.Queries()
m["tags"] // apple,orange,banana
m["filters[tags]"] // apple,orange,banana
m["filters[category][name]"] // fruits
m["filters.tags"] // apple,orange,banana
m["filters.category.name"] // fruits
})
```
## QueryBool
This property is an object containing a property for each query boolean parameter in the route, you could pass an optional default value that will be returned if the query key does not exist.
Expand Down

0 comments on commit d87065f

Please sign in to comment.