diff --git a/.github/banner.gif b/.github/banner.gif
new file mode 100644
index 000000000..dccd5516f
Binary files /dev/null and b/.github/banner.gif differ
diff --git a/README.md b/README.md
index c9b15e35e..aa9ac3ed6 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,8 @@
-# GoFr
+
+GoFr is an opinionated microservice development framework.
@@ -13,30 +14,88 @@
-
-
-
-GoFr is an opinionated microservice development framework. Listed in [CNCF Landscape](https://landscape.cncf.io/?selected=go-fr).
-
-Visit https://gofr.dev for more details and documentation.
+Listed in [CNCF Landscape](https://landscape.cncf.io/?selected=go-fr).
## 🎯 Goal
Even though generic applications can be written using GoFr, our main focus is to simplify the development of microservices.
-We will focus ourselves towards deployment in Kubernetes and aspire to provide out-of-the-box observability.
+We will focus on deployment in Kubernetes and aspire to provide out-of-the-box observability.
-## 💡 Advantages/Features
+## 💡 Key Features
1. Simple API syntax
2. REST Standards by default
3. Configuration management
-4. Inbuilt Middlewares
-5. [gRPC support](https://gofr.dev/docs/advanced-guide/grpc)
-6. [HTTP service](https://gofr.dev/docs/advanced-guide/http-communication) with support for [Circuit Breaker](https://gofr.dev/docs/advanced-guide/circuit-breaker) and
-[Health Check](https://gofr.dev/docs/advanced-guide/monitoring-service-health)
+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)
+
+![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 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:
+
+```sh
+import "github.com/gofr-dev/gofr"
+```
+
+Alternatively, use `go get`:
+
+```sh
+go get -u github.com/gofr-dev/gofr
+```
+### Running GoFr
+A basic example:
+```go
+package main
+
+import "gofr.dev/pkg/gofr"
+
+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:8080
+}
+```
+
+To run the code, use the `go run` command, like:
+
+```sh
+$ go run main.go
+```
+
+Then visit [`localhost:8080/greet`](http://localhost:8080/greet) in your browser to see the response!
+
+### 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.
+
+## 👩💻Documentation
+See the [godocs](https://pkg.go.dev/gofr.dev).
+
+The documentation is also available on [gofr.dev](https://gofr.dev/docs).
## 👍 Contribute
If you want to say thank you and/or support the active development of GoFr:
-1. Add a [GitHub Star](https://github.com/gofr-dev/gofr/stargazers) to the project.
+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. After your PR is merged to the repo, fill the [Google Form](https://forms.gle/R1Yz7ZzY3U5WWTgy5), and we will send you a GoFr T-Shirt and Stickers as a token of appreciation.
+3. Visit [CONTRIBUTING](CONTRIBUTING.md) for details on submitting patches and the contribution workflow.
+
+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.
\ No newline at end of file
diff --git a/docs/advanced-guide/key-value-store/page.md b/docs/advanced-guide/key-value-store/page.md
new file mode 100644
index 000000000..f5f09c608
--- /dev/null
+++ b/docs/advanced-guide/key-value-store/page.md
@@ -0,0 +1,80 @@
+# Key Value Store
+
+A key-value store is a type of NoSQL database that uses a simple data model: each item is stored as a pair consisting of a unique key and a value.
+This simplicity offers high performance and scalability, making key-value stores ideal for applications requiring fast and efficient data retrieval and storage.
+
+GoFr supports BadgerDB as a key value store. Support for other key-value store will be added in the future.
+
+Keeping in mind the size of the application in the final build, it felt counter-productive to keep the drivers within
+the framework itself. GoFr provide the following functionalities for its key-value store.
+
+```go
+type KVStore interface {
+ Get(ctx context.Context, key string) (string, error)
+ Set(ctx context.Context, key, value string) error
+ Delete(ctx context.Context, key string) error
+}
+```
+
+## BadgerDB
+GoFr supports injecting BadgerDB that supports the following interface. Any driver that implements the interface can be added
+using `app.AddKVStore()` method, and user's can use BadgerDB across application with `gofr.Context`.
+
+User's can easily inject a driver that supports this interface, this provides usability without
+compromising the extensibility to use multiple databases.
+### Example
+```go
+package main
+
+import (
+ "fmt"
+
+ "gofr.dev/pkg/gofr"
+ "gofr.dev/pkg/gofr/datasource/kv-store/badger"
+)
+
+type User struct {
+ ID string
+ Name string
+ Age string
+}
+
+func main() {
+ app := gofr.New()
+
+ app.AddKVStore(badger.New(badger.Configs{DirPath: "badger-example"}))
+
+ app.POST("/user", Post)
+ app.GET("/user", Get)
+ app.DELETE("/user", Delete)
+
+ app.Run()
+}
+
+func Post(ctx *gofr.Context) (interface{}, error) {
+ err := ctx.KVStore.Set(ctx, "name", "gofr")
+ if err != nil {
+ return nil, err
+ }
+
+ return "Insertion to Key Value Store Successful", nil
+}
+
+func Get(ctx *gofr.Context) (interface{}, error) {
+ value, err := ctx.KVStore.Get(ctx, "name")
+ if err != nil {
+ return nil, err
+ }
+
+ return value, nil
+}
+
+func Delete(ctx *gofr.Context) (interface{}, error) {
+ err := ctx.KVStore.Delete(ctx, "name")
+ if err != nil {
+ return nil, err
+ }
+
+ return fmt.Sprintf("Deleted Successfully key %v from Key-Value Store", "name"), nil
+}
+```
diff --git a/docs/navigation.js b/docs/navigation.js
index e5002df63..b7ad88c1c 100644
--- a/docs/navigation.js
+++ b/docs/navigation.js
@@ -27,6 +27,7 @@ export const navigation = [
{ title: 'Writing gRPC Server', href: '/docs/advanced-guide/grpc' },
{ title: 'Using Pub/Sub', href: '/docs/advanced-guide/using-publisher-subscriber' },
{ title: 'Injecting Databases', href: '/docs/advanced-guide/injecting-databases-drivers' },
+ { title: 'Key Value Store', href: '/docs/advanced-guide/key-value-store' },
{ title: 'Dealing with SQL', href: '/docs/advanced-guide/dealing-with-sql' },
{ title: 'Automatic SwaggerUI Rendering', href: '/docs/advanced-guide/swagger-documentation' },
{ title: 'Error Handling',href: '/docs/advanced-guide/gofr-errors'},
diff --git a/docs/quick-start/observability/page.md b/docs/quick-start/observability/page.md
index 43f1ad2c1..b6ace0586 100644
--- a/docs/quick-start/observability/page.md
+++ b/docs/quick-start/observability/page.md
@@ -189,8 +189,7 @@ DB_PORT=3306
# tracing configs
TRACE_EXPORTER=zipkin
-TRACER_HOST=localhost
-TRACER_PORT=2005
+TRACER_URL=http://localhost:2005/api/v2/spans
LOG_LEVEL=DEBUG
```
@@ -220,8 +219,7 @@ Add Jaeger Tracer configs in `.env` file, your .env will be updated to
# tracing configs
TRACE_EXPORTER=jaeger
-TRACER_HOST=localhost
-TRACER_PORT=14317
+TRACER_URL=localhost:14317
```
Open {% new-tab-link title="zipkin" href="http://localhost:16686/trace/" /%} and search by TraceID (correlationID) to see the trace.
diff --git a/examples/using-add-rest-handlers/configs/.env b/examples/using-add-rest-handlers/configs/.env
index 3d354ee17..22e1d1d6c 100644
--- a/examples/using-add-rest-handlers/configs/.env
+++ b/examples/using-add-rest-handlers/configs/.env
@@ -9,5 +9,4 @@ DB_PORT=2001
DB_DIALECT=mysql
TRACE_EXPORTER=zipkin
-TRACER_HOST=localhost
-TRACER_PORT=2005
+TRACER_URL=http://localhost:2005/api/v2/spans
diff --git a/examples/using-migrations/configs/.env b/examples/using-migrations/configs/.env
index 869815bab..8d58388a2 100644
--- a/examples/using-migrations/configs/.env
+++ b/examples/using-migrations/configs/.env
@@ -16,5 +16,4 @@ PUBSUB_BROKER=localhost:9092
CONSUMER_ID=test
TRACE_EXPORTER=zipkin
-TRACER_HOST=localhost
-TRACER_PORT=2005
+TRACER_URL=http://localhost:2005/api/v2/spans
diff --git a/go.mod b/go.mod
index 0829af5b6..8b1b5c69e 100644
--- a/go.mod
+++ b/go.mod
@@ -21,7 +21,7 @@ require (
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.19.1
github.com/redis/go-redis/extra/redisotel/v9 v9.0.5
- github.com/redis/go-redis/v9 v9.5.1
+ github.com/redis/go-redis/v9 v9.5.4
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.53.0
@@ -38,18 +38,18 @@ require (
golang.org/x/oauth2 v0.21.0
golang.org/x/term v0.22.0
golang.org/x/text v0.16.0
- google.golang.org/api v0.187.0
+ google.golang.org/api v0.188.0
google.golang.org/grpc v1.65.0
google.golang.org/protobuf v1.34.2
- modernc.org/sqlite v1.30.1
+ modernc.org/sqlite v1.30.2
)
require (
cloud.google.com/go v0.115.0 // indirect
- cloud.google.com/go/auth v0.6.1 // indirect
+ cloud.google.com/go/auth v0.7.0 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.2 // indirect
- cloud.google.com/go/compute/metadata v0.3.0 // indirect
- cloud.google.com/go/iam v1.1.8 // indirect
+ cloud.google.com/go/compute/metadata v0.4.0 // indirect
+ cloud.google.com/go/iam v1.1.10 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a // indirect
github.com/beorn7/perks v1.0.1 // indirect
@@ -88,14 +88,14 @@ require (
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0 // indirect
go.opentelemetry.io/proto/otlp v1.2.0 // indirect
- golang.org/x/crypto v0.24.0 // indirect
- golang.org/x/net v0.26.0 // indirect
+ golang.org/x/crypto v0.25.0 // indirect
+ golang.org/x/net v0.27.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.22.0 // indirect
golang.org/x/time v0.5.0 // indirect
- google.golang.org/genproto v0.0.0-20240624140628-dc46fd24d27d // indirect
- google.golang.org/genproto/googleapis/api v0.0.0-20240617180043-68d350f18fd4 // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20240624140628-dc46fd24d27d // indirect
+ google.golang.org/genproto v0.0.0-20240708141625-4ad9e859172b // indirect
+ google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20240708141625-4ad9e859172b // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect
modernc.org/libc v1.52.1 // indirect
diff --git a/go.sum b/go.sum
index f8146f86c..948dadfda 100644
--- a/go.sum
+++ b/go.sum
@@ -1,18 +1,18 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.115.0 h1:CnFSK6Xo3lDYRoBKEcAtia6VSC837/ZkJuRduSFnr14=
cloud.google.com/go v0.115.0/go.mod h1:8jIM5vVgoAEoiVxQ/O4BFTfHqulPZgs/ufEzMcFMdWU=
-cloud.google.com/go/auth v0.6.1 h1:T0Zw1XM5c1GlpN2HYr2s+m3vr1p2wy+8VN+Z1FKxW38=
-cloud.google.com/go/auth v0.6.1/go.mod h1:eFHG7zDzbXHKmjJddFG/rBlcGp6t25SwRUiEQSlO4x4=
+cloud.google.com/go/auth v0.7.0 h1:kf/x9B3WTbBUHkC+1VS8wwwli9TzhSt0vSTVBmMR8Ts=
+cloud.google.com/go/auth v0.7.0/go.mod h1:D+WqdrpcjmiCgWrXmLLxOVq1GACoE36chW6KXoEvuIw=
cloud.google.com/go/auth/oauth2adapt v0.2.2 h1:+TTV8aXpjeChS9M+aTtN/TjdQnzJvmzKFt//oWu7HX4=
cloud.google.com/go/auth/oauth2adapt v0.2.2/go.mod h1:wcYjgpZI9+Yu7LyYBg4pqSiaRkfEK3GQcpb7C/uyF1Q=
-cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
-cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
-cloud.google.com/go/iam v1.1.8 h1:r7umDwhj+BQyz0ScZMp4QrGXjSTI3ZINnpgU2nlB/K0=
-cloud.google.com/go/iam v1.1.8/go.mod h1:GvE6lyMmfxXauzNq8NbgJbeVQNspG+tcdL/W8QO1+zE=
-cloud.google.com/go/kms v1.18.0 h1:pqNdaVmZJFP+i8OVLocjfpdTWETTYa20FWOegSCdrRo=
-cloud.google.com/go/kms v1.18.0/go.mod h1:DyRBeWD/pYBMeyiaXFa/DGNyxMDL3TslIKb8o/JkLkw=
-cloud.google.com/go/longrunning v0.5.7 h1:WLbHekDbjK1fVFD3ibpFFVoyizlLRl73I7YKuAKilhU=
-cloud.google.com/go/longrunning v0.5.7/go.mod h1:8GClkudohy1Fxm3owmBGid8W0pSgodEMwEAztp38Xng=
+cloud.google.com/go/compute/metadata v0.4.0 h1:vHzJCWaM4g8XIcm8kopr3XmDA4Gy/lblD3EhhSux05c=
+cloud.google.com/go/compute/metadata v0.4.0/go.mod h1:SIQh1Kkb4ZJ8zJ874fqVkslA29PRXuleyj6vOzlbK7M=
+cloud.google.com/go/iam v1.1.10 h1:ZSAr64oEhQSClwBL670MsJAW5/RLiC6kfw3Bqmd5ZDI=
+cloud.google.com/go/iam v1.1.10/go.mod h1:iEgMq62sg8zx446GCaijmA2Miwg5o3UbO+nI47WHJps=
+cloud.google.com/go/kms v1.18.2 h1:EGgD0B9k9tOOkbPhYW1PHo2W0teamAUYMOUIcDRMfPk=
+cloud.google.com/go/kms v1.18.2/go.mod h1:YFz1LYrnGsXARuRePL729oINmN5J/5e7nYijgvfiIeY=
+cloud.google.com/go/longrunning v0.5.9 h1:haH9pAuXdPAMqHvzX0zlWQigXT7B0+CL4/2nXXdBo5k=
+cloud.google.com/go/longrunning v0.5.9/go.mod h1:HD+0l9/OOW0za6UWdKJtXoFAX/BGg/3Wj8p10NeWF7c=
cloud.google.com/go/pubsub v1.40.0 h1:0LdP+zj5XaPAGtWr2V6r88VXJlmtaB/+fde1q3TU8M0=
cloud.google.com/go/pubsub v1.40.0/go.mod h1:BVJI4sI2FyXp36KFKvFwcfDRDfR8MiLT8mMhmIhdAeA=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
@@ -177,8 +177,8 @@ github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5/go.mod h1:fyalQWdtzDBECAQFBJu
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.5.1 h1:H1X4D3yHPaYrkL5X06Wh6xNVM/pX0Ft4RV0vMGvLBh8=
-github.com/redis/go-redis/v9 v9.5.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
+github.com/redis/go-redis/v9 v9.5.4 h1:vOFYDKKVgrI5u++QvnMT7DksSMYg7Aw/Np4vLJLKLwY=
+github.com/redis/go-redis/v9 v9.5.4/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
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.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
@@ -255,8 +255,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
-golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
-golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
+golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
+golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
@@ -282,8 +282,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
-golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
-golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
+golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
+golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
@@ -347,20 +347,20 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
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.187.0 h1:Mxs7VATVC2v7CY+7Xwm4ndkX71hpElcvx0D1Ji/p1eo=
-google.golang.org/api v0.187.0/go.mod h1:KIHlTc4x7N7gKKuVsdmfBXN13yEEWXWFURWY6SBp2gk=
+google.golang.org/api v0.188.0 h1:51y8fJ/b1AaaBRJr4yWm96fPcuxSo0JcegXE3DaHQHw=
+google.golang.org/api v0.188.0/go.mod h1:VR0d+2SIiWOYG3r/jdm7adPW9hI2aRv9ETOSCQ9Beag=
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-20240624140628-dc46fd24d27d h1:PksQg4dV6Sem3/HkBX+Ltq8T0ke0PKIRBNBatoDTVls=
-google.golang.org/genproto v0.0.0-20240624140628-dc46fd24d27d/go.mod h1:s7iA721uChleev562UJO2OYB0PPT9CMFjV+Ce7VJH5M=
-google.golang.org/genproto/googleapis/api v0.0.0-20240617180043-68d350f18fd4 h1:MuYw1wJzT+ZkybKfaOXKp5hJiZDn2iHaXRw0mRYdHSc=
-google.golang.org/genproto/googleapis/api v0.0.0-20240617180043-68d350f18fd4/go.mod h1:px9SlOOZBg1wM1zdnr8jEL4CNGUBZ+ZKYtNPApNQc4c=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20240624140628-dc46fd24d27d h1:k3zyW3BYYR30e8v3x0bTDdE9vpYFjZHK+HcyqkrppWk=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20240624140628-dc46fd24d27d/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
+google.golang.org/genproto v0.0.0-20240708141625-4ad9e859172b h1:dSTjko30weBaMj3eERKc0ZVXW4GudCswM3m+P++ukU0=
+google.golang.org/genproto v0.0.0-20240708141625-4ad9e859172b/go.mod h1:FfBgJBJg9GcpPvKIuHSZ/aE1g2ecGL74upMzGZjiGEY=
+google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 h1:0+ozOGcrp+Y8Aq8TLNN2Aliibms5LEzsq99ZZmAGYm0=
+google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094/go.mod h1:fJ/e3If/Q67Mj99hin0hMhiNyCRmt6BQ2aWIJshUSJw=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240708141625-4ad9e859172b h1:04+jVzTs2XBnOZcPsLnmrTGqltqJbZQ1Ey26hjYdQQ0=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240708141625-4ad9e859172b/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
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=
@@ -416,8 +416,8 @@ modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc=
modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss=
-modernc.org/sqlite v1.30.1 h1:YFhPVfu2iIgUf9kuA1CR7iiHdcEEsI2i+yjRYHscyxk=
-modernc.org/sqlite v1.30.1/go.mod h1:DUmsiWQDaAvU4abhc/N+djlom/L2o8f7gZ95RCvyoLU=
+modernc.org/sqlite v1.30.2 h1:IPVVkhLu5mMVnS1dQgh3h0SAACRWcVk7aoLP9Us3UCk=
+modernc.org/sqlite v1.30.2/go.mod h1:DUmsiWQDaAvU4abhc/N+djlom/L2o8f7gZ95RCvyoLU=
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
diff --git a/pkg/gofr/config/godotenv.go b/pkg/gofr/config/godotenv.go
index 10a5d7798..af9097f86 100644
--- a/pkg/gofr/config/godotenv.go
+++ b/pkg/gofr/config/godotenv.go
@@ -1,7 +1,9 @@
package config
import (
+ "errors"
"fmt"
+ "io/fs"
"os"
"github.com/joho/godotenv"
@@ -20,6 +22,7 @@ type logger interface {
Warnf(format string, a ...interface{})
Infof(format string, a ...interface{})
Debugf(format string, a ...interface{})
+ Fatalf(format string, a ...interface{})
}
func NewEnvFile(configFolder string, logger logger) Config {
@@ -38,33 +41,28 @@ func (e *EnvLoader) read(folder string) {
err := godotenv.Load(defaultFile)
if err != nil {
+ if !errors.Is(err, fs.ErrNotExist) {
+ e.logger.Fatalf("Failed to load config from file: %v, Err: %v", defaultFile, err)
+ }
+
e.logger.Warnf("Failed to load config from file: %v, Err: %v", defaultFile, err)
} else {
e.logger.Infof("Loaded config from file: %v", defaultFile)
}
- switch env {
- case "":
- // If 'APP_ENV' is not set, then GoFr will read '.env' from configs directory, and then it will be overwritten
- // by configs present in file '.local.env'
- err = godotenv.Overload(overrideFile)
- if err != nil {
- e.logger.Debugf("Failed to load config from file: %v, Err: %v", overrideFile, err)
- } else {
- e.logger.Infof("Loaded config from file: %v", overrideFile)
- }
-
- default:
+ if env != "" {
// If 'APP_ENV' is set to x, then GoFr will read '.env' from configs directory, and then it will be overwritten
// by configs present in file '.x.env'
overrideFile = fmt.Sprintf("%s/.%s.env", folder, env)
+ }
- err = godotenv.Overload(overrideFile)
- if err != nil {
- e.logger.Warnf("Failed to load config from file: %v, Err: %v", overrideFile, err)
- } else {
- e.logger.Infof("Loaded config from file: %v", overrideFile)
+ err = godotenv.Overload(overrideFile)
+ if err != nil {
+ if !errors.Is(err, fs.ErrNotExist) {
+ e.logger.Fatalf("Failed to load config from file: %v, Err: %v", overrideFile, err)
}
+ } else {
+ e.logger.Infof("Loaded config from file: %v", overrideFile)
}
}
diff --git a/pkg/gofr/config/godotenv_test.go b/pkg/gofr/config/godotenv_test.go
index 9d42b853f..7ea6d0c31 100644
--- a/pkg/gofr/config/godotenv_test.go
+++ b/pkg/gofr/config/godotenv_test.go
@@ -104,29 +104,32 @@ func Test_EnvFailureWithHyphen(t *testing.T) {
t.Error(err)
}
- // Call the function to create the .env file
- createEnvFile(t, ".env", envData)
-
defer os.RemoveAll("configs")
- env := NewEnvFile("configs", logger)
+ configFiles := []string{".env", ".local.env"}
- assert.Equal(t, "test", env.GetOrDefault("KEY-WITH-HYPHEN", "test"), "TEST Failed.\n godotenv failure with hyphen")
- assert.Equal(t, "", env.Get("UNABLE_TO_LOAD"), "TEST Failed.\n godotenv failure with hyphen")
+ for _, file := range configFiles {
+ createEnvFile(t, file, envData)
+
+ env := NewEnvFile("configs", logger)
+
+ assert.Equal(t, "test", env.GetOrDefault("KEY-WITH-HYPHEN", "test"), "TEST Failed.\n godotenv failure with hyphen")
+ assert.Equal(t, "", env.Get("UNABLE_TO_LOAD"), "TEST Failed.\n godotenv failure with hyphen")
+ }
}
func createEnvFile(t *testing.T, fileName string, envData map[string]string) {
t.Helper()
- // Create or open the .env file for writing
+ // Create or open the env file for writing
envFile, err := os.Create("configs/" + fileName)
if err != nil {
- t.Fatalf("error creating .env file: %v", err)
+ t.Fatalf("error creating %s file: %v", fileName, err)
}
defer envFile.Close()
- // Write data to the .env file
+ // Write data to the env file
for key, value := range envData {
_, err := fmt.Fprintf(envFile, "%s=%s\n", key, value)
if err != nil {
diff --git a/pkg/gofr/container/container.go b/pkg/gofr/container/container.go
index 056ce47cf..e298829af 100644
--- a/pkg/gofr/container/container.go
+++ b/pkg/gofr/container/container.go
@@ -43,6 +43,8 @@ type Container struct {
Clickhouse Clickhouse
Mongo Mongo
+ KVStore KVStore
+
File datasource.FileSystem
}
diff --git a/pkg/gofr/container/datasources.go b/pkg/gofr/container/datasources.go
index d1bed6525..2f44034ca 100644
--- a/pkg/gofr/container/datasources.go
+++ b/pkg/gofr/container/datasources.go
@@ -10,6 +10,8 @@ import (
gofrSQL "gofr.dev/pkg/gofr/datasource/sql"
)
+//go:generate go run go.uber.org/mock/mockgen -source=datasources.go -destination=mock_datasources.go -package=container
+
type DB interface {
Query(query string, args ...interface{}) (*sql.Rows, error)
QueryRow(query string, args ...interface{}) *sql.Row
@@ -199,3 +201,17 @@ type HealthChecker interface {
// It is done to avoid adding packages which are not being used.
HealthCheck(context.Context) (any, error)
}
+
+type KVStore interface {
+ Get(ctx context.Context, key string) (string, error)
+ Set(ctx context.Context, key, value string) error
+ Delete(ctx context.Context, key string) error
+
+ HealthChecker
+}
+
+type KVStoreProvider interface {
+ KVStore
+
+ provider
+}
diff --git a/pkg/gofr/container/health.go b/pkg/gofr/container/health.go
index d65cd9e41..307a24eec 100644
--- a/pkg/gofr/container/health.go
+++ b/pkg/gofr/container/health.go
@@ -84,6 +84,15 @@ func checkExternalDBHealth(ctx context.Context, c *Container, healthMap map[stri
healthMap["clickHouse"] = health
}
+ if !isNil(c.KVStore) {
+ health, err := c.KVStore.HealthCheck(ctx)
+ if err != nil {
+ downCount++
+ }
+
+ healthMap["kv-store"] = health
+ }
+
return downCount
}
diff --git a/pkg/gofr/container/health_test.go b/pkg/gofr/container/health_test.go
index 1c331353b..2228db855 100644
--- a/pkg/gofr/container/health_test.go
+++ b/pkg/gofr/container/health_test.go
@@ -34,6 +34,12 @@ func TestContainer_Health(t *testing.T) {
for i, tc := range tests {
expected := map[string]interface{}{
+ "kv-store": datasource.Health{
+ Status: tc.datasourceHealth, Details: map[string]interface{}{
+ "host": "localhost:1234",
+ "error": "kv-store not connected",
+ },
+ },
"redis": datasource.Health{
Status: tc.datasourceHealth, Details: map[string]interface{}{
"host": "localhost:6379",
@@ -141,4 +147,12 @@ func registerMocks(mocks Mocks, health string) {
"error": "clickhouse not connected",
},
}, nil)
+
+ mocks.KVStore.EXPECT().HealthCheck(context.Background()).Return(datasource.Health{
+ Status: health,
+ Details: map[string]interface{}{
+ "host": "localhost:1234",
+ "error": "kv-store not connected",
+ },
+ }, nil)
}
diff --git a/pkg/gofr/container/mock_container.go b/pkg/gofr/container/mock_container.go
index 84f6e4bce..608081cf2 100644
--- a/pkg/gofr/container/mock_container.go
+++ b/pkg/gofr/container/mock_container.go
@@ -19,6 +19,7 @@ type Mocks struct {
Clickhouse *MockClickhouse
Cassandra *MockCassandra
Mongo *MockMongo
+ KVStore *MockKVStore
}
func NewMockContainer(t *testing.T) (*Container, Mocks) {
@@ -44,12 +45,16 @@ func NewMockContainer(t *testing.T) (*Container, Mocks) {
mongoMock := NewMockMongo(ctrl)
container.Mongo = mongoMock
+ kvStoreMock := NewMockKVStore(ctrl)
+ container.KVStore = kvStoreMock
+
mocks := Mocks{
Redis: redisMock,
SQL: sqlMock,
Clickhouse: clickhouseMock,
Cassandra: cassandraMock,
Mongo: mongoMock,
+ KVStore: kvStoreMock,
}
mockMetrics := NewMockMetrics(ctrl)
diff --git a/pkg/gofr/container/mock_datasources.go b/pkg/gofr/container/mock_datasources.go
index fe01a93b2..a7f40a443 100644
--- a/pkg/gofr/container/mock_datasources.go
+++ b/pkg/gofr/container/mock_datasources.go
@@ -17,7 +17,6 @@ import (
redis "github.com/redis/go-redis/v9"
gomock "go.uber.org/mock/gomock"
-
datasource "gofr.dev/pkg/gofr/datasource"
sql0 "gofr.dev/pkg/gofr/datasource/sql"
)
@@ -806,6 +805,25 @@ func (mr *MockRedisMockRecorder) BitField(ctx, key any, values ...any) *gomock.C
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BitField", reflect.TypeOf((*MockRedis)(nil).BitField), varargs...)
}
+// BitFieldRO mocks base method.
+func (m *MockRedis) BitFieldRO(ctx context.Context, key string, values ...any) *redis.IntSliceCmd {
+ m.ctrl.T.Helper()
+ varargs := []any{ctx, key}
+ for _, a := range values {
+ varargs = append(varargs, a)
+ }
+ ret := m.ctrl.Call(m, "BitFieldRO", varargs...)
+ ret0, _ := ret[0].(*redis.IntSliceCmd)
+ return ret0
+}
+
+// BitFieldRO indicates an expected call of BitFieldRO.
+func (mr *MockRedisMockRecorder) BitFieldRO(ctx, key any, values ...any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ varargs := append([]any{ctx, key}, values...)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BitFieldRO", reflect.TypeOf((*MockRedis)(nil).BitFieldRO), varargs...)
+}
+
// BitOpAnd mocks base method.
func (m *MockRedis) BitOpAnd(ctx context.Context, destKey string, keys ...string) *redis.IntCmd {
m.ctrl.T.Helper()
@@ -2214,6 +2232,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, skipInitalScan bool, definition []any) *redis.StatusCmd {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "FTAlter", ctx, index, skipInitalScan, definition)
+ ret0, _ := ret[0].(*redis.StatusCmd)
+ return ret0
+}
+
+// FTAlter indicates an expected call of FTAlter.
+func (mr *MockRedisMockRecorder) FTAlter(ctx, index, skipInitalScan, definition any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FTAlter", reflect.TypeOf((*MockRedis)(nil).FTAlter), ctx, index, skipInitalScan, 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()
@@ -2670,30 +3095,125 @@ func (m *MockRedis) HDel(ctx context.Context, key string, fields ...string) *red
for _, a := range fields {
varargs = append(varargs, a)
}
- ret := m.ctrl.Call(m, "HDel", varargs...)
- ret0, _ := ret[0].(*redis.IntCmd)
+ ret := m.ctrl.Call(m, "HDel", varargs...)
+ ret0, _ := ret[0].(*redis.IntCmd)
+ return ret0
+}
+
+// HDel indicates an expected call of HDel.
+func (mr *MockRedisMockRecorder) HDel(ctx, key any, fields ...any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ varargs := append([]any{ctx, key}, fields...)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HDel", reflect.TypeOf((*MockRedis)(nil).HDel), varargs...)
+}
+
+// HExists mocks base method.
+func (m *MockRedis) HExists(ctx context.Context, key, field string) *redis.BoolCmd {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "HExists", ctx, key, field)
+ ret0, _ := ret[0].(*redis.BoolCmd)
+ return ret0
+}
+
+// HExists indicates an expected call of HExists.
+func (mr *MockRedisMockRecorder) HExists(ctx, key, field any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HExists", reflect.TypeOf((*MockRedis)(nil).HExists), ctx, key, field)
+}
+
+// HExpire mocks base method.
+func (m *MockRedis) HExpire(ctx context.Context, key string, expiration time.Duration, fields ...string) *redis.IntSliceCmd {
+ m.ctrl.T.Helper()
+ varargs := []any{ctx, key, expiration}
+ for _, a := range fields {
+ varargs = append(varargs, a)
+ }
+ ret := m.ctrl.Call(m, "HExpire", varargs...)
+ ret0, _ := ret[0].(*redis.IntSliceCmd)
+ return ret0
+}
+
+// HExpire indicates an expected call of HExpire.
+func (mr *MockRedisMockRecorder) HExpire(ctx, key, expiration any, fields ...any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ varargs := append([]any{ctx, key, expiration}, fields...)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HExpire", reflect.TypeOf((*MockRedis)(nil).HExpire), varargs...)
+}
+
+// HExpireAt mocks base method.
+func (m *MockRedis) HExpireAt(ctx context.Context, key string, tm time.Time, fields ...string) *redis.IntSliceCmd {
+ m.ctrl.T.Helper()
+ varargs := []any{ctx, key, tm}
+ for _, a := range fields {
+ varargs = append(varargs, a)
+ }
+ ret := m.ctrl.Call(m, "HExpireAt", varargs...)
+ ret0, _ := ret[0].(*redis.IntSliceCmd)
+ return ret0
+}
+
+// HExpireAt indicates an expected call of HExpireAt.
+func (mr *MockRedisMockRecorder) HExpireAt(ctx, key, tm any, fields ...any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ varargs := append([]any{ctx, key, tm}, fields...)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HExpireAt", reflect.TypeOf((*MockRedis)(nil).HExpireAt), varargs...)
+}
+
+// HExpireAtWithArgs mocks base method.
+func (m *MockRedis) HExpireAtWithArgs(ctx context.Context, key string, tm time.Time, expirationArgs redis.HExpireArgs, fields ...string) *redis.IntSliceCmd {
+ m.ctrl.T.Helper()
+ varargs := []any{ctx, key, tm, expirationArgs}
+ for _, a := range fields {
+ varargs = append(varargs, a)
+ }
+ ret := m.ctrl.Call(m, "HExpireAtWithArgs", varargs...)
+ ret0, _ := ret[0].(*redis.IntSliceCmd)
+ return ret0
+}
+
+// HExpireAtWithArgs indicates an expected call of HExpireAtWithArgs.
+func (mr *MockRedisMockRecorder) HExpireAtWithArgs(ctx, key, tm, expirationArgs any, fields ...any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ varargs := append([]any{ctx, key, tm, expirationArgs}, fields...)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HExpireAtWithArgs", reflect.TypeOf((*MockRedis)(nil).HExpireAtWithArgs), varargs...)
+}
+
+// HExpireTime mocks base method.
+func (m *MockRedis) HExpireTime(ctx context.Context, key string, fields ...string) *redis.IntSliceCmd {
+ m.ctrl.T.Helper()
+ varargs := []any{ctx, key}
+ for _, a := range fields {
+ varargs = append(varargs, a)
+ }
+ ret := m.ctrl.Call(m, "HExpireTime", varargs...)
+ ret0, _ := ret[0].(*redis.IntSliceCmd)
return ret0
}
-// HDel indicates an expected call of HDel.
-func (mr *MockRedisMockRecorder) HDel(ctx, key any, fields ...any) *gomock.Call {
+// HExpireTime indicates an expected call of HExpireTime.
+func (mr *MockRedisMockRecorder) HExpireTime(ctx, key any, fields ...any) *gomock.Call {
mr.mock.ctrl.T.Helper()
varargs := append([]any{ctx, key}, fields...)
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HDel", reflect.TypeOf((*MockRedis)(nil).HDel), varargs...)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HExpireTime", reflect.TypeOf((*MockRedis)(nil).HExpireTime), varargs...)
}
-// HExists mocks base method.
-func (m *MockRedis) HExists(ctx context.Context, key, field string) *redis.BoolCmd {
+// HExpireWithArgs mocks base method.
+func (m *MockRedis) HExpireWithArgs(ctx context.Context, key string, expiration time.Duration, expirationArgs redis.HExpireArgs, fields ...string) *redis.IntSliceCmd {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "HExists", ctx, key, field)
- ret0, _ := ret[0].(*redis.BoolCmd)
+ varargs := []any{ctx, key, expiration, expirationArgs}
+ for _, a := range fields {
+ varargs = append(varargs, a)
+ }
+ ret := m.ctrl.Call(m, "HExpireWithArgs", varargs...)
+ ret0, _ := ret[0].(*redis.IntSliceCmd)
return ret0
}
-// HExists indicates an expected call of HExists.
-func (mr *MockRedisMockRecorder) HExists(ctx, key, field any) *gomock.Call {
+// HExpireWithArgs indicates an expected call of HExpireWithArgs.
+func (mr *MockRedisMockRecorder) HExpireWithArgs(ctx, key, expiration, expirationArgs any, fields ...any) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HExists", reflect.TypeOf((*MockRedis)(nil).HExists), ctx, key, field)
+ varargs := append([]any{ctx, key, expiration, expirationArgs}, fields...)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HExpireWithArgs", reflect.TypeOf((*MockRedis)(nil).HExpireWithArgs), varargs...)
}
// HGet mocks base method.
@@ -2818,6 +3338,139 @@ func (mr *MockRedisMockRecorder) HMSet(ctx, key any, values ...any) *gomock.Call
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HMSet", reflect.TypeOf((*MockRedis)(nil).HMSet), varargs...)
}
+// HPExpire mocks base method.
+func (m *MockRedis) HPExpire(ctx context.Context, key string, expiration time.Duration, fields ...string) *redis.IntSliceCmd {
+ m.ctrl.T.Helper()
+ varargs := []any{ctx, key, expiration}
+ for _, a := range fields {
+ varargs = append(varargs, a)
+ }
+ ret := m.ctrl.Call(m, "HPExpire", varargs...)
+ ret0, _ := ret[0].(*redis.IntSliceCmd)
+ return ret0
+}
+
+// HPExpire indicates an expected call of HPExpire.
+func (mr *MockRedisMockRecorder) HPExpire(ctx, key, expiration any, fields ...any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ varargs := append([]any{ctx, key, expiration}, fields...)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HPExpire", reflect.TypeOf((*MockRedis)(nil).HPExpire), varargs...)
+}
+
+// HPExpireAt mocks base method.
+func (m *MockRedis) HPExpireAt(ctx context.Context, key string, tm time.Time, fields ...string) *redis.IntSliceCmd {
+ m.ctrl.T.Helper()
+ varargs := []any{ctx, key, tm}
+ for _, a := range fields {
+ varargs = append(varargs, a)
+ }
+ ret := m.ctrl.Call(m, "HPExpireAt", varargs...)
+ ret0, _ := ret[0].(*redis.IntSliceCmd)
+ return ret0
+}
+
+// HPExpireAt indicates an expected call of HPExpireAt.
+func (mr *MockRedisMockRecorder) HPExpireAt(ctx, key, tm any, fields ...any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ varargs := append([]any{ctx, key, tm}, fields...)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HPExpireAt", reflect.TypeOf((*MockRedis)(nil).HPExpireAt), varargs...)
+}
+
+// HPExpireAtWithArgs mocks base method.
+func (m *MockRedis) HPExpireAtWithArgs(ctx context.Context, key string, tm time.Time, expirationArgs redis.HExpireArgs, fields ...string) *redis.IntSliceCmd {
+ m.ctrl.T.Helper()
+ varargs := []any{ctx, key, tm, expirationArgs}
+ for _, a := range fields {
+ varargs = append(varargs, a)
+ }
+ ret := m.ctrl.Call(m, "HPExpireAtWithArgs", varargs...)
+ ret0, _ := ret[0].(*redis.IntSliceCmd)
+ return ret0
+}
+
+// HPExpireAtWithArgs indicates an expected call of HPExpireAtWithArgs.
+func (mr *MockRedisMockRecorder) HPExpireAtWithArgs(ctx, key, tm, expirationArgs any, fields ...any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ varargs := append([]any{ctx, key, tm, expirationArgs}, fields...)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HPExpireAtWithArgs", reflect.TypeOf((*MockRedis)(nil).HPExpireAtWithArgs), varargs...)
+}
+
+// HPExpireTime mocks base method.
+func (m *MockRedis) HPExpireTime(ctx context.Context, key string, fields ...string) *redis.IntSliceCmd {
+ m.ctrl.T.Helper()
+ varargs := []any{ctx, key}
+ for _, a := range fields {
+ varargs = append(varargs, a)
+ }
+ ret := m.ctrl.Call(m, "HPExpireTime", varargs...)
+ ret0, _ := ret[0].(*redis.IntSliceCmd)
+ return ret0
+}
+
+// HPExpireTime indicates an expected call of HPExpireTime.
+func (mr *MockRedisMockRecorder) HPExpireTime(ctx, key any, fields ...any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ varargs := append([]any{ctx, key}, fields...)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HPExpireTime", reflect.TypeOf((*MockRedis)(nil).HPExpireTime), varargs...)
+}
+
+// HPExpireWithArgs mocks base method.
+func (m *MockRedis) HPExpireWithArgs(ctx context.Context, key string, expiration time.Duration, expirationArgs redis.HExpireArgs, fields ...string) *redis.IntSliceCmd {
+ m.ctrl.T.Helper()
+ varargs := []any{ctx, key, expiration, expirationArgs}
+ for _, a := range fields {
+ varargs = append(varargs, a)
+ }
+ ret := m.ctrl.Call(m, "HPExpireWithArgs", varargs...)
+ ret0, _ := ret[0].(*redis.IntSliceCmd)
+ return ret0
+}
+
+// HPExpireWithArgs indicates an expected call of HPExpireWithArgs.
+func (mr *MockRedisMockRecorder) HPExpireWithArgs(ctx, key, expiration, expirationArgs any, fields ...any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ varargs := append([]any{ctx, key, expiration, expirationArgs}, fields...)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HPExpireWithArgs", reflect.TypeOf((*MockRedis)(nil).HPExpireWithArgs), varargs...)
+}
+
+// HPTTL mocks base method.
+func (m *MockRedis) HPTTL(ctx context.Context, key string, fields ...string) *redis.IntSliceCmd {
+ m.ctrl.T.Helper()
+ varargs := []any{ctx, key}
+ for _, a := range fields {
+ varargs = append(varargs, a)
+ }
+ ret := m.ctrl.Call(m, "HPTTL", varargs...)
+ ret0, _ := ret[0].(*redis.IntSliceCmd)
+ return ret0
+}
+
+// HPTTL indicates an expected call of HPTTL.
+func (mr *MockRedisMockRecorder) HPTTL(ctx, key any, fields ...any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ varargs := append([]any{ctx, key}, fields...)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HPTTL", reflect.TypeOf((*MockRedis)(nil).HPTTL), varargs...)
+}
+
+// HPersist mocks base method.
+func (m *MockRedis) HPersist(ctx context.Context, key string, fields ...string) *redis.IntSliceCmd {
+ m.ctrl.T.Helper()
+ varargs := []any{ctx, key}
+ for _, a := range fields {
+ varargs = append(varargs, a)
+ }
+ ret := m.ctrl.Call(m, "HPersist", varargs...)
+ ret0, _ := ret[0].(*redis.IntSliceCmd)
+ return ret0
+}
+
+// HPersist indicates an expected call of HPersist.
+func (mr *MockRedisMockRecorder) HPersist(ctx, key any, fields ...any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ varargs := append([]any{ctx, key}, fields...)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HPersist", reflect.TypeOf((*MockRedis)(nil).HPersist), varargs...)
+}
+
// HRandField mocks base method.
func (m *MockRedis) HRandField(ctx context.Context, key string, count int) *redis.StringSliceCmd {
m.ctrl.T.Helper()
@@ -2860,6 +3513,20 @@ func (mr *MockRedisMockRecorder) HScan(ctx, key, cursor, match, count any) *gomo
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HScan", reflect.TypeOf((*MockRedis)(nil).HScan), ctx, key, cursor, match, count)
}
+// HScanNoValues mocks base method.
+func (m *MockRedis) HScanNoValues(ctx context.Context, key string, cursor uint64, match string, count int64) *redis.ScanCmd {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "HScanNoValues", ctx, key, cursor, match, count)
+ ret0, _ := ret[0].(*redis.ScanCmd)
+ return ret0
+}
+
+// HScanNoValues indicates an expected call of HScanNoValues.
+func (mr *MockRedisMockRecorder) HScanNoValues(ctx, key, cursor, match, count any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HScanNoValues", reflect.TypeOf((*MockRedis)(nil).HScanNoValues), ctx, key, cursor, match, count)
+}
+
// HSet mocks base method.
func (m *MockRedis) HSet(ctx context.Context, key string, values ...any) *redis.IntCmd {
m.ctrl.T.Helper()
@@ -2893,6 +3560,25 @@ func (mr *MockRedisMockRecorder) HSetNX(ctx, key, field, value any) *gomock.Call
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HSetNX", reflect.TypeOf((*MockRedis)(nil).HSetNX), ctx, key, field, value)
}
+// HTTL mocks base method.
+func (m *MockRedis) HTTL(ctx context.Context, key string, fields ...string) *redis.IntSliceCmd {
+ m.ctrl.T.Helper()
+ varargs := []any{ctx, key}
+ for _, a := range fields {
+ varargs = append(varargs, a)
+ }
+ ret := m.ctrl.Call(m, "HTTL", varargs...)
+ ret0, _ := ret[0].(*redis.IntSliceCmd)
+ return ret0
+}
+
+// HTTL indicates an expected call of HTTL.
+func (mr *MockRedisMockRecorder) HTTL(ctx, key any, fields ...any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ varargs := append([]any{ctx, key}, fields...)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HTTL", reflect.TypeOf((*MockRedis)(nil).HTTL), varargs...)
+}
+
// HVals mocks base method.
func (m *MockRedis) HVals(ctx context.Context, key string) *redis.StringSliceCmd {
m.ctrl.T.Helper()
@@ -8385,3 +9071,201 @@ func (mr *MockHealthCheckerMockRecorder) HealthCheck(arg0 any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HealthCheck", reflect.TypeOf((*MockHealthChecker)(nil).HealthCheck), arg0)
}
+
+// MockKVStore is a mock of KVStore interface.
+type MockKVStore struct {
+ ctrl *gomock.Controller
+ recorder *MockKVStoreMockRecorder
+}
+
+// MockKVStoreMockRecorder is the mock recorder for MockKVStore.
+type MockKVStoreMockRecorder struct {
+ mock *MockKVStore
+}
+
+// NewMockKVStore creates a new mock instance.
+func NewMockKVStore(ctrl *gomock.Controller) *MockKVStore {
+ mock := &MockKVStore{ctrl: ctrl}
+ mock.recorder = &MockKVStoreMockRecorder{mock}
+ return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use.
+func (m *MockKVStore) EXPECT() *MockKVStoreMockRecorder {
+ return m.recorder
+}
+
+// Delete mocks base method.
+func (m *MockKVStore) Delete(ctx context.Context, key string) error {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "Delete", ctx, key)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// Delete indicates an expected call of Delete.
+func (mr *MockKVStoreMockRecorder) Delete(ctx, key any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockKVStore)(nil).Delete), ctx, key)
+}
+
+// Get mocks base method.
+func (m *MockKVStore) Get(ctx context.Context, key string) (string, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "Get", ctx, key)
+ ret0, _ := ret[0].(string)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// Get indicates an expected call of Get.
+func (mr *MockKVStoreMockRecorder) Get(ctx, key any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockKVStore)(nil).Get), ctx, key)
+}
+
+// HealthCheck mocks base method.
+func (m *MockKVStore) HealthCheck(arg0 context.Context) (any, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "HealthCheck", arg0)
+ ret0, _ := ret[0].(any)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// HealthCheck indicates an expected call of HealthCheck.
+func (mr *MockKVStoreMockRecorder) HealthCheck(arg0 any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HealthCheck", reflect.TypeOf((*MockKVStore)(nil).HealthCheck), arg0)
+}
+
+// Set mocks base method.
+func (m *MockKVStore) Set(ctx context.Context, key, value string) error {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "Set", ctx, key, value)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// Set indicates an expected call of Set.
+func (mr *MockKVStoreMockRecorder) Set(ctx, key, value any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Set", reflect.TypeOf((*MockKVStore)(nil).Set), ctx, key, value)
+}
+
+// MockKVStoreProvider is a mock of KVStoreProvider interface.
+type MockKVStoreProvider struct {
+ ctrl *gomock.Controller
+ recorder *MockKVStoreProviderMockRecorder
+}
+
+// MockKVStoreProviderMockRecorder is the mock recorder for MockKVStoreProvider.
+type MockKVStoreProviderMockRecorder struct {
+ mock *MockKVStoreProvider
+}
+
+// NewMockKVStoreProvider creates a new mock instance.
+func NewMockKVStoreProvider(ctrl *gomock.Controller) *MockKVStoreProvider {
+ mock := &MockKVStoreProvider{ctrl: ctrl}
+ mock.recorder = &MockKVStoreProviderMockRecorder{mock}
+ return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use.
+func (m *MockKVStoreProvider) EXPECT() *MockKVStoreProviderMockRecorder {
+ return m.recorder
+}
+
+// Connect mocks base method.
+func (m *MockKVStoreProvider) Connect() {
+ m.ctrl.T.Helper()
+ m.ctrl.Call(m, "Connect")
+}
+
+// Connect indicates an expected call of Connect.
+func (mr *MockKVStoreProviderMockRecorder) Connect() *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Connect", reflect.TypeOf((*MockKVStoreProvider)(nil).Connect))
+}
+
+// Delete mocks base method.
+func (m *MockKVStoreProvider) Delete(ctx context.Context, key string) error {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "Delete", ctx, key)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// Delete indicates an expected call of Delete.
+func (mr *MockKVStoreProviderMockRecorder) Delete(ctx, key any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockKVStoreProvider)(nil).Delete), ctx, key)
+}
+
+// Get mocks base method.
+func (m *MockKVStoreProvider) Get(ctx context.Context, key string) (string, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "Get", ctx, key)
+ ret0, _ := ret[0].(string)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// Get indicates an expected call of Get.
+func (mr *MockKVStoreProviderMockRecorder) Get(ctx, key any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockKVStoreProvider)(nil).Get), ctx, key)
+}
+
+// HealthCheck mocks base method.
+func (m *MockKVStoreProvider) HealthCheck(arg0 context.Context) (any, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "HealthCheck", arg0)
+ ret0, _ := ret[0].(any)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// HealthCheck indicates an expected call of HealthCheck.
+func (mr *MockKVStoreProviderMockRecorder) HealthCheck(arg0 any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HealthCheck", reflect.TypeOf((*MockKVStoreProvider)(nil).HealthCheck), arg0)
+}
+
+// Set mocks base method.
+func (m *MockKVStoreProvider) Set(ctx context.Context, key, value string) error {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "Set", ctx, key, value)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// Set indicates an expected call of Set.
+func (mr *MockKVStoreProviderMockRecorder) Set(ctx, key, value any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Set", reflect.TypeOf((*MockKVStoreProvider)(nil).Set), ctx, key, value)
+}
+
+// UseLogger mocks base method.
+func (m *MockKVStoreProvider) UseLogger(logger any) {
+ m.ctrl.T.Helper()
+ m.ctrl.Call(m, "UseLogger", logger)
+}
+
+// UseLogger indicates an expected call of UseLogger.
+func (mr *MockKVStoreProviderMockRecorder) UseLogger(logger any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UseLogger", reflect.TypeOf((*MockKVStoreProvider)(nil).UseLogger), logger)
+}
+
+// UseMetrics mocks base method.
+func (m *MockKVStoreProvider) UseMetrics(metrics any) {
+ m.ctrl.T.Helper()
+ m.ctrl.Call(m, "UseMetrics", metrics)
+}
+
+// UseMetrics indicates an expected call of UseMetrics.
+func (mr *MockKVStoreProviderMockRecorder) UseMetrics(metrics any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UseMetrics", reflect.TypeOf((*MockKVStoreProvider)(nil).UseMetrics), metrics)
+}
diff --git a/pkg/gofr/datasource/kv-store/badger/badger.go b/pkg/gofr/datasource/kv-store/badger/badger.go
new file mode 100644
index 000000000..7cc3b4818
--- /dev/null
+++ b/pkg/gofr/datasource/kv-store/badger/badger.go
@@ -0,0 +1,161 @@
+package badger
+
+import (
+ "context"
+ "errors"
+ "strings"
+ "time"
+
+ "github.com/dgraph-io/badger/v4"
+)
+
+type Configs struct {
+ DirPath string
+}
+
+type client struct {
+ db *badger.DB
+ configs Configs
+ logger Logger
+ metrics Metrics
+}
+
+func New(configs Configs) *client {
+ return &client{configs: configs}
+}
+
+// UseLogger sets the logger for the BadgerDB client which asserts the Logger interface.
+func (c *client) UseLogger(logger any) {
+ if l, ok := logger.(Logger); ok {
+ c.logger = l
+ }
+}
+
+// UseMetrics sets the metrics for the BadgerDB client which asserts the Metrics interface.
+func (c *client) UseMetrics(metrics any) {
+ if m, ok := metrics.(Metrics); ok {
+ c.metrics = m
+ }
+}
+
+// Connect establishes a connection to BadgerDB and registers metrics using the provided configuration when the client was Created.
+func (c *client) Connect() {
+ c.logger.Infof("connecting to BadgerDB at %v", c.configs.DirPath)
+
+ badgerBuckets := []float64{.05, .075, .1, .125, .15, .2, .3, .5, .75, 1, 2, 3, 4, 5, 7.5, 10}
+ c.metrics.NewHistogram("app_badger_stats", "Response time of Badger queries in milliseconds.", badgerBuckets...)
+
+ db, err := badger.Open(badger.DefaultOptions(c.configs.DirPath))
+ if err != nil {
+ c.logger.Errorf("error while connecting to BadgerDB: %v", err)
+ }
+
+ c.db = db
+}
+
+func (c *client) Get(_ context.Context, key string) (string, error) {
+ defer c.logQueryAndSendMetrics(time.Now(), "GET", key, "")
+
+ var value []byte
+
+ // transaction is set to false as we don't want to make any changes to data.
+ txn := c.db.NewTransaction(false)
+ defer txn.Discard()
+
+ item, err := txn.Get([]byte(key))
+ if err != nil {
+ c.logger.Debugf("error while fetching data for key: %v, error: %v", key, err)
+
+ return "", err
+ }
+
+ value, err = item.ValueCopy(nil)
+ if err != nil {
+ c.logger.Debugf("error while reading value for key: %v, error: %v", key, err)
+
+ return "", err
+ }
+
+ err = txn.Commit()
+ if err != nil {
+ c.logger.Debugf("error while commiting transaction: %v", err)
+
+ return "", err
+ }
+
+ return string(value), nil
+}
+
+func (c *client) Set(_ context.Context, key, value string) error {
+ defer c.logQueryAndSendMetrics(time.Now(), "SET", 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.logQueryAndSendMetrics(time.Now(), "DELETE", key, "")
+
+ return c.useTransaction(func(txn *badger.Txn) error {
+ return txn.Delete([]byte(key))
+ })
+}
+
+func (c *client) useTransaction(f func(txn *badger.Txn) error) error {
+ txn := c.db.NewTransaction(true)
+ defer txn.Discard()
+
+ err := f(txn)
+ if err != nil {
+ c.logger.Debugf("error while executing transaction: %v", err)
+
+ return err
+ }
+
+ err = txn.Commit()
+ if err != nil {
+ c.logger.Debugf("error while commiting transaction: %v", err)
+
+ return err
+ }
+
+ return nil
+}
+
+func (c *client) logQueryAndSendMetrics(start time.Time, methodType string, kv ...string) {
+ duration := time.Since(start).Milliseconds()
+
+ c.logger.Debug(&Log{
+ Type: methodType,
+ Duration: duration,
+ Key: strings.Join(kv, " "),
+ })
+
+ c.metrics.RecordHistogram(context.Background(), "app_badger_stats", float64(duration), "database", c.configs.DirPath,
+ "type", methodType)
+}
+
+type Health struct {
+ Status string `json:"status,omitempty"`
+ Details map[string]any `json:"details,omitempty"`
+}
+
+func (c *client) HealthCheck(context.Context) (any, error) {
+ h := Health{
+ Details: make(map[string]any),
+ }
+
+ h.Details["location"] = c.configs.DirPath
+
+ closed := c.db.IsClosed()
+ if closed {
+ h.Status = "DOWN"
+
+ return &h, errors.New("status down")
+ }
+
+ h.Status = "UP"
+
+ return &h, nil
+}
diff --git a/pkg/gofr/datasource/kv-store/badger/badger_test.go b/pkg/gofr/datasource/kv-store/badger/badger_test.go
new file mode 100644
index 000000000..66b9f7736
--- /dev/null
+++ b/pkg/gofr/datasource/kv-store/badger/badger_test.go
@@ -0,0 +1,77 @@
+package badger
+
+import (
+ "bytes"
+ "context"
+ "fmt"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "go.uber.org/mock/gomock"
+)
+
+func setupDB(t *testing.T) *client {
+ t.Helper()
+ cl := New(Configs{DirPath: t.TempDir()})
+
+ var logs []byte
+
+ ctrl := gomock.NewController(t)
+ mockMetrics := NewMockMetrics(ctrl)
+
+ mockMetrics.EXPECT().NewHistogram("app_badger_stats", "Response time of Badger queries in milliseconds.", gomock.Any())
+
+ mockMetrics.EXPECT().RecordHistogram(gomock.Any(), "app_badger_stats", gomock.Any(), "database", cl.configs.DirPath,
+ "type", gomock.Any()).AnyTimes()
+
+ cl.UseLogger(NewMockLogger(DEBUG, bytes.NewBuffer(logs)))
+ cl.UseMetrics(mockMetrics)
+ cl.Connect()
+
+ return cl
+}
+
+func Test_ClientSet(t *testing.T) {
+ cl := setupDB(t)
+
+ err := cl.Set(context.Background(), "lkey", "lvalue")
+
+ assert.NoError(t, err)
+}
+
+func Test_ClientGet(t *testing.T) {
+ cl := setupDB(t)
+
+ err := cl.Set(context.Background(), "lkey", "lvalue")
+
+ val, err := cl.Get(context.Background(), "lkey")
+
+ assert.NoError(t, err)
+ assert.Equal(t, "lvalue", val)
+}
+
+func Test_ClientGetError(t *testing.T) {
+ cl := setupDB(t)
+
+ val, err := cl.Get(context.Background(), "lkey")
+
+ assert.EqualError(t, err, "Key not found")
+ assert.Empty(t, val)
+}
+
+func Test_ClientDeleteSuccessError(t *testing.T) {
+ cl := setupDB(t)
+
+ err := cl.Delete(context.Background(), "lkey")
+
+ assert.NoError(t, err)
+}
+
+func Test_ClientHealthCheck(t *testing.T) {
+ cl := setupDB(t)
+
+ val, err := cl.HealthCheck(context.Background())
+
+ assert.NoError(t, err)
+ assert.Contains(t, fmt.Sprint(val), "UP")
+}
diff --git a/pkg/gofr/datasource/kv-store/badger/go.mod b/pkg/gofr/datasource/kv-store/badger/go.mod
new file mode 100644
index 000000000..fad00618b
--- /dev/null
+++ b/pkg/gofr/datasource/kv-store/badger/go.mod
@@ -0,0 +1,31 @@
+module gofr.dev/pkg/gofr/datasource/kv-store/badger
+
+go 1.22
+
+require (
+ github.com/dgraph-io/badger/v4 v4.2.0
+ github.com/stretchr/testify v1.9.0
+ go.uber.org/mock v0.4.0
+)
+
+require (
+ github.com/cespare/xxhash/v2 v2.2.0 // indirect
+ github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/dgraph-io/ristretto v0.1.1 // indirect
+ github.com/dustin/go-humanize v1.0.0 // indirect
+ github.com/gogo/protobuf v1.3.2 // indirect
+ github.com/golang/glog v1.0.0 // indirect
+ github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 // indirect
+ github.com/golang/protobuf v1.5.2 // indirect
+ github.com/golang/snappy v0.0.3 // indirect
+ github.com/google/flatbuffers v1.12.1 // indirect
+ github.com/klauspost/compress v1.12.3 // indirect
+ github.com/kr/text v0.2.0 // indirect
+ github.com/pkg/errors v0.9.1 // indirect
+ github.com/pmezard/go-difflib v1.0.0 // indirect
+ go.opencensus.io v0.22.5 // 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
+ gopkg.in/yaml.v3 v3.0.1 // indirect
+)
diff --git a/pkg/gofr/datasource/kv-store/badger/go.sum b/pkg/gofr/datasource/kv-store/badger/go.sum
new file mode 100644
index 000000000..425187314
--- /dev/null
+++ b/pkg/gofr/datasource/kv-store/badger/go.sum
@@ -0,0 +1,125 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
+github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dgraph-io/badger/v4 v4.2.0 h1:kJrlajbXXL9DFTNuhhu9yCx7JJa4qpYWxtE8BzuWsEs=
+github.com/dgraph-io/badger/v4 v4.2.0/go.mod h1:qfCqhPoWDFJRx1gp5QwwyGo8xk1lbHUxvK9nK0OGAak=
+github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8=
+github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA=
+github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
+github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
+github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
+github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
+github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ=
+github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
+github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I=
+github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
+github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA=
+github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/google/flatbuffers v1.12.1 h1:MVlul7pQNoDzWRLTw5imwYsl+usrS1TXG2H4jg6ImGw=
+github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/klauspost/compress v1.12.3 h1:G5AfA94pHPysR56qqrkO2pxEexdDzrpFJ6yt/VqWxVU=
+github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
+github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
+github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+go.opencensus.io v0.22.5 h1:dntmOdLpSpHlVqbW5Eay97DelsZHe+55D+xC6i0dDS0=
+go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
+go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
+go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+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/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=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
+golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
+golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+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 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+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-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
+google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/pkg/gofr/datasource/kv-store/badger/logger.go b/pkg/gofr/datasource/kv-store/badger/logger.go
new file mode 100644
index 000000000..073df9f4c
--- /dev/null
+++ b/pkg/gofr/datasource/kv-store/badger/logger.go
@@ -0,0 +1,28 @@
+package badger
+
+import (
+ "fmt"
+ "io"
+ "strings"
+)
+
+type Logger interface {
+ Debug(args ...any)
+ Debugf(pattern string, args ...any)
+ Info(args ...any)
+ Infof(pattern string, args ...any)
+ Error(args ...any)
+ Errorf(patter string, args ...any)
+}
+
+type Log struct {
+ Type string `json:"type"`
+ Duration int64 `json:"duration"`
+ Key string `json:"key"`
+ Value string `json:"value,omitempty"`
+}
+
+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}, " "))
+}
diff --git a/pkg/gofr/datasource/kv-store/badger/logger_test.go b/pkg/gofr/datasource/kv-store/badger/logger_test.go
new file mode 100644
index 000000000..c6d71bf8a
--- /dev/null
+++ b/pkg/gofr/datasource/kv-store/badger/logger_test.go
@@ -0,0 +1,22 @@
+package badger
+
+import (
+ "bytes"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func Test_PrettyPrint(t *testing.T) {
+ queryLog := Log{
+ Type: "GET",
+ Duration: 12345,
+ }
+ expected := "GET"
+
+ var buf bytes.Buffer
+
+ queryLog.PrettyPrint(&buf)
+
+ assert.Contains(t, buf.String(), expected)
+}
diff --git a/pkg/gofr/datasource/kv-store/badger/metrics.go b/pkg/gofr/datasource/kv-store/badger/metrics.go
new file mode 100644
index 000000000..950f78a16
--- /dev/null
+++ b/pkg/gofr/datasource/kv-store/badger/metrics.go
@@ -0,0 +1,9 @@
+package badger
+
+import "context"
+
+type Metrics interface {
+ NewHistogram(name, desc string, buckets ...float64)
+
+ RecordHistogram(ctx context.Context, name string, value float64, labels ...string)
+}
diff --git a/pkg/gofr/datasource/kv-store/badger/mock_logger.go b/pkg/gofr/datasource/kv-store/badger/mock_logger.go
new file mode 100644
index 000000000..e6527ad6e
--- /dev/null
+++ b/pkg/gofr/datasource/kv-store/badger/mock_logger.go
@@ -0,0 +1,74 @@
+package badger
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+)
+
+// Level represents different logging levels.
+type Level int
+
+const (
+ DEBUG Level = iota + 1
+ INFO
+ ERROR
+)
+
+type MockLogger struct {
+ level Level
+ out io.Writer
+ errOut io.Writer
+}
+
+func NewMockLogger(level Level, b *bytes.Buffer) Logger {
+ return &MockLogger{
+ level: level,
+ out: b,
+ errOut: b,
+ }
+}
+
+func (m *MockLogger) Debugf(pattern string, args ...interface{}) {
+ m.logf(DEBUG, pattern, args...)
+}
+
+func (m *MockLogger) Debug(args ...interface{}) {
+ m.log(DEBUG, args...)
+}
+
+func (m *MockLogger) Infof(pattern string, args ...interface{}) {
+ m.logf(INFO, pattern, args...)
+}
+
+func (m *MockLogger) Info(args ...interface{}) {
+ m.log(INFO, args...)
+}
+
+func (m *MockLogger) Errorf(patter string, args ...interface{}) {
+ m.logf(ERROR, patter, args...)
+}
+
+func (m *MockLogger) Error(args ...interface{}) {
+ m.log(ERROR, args...)
+}
+
+func (m *MockLogger) logf(level Level, format string, args ...interface{}) {
+ out := m.out
+ if level == ERROR {
+ out = m.errOut
+ }
+
+ fmt.Fprintf(out, format+"\n", args...)
+}
+
+func (m *MockLogger) log(level Level, args ...interface{}) {
+ out := m.out
+ if level == ERROR {
+ out = m.errOut
+ }
+
+ message := fmt.Sprint(args...)
+
+ fmt.Fprintf(out, "%v\n", message)
+}
diff --git a/pkg/gofr/datasource/kv-store/badger/mock_metrics.go b/pkg/gofr/datasource/kv-store/badger/mock_metrics.go
new file mode 100644
index 000000000..643e7c16e
--- /dev/null
+++ b/pkg/gofr/datasource/kv-store/badger/mock_metrics.go
@@ -0,0 +1,74 @@
+// Code generated by MockGen. DO NOT EDIT.
+// Source: metrics.go
+//
+// Generated by this command:
+//
+// mockgen -source=metrics.go -destination=mock_metrics.go -package=badger
+//
+
+// Package badger is a generated GoMock package.
+package badger
+
+import (
+ context "context"
+ reflect "reflect"
+
+ gomock "go.uber.org/mock/gomock"
+)
+
+// MockMetrics is a mock of Metrics interface.
+type MockMetrics struct {
+ ctrl *gomock.Controller
+ recorder *MockMetricsMockRecorder
+}
+
+// MockMetricsMockRecorder is the mock recorder for MockMetrics.
+type MockMetricsMockRecorder struct {
+ mock *MockMetrics
+}
+
+// NewMockMetrics creates a new mock instance.
+func NewMockMetrics(ctrl *gomock.Controller) *MockMetrics {
+ mock := &MockMetrics{ctrl: ctrl}
+ mock.recorder = &MockMetricsMockRecorder{mock}
+ return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use.
+func (m *MockMetrics) EXPECT() *MockMetricsMockRecorder {
+ return m.recorder
+}
+
+// NewHistogram mocks base method.
+func (m *MockMetrics) NewHistogram(name, desc string, buckets ...float64) {
+ m.ctrl.T.Helper()
+ varargs := []any{name, desc}
+ for _, a := range buckets {
+ varargs = append(varargs, a)
+ }
+ m.ctrl.Call(m, "NewHistogram", varargs...)
+}
+
+// NewHistogram indicates an expected call of NewHistogram.
+func (mr *MockMetricsMockRecorder) NewHistogram(name, desc any, buckets ...any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ varargs := append([]any{name, desc}, buckets...)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewHistogram", reflect.TypeOf((*MockMetrics)(nil).NewHistogram), varargs...)
+}
+
+// RecordHistogram mocks base method.
+func (m *MockMetrics) RecordHistogram(ctx context.Context, name string, value float64, labels ...string) {
+ m.ctrl.T.Helper()
+ varargs := []any{ctx, name, value}
+ for _, a := range labels {
+ varargs = append(varargs, a)
+ }
+ m.ctrl.Call(m, "RecordHistogram", varargs...)
+}
+
+// RecordHistogram indicates an expected call of RecordHistogram.
+func (mr *MockMetricsMockRecorder) RecordHistogram(ctx, name, value any, labels ...any) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ varargs := append([]any{ctx, name, value}, labels...)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordHistogram", reflect.TypeOf((*MockMetrics)(nil).RecordHistogram), varargs...)
+}
diff --git a/pkg/gofr/external_db.go b/pkg/gofr/external_db.go
index e12b78c80..1c9f7825a 100644
--- a/pkg/gofr/external_db.go
+++ b/pkg/gofr/external_db.go
@@ -39,3 +39,13 @@ func (a *App) AddCassandra(db container.CassandraProvider) {
a.container.Cassandra = db
}
+
+// AddKVStore sets the KV-Store datasource in the app's container.
+func (a *App) AddKVStore(db container.KVStoreProvider) {
+ db.UseLogger(a.Logger())
+ db.UseMetrics(a.Metrics())
+
+ db.Connect()
+
+ a.container.KVStore = db
+}
diff --git a/pkg/gofr/gofr.go b/pkg/gofr/gofr.go
index 0140be63a..1b16bba47 100644
--- a/pkg/gofr/gofr.go
+++ b/pkg/gofr/gofr.go
@@ -295,18 +295,8 @@ func (a *App) Migrate(migrationsMap map[int64]migration.Migrate) {
migration.Run(migrationsMap, a.container)
}
+//nolint:gocyclo // once deprecated configs are removed, multiple if conditions will be removed and complexity will decrease
func (a *App) initTracer() {
- traceExporter := a.Config.Get("TRACE_EXPORTER")
- tracerURL := a.Config.Get("TRACER_URL")
-
- // deprecated : tracer_host and tracer_port is deprecated and will be removed in upcoming versions
- tracerHost := a.Config.Get("TRACER_HOST")
- tracerPort := a.Config.GetOrDefault("TRACER_PORT", "9411")
-
- if tracerURL == "" && tracerHost != "" && tracerPort != "" {
- a.Logger().Warn("TRACER_HOST and TRACER_PORT are deprecated, use TRACER_URL instead")
- }
-
tp := sdktrace.NewTracerProvider(
sdktrace.WithResource(resource.NewWithAttributes(
semconv.SchemaURL,
@@ -317,6 +307,28 @@ func (a *App) initTracer() {
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}))
otel.SetErrorHandler(&otelErrorHandler{logger: a.container.Logger})
+ traceExporter := a.Config.Get("TRACE_EXPORTER")
+ tracerURL := a.Config.Get("TRACER_URL")
+
+ // deprecated : tracer_host and tracer_port is deprecated and will be removed in upcoming versions
+ tracerHost := a.Config.Get("TRACER_HOST")
+ tracerPort := a.Config.GetOrDefault("TRACER_PORT", "9411")
+
+ if tracerURL != "" && traceExporter == "" {
+ a.Logger().Error("missing TRACE_EXPORTER config, should be provided with TRACER_URL to enable tracing")
+ return
+ }
+
+ //nolint:revive // early-return is not possible here, as below is the intentional logging flow
+ if tracerURL == "" && traceExporter != "" && !strings.EqualFold(traceExporter, "gofr") {
+ if tracerHost != "" && tracerPort != "" {
+ a.Logger().Warn("TRACER_HOST and TRACER_PORT are deprecated, use TRACER_URL instead")
+ } else {
+ a.Logger().Error("missing TRACER_URL config, should be provided with TRACE_EXPORTER to enable tracing")
+ return
+ }
+ }
+
if (traceExporter != "" && tracerHost != "") || tracerURL != "" || traceExporter == gofrTraceExporter {
exporter, err := a.getExporter(traceExporter, tracerHost, tracerPort, tracerURL)
diff --git a/pkg/gofr/gofr_test.go b/pkg/gofr/gofr_test.go
index 44f0e1e1b..cf03fa07c 100644
--- a/pkg/gofr/gofr_test.go
+++ b/pkg/gofr/gofr_test.go
@@ -323,14 +323,12 @@ func Test_AddRESTHandlers(t *testing.T) {
func Test_initTracer(t *testing.T) {
mockConfig1 := config.NewMockConfig(map[string]string{
"TRACE_EXPORTER": "zipkin",
- "TRACER_HOST": "localhost",
- "TRACER_PORT": "2005",
+ "TRACER_URL": "http://localhost:2005/api/v2/spans",
})
mockConfig2 := config.NewMockConfig(map[string]string{
"TRACE_EXPORTER": "jaeger",
- "TRACER_HOST": "localhost",
- "TRACER_PORT": "2005",
+ "TRACER_URL": "localhost:2005",
})
mockConfig3 := config.NewMockConfig(map[string]string{
@@ -339,15 +337,13 @@ func Test_initTracer(t *testing.T) {
mockConfig4 := config.NewMockConfig(map[string]string{
"TRACE_EXPORTER": "zipkin",
- "TRACER_HOST": "localhost",
- "TRACER_PORT": "2005",
+ "TRACER_URL": "http://localhost:2005/api/v2/spans",
"TRACER_AUTH_KEY": "valid-token",
})
mockConfig5 := config.NewMockConfig(map[string]string{
"TRACE_EXPORTER": "jaeger",
- "TRACER_HOST": "localhost",
- "TRACER_PORT": "2005",
+ "TRACER_URL": "localhost:2005",
"TRACER_AUTH_KEY": "valid-token",
})
@@ -394,8 +390,7 @@ func Test_initTracer(t *testing.T) {
func Test_initTracer_invalidConfig(t *testing.T) {
mockConfig := config.NewMockConfig(map[string]string{
"TRACE_EXPORTER": "abc",
- "TRACER_HOST": "localhost",
- "TRACER_PORT": "2005",
+ "TRACER_URL": "localhost:2005",
})
errLogMessage := testutil.StderrOutputForFunc(func() {
diff --git a/pkg/gofr/logging/logger_test.go b/pkg/gofr/logging/logger_test.go
index f009d1e09..72981a415 100644
--- a/pkg/gofr/logging/logger_test.go
+++ b/pkg/gofr/logging/logger_test.go
@@ -6,10 +6,12 @@ import (
"fmt"
"io"
"os"
+ "os/exec"
"strings"
"testing"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
"golang.org/x/term"
"gofr.dev/pkg/gofr/testutil"
@@ -104,29 +106,60 @@ func TestLogger_LevelWarn(t *testing.T) {
infoLog := testutil.StdoutOutputForFunc(printLog)
errLog := testutil.StderrOutputForFunc(printLog)
- if strings.ContainsAny(infoLog, "NOTICE|INFO|DEBUG") && !strings.Contains(errLog, "ERROR") {
- // Warn Log Level will not contain DEBUG,INFO, NOTICE logs
- t.Errorf("TestLogger_LevelDebug Failed!")
+ levels := []Level{DEBUG, INFO, NOTICE}
+
+ for i, l := range levels {
+ assert.NotContainsf(t, infoLog, l.String(), "TEST[%d], Failed.\nunexpected %s log", i, l)
}
assertMessageInJSONLog(t, errLog, "Test Error Log")
}
func TestLogger_LevelFatal(t *testing.T) {
- printLog := func() {
+ // running the failing part only when a specific env variable is set
+ if os.Getenv("GOFR_EXITER") == "1" {
logger := NewLogger(FATAL)
+
logger.Debugf("%s", "Test Debug Log")
logger.Infof("%s", "Test Info Log")
+ logger.Logf("%s", "Test Log")
logger.Noticef("%s", "Test Notice Log")
logger.Warnf("%s", "Test Warn Log")
logger.Errorf("%s", "Test Error Log")
+ logger.Fatalf("%s", "Test Fatal Log")
+
+ return
}
- infoLog := testutil.StdoutOutputForFunc(printLog)
- errLog := testutil.StderrOutputForFunc(printLog)
+ //nolint:gosec // starting the actual test in a different subprocess
+ cmd := exec.Command(os.Args[0], "-test.run=TestLogger_LevelFatal")
+ cmd.Env = append(os.Environ(), "GOFR_EXITER=1")
+
+ stdout, err := cmd.StderrPipe()
+ require.NoError(t, err)
+
+ require.NoError(t, cmd.Start())
+
+ logBytes, err := io.ReadAll(stdout)
+ require.NoError(t, err)
+
+ log := string(logBytes)
+
+ levels := []Level{DEBUG, INFO, NOTICE, WARN, ERROR} // levels which should not be present in case of FATAL log_level
+
+ for i, l := range levels {
+ assert.NotContainsf(t, log, l.String(), "TEST[%d], Failed.\nunexpected %s log", i, l)
+ }
+
+ assertMessageInJSONLog(t, log, "Test Fatal Log")
+
+ // Check that the program exited
+ err = cmd.Wait()
+
+ var e *exec.ExitError
- assert.Equal(t, "", infoLog, "TestLogger_LevelFatal Failed!")
- assert.Equal(t, "", errLog, "TestLogger_LevelFatal Failed")
+ require.ErrorAs(t, err, &e)
+ assert.False(t, e.Success())
}
func assertMessageInJSONLog(t *testing.T, logLine, expectation string) {
diff --git a/pkg/gofr/version/version.go b/pkg/gofr/version/version.go
index 284c7b80f..4093a46ff 100644
--- a/pkg/gofr/version/version.go
+++ b/pkg/gofr/version/version.go
@@ -1,3 +1,3 @@
package version
-const Framework = "v1.14.1"
+const Framework = "v1.15.0"