diff --git a/README.md b/README.md
index 53729c272..215d6bae4 100644
--- a/README.md
+++ b/README.md
@@ -1,63 +1,75 @@
+
-
-
-
-
-
-
-
+
+
GoFr: An Opinionated Microservice Development Framework
+
+
-Listed in [CNCF Landscape](https://landscape.cncf.io/?selected=go-fr).
+
+
+## π― **Goal**
+GoFr is designed to **simplify microservice development**, with key focuses on **Kubernetes deployment** and **out-of-the-box observability**. While capable of building generic applications, **microservices** remain at its core.
-## π― Goal
-Even though generic applications can be written using GoFr, our main focus is to simplify the development of microservices.
-We will focus on deployment in Kubernetes and aspire to provide out-of-the-box observability.
+---
-## π‘ Key Features
+## π‘ **Key Features**
-1. Simple API syntax
-2. REST Standards by default
-3. Configuration management
-4. [Observability](https://gofr.dev/docs/quick-start/observability) (Logs, Traces, Metrics)
-5. Inbuilt [Auth Middleware](https://gofr.dev/docs/advanced-guide/http-authentication) & Support for [Custom Middleware](https://gofr.dev/docs/advanced-guide/middlewares)
-6. [gRPC support](https://gofr.dev/docs/advanced-guide/grpc)
-7. [HTTP service](https://gofr.dev/docs/advanced-guide/http-communication) with support for [Circuit Breaker](https://gofr.dev/docs/advanced-guide/circuit-breaker)
-8. [Pub/Sub](https://gofr.dev/docs/advanced-guide/using-publisher-subscriber)
-9. [Health Check](https://gofr.dev/docs/advanced-guide/monitoring-service-health) by default for all datasources.
-10. [Database Migration](https://gofr.dev/docs/advanced-guide/handling-data-migrations)
-11. [Cron Jobs](https://gofr.dev/docs/advanced-guide/using-cron)
-12. Support for [changing Log Level](https://gofr.dev/docs/advanced-guide/remote-log-level-change) without restarting the application.
-13. [Swagger Rendering](https://gofr.dev/docs/advanced-guide/swagger-documentation)
-14. [Abstracted File Systems](https://gofr.dev/docs/advanced-guide/handling-file)
-15. [Websockets](https://gofr.dev/docs/advanced-guide/handling-file)
+1. **Simple API Syntax**
+2. **REST Standards by Default**
+3. **Configuration Management**
+4. **[Observability](https://gofr.dev/docs/quick-start/observability)** (Logs, Traces, Metrics)
+5. **Inbuilt [Auth Middleware](https://gofr.dev/docs/advanced-guide/http-authentication)** & Custom Middleware Support
+6. **[gRPC Support](https://gofr.dev/docs/advanced-guide/grpc)**
+7. **[HTTP Service](https://gofr.dev/docs/advanced-guide/http-communication)** with Circuit Breaker Support
+8. **[Pub/Sub](https://gofr.dev/docs/advanced-guide/using-publisher-subscriber)**
+9. **[Health Check](https://gofr.dev/docs/advanced-guide/monitoring-service-health)** for All Datasources
+10. **[Database Migration](https://gofr.dev/docs/advanced-guide/handling-data-migrations)**
+11. **[Cron Jobs](https://gofr.dev/docs/advanced-guide/using-cron)**
+12. **Support for [Changing Log Level](https://gofr.dev/docs/advanced-guide/remote-log-level-change) Without Restarting**
+13. **[Swagger Rendering](https://gofr.dev/docs/advanced-guide/swagger-documentation)**
+14. **[Abstracted File Systems](https://gofr.dev/docs/advanced-guide/handling-file)**
+15. **[Websockets](https://gofr.dev/docs/advanced-guide/handling-file)**
-![banner.gif](.github/banner.gif)
+---
-## Getting started
-### Prerequisites
-GoFr requires [Go](https://go.dev/) version [1.21](https://go.dev/doc/devel/release#go1.21.0) or above.
+## π **Getting Started**
-### Getting GoFr
-With [Go's module support](https://go.dev/wiki/Modules#how-to-use-modules), `go [build|run|test]` automatically fetches the necessary dependencies when you add the import in your code:
+### **Prerequisites**
+- GoFr requires **[Go](https://go.dev/)** version **[1.21](https://go.dev/doc/devel/release#go1.21.0)** or above.
-```sh
+### **Installation**
+To get started with GoFr, add the following import to your code and use Goβs module support to automatically fetch dependencies:
+
+```go
import "gofr.dev/pkg/gofr"
```
-Alternatively, use `go get`:
+Alternatively, use the command:
-```sh
+```bash
go get -u gofr.dev/pkg/gofr
```
-### Running GoFr
-A basic example:
+
+---
+
+## π **Running GoFr**
+
+Here's a simple example to get a GoFr application up and running:
+
```go
package main
@@ -67,35 +79,46 @@ func main() {
app := gofr.New()
app.GET("/greet", func(ctx *gofr.Context) (interface{}, error) {
-
return "Hello World!", nil
})
- app.Run() // listen and serve on localhost:8000
+ app.Run() // listens and serves on localhost:8000
}
```
-To run the code, use the `go run` command, like:
+To run this code:
-```sh
+```bash
$ go run main.go
```
-Then visit [`localhost:8000/greet`](http://localhost:8000/greet) in your browser to see the response!
+Visit [`localhost:8000/greet`](http://localhost:8000/greet) to see the result.
+
+---
+
+## π **More Examples**
+
+Explore a variety of ready-to-run examples in the [GoFr examples directory](https://github.com/gofr-dev/gofr/tree/development/examples).
+
+---
+
+## π©βπ» **Documentation**
+
+- **[GoDoc](https://pkg.go.dev/gofr.dev)**: Official API documentation.
+- **[GoFr Documentation](https://gofr.dev/docs)**: Comprehensive guides and resources.
+
+---
-### See more examples
-A number of ready-to-run examples demonstrating various use cases of GoFr are available in the [GoFr examples](https://github.com/gofr-dev/gofr/tree/development/examples) directory.
+## π **Contribute**
-## π©βπ»Documentation
-See the [godocs](https://pkg.go.dev/gofr.dev).
+Help us make GoFr even better:
-The documentation is also available on [gofr.dev](https://gofr.dev/docs).
+1. **Star** this repo on GitHub! π
+2. Write a review or tutorial on **[Medium](https://medium.com/)**, **[Dev.to](https://dev.to/)**, or your blog.
+3. Review the **[CONTRIBUTING.md](CONTRIBUTING.md)** guide to learn how to contribute to the project.
-## π Contribute
-If you want to say thank you and/or support the active development of GoFr:
+---
-1. [Star](https://docs.github.com/en/get-started/exploring-projects-on-github/saving-repositories-with-stars) the repo.
-2. Write a review or tutorial on [Medium](https://medium.com/), [Dev.to](https://dev.to/) or personal blog.
-3. Visit [CONTRIBUTING](CONTRIBUTING.md) for details on submitting patches and the contribution workflow.
+### π **Get a GoFr T-Shirt & Stickers!**
-If your PR is merged or you have written an article or contributed in someway to development or spreading the word about GoFr, fill the [Google Form](https://forms.gle/R1Yz7ZzY3U5WWTgy5), and we will send you a
GoFr T-Shirt and Stickers as a token of appreciation.
+If your PR is merged, or if you contribute by writing articles or promoting GoFr, we invite you to fill out [this form](https://forms.gle/R1Yz7ZzY3U5WWTgy5) to claim your GoFr merchandise as a token of our appreciation!
diff --git a/docs/quick-start/connecting-redis/page.md b/docs/quick-start/connecting-redis/page.md
index 7fe09bfaf..3e002bdf5 100644
--- a/docs/quick-start/connecting-redis/page.md
+++ b/docs/quick-start/connecting-redis/page.md
@@ -12,6 +12,14 @@ Optionally, you can use Docker to set up a development environment as described
docker run --name gofr-redis -p 6379:6379 -d redis
```
+You can also set up a development environment with password authentication as described below.
+
+```bash
+docker run --name gofr-redis -p 2002:6379 -d \
+ -e REDIS_PASSWORD=password \
+ redis:7.0.5 --requirepass password
+```
+
You can set a sample key `greeting` using the following command:
```bash
@@ -27,13 +35,15 @@ Following configuration keys are required for Redis connectivity:
* `REDIS_HOST`: It specifies the hostname or IP address of your Redis server.
* `REDIS_PORT`: It specifies the port number on which your Redis server is listening. The default Redis port is 6379.
-
+* `REDIS_USER` : This is the user you'll use to connect to your Redis server. You can configure multiple users with different permissions in a single Redis container. For more details, refer to the [official docs](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl/)
+* `REDIS_PASSWORD`: The password is required only if your Redis server is configured for authentication; if authentication is not enabled, no password is necessary.
```dotenv
APP_NAME=test-service
HTTP_PORT=9000
REDIS_HOST=localhost
REDIS_PORT=6379
+REDIS_PASSWORD=password
```
The following code snippet demonstrates how to retrieve data from a Redis key named "greeting":
diff --git a/go.mod b/go.mod
index 27803a6f5..54701577f 100644
--- a/go.mod
+++ b/go.mod
@@ -3,7 +3,7 @@ module gofr.dev
go 1.22
require (
- cloud.google.com/go/pubsub v1.44.0
+ cloud.google.com/go/pubsub v1.45.1
github.com/DATA-DOG/go-sqlmock v1.5.2
github.com/XSAM/otelsql v0.34.0
github.com/alicebob/miniredis/v2 v2.33.0
@@ -19,35 +19,35 @@ require (
github.com/joho/godotenv v1.5.1
github.com/lib/pq v1.10.9
github.com/pkg/errors v0.9.1
- github.com/prometheus/client_golang v1.20.4
- github.com/redis/go-redis/extra/redisotel/v9 v9.0.5
- github.com/redis/go-redis/v9 v9.6.2
+ github.com/prometheus/client_golang v1.20.5
+ github.com/redis/go-redis/extra/redisotel/v9 v9.7.0
+ github.com/redis/go-redis/v9 v9.7.0
github.com/segmentio/kafka-go v0.4.47
github.com/stretchr/testify v1.9.0
- go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.55.0
+ go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.56.0
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0
go.opentelemetry.io/otel v1.31.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0
go.opentelemetry.io/otel/exporters/prometheus v0.52.0
- go.opentelemetry.io/otel/exporters/zipkin v1.30.0
+ go.opentelemetry.io/otel/exporters/zipkin v1.31.0
go.opentelemetry.io/otel/metric v1.31.0
go.opentelemetry.io/otel/sdk v1.31.0
go.opentelemetry.io/otel/sdk/metric v1.30.0
go.opentelemetry.io/otel/trace v1.31.0
- go.uber.org/mock v0.4.0
+ go.uber.org/mock v0.5.0
golang.org/x/oauth2 v0.23.0
golang.org/x/sync v0.8.0
golang.org/x/term v0.25.0
golang.org/x/text v0.19.0
- google.golang.org/api v0.200.0
+ google.golang.org/api v0.203.0
google.golang.org/grpc v1.67.1
google.golang.org/protobuf v1.35.1
modernc.org/sqlite v1.33.1
)
require (
- cloud.google.com/go v0.115.1 // indirect
- cloud.google.com/go/auth v0.9.8 // indirect
+ cloud.google.com/go v0.116.0 // indirect
+ cloud.google.com/go/auth v0.9.9 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect
cloud.google.com/go/compute/metadata v0.5.2 // indirect
cloud.google.com/go/iam v1.2.1 // indirect
@@ -79,7 +79,7 @@ require (
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.59.1 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
- github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5 // indirect
+ github.com/redis/go-redis/extra/rediscmd/v9 v9.7.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/yuin/gopher-lua v1.1.1 // indirect
@@ -92,9 +92,9 @@ require (
golang.org/x/net v0.30.0 // indirect
golang.org/x/sys v0.26.0 // indirect
golang.org/x/time v0.7.0 // indirect
- google.golang.org/genproto v0.0.0-20241007155032-5fefd90f89a9 // indirect
+ google.golang.org/genproto v0.0.0-20241015192408-796eee8c2d53 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect
modernc.org/libc v1.55.3 // indirect
diff --git a/go.sum b/go.sum
index 5e7137940..7821ecc6b 100644
--- a/go.sum
+++ b/go.sum
@@ -1,8 +1,8 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-cloud.google.com/go v0.115.1 h1:Jo0SM9cQnSkYfp44+v+NQXHpcHqlnRJk2qxh6yvxxxQ=
-cloud.google.com/go v0.115.1/go.mod h1:DuujITeaufu3gL68/lOFIirVNJwQeyf5UXyi+Wbgknc=
-cloud.google.com/go/auth v0.9.8 h1:+CSJ0Gw9iVeSENVCKJoLHhdUykDgXSc4Qn+gu2BRtR8=
-cloud.google.com/go/auth v0.9.8/go.mod h1:xxA5AqpDrvS+Gkmo9RqrGGRh6WSNKKOXhY3zNOr38tI=
+cloud.google.com/go v0.116.0 h1:B3fRrSDkLRt5qSHWe40ERJvhvnQwdZiHu0bJOpldweE=
+cloud.google.com/go v0.116.0/go.mod h1:cEPSRWPzZEswwdr9BxE6ChEn01dWlTaF05LiC2Xs70U=
+cloud.google.com/go/auth v0.9.9 h1:BmtbpNQozo8ZwW2t7QJjnrQtdganSdmqeIBxHxNkEZQ=
+cloud.google.com/go/auth v0.9.9/go.mod h1:xxA5AqpDrvS+Gkmo9RqrGGRh6WSNKKOXhY3zNOr38tI=
cloud.google.com/go/auth/oauth2adapt v0.2.4 h1:0GWE/FUsXhf6C+jAkWgYm7X9tK8cuEIfy19DBn6B6bY=
cloud.google.com/go/auth/oauth2adapt v0.2.4/go.mod h1:jC/jOpwFP6JBxhB3P5Rr0a9HLMC/Pe3eaL4NmdvqPtc=
cloud.google.com/go/compute/metadata v0.5.2 h1:UxK4uu/Tn+I3p2dYWTfiX4wva7aYlKixAHn3fyqngqo=
@@ -13,8 +13,8 @@ cloud.google.com/go/kms v1.20.0 h1:uKUvjGqbBlI96xGE669hcVnEMw1Px/Mvfa62dhM5UrY=
cloud.google.com/go/kms v1.20.0/go.mod h1:/dMbFF1tLLFnQV44AoI2GlotbjowyUfgVwezxW291fM=
cloud.google.com/go/longrunning v0.6.1 h1:lOLTFxYpr8hcRtcwWir5ITh1PAKUD/sG2lKrTSYjyMc=
cloud.google.com/go/longrunning v0.6.1/go.mod h1:nHISoOZpBcmlwbJmiVk5oDRz0qG/ZxPynEGs1iZ79s0=
-cloud.google.com/go/pubsub v1.44.0 h1:pLaMJVDTlnUDIKT5L0k53YyLszfBbGoUBo/IqDK/fEI=
-cloud.google.com/go/pubsub v1.44.0/go.mod h1:BD4a/kmE8OePyHoa1qAHEw1rMzXX+Pc8Se54T/8mc3I=
+cloud.google.com/go/pubsub v1.45.1 h1:ZC/UzYcrmK12THWn1P72z+Pnp2vu/zCZRXyhAfP1hJY=
+cloud.google.com/go/pubsub v1.45.1/go.mod h1:3bn7fTmzZFwaUjllitv1WlsNMkqBgGUb3UdMhI54eCc=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
@@ -29,16 +29,13 @@ github.com/alicebob/miniredis/v2 v2.33.0/go.mod h1:MhP4a3EU7aENRi9aO+tHfTBZicLqQ
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
-github.com/bsm/ginkgo/v2 v2.7.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
-github.com/bsm/gomega v1.26.0/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
-github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
@@ -165,8 +162,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/prometheus/client_golang v1.20.4 h1:Tgh3Yr67PaOv/uTqloMsCEdeuFTatm5zIq5+qNN23vI=
-github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
+github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
+github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
@@ -174,13 +171,12 @@ github.com/prometheus/common v0.59.1 h1:LXb1quJHWm1P6wq/U824uxYi4Sg0oGvNeUm1z5dJ
github.com/prometheus/common v0.59.1/go.mod h1:GpWM7dewqmVYcd7SmRaiWVe9SSqjf0UrwnYnpEZNuT0=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
-github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5 h1:EaDatTxkdHG+U3Bk4EUr+DZ7fOGwTfezUiUJMaIcaho=
-github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5/go.mod h1:fyalQWdtzDBECAQFBJuQe5bzQ02jGd5Qcbgb97Flm7U=
-github.com/redis/go-redis/extra/redisotel/v9 v9.0.5 h1:EfpWLLCyXw8PSM2/XNJLjI3Pb27yVE+gIAfeqp8LUCc=
-github.com/redis/go-redis/extra/redisotel/v9 v9.0.5/go.mod h1:WZjPDy7VNzn77AAfnAfVjZNvfJTYfPetfZk5yoSTLaQ=
-github.com/redis/go-redis/v9 v9.0.5/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk=
-github.com/redis/go-redis/v9 v9.6.2 h1:w0uvkRbc9KpgD98zcvo5IrVUsn0lXpRMuhNgiHDJzdk=
-github.com/redis/go-redis/v9 v9.6.2/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA=
+github.com/redis/go-redis/extra/rediscmd/v9 v9.7.0 h1:BIx9TNZH/Jsr4l1i7VVxnV0JPiwYj8qyrHyuL0fGZrk=
+github.com/redis/go-redis/extra/rediscmd/v9 v9.7.0/go.mod h1:eTg/YQtGYAZD5r3DlGlJptJ45AHA+/G+2NPn30PKzik=
+github.com/redis/go-redis/extra/redisotel/v9 v9.7.0 h1:bQk8xiVFw+3ln4pfELVktpWgYdFpgLLU+quwSoeIof0=
+github.com/redis/go-redis/extra/redisotel/v9 v9.7.0/go.mod h1:0LyN+GHLIJmKtjYRPF7nHyTTMV6E91YngoOopNifQRo=
+github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E=
+github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
@@ -220,8 +216,8 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 h1:r6I7RJCN86bpD/FQwedZ0vSixDpwuWREjW9oRMsmqDc=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0/go.mod h1:B9yO6b04uB80CzjedvewuqDhxJxi11s7/GtiGa8bAjI=
-go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.55.0 h1:sqmsIQ75l6lfZjjpnXXT9DFVtYEDg6CH0/Cn4/3A1Wg=
-go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.55.0/go.mod h1:rsg1EO8LXSs2po50PB5CeY/MSVlhghuKBgXlKnqm6ks=
+go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.56.0 h1:4BZHA+B1wXEQoGNHxW8mURaLhcdGwvRnmhGbm+odRbc=
+go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.56.0/go.mod h1:3qi2EEwMgB4xnKgPLqsDP3j9qxnHDZeHsnAxfjQqTko=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM=
go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY=
@@ -232,8 +228,8 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0 h1:FFeLy
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0/go.mod h1:TMu73/k1CP8nBUpDLc71Wj/Kf7ZS9FK5b53VapRsP9o=
go.opentelemetry.io/otel/exporters/prometheus v0.52.0 h1:kmU3H0b9ufFSi8IQCcxack+sWUblKkFbqWYs6YiACGQ=
go.opentelemetry.io/otel/exporters/prometheus v0.52.0/go.mod h1:+wsAp2+JhuGXX7YRkjlkx6hyWY3ogFPfNA4x3nyiAh0=
-go.opentelemetry.io/otel/exporters/zipkin v1.30.0 h1:1uYaSfxiCLdJATlGEtYjQe4jZYfqCjVwxeSTMXe8VF4=
-go.opentelemetry.io/otel/exporters/zipkin v1.30.0/go.mod h1:r/4BhMc3kiKxD61wGh9J3NVQ3/cZ45F2NHkQgVnql48=
+go.opentelemetry.io/otel/exporters/zipkin v1.31.0 h1:CgucL0tj3717DJnni7HVVB2wExzi8c2zJNEA2BhLMvI=
+go.opentelemetry.io/otel/exporters/zipkin v1.31.0/go.mod h1:rfzOVNiSwIcWtEC2J8epwG26fiaXlYvLySJ7bwsrtAE=
go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE=
go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY=
go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk=
@@ -248,8 +244,8 @@ go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
-go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
-go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
+go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
+go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@@ -268,8 +264,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
-golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
+golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -343,26 +339,26 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
-golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
-golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
+golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
+golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-google.golang.org/api v0.200.0 h1:0ytfNWn101is6e9VBoct2wrGDjOi5vn7jw5KtaQgDrU=
-google.golang.org/api v0.200.0/go.mod h1:Tc5u9kcbjO7A8SwGlYj4IiVifJU01UqXtEgDMYmBmV8=
+google.golang.org/api v0.203.0 h1:SrEeuwU3S11Wlscsn+LA1kb/Y5xT8uggJSkIhD08NAU=
+google.golang.org/api v0.203.0/go.mod h1:BuOVyCSYEPwJb3npWvDnNmFI92f3GeRnHNkETneT3SI=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
-google.golang.org/genproto v0.0.0-20241007155032-5fefd90f89a9 h1:nFS3IivktIU5Mk6KQa+v6RKkHUpdQpphqGNLxqNnbEk=
-google.golang.org/genproto v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:tEzYTYZxbmVNOu0OAFH9HzdJtLn6h4Aj89zzlBCdHms=
+google.golang.org/genproto v0.0.0-20241015192408-796eee8c2d53 h1:Df6WuGvthPzc+JiQ/G+m+sNX24kc0aTBqoDN/0yyykE=
+google.golang.org/genproto v0.0.0-20241015192408-796eee8c2d53/go.mod h1:fheguH3Am2dGp1LfXkrvwqC/KlFq8F0nLq3LryOMrrE=
google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 h1:T6rh4haD3GVYsgEfWExoCZA2o2FmbNyKpTuAxbEFPTg=
google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:wp2WsuBYj6j8wUdo3ToZsdxxixbvQNAHqVJrTgi5E5M=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 h1:QCqS/PdaHTSWGvupk2F/ehwHtGc0/GYkT+3GAcR1CCc=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 h1:X58yt85/IXCx0Y3ZwN6sEIKZzQtDEYaBWrDvErdXrRE=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
diff --git a/pkg/gofr/container/mock_datasources.go b/pkg/gofr/container/mock_datasources.go
index d68711ea1..e88e028ac 100644
--- a/pkg/gofr/container/mock_datasources.go
+++ b/pkg/gofr/container/mock_datasources.go
@@ -2262,6 +2262,413 @@ func (mr *MockRedisMockRecorder) FCallRo(ctx, function, keys any, args ...any) *
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FCallRo", reflect.TypeOf((*MockRedis)(nil).FCallRo), varargs...)
}
+// FTAggregate mocks base method.
+func (m *MockRedis) FTAggregate(ctx context.Context, index, query string) *redis.MapStringInterfaceCmd {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "FTAggregate", ctx, index, query)
+ ret0, _ := ret[0].(*redis.MapStringInterfaceCmd)
+ return ret0
+}
+
+// FTAggregate indicates an expected call of FTAggregate.
+func (mr *MockRedisMockRecorder) FTAggregate(ctx, index, query any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FTAggregate", reflect.TypeOf((*MockRedis)(nil).FTAggregate), ctx, index, query)
+}
+
+// FTAggregateWithArgs mocks base method.
+func (m *MockRedis) FTAggregateWithArgs(ctx context.Context, index, query string, options *redis.FTAggregateOptions) *redis.AggregateCmd {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "FTAggregateWithArgs", ctx, index, query, options)
+ ret0, _ := ret[0].(*redis.AggregateCmd)
+ return ret0
+}
+
+// FTAggregateWithArgs indicates an expected call of FTAggregateWithArgs.
+func (mr *MockRedisMockRecorder) FTAggregateWithArgs(ctx, index, query, options any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FTAggregateWithArgs", reflect.TypeOf((*MockRedis)(nil).FTAggregateWithArgs), ctx, index, query, options)
+}
+
+// FTAliasAdd mocks base method.
+func (m *MockRedis) FTAliasAdd(ctx context.Context, index, alias string) *redis.StatusCmd {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "FTAliasAdd", ctx, index, alias)
+ ret0, _ := ret[0].(*redis.StatusCmd)
+ return ret0
+}
+
+// FTAliasAdd indicates an expected call of FTAliasAdd.
+func (mr *MockRedisMockRecorder) FTAliasAdd(ctx, index, alias any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FTAliasAdd", reflect.TypeOf((*MockRedis)(nil).FTAliasAdd), ctx, index, alias)
+}
+
+// FTAliasDel mocks base method.
+func (m *MockRedis) FTAliasDel(ctx context.Context, alias string) *redis.StatusCmd {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "FTAliasDel", ctx, alias)
+ ret0, _ := ret[0].(*redis.StatusCmd)
+ return ret0
+}
+
+// FTAliasDel indicates an expected call of FTAliasDel.
+func (mr *MockRedisMockRecorder) FTAliasDel(ctx, alias any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FTAliasDel", reflect.TypeOf((*MockRedis)(nil).FTAliasDel), ctx, alias)
+}
+
+// FTAliasUpdate mocks base method.
+func (m *MockRedis) FTAliasUpdate(ctx context.Context, index, alias string) *redis.StatusCmd {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "FTAliasUpdate", ctx, index, alias)
+ ret0, _ := ret[0].(*redis.StatusCmd)
+ return ret0
+}
+
+// FTAliasUpdate indicates an expected call of FTAliasUpdate.
+func (mr *MockRedisMockRecorder) FTAliasUpdate(ctx, index, alias any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FTAliasUpdate", reflect.TypeOf((*MockRedis)(nil).FTAliasUpdate), ctx, index, alias)
+}
+
+// FTAlter mocks base method.
+func (m *MockRedis) FTAlter(ctx context.Context, index string, skipInitialScan bool, definition []any) *redis.StatusCmd {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "FTAlter", ctx, index, skipInitialScan, definition)
+ ret0, _ := ret[0].(*redis.StatusCmd)
+ return ret0
+}
+
+// FTAlter indicates an expected call of FTAlter.
+func (mr *MockRedisMockRecorder) FTAlter(ctx, index, skipInitialScan, definition any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FTAlter", reflect.TypeOf((*MockRedis)(nil).FTAlter), ctx, index, skipInitialScan, definition)
+}
+
+// FTConfigGet mocks base method.
+func (m *MockRedis) FTConfigGet(ctx context.Context, option string) *redis.MapMapStringInterfaceCmd {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "FTConfigGet", ctx, option)
+ ret0, _ := ret[0].(*redis.MapMapStringInterfaceCmd)
+ return ret0
+}
+
+// FTConfigGet indicates an expected call of FTConfigGet.
+func (mr *MockRedisMockRecorder) FTConfigGet(ctx, option any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FTConfigGet", reflect.TypeOf((*MockRedis)(nil).FTConfigGet), ctx, option)
+}
+
+// FTConfigSet mocks base method.
+func (m *MockRedis) FTConfigSet(ctx context.Context, option string, value any) *redis.StatusCmd {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "FTConfigSet", ctx, option, value)
+ ret0, _ := ret[0].(*redis.StatusCmd)
+ return ret0
+}
+
+// FTConfigSet indicates an expected call of FTConfigSet.
+func (mr *MockRedisMockRecorder) FTConfigSet(ctx, option, value any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FTConfigSet", reflect.TypeOf((*MockRedis)(nil).FTConfigSet), ctx, option, value)
+}
+
+// FTCreate mocks base method.
+func (m *MockRedis) FTCreate(ctx context.Context, index string, options *redis.FTCreateOptions, schema ...*redis.FieldSchema) *redis.StatusCmd {
+ m.ctrl.T.Helper()
+ varargs := []any{ctx, index, options}
+ for _, a := range schema {
+ varargs = append(varargs, a)
+ }
+ ret := m.ctrl.Call(m, "FTCreate", varargs...)
+ ret0, _ := ret[0].(*redis.StatusCmd)
+ return ret0
+}
+
+// FTCreate indicates an expected call of FTCreate.
+func (mr *MockRedisMockRecorder) FTCreate(ctx, index, options any, schema ...any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ varargs := append([]any{ctx, index, options}, schema...)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FTCreate", reflect.TypeOf((*MockRedis)(nil).FTCreate), varargs...)
+}
+
+// FTCursorDel mocks base method.
+func (m *MockRedis) FTCursorDel(ctx context.Context, index string, cursorId int) *redis.StatusCmd {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "FTCursorDel", ctx, index, cursorId)
+ ret0, _ := ret[0].(*redis.StatusCmd)
+ return ret0
+}
+
+// FTCursorDel indicates an expected call of FTCursorDel.
+func (mr *MockRedisMockRecorder) FTCursorDel(ctx, index, cursorId any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FTCursorDel", reflect.TypeOf((*MockRedis)(nil).FTCursorDel), ctx, index, cursorId)
+}
+
+// FTCursorRead mocks base method.
+func (m *MockRedis) FTCursorRead(ctx context.Context, index string, cursorId, count int) *redis.MapStringInterfaceCmd {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "FTCursorRead", ctx, index, cursorId, count)
+ ret0, _ := ret[0].(*redis.MapStringInterfaceCmd)
+ return ret0
+}
+
+// FTCursorRead indicates an expected call of FTCursorRead.
+func (mr *MockRedisMockRecorder) FTCursorRead(ctx, index, cursorId, count any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FTCursorRead", reflect.TypeOf((*MockRedis)(nil).FTCursorRead), ctx, index, cursorId, count)
+}
+
+// FTDictAdd mocks base method.
+func (m *MockRedis) FTDictAdd(ctx context.Context, dict string, term ...any) *redis.IntCmd {
+ m.ctrl.T.Helper()
+ varargs := []any{ctx, dict}
+ for _, a := range term {
+ varargs = append(varargs, a)
+ }
+ ret := m.ctrl.Call(m, "FTDictAdd", varargs...)
+ ret0, _ := ret[0].(*redis.IntCmd)
+ return ret0
+}
+
+// FTDictAdd indicates an expected call of FTDictAdd.
+func (mr *MockRedisMockRecorder) FTDictAdd(ctx, dict any, term ...any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ varargs := append([]any{ctx, dict}, term...)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FTDictAdd", reflect.TypeOf((*MockRedis)(nil).FTDictAdd), varargs...)
+}
+
+// FTDictDel mocks base method.
+func (m *MockRedis) FTDictDel(ctx context.Context, dict string, term ...any) *redis.IntCmd {
+ m.ctrl.T.Helper()
+ varargs := []any{ctx, dict}
+ for _, a := range term {
+ varargs = append(varargs, a)
+ }
+ ret := m.ctrl.Call(m, "FTDictDel", varargs...)
+ ret0, _ := ret[0].(*redis.IntCmd)
+ return ret0
+}
+
+// FTDictDel indicates an expected call of FTDictDel.
+func (mr *MockRedisMockRecorder) FTDictDel(ctx, dict any, term ...any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ varargs := append([]any{ctx, dict}, term...)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FTDictDel", reflect.TypeOf((*MockRedis)(nil).FTDictDel), varargs...)
+}
+
+// FTDictDump mocks base method.
+func (m *MockRedis) FTDictDump(ctx context.Context, dict string) *redis.StringSliceCmd {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "FTDictDump", ctx, dict)
+ ret0, _ := ret[0].(*redis.StringSliceCmd)
+ return ret0
+}
+
+// FTDictDump indicates an expected call of FTDictDump.
+func (mr *MockRedisMockRecorder) FTDictDump(ctx, dict any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FTDictDump", reflect.TypeOf((*MockRedis)(nil).FTDictDump), ctx, dict)
+}
+
+// FTDropIndex mocks base method.
+func (m *MockRedis) FTDropIndex(ctx context.Context, index string) *redis.StatusCmd {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "FTDropIndex", ctx, index)
+ ret0, _ := ret[0].(*redis.StatusCmd)
+ return ret0
+}
+
+// FTDropIndex indicates an expected call of FTDropIndex.
+func (mr *MockRedisMockRecorder) FTDropIndex(ctx, index any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FTDropIndex", reflect.TypeOf((*MockRedis)(nil).FTDropIndex), ctx, index)
+}
+
+// FTDropIndexWithArgs mocks base method.
+func (m *MockRedis) FTDropIndexWithArgs(ctx context.Context, index string, options *redis.FTDropIndexOptions) *redis.StatusCmd {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "FTDropIndexWithArgs", ctx, index, options)
+ ret0, _ := ret[0].(*redis.StatusCmd)
+ return ret0
+}
+
+// FTDropIndexWithArgs indicates an expected call of FTDropIndexWithArgs.
+func (mr *MockRedisMockRecorder) FTDropIndexWithArgs(ctx, index, options any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FTDropIndexWithArgs", reflect.TypeOf((*MockRedis)(nil).FTDropIndexWithArgs), ctx, index, options)
+}
+
+// FTExplain mocks base method.
+func (m *MockRedis) FTExplain(ctx context.Context, index, query string) *redis.StringCmd {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "FTExplain", ctx, index, query)
+ ret0, _ := ret[0].(*redis.StringCmd)
+ return ret0
+}
+
+// FTExplain indicates an expected call of FTExplain.
+func (mr *MockRedisMockRecorder) FTExplain(ctx, index, query any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FTExplain", reflect.TypeOf((*MockRedis)(nil).FTExplain), ctx, index, query)
+}
+
+// FTExplainWithArgs mocks base method.
+func (m *MockRedis) FTExplainWithArgs(ctx context.Context, index, query string, options *redis.FTExplainOptions) *redis.StringCmd {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "FTExplainWithArgs", ctx, index, query, options)
+ ret0, _ := ret[0].(*redis.StringCmd)
+ return ret0
+}
+
+// FTExplainWithArgs indicates an expected call of FTExplainWithArgs.
+func (mr *MockRedisMockRecorder) FTExplainWithArgs(ctx, index, query, options any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FTExplainWithArgs", reflect.TypeOf((*MockRedis)(nil).FTExplainWithArgs), ctx, index, query, options)
+}
+
+// FTInfo mocks base method.
+func (m *MockRedis) FTInfo(ctx context.Context, index string) *redis.FTInfoCmd {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "FTInfo", ctx, index)
+ ret0, _ := ret[0].(*redis.FTInfoCmd)
+ return ret0
+}
+
+// FTInfo indicates an expected call of FTInfo.
+func (mr *MockRedisMockRecorder) FTInfo(ctx, index any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FTInfo", reflect.TypeOf((*MockRedis)(nil).FTInfo), ctx, index)
+}
+
+// FTSearch mocks base method.
+func (m *MockRedis) FTSearch(ctx context.Context, index, query string) *redis.FTSearchCmd {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "FTSearch", ctx, index, query)
+ ret0, _ := ret[0].(*redis.FTSearchCmd)
+ return ret0
+}
+
+// FTSearch indicates an expected call of FTSearch.
+func (mr *MockRedisMockRecorder) FTSearch(ctx, index, query any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FTSearch", reflect.TypeOf((*MockRedis)(nil).FTSearch), ctx, index, query)
+}
+
+// FTSearchWithArgs mocks base method.
+func (m *MockRedis) FTSearchWithArgs(ctx context.Context, index, query string, options *redis.FTSearchOptions) *redis.FTSearchCmd {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "FTSearchWithArgs", ctx, index, query, options)
+ ret0, _ := ret[0].(*redis.FTSearchCmd)
+ return ret0
+}
+
+// FTSearchWithArgs indicates an expected call of FTSearchWithArgs.
+func (mr *MockRedisMockRecorder) FTSearchWithArgs(ctx, index, query, options any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FTSearchWithArgs", reflect.TypeOf((*MockRedis)(nil).FTSearchWithArgs), ctx, index, query, options)
+}
+
+// FTSpellCheck mocks base method.
+func (m *MockRedis) FTSpellCheck(ctx context.Context, index, query string) *redis.FTSpellCheckCmd {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "FTSpellCheck", ctx, index, query)
+ ret0, _ := ret[0].(*redis.FTSpellCheckCmd)
+ return ret0
+}
+
+// FTSpellCheck indicates an expected call of FTSpellCheck.
+func (mr *MockRedisMockRecorder) FTSpellCheck(ctx, index, query any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FTSpellCheck", reflect.TypeOf((*MockRedis)(nil).FTSpellCheck), ctx, index, query)
+}
+
+// FTSpellCheckWithArgs mocks base method.
+func (m *MockRedis) FTSpellCheckWithArgs(ctx context.Context, index, query string, options *redis.FTSpellCheckOptions) *redis.FTSpellCheckCmd {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "FTSpellCheckWithArgs", ctx, index, query, options)
+ ret0, _ := ret[0].(*redis.FTSpellCheckCmd)
+ return ret0
+}
+
+// FTSpellCheckWithArgs indicates an expected call of FTSpellCheckWithArgs.
+func (mr *MockRedisMockRecorder) FTSpellCheckWithArgs(ctx, index, query, options any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FTSpellCheckWithArgs", reflect.TypeOf((*MockRedis)(nil).FTSpellCheckWithArgs), ctx, index, query, options)
+}
+
+// FTSynDump mocks base method.
+func (m *MockRedis) FTSynDump(ctx context.Context, index string) *redis.FTSynDumpCmd {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "FTSynDump", ctx, index)
+ ret0, _ := ret[0].(*redis.FTSynDumpCmd)
+ return ret0
+}
+
+// FTSynDump indicates an expected call of FTSynDump.
+func (mr *MockRedisMockRecorder) FTSynDump(ctx, index any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FTSynDump", reflect.TypeOf((*MockRedis)(nil).FTSynDump), ctx, index)
+}
+
+// FTSynUpdate mocks base method.
+func (m *MockRedis) FTSynUpdate(ctx context.Context, index string, synGroupId any, terms []any) *redis.StatusCmd {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "FTSynUpdate", ctx, index, synGroupId, terms)
+ ret0, _ := ret[0].(*redis.StatusCmd)
+ return ret0
+}
+
+// FTSynUpdate indicates an expected call of FTSynUpdate.
+func (mr *MockRedisMockRecorder) FTSynUpdate(ctx, index, synGroupId, terms any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FTSynUpdate", reflect.TypeOf((*MockRedis)(nil).FTSynUpdate), ctx, index, synGroupId, terms)
+}
+
+// FTSynUpdateWithArgs mocks base method.
+func (m *MockRedis) FTSynUpdateWithArgs(ctx context.Context, index string, synGroupId any, options *redis.FTSynUpdateOptions, terms []any) *redis.StatusCmd {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "FTSynUpdateWithArgs", ctx, index, synGroupId, options, terms)
+ ret0, _ := ret[0].(*redis.StatusCmd)
+ return ret0
+}
+
+// FTSynUpdateWithArgs indicates an expected call of FTSynUpdateWithArgs.
+func (mr *MockRedisMockRecorder) FTSynUpdateWithArgs(ctx, index, synGroupId, options, terms any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FTSynUpdateWithArgs", reflect.TypeOf((*MockRedis)(nil).FTSynUpdateWithArgs), ctx, index, synGroupId, options, terms)
+}
+
+// FTTagVals mocks base method.
+func (m *MockRedis) FTTagVals(ctx context.Context, index, field string) *redis.StringSliceCmd {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "FTTagVals", ctx, index, field)
+ ret0, _ := ret[0].(*redis.StringSliceCmd)
+ return ret0
+}
+
+// FTTagVals indicates an expected call of FTTagVals.
+func (mr *MockRedisMockRecorder) FTTagVals(ctx, index, field any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FTTagVals", reflect.TypeOf((*MockRedis)(nil).FTTagVals), ctx, index, field)
+}
+
+// FT_List mocks base method.
+func (m *MockRedis) FT_List(ctx context.Context) *redis.StringSliceCmd {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "FT_List", ctx)
+ ret0, _ := ret[0].(*redis.StringSliceCmd)
+ return ret0
+}
+
+// FT_List indicates an expected call of FT_List.
+func (mr *MockRedisMockRecorder) FT_List(ctx any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FT_List", reflect.TypeOf((*MockRedis)(nil).FT_List), ctx)
+}
+
// FlushAll mocks base method.
func (m *MockRedis) FlushAll(ctx context.Context) *redis.StatusCmd {
m.ctrl.T.Helper()
@@ -8125,25 +8532,6 @@ func (m *MockCassandraBatchWithContext) EXPECT() *MockCassandraBatchWithContextM
return m.recorder
}
-// BatchQuery mocks base method.
-func (m *MockCassandraBatchWithContext) BatchQuery(name, stmt string, values ...any) error {
- m.ctrl.T.Helper()
- varargs := []any{name, stmt}
- for _, a := range values {
- varargs = append(varargs, a)
- }
- ret := m.ctrl.Call(m, "BatchQuery", varargs...)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// BatchQuery indicates an expected call of BatchQuery.
-func (mr *MockCassandraBatchWithContextMockRecorder) BatchQuery(name, stmt any, values ...any) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- varargs := append([]any{name, stmt}, values...)
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BatchQuery", reflect.TypeOf((*MockCassandraBatchWithContext)(nil).BatchQuery), varargs...)
-}
-
// BatchQueryWithCtx mocks base method.
func (m *MockCassandraBatchWithContext) BatchQueryWithCtx(ctx context.Context, name, stmt string, values ...any) error {
m.ctrl.T.Helper()
@@ -8163,40 +8551,6 @@ func (mr *MockCassandraBatchWithContextMockRecorder) BatchQueryWithCtx(ctx, name
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BatchQueryWithCtx", reflect.TypeOf((*MockCassandraBatchWithContext)(nil).BatchQueryWithCtx), varargs...)
}
-// ExecuteBatch mocks base method.
-func (m *MockCassandraBatchWithContext) ExecuteBatch(name string) error {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "ExecuteBatch", name)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// ExecuteBatch indicates an expected call of ExecuteBatch.
-func (mr *MockCassandraBatchWithContextMockRecorder) ExecuteBatch(name any) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecuteBatch", reflect.TypeOf((*MockCassandraBatchWithContext)(nil).ExecuteBatch), name)
-}
-
-// ExecuteBatchCAS mocks base method.
-func (m *MockCassandraBatchWithContext) ExecuteBatchCAS(name string, dest ...any) (bool, error) {
- m.ctrl.T.Helper()
- varargs := []any{name}
- for _, a := range dest {
- varargs = append(varargs, a)
- }
- ret := m.ctrl.Call(m, "ExecuteBatchCAS", varargs...)
- ret0, _ := ret[0].(bool)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// ExecuteBatchCAS indicates an expected call of ExecuteBatchCAS.
-func (mr *MockCassandraBatchWithContextMockRecorder) ExecuteBatchCAS(name any, dest ...any) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- varargs := append([]any{name}, dest...)
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecuteBatchCAS", reflect.TypeOf((*MockCassandraBatchWithContext)(nil).ExecuteBatchCAS), varargs...)
-}
-
// ExecuteBatchCASWithCtx mocks base method.
func (m *MockCassandraBatchWithContext) ExecuteBatchCASWithCtx(ctx context.Context, name string, dest ...any) (bool, error) {
m.ctrl.T.Helper()
diff --git a/pkg/gofr/context.go b/pkg/gofr/context.go
index 9858a1673..5788fd597 100644
--- a/pkg/gofr/context.go
+++ b/pkg/gofr/context.go
@@ -3,12 +3,14 @@ package gofr
import (
"context"
+ "github.com/golang-jwt/jwt/v5"
"github.com/gorilla/websocket"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/trace"
"gofr.dev/pkg/gofr/container"
+ "gofr.dev/pkg/gofr/http/middleware"
)
type Context struct {
@@ -28,6 +30,12 @@ type Context struct {
responder Responder
}
+type AuthInfo interface {
+ GetClaims() jwt.MapClaims
+ GetUsername() string
+ GetAPIKey() string
+}
+
/*
Trace returns an open telemetry span. We have to always close the span after corresponding work is done. Usages:
@@ -75,6 +83,49 @@ func (c *Context) WriteMessageToSocket(data any) error {
return conn.WriteMessage(websocket.TextMessage, message)
}
+type authInfo struct {
+ claims jwt.MapClaims
+ username string
+ apiKey string
+}
+
+// GetAuthInfo is a method on context, to access different methods to retrieve authentication info.
+//
+// GetAuthInfo().GetClaims() : retrieves the jwt claims.
+// GetAuthInfo().GetUsername() : retrieves the username while basic authentication.
+// GetAuthInfo().GetAPIKey() : retrieves the APIKey being used for authentication.
+func (c *Context) GetAuthInfo() AuthInfo {
+ claims, _ := c.Request.Context().Value(middleware.JWTClaim).(jwt.MapClaims)
+
+ APIKey, _ := c.Request.Context().Value(middleware.APIKey).(string)
+
+ username, _ := c.Request.Context().Value(middleware.Username).(string)
+
+ return &authInfo{
+ claims: claims,
+ username: username,
+ apiKey: APIKey,
+ }
+}
+
+// GetClaims returns a response of jwt.MapClaims type when OAuth is enabled.
+// It returns nil if called, when OAuth is not enabled.
+func (a *authInfo) GetClaims() jwt.MapClaims {
+ return a.claims
+}
+
+// GetUsername returns the username when basic auth is enabled.
+// It returns an empty string if called, when basic auth is not enabled.
+func (a *authInfo) GetUsername() string {
+ return a.username
+}
+
+// GetAPIKey returns the APIKey when APIKey auth is enabled.
+// It returns an empty strung if called, when APIKey auth is not enabled.
+func (a *authInfo) GetAPIKey() string {
+ return a.apiKey
+}
+
// func (c *Context) reset(w Responder, r Request) {
// c.Request = r
// c.responder = w
diff --git a/pkg/gofr/context_test.go b/pkg/gofr/context_test.go
index 599791bf4..9aa23958e 100644
--- a/pkg/gofr/context_test.go
+++ b/pkg/gofr/context_test.go
@@ -7,6 +7,7 @@ import (
"net/http/httptest"
"testing"
+ "github.com/golang-jwt/jwt/v5"
"github.com/gorilla/websocket"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -16,6 +17,7 @@ import (
"gofr.dev/pkg/gofr/config"
"gofr.dev/pkg/gofr/container"
gofrHTTP "gofr.dev/pkg/gofr/http"
+ "gofr.dev/pkg/gofr/http/middleware"
"gofr.dev/pkg/gofr/logging"
"gofr.dev/pkg/gofr/version"
)
@@ -109,3 +111,71 @@ func TestContext_WriteMessageToSocket(t *testing.T) {
expectedResponse := "Hello! GoFr"
assert.Equal(t, expectedResponse, string(message))
}
+
+func TestGetAuthInfo_BasicAuth(t *testing.T) {
+ req := httptest.NewRequest(http.MethodGet, "/", http.NoBody)
+
+ ctx := context.WithValue(req.Context(), middleware.Username, "validUser")
+ *req = *req.Clone(ctx)
+
+ mockContainer, _ := container.NewMockContainer(t)
+ gofrRq := gofrHTTP.NewRequest(req)
+
+ c := &Context{
+ Context: ctx,
+ Request: gofrRq,
+ Container: mockContainer,
+ }
+
+ res := c.GetAuthInfo().GetUsername()
+
+ assert.Equal(t, "validUser", res)
+}
+
+func TestGetAuthInfo_ApiKey(t *testing.T) {
+ req := httptest.NewRequest(http.MethodGet, "/", http.NoBody)
+
+ ctx := context.WithValue(req.Context(), middleware.APIKey, "9221e451-451f-4cd6-a23d-2b2d3adea9cf")
+
+ *req = *req.Clone(ctx)
+ gofrRq := gofrHTTP.NewRequest(req)
+
+ mockContainer, _ := container.NewMockContainer(t)
+
+ c := &Context{
+ Context: ctx,
+ Request: gofrRq,
+ Container: mockContainer,
+ }
+
+ res := c.GetAuthInfo().GetAPIKey()
+
+ assert.Equal(t, "9221e451-451f-4cd6-a23d-2b2d3adea9cf", res)
+}
+
+func TestGetAuthInfo_JWTClaims(t *testing.T) {
+ claims := jwt.MapClaims{
+ "sub": "1234567890",
+ "name": "John Doe",
+ "admin": true,
+ }
+
+ req := httptest.NewRequest(http.MethodGet, "/", http.NoBody)
+
+ ctx := context.WithValue(req.Context(), middleware.JWTClaim, claims)
+
+ *req = *req.Clone(ctx)
+ gofrRq := gofrHTTP.NewRequest(req)
+
+ mockContainer, _ := container.NewMockContainer(t)
+
+ c := &Context{
+ Context: ctx,
+ Request: gofrRq,
+ Container: mockContainer,
+ }
+
+ res := c.GetAuthInfo().GetClaims()
+
+ assert.Equal(t, claims, res)
+}
diff --git a/pkg/gofr/datasource/README.md b/pkg/gofr/datasource/README.md
index 6bc172eb1..aa34b8602 100644
--- a/pkg/gofr/datasource/README.md
+++ b/pkg/gofr/datasource/README.md
@@ -74,15 +74,15 @@ Therefore, GoFr utilizes a pluggable approach for new datasources by separating
| MySQL | β
| β
| β
| β
| |
| REDIS | β
| β
| β
| β
| |
| PostgreSQL | β
| β
| β
| β
| |
-| MongoDB | β
| β
| β
| | β
|
+| MongoDB | β
| β
| β
| β
| β
|
| SQLite | β
| β
| β
| β
| |
-| BadgerDB | β
| β
| | | β
|
-| Cassandra | β
| β
| β
| | β
|
-| Clickhouse | | β
| β
| | β
|
+| BadgerDB | β
| β
| β
| β
| β
|
+| Cassandra | β
| β
| β
| β
| β
|
+| Clickhouse | | β
| β
| β
| β
|
| FTP | | β
| | | β
|
| SFTP | | β
| | | β
|
-| Solr | | β
| β
| | β
|
-| DGraph | β
| β
|β
| ||
+| Solr | | β
| β
| β
| β
|
+| DGraph | β
| β
|β
| β
||
| Azure Eventhub | | β
|β
| |β
|
diff --git a/pkg/gofr/datasource/kv-store/badger/badger.go b/pkg/gofr/datasource/kv-store/badger/badger.go
index 8342bd33b..e8c4e93b0 100644
--- a/pkg/gofr/datasource/kv-store/badger/badger.go
+++ b/pkg/gofr/datasource/kv-store/badger/badger.go
@@ -3,14 +3,18 @@ package badger
import (
"context"
"errors"
+ "fmt"
"strings"
"time"
+ "go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
"github.com/dgraph-io/badger/v4"
)
+var errStatusDown = errors.New("status down")
+
type Configs struct {
DirPath string
}
@@ -63,8 +67,10 @@ func (c *client) Connect() {
c.db = db
}
-func (c *client) Get(_ context.Context, key string) (string, error) {
- defer c.sendOperationStats(time.Now(), "GET", key, "")
+func (c *client) Get(ctx context.Context, key string) (string, error) {
+ span := c.addTrace(ctx, "get", key)
+
+ defer c.sendOperationStats(time.Now(), "GET", "get", span, key)
var value []byte
@@ -88,7 +94,7 @@ func (c *client) Get(_ context.Context, key string) (string, error) {
err = txn.Commit()
if err != nil {
- c.logger.Debugf("error while commiting transaction: %v", err)
+ c.logger.Debugf("error while committing transaction: %v", err)
return "", err
}
@@ -96,16 +102,20 @@ func (c *client) Get(_ context.Context, key string) (string, error) {
return string(value), nil
}
-func (c *client) Set(_ context.Context, key, value string) error {
- defer c.sendOperationStats(time.Now(), "SET", key, value)
+func (c *client) Set(ctx context.Context, key, value string) error {
+ span := c.addTrace(ctx, "set", key)
+
+ defer c.sendOperationStats(time.Now(), "SET", "set", span, key, value)
return c.useTransaction(func(txn *badger.Txn) error {
return txn.Set([]byte(key), []byte(value))
})
}
-func (c *client) Delete(_ context.Context, key string) error {
- defer c.sendOperationStats(time.Now(), "DELETE", key, "")
+func (c *client) Delete(ctx context.Context, key string) error {
+ span := c.addTrace(ctx, "delete", key)
+
+ defer c.sendOperationStats(time.Now(), "DELETE", "delete", span, key, "")
return c.useTransaction(func(txn *badger.Txn) error {
return txn.Delete([]byte(key))
@@ -125,7 +135,7 @@ func (c *client) useTransaction(f func(txn *badger.Txn) error) error {
err = txn.Commit()
if err != nil {
- c.logger.Debugf("error while commiting transaction: %v", err)
+ c.logger.Debugf("error while committing transaction: %v", err)
return err
}
@@ -133,7 +143,8 @@ func (c *client) useTransaction(f func(txn *badger.Txn) error) error {
return nil
}
-func (c *client) sendOperationStats(start time.Time, methodType string, kv ...string) {
+func (c *client) sendOperationStats(start time.Time, methodType string, method string,
+ span trace.Span, kv ...string) {
duration := time.Since(start).Milliseconds()
c.logger.Debug(&Log{
@@ -142,6 +153,11 @@ func (c *client) sendOperationStats(start time.Time, methodType string, kv ...st
Key: strings.Join(kv, " "),
})
+ if span != nil {
+ defer span.End()
+ span.SetAttributes(attribute.Int64(fmt.Sprintf("badger.%v.duration(ΞΌs)", method), time.Since(start).Microseconds()))
+ }
+
c.metrics.RecordHistogram(context.Background(), "app_badger_stats", float64(duration), "database", c.configs.DirPath,
"type", methodType)
}
@@ -162,10 +178,24 @@ func (c *client) HealthCheck(context.Context) (any, error) {
if closed {
h.Status = "DOWN"
- return &h, errors.New("status down")
+ return &h, errStatusDown
}
h.Status = "UP"
return &h, nil
}
+
+func (c *client) addTrace(ctx context.Context, method, key string) trace.Span {
+ if c.tracer != nil {
+ _, span := c.tracer.Start(ctx, fmt.Sprintf("badger-%v", method))
+
+ span.SetAttributes(
+ attribute.String("badger.key", key),
+ )
+
+ return span
+ }
+
+ return nil
+}
diff --git a/pkg/gofr/datasource/kv-store/badger/badger_test.go b/pkg/gofr/datasource/kv-store/badger/badger_test.go
index cf355f83d..827d2b233 100644
--- a/pkg/gofr/datasource/kv-store/badger/badger_test.go
+++ b/pkg/gofr/datasource/kv-store/badger/badger_test.go
@@ -46,6 +46,7 @@ func Test_ClientGet(t *testing.T) {
cl := setupDB(t)
err := cl.Set(context.Background(), "lkey", "lvalue")
+ require.NoError(t, err)
val, err := cl.Get(context.Background(), "lkey")
@@ -58,7 +59,7 @@ func Test_ClientGetError(t *testing.T) {
val, err := cl.Get(context.Background(), "lkey")
- assert.EqualError(t, err, "Key not found")
+ require.EqualError(t, err, "Key not found")
assert.Empty(t, val)
}
diff --git a/pkg/gofr/datasource/kv-store/badger/go.mod b/pkg/gofr/datasource/kv-store/badger/go.mod
index 43cf8b6c0..bc706f6df 100644
--- a/pkg/gofr/datasource/kv-store/badger/go.mod
+++ b/pkg/gofr/datasource/kv-store/badger/go.mod
@@ -5,6 +5,7 @@ go 1.22
require (
github.com/dgraph-io/badger/v4 v4.2.0
github.com/stretchr/testify v1.9.0
+ go.opentelemetry.io/otel v1.30.0
go.opentelemetry.io/otel/trace v1.30.0
go.uber.org/mock v0.4.0
)
@@ -25,7 +26,6 @@ require (
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
go.opencensus.io v0.22.5 // indirect
- go.opentelemetry.io/otel v1.30.0 // indirect
golang.org/x/net v0.23.0 // indirect
golang.org/x/sys v0.18.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect
diff --git a/pkg/gofr/datasource/kv-store/badger/logger.go b/pkg/gofr/datasource/kv-store/badger/logger.go
index 073df9f4c..042295877 100644
--- a/pkg/gofr/datasource/kv-store/badger/logger.go
+++ b/pkg/gofr/datasource/kv-store/badger/logger.go
@@ -3,7 +3,6 @@ package badger
import (
"fmt"
"io"
- "strings"
)
type Logger interface {
@@ -24,5 +23,5 @@ type Log struct {
func (l *Log) PrettyPrint(writer io.Writer) {
fmt.Fprintf(writer, "\u001B[38;5;8m%-32s \u001B[38;5;162m%-6s\u001B[0m %8d\u001B[38;5;8mΒ΅s\u001B[0m %s \n",
- l.Type, "BADGR", l.Duration, strings.Join([]string{l.Key, l.Value}, " "))
+ l.Type, "BADGR", l.Duration, l.Key+" "+l.Value)
}
diff --git a/pkg/gofr/datasource/redis/redis.go b/pkg/gofr/datasource/redis/redis.go
index ac0893338..3db974e5f 100644
--- a/pkg/gofr/datasource/redis/redis.go
+++ b/pkg/gofr/datasource/redis/redis.go
@@ -20,6 +20,8 @@ const (
type Config struct {
HostName string
+ Username string
+ Password string
Port int
Options *redis.Options
}
@@ -75,6 +77,10 @@ func getRedisConfig(c config.Config) *Config {
redisConfig.HostName = c.Get("REDIS_HOST")
+ redisConfig.Username = c.Get("REDIS_USER")
+
+ redisConfig.Password = c.Get("REDIS_PASSWORD")
+
port, err := strconv.Atoi(c.Get("REDIS_PORT"))
if err != nil {
port = defaultRedisPort
@@ -88,6 +94,14 @@ func getRedisConfig(c config.Config) *Config {
options.Addr = fmt.Sprintf("%s:%d", redisConfig.HostName, redisConfig.Port)
}
+ if options.Username == "" {
+ options.Username = redisConfig.Username
+ }
+
+ if options.Password == "" {
+ options.Password = redisConfig.Password
+ }
+
redisConfig.Options = options
return redisConfig
diff --git a/pkg/gofr/external_db.go b/pkg/gofr/external_db.go
index 0c7d40853..11706715e 100644
--- a/pkg/gofr/external_db.go
+++ b/pkg/gofr/external_db.go
@@ -92,6 +92,10 @@ func (a *App) AddKVStore(db container.KVStoreProvider) {
db.UseLogger(a.Logger())
db.UseMetrics(a.Metrics())
+ tracer := otel.GetTracerProvider().Tracer("gofr-badger")
+
+ db.UseTracer(tracer)
+
db.Connect()
a.container.KVStore = db
diff --git a/pkg/gofr/external_db_test.go b/pkg/gofr/external_db_test.go
index f60f0b66e..7e2d38bc4 100644
--- a/pkg/gofr/external_db_test.go
+++ b/pkg/gofr/external_db_test.go
@@ -22,6 +22,7 @@ func TestApp_AddKVStore(t *testing.T) {
mock.EXPECT().UseLogger(app.Logger())
mock.EXPECT().UseMetrics(app.Metrics())
+ mock.EXPECT().UseTracer(otel.GetTracerProvider().Tracer("gofr-badger"))
mock.EXPECT().Connect()
app.AddKVStore(mock)
diff --git a/pkg/gofr/gofr.go b/pkg/gofr/gofr.go
index ab4ae49c4..6e7adc5be 100644
--- a/pkg/gofr/gofr.go
+++ b/pkg/gofr/gofr.go
@@ -88,6 +88,22 @@ func New() *App {
app.httpServer.certFile = app.Config.GetOrDefault("CERT_FILE", "")
app.httpServer.keyFile = app.Config.GetOrDefault("KEY_FILE", "")
+ // Add Default routes
+ app.add(http.MethodGet, "/.well-known/health", healthHandler)
+ app.add(http.MethodGet, "/.well-known/alive", liveHandler)
+ app.add(http.MethodGet, "/favicon.ico", faviconHandler)
+
+ // If the openapi.json file exists in the static directory, set up routes for OpenAPI and Swagger documentation.
+ if _, err = os.Stat("./static/" + gofrHTTP.DefaultSwaggerFileName); err == nil {
+ // Route to serve the OpenAPI JSON specification file.
+ app.add(http.MethodGet, "/.well-known/"+gofrHTTP.DefaultSwaggerFileName, OpenAPIHandler)
+ // Route to serve the Swagger UI, providing a user interface for the API documentation.
+ app.add(http.MethodGet, "/.well-known/swagger", SwaggerUIHandler)
+ // Catchall route: any request to /.well-known/{name} (e.g., /.well-known/other)
+ // will be handled by the SwaggerUIHandler, serving the Swagger UI.
+ app.add(http.MethodGet, "/.well-known/{name}", SwaggerUIHandler)
+ }
+
if app.Config.Get("APP_ENV") == "DEBUG" {
app.httpServer.RegisterProfilingRoutes()
}
@@ -226,17 +242,6 @@ func (a *App) Shutdown(ctx context.Context) error {
}
func (a *App) httpServerSetup() {
- // Add Default routes
- a.add(http.MethodGet, "/.well-known/health", healthHandler)
- a.add(http.MethodGet, "/.well-known/alive", liveHandler)
- a.add(http.MethodGet, "/favicon.ico", faviconHandler)
-
- if _, err := os.Stat("./static/openapi.json"); err == nil {
- a.add(http.MethodGet, "/.well-known/openapi.json", OpenAPIHandler)
- a.add(http.MethodGet, "/.well-known/swagger", SwaggerUIHandler)
- a.add(http.MethodGet, "/.well-known/{name}", SwaggerUIHandler)
- }
-
// TODO: find a way to read REQUEST_TIMEOUT config only once and log it there. currently doing it twice one for populating
// the value and other for logging
requestTimeout := a.Config.Get("REQUEST_TIMEOUT")
diff --git a/pkg/gofr/http/middleware/apikey_auth.go b/pkg/gofr/http/middleware/apikey_auth.go
index cf6961b90..5ed1210fb 100644
--- a/pkg/gofr/http/middleware/apikey_auth.go
+++ b/pkg/gofr/http/middleware/apikey_auth.go
@@ -3,6 +3,7 @@
package middleware
import (
+ "context"
"net/http"
"gofr.dev/pkg/gofr/container"
@@ -15,6 +16,8 @@ type APIKeyAuthProvider struct {
Container *container.Container
}
+const APIKey authMethod = 2
+
// APIKeyAuthMiddleware creates a middleware function that enforces API key authentication based on the provided API
// keys or a validation function.
func APIKeyAuthMiddleware(a APIKeyAuthProvider, apiKeys ...string) func(handler http.Handler) http.Handler {
@@ -36,6 +39,9 @@ func APIKeyAuthMiddleware(a APIKeyAuthProvider, apiKeys ...string) func(handler
return
}
+ ctx := context.WithValue(r.Context(), APIKey, authKey)
+ *r = *r.Clone(ctx)
+
handler.ServeHTTP(w, r)
})
}
diff --git a/pkg/gofr/http/middleware/basic_auth.go b/pkg/gofr/http/middleware/basic_auth.go
index 37390acb6..0f389f016 100644
--- a/pkg/gofr/http/middleware/basic_auth.go
+++ b/pkg/gofr/http/middleware/basic_auth.go
@@ -1,6 +1,7 @@
package middleware
import (
+ "context"
"encoding/base64"
"net/http"
"strings"
@@ -16,6 +17,8 @@ type BasicAuthProvider struct {
Container *container.Container
}
+const Username authMethod = 1
+
// BasicAuthMiddleware creates a middleware function that enforces basic authentication using the provided BasicAuthProvider.
func BasicAuthMiddleware(basicAuthProvider BasicAuthProvider) func(handler http.Handler) http.Handler {
return func(handler http.Handler) http.Handler {
@@ -54,6 +57,9 @@ func BasicAuthMiddleware(basicAuthProvider BasicAuthProvider) func(handler http.
return
}
+ ctx := context.WithValue(r.Context(), Username, username)
+ *r = *r.Clone(ctx)
+
handler.ServeHTTP(w, r)
})
}
diff --git a/pkg/gofr/http/router.go b/pkg/gofr/http/router.go
index 48c3ecee1..137e260f2 100644
--- a/pkg/gofr/http/router.go
+++ b/pkg/gofr/http/router.go
@@ -2,14 +2,16 @@ package http
import (
"net/http"
- "os"
"path/filepath"
"strings"
"github.com/gorilla/mux"
+
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
)
+const DefaultSwaggerFileName = "openapi.json"
+
// Router is responsible for routing HTTP request.
type Router struct {
mux.Router
@@ -56,6 +58,13 @@ func (rou *Router) AddStaticFiles(endpoint, dirName string) {
cfg := staticFileConfig{directoryName: dirName}
fileServer := http.FileServer(http.Dir(cfg.directoryName))
+
+ if endpoint == "/" {
+ rou.Router.NewRoute().PathPrefix("/").Handler(cfg.staticHandler(fileServer))
+
+ return
+ }
+
rou.Router.NewRoute().PathPrefix(endpoint + "/").Handler(http.StripPrefix(endpoint, cfg.staticHandler(fileServer)))
}
@@ -67,9 +76,11 @@ func (staticConfig staticFileConfig) staticHandler(fileServer http.Handler) http
fileName := filePath[len(filePath)-1]
- const defaultSwaggerFileName = "openapi.json"
-
- if _, err := os.Stat(filepath.Clean(filepath.Join(staticConfig.directoryName, url))); fileName == defaultSwaggerFileName && err == nil {
+ // Prevent direct access to the openapi.json file via static file routes.
+ // The file should only be accessible through the explicitly defined /.well-known/swagger or
+ // /.well-known/openapi.json for controlled access.
+ absPath, err := filepath.Abs(filepath.Join(staticConfig.directoryName, url))
+ if err != nil || !strings.HasPrefix(absPath, staticConfig.directoryName) || (fileName == DefaultSwaggerFileName && err == nil) {
w.WriteHeader(http.StatusForbidden)
_, _ = w.Write([]byte("403 forbidden"))
diff --git a/pkg/gofr/version/version.go b/pkg/gofr/version/version.go
index 56fcb12ae..7f5768459 100644
--- a/pkg/gofr/version/version.go
+++ b/pkg/gofr/version/version.go
@@ -1,3 +1,3 @@
package version
-const Framework = "v1.24.2"
+const Framework = "v1.25.0"