diff --git a/README.md b/README.md index 53729c272..215d6bae4 100644 --- a/README.md +++ b/README.md @@ -1,63 +1,75 @@ +

+ GoFr +

+

logo

-

-GoFr is an opinionated microservice development framework.

- -
- - - - - - - + +

GoFr: An Opinionated Microservice Development Framework

+ +
+godoc +gofr-docs +maintainability +test-coverage +Go Report Card +Apache 2.0 License +discord +
-Listed in [CNCF Landscape](https://landscape.cncf.io/?selected=go-fr). +

Listed in the CNCF Landscape

+ +## 🎯 **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"