From 380715d910f73745735caf1bc454686576f592e8 Mon Sep 17 00:00:00 2001 From: vaidehiadhi Date: Mon, 20 Jan 2025 17:12:10 +0530 Subject: [PATCH 01/22] added gofrcli docs --- docs/navigation.js | 2 +- docs/references/gofrcli/page.md | 122 ++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 docs/references/gofrcli/page.md diff --git a/docs/navigation.js b/docs/navigation.js index e4b2f2433..6e8d48a4e 100644 --- a/docs/navigation.js +++ b/docs/navigation.js @@ -165,7 +165,7 @@ export const navigation = [ { title: 'Testing', href: '/docs/references/testing', - desc: "GoFR provides a centralized collection of mocks to facilitate writing effective unit tests. Explore testing strategies and tools for GoFR applications, ensuring your code is robust, reliable, and maintainable." + desc: "GoFR provides a centralized collection of mocks to facilitate writing effective unit tests. Explore testing strategies and tools for GoFR applications, ensuring the code is robust, reliable, and maintainable." } ], }, diff --git a/docs/references/gofrcli/page.md b/docs/references/gofrcli/page.md new file mode 100644 index 000000000..41bacf808 --- /dev/null +++ b/docs/references/gofrcli/page.md @@ -0,0 +1,122 @@ +# **GoFR Command Line Interface** + +Managing repetitive tasks, ensuring consistency across projects, and maintaining efficiency in a development workflow can be challenging, especially when working on large-scale applications. Tasks like initializing projects, handling database migrations can become tedious and repetitive + +GoFr CLI addresses these challenges by providing a streamlined, all-in-one command-line tool specifically designed for GoFr applications. It simplifies tasks such as creating and managing database migrations, generating gRPC wrappers, and initializing new projects with standard GoFr conventions. + +For example, instead of manually setting up a new project structure, GoFr CLI can instantly scaffold a project with best practices in place. Similarly, managing database schema changes becomes effortless with its migration subcommands, ensuring that your database remains synchronized across environments. + +--- + +# **Prerequisite** +- Go 1.22 or above. To check Go version use the following command +```bash + go version. +```` + +# **Installation** +To get started with GoFr CLI, use the below commands + +```bash + go install gofr.dev/cli/gofr@latest +``` + +To check the installation: +```bash + gofr version +``` +--- + +## Usage + +The CLI can be run directly from the terminal after installation. Here’s the general syntax: + +```go +gofr [flags]=[arguments] +``` +--- + +# **Commands** + +1. **`init`** + + The init command initializes a new GoFr project. It sets up the foundational structure for the project and generates a basic "Hello World!" program as a starting point. This allows developers to quickly dive into building their application with a ready-made structure. + +### Command Usage +```go +gofr init +``` +--- + +2. **`migrate create`** + + The migrate create command generates a migration template file with pre-defined structure in your migrations directory. + This boilerplate code helps you maintain consistent patterns when writing database schema modifications across your project. + +### Command Usage +```go +gofr migrate create -name= +``` + +For detailed instructions on handling database migrations, see the [handling-data-migrations documentation](../../advanced-guide/handling-data-migrations/page.md) + +--- + +3. ***`wrap grpc`*** + + The gofr wrap grpc command streamlines gRPC integration in a GoFr project by generating context-aware structures. + It simplifies accessing datasources, adding tracing, and setting up gRPC handlers with minimal configuration, based on the proto file it creates the handler with gofr context. + For detailed instructions on using grpc with GoFr see the [gRPC documentation](../../advanced-guide/grpc/page.md) + +### Command Usage +```go +gofr wrap grpc server --proto= +``` +### Generated Files +- ```{serviceName}_gofr.go (auto-generated; do not modify)``` +- ```{serviceName}_server.go (example structure below)``` +- ```{serviceName}_client.go (auto-generated; do not modify)``` + +### Example Usage: +After generating the {serviceName}_client.go file, you can register and access the gRPC service as follows: + +```go +func main() { + app := gofr.New() + + // Create a gRPC client for the Hello service + helloGRPCClient, err := {clientPackage}.NewHelloGoFrClient(app.Config.Get("GRPC_SERVER_HOST")) + if err != nil { + app.Logger().Errorf("Failed to create Hello gRPC client: %v", err) + return + } + + greetHandler := NewGreetHandler(helloGRPCClient) + + // Register HTTP endpoint for Hello service + app.GET("/hello", greetHandler.Hello) + + // Run the application + app.Run() +} + +``` + +### Benefits +- **Streamlined gRPC Integration**: Automatically generates necessary files according to your protofiles to quickly set up gRPC services in your GoFr project. +- **Context-Aware Handling**: The generated server structure includes the necessary hooks to handle requests and responses seamlessly. +- **Minimal Configuration**: Simplifies gRPC handler setup, focusing on business logic rather than the infrastructure. + +--- + +4. **`version`** + + The version command allows you to check the current version of the GoFr CLI tool installed on your system. + Running this command ensures that you are using the latest version of the tool, helping you stay up to date with any new features, improvements, or bug fixes. + It's a quick way to verify which version of the GoFr CLI you have. + +### Command Usage + +```go +gofr version +``` From dc76ddaa3e88520945c28032cc128649316e9f50 Mon Sep 17 00:00:00 2001 From: vaidehiadhi Date: Mon, 20 Jan 2025 18:26:44 +0530 Subject: [PATCH 02/22] add changes to readme --- docs/references/gofrcli/page.md | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/docs/references/gofrcli/page.md b/docs/references/gofrcli/page.md index 41bacf808..82134dc83 100644 --- a/docs/references/gofrcli/page.md +++ b/docs/references/gofrcli/page.md @@ -1,20 +1,19 @@ -# **GoFR Command Line Interface** +# GoFR Command Line Interface -Managing repetitive tasks, ensuring consistency across projects, and maintaining efficiency in a development workflow can be challenging, especially when working on large-scale applications. Tasks like initializing projects, handling database migrations can become tedious and repetitive +Managing repetitive tasks, ensuring consistency across projects, and maintaining efficiency in a development workflow can be challenging, especially when working on large-scale applications. Tasks like initializing projects, handling database migrations can become tedious and repetitive. GoFr CLI addresses these challenges by providing a streamlined, all-in-one command-line tool specifically designed for GoFr applications. It simplifies tasks such as creating and managing database migrations, generating gRPC wrappers, and initializing new projects with standard GoFr conventions. For example, instead of manually setting up a new project structure, GoFr CLI can instantly scaffold a project with best practices in place. Similarly, managing database schema changes becomes effortless with its migration subcommands, ensuring that your database remains synchronized across environments. ---- +## Prerequisites -# **Prerequisite** -- Go 1.22 or above. To check Go version use the following command +- Go 1.22 or above. To check Go version use the following command: ```bash - go version. -```` +go version +``` -# **Installation** +## **Installation** To get started with GoFr CLI, use the below commands ```bash @@ -31,30 +30,30 @@ To check the installation: The CLI can be run directly from the terminal after installation. Here’s the general syntax: -```go +```bash gofr [flags]=[arguments] ``` --- -# **Commands** +## **Commands** -1. **`init`** +1. ***`init`*** The init command initializes a new GoFr project. It sets up the foundational structure for the project and generates a basic "Hello World!" program as a starting point. This allows developers to quickly dive into building their application with a ready-made structure. ### Command Usage -```go +```bash gofr init ``` --- -2. **`migrate create`** +2. ***`migrate create`*** The migrate create command generates a migration template file with pre-defined structure in your migrations directory. This boilerplate code helps you maintain consistent patterns when writing database schema modifications across your project. ### Command Usage -```go +```bash gofr migrate create -name= ``` @@ -69,7 +68,7 @@ For detailed instructions on handling database migrations, see the [handling-dat For detailed instructions on using grpc with GoFr see the [gRPC documentation](../../advanced-guide/grpc/page.md) ### Command Usage -```go +```bash gofr wrap grpc server --proto= ``` ### Generated Files @@ -109,7 +108,7 @@ func main() { --- -4. **`version`** +4. ***`version`*** The version command allows you to check the current version of the GoFr CLI tool installed on your system. Running this command ensures that you are using the latest version of the tool, helping you stay up to date with any new features, improvements, or bug fixes. @@ -117,6 +116,6 @@ func main() { ### Command Usage -```go +```bash gofr version ``` From 450ecf848a19da7b400067b9defd636a771f1ab8 Mon Sep 17 00:00:00 2001 From: vaidehiadhi Date: Thu, 23 Jan 2025 19:02:38 +0530 Subject: [PATCH 03/22] modified gofrclidocs --- docs/navigation.js | 5 ++ docs/references/gofrcli/page.md | 135 +++++++++++++++++++++++--------- 2 files changed, 104 insertions(+), 36 deletions(-) diff --git a/docs/navigation.js b/docs/navigation.js index 6e8d48a4e..912d9a6ec 100644 --- a/docs/navigation.js +++ b/docs/navigation.js @@ -166,6 +166,11 @@ export const navigation = [ title: 'Testing', href: '/docs/references/testing', desc: "GoFR provides a centralized collection of mocks to facilitate writing effective unit tests. Explore testing strategies and tools for GoFR applications, ensuring the code is robust, reliable, and maintainable." + }, + { + title: 'GoFr CLI', + href: '/docs/references/gofrcli', + desc: "GoFr CLI is the command line tool for initializing projects and performing tasks in accordance with GoFr framework." } ], }, diff --git a/docs/references/gofrcli/page.md b/docs/references/gofrcli/page.md index 82134dc83..a8ff62dc3 100644 --- a/docs/references/gofrcli/page.md +++ b/docs/references/gofrcli/page.md @@ -1,16 +1,24 @@ # GoFR Command Line Interface -Managing repetitive tasks, ensuring consistency across projects, and maintaining efficiency in a development workflow can be challenging, especially when working on large-scale applications. Tasks like initializing projects, handling database migrations can become tedious and repetitive. +Managing repetitive tasks and maintaining consistency across large-scale applications is challenging: -GoFr CLI addresses these challenges by providing a streamlined, all-in-one command-line tool specifically designed for GoFr applications. It simplifies tasks such as creating and managing database migrations, generating gRPC wrappers, and initializing new projects with standard GoFr conventions. +**GoFr CLI provides the following:** -For example, instead of manually setting up a new project structure, GoFr CLI can instantly scaffold a project with best practices in place. Similarly, managing database schema changes becomes effortless with its migration subcommands, ensuring that your database remains synchronized across environments. +* All-in-one command-line tool designed specifically for GoFr applications +* Simplifies **database migrations** management +* Automates **gRPC wrapper** generation +* Enforces standard **GoFr conventions** in new projects + +**Key Benefits** +- **Streamlined gRPC Integration**: Automatically generates necessary files according to your protofiles to quickly set up gRPC services in your GoFr project. +- **Context-Aware Handling**: The generated server structure includes the necessary hooks to handle requests and responses seamlessly. +- **Minimal Configuration**: Simplifies gRPC handler setup, focusing on business logic rather than the infrastructure. ## Prerequisites - Go 1.22 or above. To check Go version use the following command: ```bash -go version + go version ``` ## **Installation** @@ -31,7 +39,7 @@ To check the installation: The CLI can be run directly from the terminal after installation. Here’s the general syntax: ```bash -gofr [flags]=[arguments] + gofr [flags]=[arguments] ``` --- @@ -43,7 +51,7 @@ gofr [flags]=[arguments] ### Command Usage ```bash -gofr init + gofr init ``` --- @@ -54,7 +62,7 @@ gofr init ### Command Usage ```bash -gofr migrate create -name= + gofr migrate create -name= ``` For detailed instructions on handling database migrations, see the [handling-data-migrations documentation](../../advanced-guide/handling-data-migrations/page.md) @@ -63,32 +71,106 @@ For detailed instructions on handling database migrations, see the [handling-dat 3. ***`wrap grpc`*** - The gofr wrap grpc command streamlines gRPC integration in a GoFr project by generating context-aware structures. - It simplifies accessing datasources, adding tracing, and setting up gRPC handlers with minimal configuration, based on the proto file it creates the handler with gofr context. + * The gofr wrap grpc command streamlines gRPC integration in a GoFr project by generating context-aware structures. + * It simplifies accessing datasources, adding tracing, and setting up gRPC handlers with minimal configuration, based on the proto file it creates the handler with gofr context. For detailed instructions on using grpc with GoFr see the [gRPC documentation](../../advanced-guide/grpc/page.md) ### Command Usage +**gRPC Server** ```bash -gofr wrap grpc server --proto= + gofr wrap grpc server --proto= ``` ### Generated Files +**Server** - ```{serviceName}_gofr.go (auto-generated; do not modify)``` - ```{serviceName}_server.go (example structure below)``` -- ```{serviceName}_client.go (auto-generated; do not modify)``` + +### Example Usage +**gRPC Server** + +The generated Server code can be modified like this +```go +package server + +import ( +"fmt" + + "gofr.dev/pkg/gofr" +) + +// Register the gRPC service in your app using the following code in your main.go: +// +// client.RegisterHelloServerWithGofr(app, &client.HelloGoFrServer{}) +// +// HelloGoFrServer defines the gRPC client implementation. +// Customize the struct with required dependencies and fields as needed. +type HelloGoFrServer struct { +} + +func (s *HelloGoFrServer) SayHello(ctx *gofr.Context) (any, error) { + request := HelloRequest{} + + err := ctx.Bind(&request) + if err != nil { + return nil, err + } + + name := request.Name + if name == "" { + name = "World" + } + + return &HelloResponse{ + Message: fmt.Sprintf("Hello %s!", name), + }, nil +} +``` + +For detailed instruction on setting up a gRPC server with GoFr see the [gRPC Server Documentation](https://gofr.dev/docs/advanced-guide/grpc#generating-g-rpc-server-handler-template-using) + +**gRPC Client** +```bash + gofr wrap grpc client --proto= +``` + +**Client** +- ```{serviceName}_gofr.go (auto-generated; do not modify)``` +- ```{serviceName}_client.go (example structure below)``` + ### Example Usage: After generating the {serviceName}_client.go file, you can register and access the gRPC service as follows: ```go +type GreetHandler struct { + helloGRPCClient client.HelloGoFrClient +} + +func NewGreetHandler(helloClient client.HelloGoFrClient) *GreetHandler { + return &GreetHandler{ + helloGRPCClient: helloClient, + } +} + +func (g GreetHandler) Hello(ctx *gofr.Context) (interface{}, error) { + userName := ctx.Param("name") + helloResponse, err := g.helloGRPCClient.SayHello(ctx, &client.HelloRequest{Name: userName}) + if err != nil { + return nil, err + } + + return helloResponse, nil +} + func main() { app := gofr.New() - // Create a gRPC client for the Hello service - helloGRPCClient, err := {clientPackage}.NewHelloGoFrClient(app.Config.Get("GRPC_SERVER_HOST")) +// Create a gRPC client for the Hello service + helloGRPCClient, err := client.NewHelloGoFrClient(app.Config.Get("GRPC_SERVER_HOST")) if err != nil { - app.Logger().Errorf("Failed to create Hello gRPC client: %v", err) - return - } + app.Logger().Errorf("Failed to create Hello gRPC client: %v", err) + return +} greetHandler := NewGreetHandler(helloGRPCClient) @@ -98,24 +180,5 @@ func main() { // Run the application app.Run() } - -``` - -### Benefits -- **Streamlined gRPC Integration**: Automatically generates necessary files according to your protofiles to quickly set up gRPC services in your GoFr project. -- **Context-Aware Handling**: The generated server structure includes the necessary hooks to handle requests and responses seamlessly. -- **Minimal Configuration**: Simplifies gRPC handler setup, focusing on business logic rather than the infrastructure. - ---- - -4. ***`version`*** - - The version command allows you to check the current version of the GoFr CLI tool installed on your system. - Running this command ensures that you are using the latest version of the tool, helping you stay up to date with any new features, improvements, or bug fixes. - It's a quick way to verify which version of the GoFr CLI you have. - -### Command Usage - -```bash -gofr version ``` +For examples refer [gRPC Examples](https://github.com/gofr-dev/gofr/tree/development/examples/grpc) From 6ce124a6faaede1ab23d27674cd907516bc7b92f Mon Sep 17 00:00:00 2001 From: vaidehiadhi Date: Mon, 27 Jan 2025 15:52:22 +0530 Subject: [PATCH 04/22] resolved review comments --- docs/references/gofrcli/page.md | 87 ++++++++++++++++++++------------- 1 file changed, 53 insertions(+), 34 deletions(-) diff --git a/docs/references/gofrcli/page.md b/docs/references/gofrcli/page.md index a8ff62dc3..1d18a8eb9 100644 --- a/docs/references/gofrcli/page.md +++ b/docs/references/gofrcli/page.md @@ -6,14 +6,9 @@ Managing repetitive tasks and maintaining consistency across large-scale applica * All-in-one command-line tool designed specifically for GoFr applications * Simplifies **database migrations** management -* Automates **gRPC wrapper** generation +* Abstracts **tracing**, **metrics** and structured **logging** for GoFr's gRPC server/client * Enforces standard **GoFr conventions** in new projects -**Key Benefits** -- **Streamlined gRPC Integration**: Automatically generates necessary files according to your protofiles to quickly set up gRPC services in your GoFr project. -- **Context-Aware Handling**: The generated server structure includes the necessary hooks to handle requests and responses seamlessly. -- **Minimal Configuration**: Simplifies gRPC handler setup, focusing on business logic rather than the infrastructure. - ## Prerequisites - Go 1.22 or above. To check Go version use the following command: @@ -60,13 +55,53 @@ The CLI can be run directly from the terminal after installation. Here’s the g The migrate create command generates a migration template file with pre-defined structure in your migrations directory. This boilerplate code helps you maintain consistent patterns when writing database schema modifications across your project. + ### Command Usage ```bash - gofr migrate create -name= + gofr migrate create -name= Date: Mon, 27 Jan 2025 16:43:27 +0530 Subject: [PATCH 05/22] resolved review comments --- docs/navigation.js | 2 +- docs/references/gofrcli/page.md | 23 +++++++++++++---------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/docs/navigation.js b/docs/navigation.js index 912d9a6ec..08dafc73c 100644 --- a/docs/navigation.js +++ b/docs/navigation.js @@ -165,7 +165,7 @@ export const navigation = [ { title: 'Testing', href: '/docs/references/testing', - desc: "GoFR provides a centralized collection of mocks to facilitate writing effective unit tests. Explore testing strategies and tools for GoFR applications, ensuring the code is robust, reliable, and maintainable." + desc: "GoFr provides a centralized collection of mocks to facilitate writing effective unit tests. Explore testing strategies and tools for GoFR applications, ensuring the code is robust, reliable, and maintainable." }, { title: 'GoFr CLI', diff --git a/docs/references/gofrcli/page.md b/docs/references/gofrcli/page.md index 1d18a8eb9..52ed9c05f 100644 --- a/docs/references/gofrcli/page.md +++ b/docs/references/gofrcli/page.md @@ -58,16 +58,15 @@ The CLI can be run directly from the terminal after installation. Here’s the g ### Command Usage ```bash - gofr migrate create -name= ``` -For detailed instructions on handling database migrations, see the [handling-data-migrations documentation](../../advanced-guide/handling-data-migrations/page.md) ### Example Usage ```bash gofr migrate create -name=create_employee_table ``` -This command generates: +This command generates a migration directory which has the below files: 1. A new migration file with timestamp prefix (e.g., `20250127152047_create_employee_table.go`) containing: ```go @@ -101,13 +100,14 @@ func All() map[int64]migration.Migrate { } } ``` +For detailed instructions on handling database migrations, see the [handling-data-migrations documentation](../../advanced-guide/handling-data-migrations/page.md) For more examples, see the [using-migrations](https://github.com/gofr-dev/gofr/tree/main/examples/using-migrations) --- 3. ***`wrap grpc`*** - * The gofr wrap grpc command streamlines gRPC integration in a GoFr project by generating context-aware structures. - * It simplifies accessing datasources, adding tracing, and setting up gRPC handlers with minimal configuration, based on the proto file it creates the handler with gofr context. + * The gofr wrap grpc command streamlines gRPC integration in a GoFr project by generating GoFr's context-aware structures. + * It simplifies accessing datasources, adding tracing as well as custom metrics, and setting up gRPC handlers with minimal configuration. Based on the proto file it creates the handler/client with GoFr's context. For detailed instructions on using grpc with GoFr see the [gRPC documentation](../../advanced-guide/grpc/page.md) ### Command Usage @@ -141,11 +141,13 @@ type {ServiceName}Server struct { } // Example method (actual methods will depend on your proto file) -func (s *{ServiceName}Server) MethodName(ctx *gofr.Context) (any, error) { -return nil, nil +func (s *MyServiceServer) MethodName(ctx *gofr.Context) (any, error) { + // Replace with actual logic if needed + return &ServiceResponse{ + Message: "", + }, nil } ``` - For detailed instruction on setting up a gRPC server with GoFr see the [gRPC Server Documentation](https://gofr.dev/docs/advanced-guide/grpc#generating-g-rpc-server-handler-template-using) **gRPC Client** @@ -158,7 +160,7 @@ For detailed instruction on setting up a gRPC server with GoFr see the [gRPC Ser ### Example Usage: -After generating the {serviceName}_client.go file, you can register and access the gRPC service as follows: +After generating the hello_client.go file, you can register and access the gRPC service as follows: ```go type GreetHandler struct { @@ -200,4 +202,5 @@ func main() { app.Run() } ``` -For examples refer [gRPC Examples](https://github.com/gofr-dev/gofr/tree/main/examples/grpc) +For detailed instruction on setting up a gRPC server with GoFr see the [gRPC Client Documentation](https://gofr.dev/docs/advanced-guide/grpc#generating-tracing-enabled-g-rpc-client-using) +For more examples refer [gRPC Examples](https://github.com/gofr-dev/gofr/tree/main/examples/grpc) From 5fe0517c6ea080f3b0d6f5e77a95cc5b0a69e30d Mon Sep 17 00:00:00 2001 From: vaidehiadhi Date: Mon, 27 Jan 2025 17:04:38 +0530 Subject: [PATCH 06/22] resolved review comments --- docs/navigation.js | 2 +- docs/references/gofrcli/page.md | 13 ++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/docs/navigation.js b/docs/navigation.js index 08dafc73c..68634040f 100644 --- a/docs/navigation.js +++ b/docs/navigation.js @@ -165,7 +165,7 @@ export const navigation = [ { title: 'Testing', href: '/docs/references/testing', - desc: "GoFr provides a centralized collection of mocks to facilitate writing effective unit tests. Explore testing strategies and tools for GoFR applications, ensuring the code is robust, reliable, and maintainable." + desc: "GoFr provides a centralized collection of mocks to facilitate writing effective unit tests. Explore testing strategies and tools for GoFr applications, ensuring the code is robust, reliable, and maintainable." }, { title: 'GoFr CLI', diff --git a/docs/references/gofrcli/page.md b/docs/references/gofrcli/page.md index 52ed9c05f..74d275284 100644 --- a/docs/references/gofrcli/page.md +++ b/docs/references/gofrcli/page.md @@ -1,6 +1,6 @@ # GoFR Command Line Interface -Managing repetitive tasks and maintaining consistency across large-scale applications is challenging: +Managing repetitive tasks and maintaining consistency across large-scale applications is challenging! **GoFr CLI provides the following:** @@ -40,7 +40,7 @@ The CLI can be run directly from the terminal after installation. Here’s the g ## **Commands** -1. ***`init`*** +## 1. ***`init`*** The init command initializes a new GoFr project. It sets up the foundational structure for the project and generates a basic "Hello World!" program as a starting point. This allows developers to quickly dive into building their application with a ready-made structure. @@ -50,7 +50,7 @@ The CLI can be run directly from the terminal after installation. Here’s the g ``` --- -2. ***`migrate create`*** +## 2. ***`migrate create`*** The migrate create command generates a migration template file with pre-defined structure in your migrations directory. This boilerplate code helps you maintain consistent patterns when writing database schema modifications across your project. @@ -104,10 +104,10 @@ For detailed instructions on handling database migrations, see the [handling-dat For more examples, see the [using-migrations](https://github.com/gofr-dev/gofr/tree/main/examples/using-migrations) --- -3. ***`wrap grpc`*** +## 3. ***`wrap grpc`*** * The gofr wrap grpc command streamlines gRPC integration in a GoFr project by generating GoFr's context-aware structures. - * It simplifies accessing datasources, adding tracing as well as custom metrics, and setting up gRPC handlers with minimal configuration. Based on the proto file it creates the handler/client with GoFr's context. + * It simplifies setting up gRPC handlers with minimal steps, and accessing datasources, adding tracing as well as custom metrics. Based on the proto file it creates the handler/client with GoFr's context. For detailed instructions on using grpc with GoFr see the [gRPC documentation](../../advanced-guide/grpc/page.md) ### Command Usage @@ -144,7 +144,6 @@ type {ServiceName}Server struct { func (s *MyServiceServer) MethodName(ctx *gofr.Context) (any, error) { // Replace with actual logic if needed return &ServiceResponse{ - Message: "", }, nil } ``` @@ -160,7 +159,7 @@ For detailed instruction on setting up a gRPC server with GoFr see the [gRPC Ser ### Example Usage: -After generating the hello_client.go file, you can register and access the gRPC service as follows: +Assuming the service is named hello, after generating the hello_client.go file, you can seamlessly register and access the gRPC service using the following steps: ```go type GreetHandler struct { From 78a9efe6c98bca19295e683ac7f2d29c41a4973a Mon Sep 17 00:00:00 2001 From: vaidehiadhi Date: Mon, 27 Jan 2025 17:26:22 +0530 Subject: [PATCH 07/22] changed directory name --- docs/references/{gofrcli => gofr-cli}/page.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/references/{gofrcli => gofr-cli}/page.md (100%) diff --git a/docs/references/gofrcli/page.md b/docs/references/gofr-cli/page.md similarity index 100% rename from docs/references/gofrcli/page.md rename to docs/references/gofr-cli/page.md From b9519c8c845fd5d049bb68a0981568c681951bd0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Jan 2025 10:44:51 +0530 Subject: [PATCH 08/22] Bump google.golang.org/grpc from 1.69.4 to 1.70.0 (#1450) --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index b4c72a4b2..992926566 100644 --- a/go.mod +++ b/go.mod @@ -42,7 +42,7 @@ require ( golang.org/x/term v0.28.0 golang.org/x/text v0.21.0 google.golang.org/api v0.217.0 - google.golang.org/grpc v1.69.4 + google.golang.org/grpc v1.70.0 google.golang.org/protobuf v1.36.3 modernc.org/sqlite v1.34.5 ) diff --git a/go.sum b/go.sum index 767a32593..c6eedf17a 100644 --- a/go.sum +++ b/go.sum @@ -365,8 +365,8 @@ google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A= -google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= +google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= +google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= From 505b1e8f9a0ca29c1fe99ae05220a73216482d23 Mon Sep 17 00:00:00 2001 From: umang01-hash Date: Tue, 28 Jan 2025 11:30:30 +0530 Subject: [PATCH 09/22] minor correction in documentation's tone --- .../advanced-guide/custom-spans-in-tracing/page.md | 6 +++--- docs/advanced-guide/gofr-errors/page.md | 2 +- docs/advanced-guide/grpc/page.md | 2 +- .../publishing-custom-metrics/page.md | 6 +++--- .../using-publisher-subscriber/page.md | 2 +- docs/advanced-guide/websocket/page.md | 8 ++++---- docs/quick-start/add-rest-handlers/page.md | 2 +- docs/quick-start/connecting-redis/page.md | 10 +++++----- docs/quick-start/observability/page.md | 6 +++--- docs/references/context/page.md | 2 +- docs/references/gofr-cli/page.md | 2 +- pkg/gofr/metrics/register.go | 14 +++++++------- 12 files changed, 31 insertions(+), 31 deletions(-) diff --git a/docs/advanced-guide/custom-spans-in-tracing/page.md b/docs/advanced-guide/custom-spans-in-tracing/page.md index b6514e74d..0ae626a23 100644 --- a/docs/advanced-guide/custom-spans-in-tracing/page.md +++ b/docs/advanced-guide/custom-spans-in-tracing/page.md @@ -1,12 +1,12 @@ # Custom Spans In Tracing -GoFr's built-in tracing provides valuable insights into application's behavior. However, sometimes you might need +GoFr's built-in tracing provides valuable insights into application's behavior. However, sometimes we might need even more granular details about specific operations within your application. This is where `custom spans` can be used. ## How it helps? -By adding custom spans in traces to your requests, you can: +By adding custom spans in traces to our requests, we can: -- **Gain granular insights:** Custom spans allow you to track specific operations or functions within your application, +- **Gain granular insights:** Custom spans allows us to track specific operations or functions within your application, providing detailed performance data. - **Identify bottlenecks:** Analyzing custom spans helps to pinpoint areas of your code that may be causing performance bottlenecks or inefficiencies. diff --git a/docs/advanced-guide/gofr-errors/page.md b/docs/advanced-guide/gofr-errors/page.md index 76764afa1..188e89cc3 100644 --- a/docs/advanced-guide/gofr-errors/page.md +++ b/docs/advanced-guide/gofr-errors/page.md @@ -44,7 +44,7 @@ dbErr2 := datasource.ErrorDB{Message : "database connection timed out!"} GoFr's error structs implements an interface with `Error() string` and `StatusCode() int` methods, users can override the status code by implementing it for their custom error. -You can optionally define a log level for your error with the `LogLevel() logging.Level` methods +Users can optionally define a log level for your error with the `LogLevel() logging.Level` methods #### Usage: ```go diff --git a/docs/advanced-guide/grpc/page.md b/docs/advanced-guide/grpc/page.md index 02ace140e..3bdedb174 100644 --- a/docs/advanced-guide/grpc/page.md +++ b/docs/advanced-guide/grpc/page.md @@ -4,7 +4,7 @@ We have already seen how GoFr can help ease the development of HTTP servers, but GoFr streamlines the creation of gRPC servers and clients with unified GoFr's context support. It provides built-in tracing, metrics, and logging to ensure seamless performance monitoring for both -gRPC servers and inter-service gRPC communication. Using GoFr's context, you can easily define custom metrics and +gRPC servers and inter-service gRPC communication. Using GoFr's context, we can easily define custom metrics and traces across gRPC handlers, enabling consistent observability and simplified debugging throughout your system. ## Prerequisites diff --git a/docs/advanced-guide/publishing-custom-metrics/page.md b/docs/advanced-guide/publishing-custom-metrics/page.md index e9b38ea0a..e3fb0dc66 100644 --- a/docs/advanced-guide/publishing-custom-metrics/page.md +++ b/docs/advanced-guide/publishing-custom-metrics/page.md @@ -141,7 +141,7 @@ func main() { ## Adding Labels to Custom Metrics -GoFr leverages metrics support by enabling labels. Labels are a key feature in metrics that allow you to categorize and filter metrics based on relevant information. +GoFr leverages metrics support by enabling labels. Labels are a key feature in metrics that allows us to categorize and filter metrics based on relevant information. ### Understanding Labels @@ -152,7 +152,7 @@ Common examples of labels include: - service: (e.g., "api-gateway", "database") - status: (e.g., "success", "failure") -By adding labels, you can create different time series for the same metric based on the label values. +By adding labels, we can create different time series for the same metric based on the label values. This allows for more granular analysis and visualization in Grafana (or any other) dashboards. ### Additional Considerations @@ -161,7 +161,7 @@ This allows for more granular analysis and visualization in Grafana (or any othe - Choose meaningful label names that clearly describe the data point. - Ensure consistency in label naming conventions across your application. -By effectively using labels in GoFr, you can enrich your custom metrics and gain deeper insights into your application's performance and behavior. +By effectively using labels in GoFr, we can enrich your custom metrics and gain deeper insights into your application's performance and behavior. ### Usage: diff --git a/docs/advanced-guide/using-publisher-subscriber/page.md b/docs/advanced-guide/using-publisher-subscriber/page.md index 9a1b8baf1..bdbc50644 100644 --- a/docs/advanced-guide/using-publisher-subscriber/page.md +++ b/docs/advanced-guide/using-publisher-subscriber/page.md @@ -164,7 +164,7 @@ MQTT_USER=username // authentication username MQTT_PASSWORD=password // authentication password ``` > **Note** : If `MQTT_HOST` config is not provided, the application will connect to a public broker -> {% new-tab-link title="HiveMQ" href="https://www.hivemq.com/mqtt/public-mqtt-broker/" /%} +> {% new-tab-link title="EMQX Broker" href="https://www.emqx.com/en/mqtt/public-mqtt5-broker" /%} #### Docker setup ```shell diff --git a/docs/advanced-guide/websocket/page.md b/docs/advanced-guide/websocket/page.md index 1a1a514eb..137eeeb87 100644 --- a/docs/advanced-guide/websocket/page.md +++ b/docs/advanced-guide/websocket/page.md @@ -3,7 +3,7 @@ WebSockets provide a full-duplex communication channel over a single, long-lived connection, making them ideal for real-time applications like chat, notifications, and live updates. GoFr provides a convenient way to integrate websockets into your application. By leveraging GoFr's WebSocket support and customizable upgrader options, -you can efficiently manage real-time communication in your applications. +users can efficiently manage real-time communication in your applications. ## Usage in GoFr @@ -40,8 +40,8 @@ func WSHandler(ctx *gofr.Context) (any, error) { ``` ## Configuration Options -GoFr allows you to customize the WebSocket upgrader with several options. You can set these options using the -`websocket.NewWSUpgrader` function. Here is the list of options you can apply to your websocket upgrader using GoFr. +GoFr allows us to customize the WebSocket upgrader with several options. We can set these options using the +`websocket.NewWSUpgrader` function. Here is the list of options we can apply to your websocket upgrader using GoFr. - `HandshakeTimeout (WithHandshakeTimeout)`: Sets the handshake timeout. - `ReadBufferSize (WithReadBufferSize)`: Sets the size of the read buffer. @@ -52,7 +52,7 @@ GoFr allows you to customize the WebSocket upgrader with several options. You ca - `Compression (WithCompression)`: Enables compression. ## Example: -You can configure the Upgrader by creating a chain of option functions provided by GoFr. +We can configure the Upgrader by creating a chain of option functions provided by GoFr. ```go package main diff --git a/docs/quick-start/add-rest-handlers/page.md b/docs/quick-start/add-rest-handlers/page.md index c3498c991..745a752cb 100644 --- a/docs/quick-start/add-rest-handlers/page.md +++ b/docs/quick-start/add-rest-handlers/page.md @@ -14,7 +14,7 @@ If the custom handlers ain't implemented on the struct, GoFr provides default ha - **Update**: `/entity/{id}` Updates an existing record identified by the {id} path parameter, based on data provided in a JSON request body. - **Delete** `/entity/{id}` Deletes an existing record identified by the {id} path parameter. -**NOTE**: The registered routes will have the same name as the given struct, but if you want to change route name, you can implement `RestPath` method in the struct: +**NOTE**: The registered routes will have the same name as the given struct, but if we want to change route name, we can implement `RestPath` method in the struct: ```go type userEntity struct { Id int `json:"id"` diff --git a/docs/quick-start/connecting-redis/page.md b/docs/quick-start/connecting-redis/page.md index b56aa0431..85c534caf 100644 --- a/docs/quick-start/connecting-redis/page.md +++ b/docs/quick-start/connecting-redis/page.md @@ -4,15 +4,15 @@ GoFr simplifies the process of connecting to Redis. ## Setup: -Ensure you have Redis installed on your system. +Ensure we have Redis installed on our system. -Optionally, you can use Docker to set up a development environment as described below. +Optionally, We can use Docker to set up a development environment as described below. ```bash docker run --name gofr-redis -p 6379:6379 -d redis ``` -You can also set up a development environment with password authentication as described below. +We can also set up a development environment with password authentication as described below. ```bash docker run --name gofr-redis -p 2002:6379 -d \ @@ -20,7 +20,7 @@ docker run --name gofr-redis -p 2002:6379 -d \ redis:7.0.5 --requirepass password ``` -You can set a sample key `greeting` using the following command: +We can set a sample key `greeting` using the following command: ```bash docker exec -it gofr-redis bash -c 'redis-cli SET greeting "Hello from Redis."' @@ -35,7 +35,7 @@ Following configuration keys are required for Redis connectivity: * `REDIS_HOST`: It specifies the hostname or IP address of your Redis server. * `REDIS_PORT`: It specifies the port number on which your Redis server is listening. The default Redis port is 6379. -* `REDIS_USER` : This is the user you'll use to connect to your Redis server. You can configure multiple users with different permissions in a single Redis container. For more details, refer to the [official docs](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl/) +* `REDIS_USER` : This is the user we'll use to connect to your Redis server. We can configure multiple users with different permissions in a single Redis container. For more details, refer to the [official docs](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl/) * `REDIS_PASSWORD`: The password is required only if your Redis server is configured for authentication; if authentication is not enabled, no password is necessary. * `REDIS_DB`: The database number to use for the Redis server. The default value is 0. ```dotenv diff --git a/docs/quick-start/observability/page.md b/docs/quick-start/observability/page.md index a8ba8096f..398bd44fc 100644 --- a/docs/quick-start/observability/page.md +++ b/docs/quick-start/observability/page.md @@ -133,7 +133,7 @@ GoFr publishes metrics to port: _2121_ on _/metrics_ endpoint in Prometheus form {% /table %} -For example: When running application locally, you can access /metrics endpoint on port 2121 from: {% new-tab-link title="http://localhost:2121/metrics" href="http://localhost:2121/metrics" /%} +For example: When running application locally, we can access /metrics endpoint on port 2121 from: {% new-tab-link title="http://localhost:2121/metrics" href="http://localhost:2121/metrics" /%} GoFr also supports creating {% new-tab-link newtab=false title="custom metrics" href="/docs/advanced-guide/publishing-custom-metrics" /%}. @@ -158,7 +158,7 @@ automatically add traces to all requests and responses. **Automatic Correlation ID Propagation:** When a request enters your GoFr application, GoFr automatically generates a correlation-ID `X-Correlation-ID` and adds it -to the response headers. This correlation ID is then propagated to all downstream requests. This means that you can track +to the response headers. This correlation ID is then propagated to all downstream requests. This means that user can track a request as it travels through your distributed system by simply looking at the correlation ID in the request headers. ### Configuration & Usage: @@ -245,7 +245,7 @@ TRACER_RATIO=0.1 #### 4. [GoFr Tracer](https://tracer.gofr.dev/): -GoFr tracer is GoFr's own custom trace exporter as well as collector. You can search a trace by its TraceID (correlationID) +GoFr tracer is GoFr's own custom trace exporter as well as collector. User can search a trace by its TraceID (correlationID) in GoFr's own tracer service available anywhere, anytime. Add GoFr Tracer configs in `.env` file, your .env will be updated to diff --git a/docs/references/context/page.md b/docs/references/context/page.md index ddd9e0f4f..220d9d7a9 100644 --- a/docs/references/context/page.md +++ b/docs/references/context/page.md @@ -56,7 +56,7 @@ ctx.Bind(&p) ``` - `Binding multipart-form data / urlencoded form data ` - - To bind multipart-form data or url-encoded form, you can use the Bind method similarly. The struct fields should be tagged appropriately + - To bind multipart-form data or url-encoded form, we can use the Bind method similarly. The struct fields should be tagged appropriately to map the form fields to the struct fields. The supported content types are `multipart/form-data` and `application/x-www-form-urlencoded` ```go diff --git a/docs/references/gofr-cli/page.md b/docs/references/gofr-cli/page.md index 74d275284..888591c79 100644 --- a/docs/references/gofr-cli/page.md +++ b/docs/references/gofr-cli/page.md @@ -159,7 +159,7 @@ For detailed instruction on setting up a gRPC server with GoFr see the [gRPC Ser ### Example Usage: -Assuming the service is named hello, after generating the hello_client.go file, you can seamlessly register and access the gRPC service using the following steps: +Assuming the service is named hello, after generating the hello_client.go file, we can seamlessly register and access the gRPC service using the following steps: ```go type GreetHandler struct { diff --git a/pkg/gofr/metrics/register.go b/pkg/gofr/metrics/register.go index 1bb593c99..aedfe6f3d 100644 --- a/pkg/gofr/metrics/register.go +++ b/pkg/gofr/metrics/register.go @@ -98,11 +98,11 @@ func (m *metricsManager) NewUpDownCounter(name, desc string) { // Usage: // m.NewHistogram("another_histogram", "Another histogram metric", 0, 10, 100, 1000) // -// When creating a histogram metric, you can specify custom bucket boundaries to group data points +// When creating a histogram metric, we can specify custom bucket boundaries to group data points // into ranges. Buckets represent specific ranges of values. Each value recorded in the histogram // is placed into the appropriate bucket based on its value compared to the bucket boundaries. // -// For example, when tracking response times in milliseconds, you might define buckets like [0, 10), +// For example, when tracking response times in milliseconds, we might define buckets like [0, 10), // [10, 100), [100, 1000), [1000, +Inf), where each range represents response times // within a certain range, and the last bucket includes all values above 1000ms (represented by +Inf, // which stands for positive infinity). @@ -163,9 +163,9 @@ func (f *float64Gauge) callbackFunc(_ context.Context, o metric.Float64Observer) // 2. m.IncrementCounter(ctx, "example_counter_with_labels", "label1", "value1", "label2", "value2") // // The IncrementCounter method is used to increase the specified counter metric by 1. If the counter metric -// does not exist, an error is logged. Optionally, you can provide labels to associate additional information +// does not exist, an error is logged. Optionally, we can provide labels to associate additional information // with the counter metric. Labels are provided as key-value pairs where the label name and value alternate. -// For example, "label1", "value1", "label2", "value2". Labels allow you to segment and filter your metrics +// For example, "label1", "value1", "label2", "value2". Labels allow us to segment and filter your metrics // based on different dimensions. func (m *metricsManager) IncrementCounter(ctx context.Context, name string, labels ...string) { counter, err := m.store.getCounter(name) @@ -189,9 +189,9 @@ func (m *metricsManager) IncrementCounter(ctx context.Context, name string, labe // 2. m.DeltaUpDownCounter(ctx, "successful_logins", 1.5, "label1", "value1", "label2", "value2") // // The DeltaUpDownCounter method is used to increase or decrease the last value of the specified UpDown counter metric -// by the given value. For example, you might use this method to track changes in the number of active users or +// by the given value. For example, we might use this method to track changes in the number of active users or // successful login attempts. Labels can provide additional context, such as the method and endpoint of the request, -// allowing you to analyze metrics based on different dimensions. +// allowing us to analyze metrics based on different dimensions. func (m *metricsManager) DeltaUpDownCounter(ctx context.Context, name string, value float64, labels ...string) { upDownCounter, err := m.store.getUpDownCounter(name) if err != nil { @@ -224,7 +224,7 @@ func (m *metricsManager) RecordHistogram(ctx context.Context, name string, value } // SetGauge gets the value and sets the metric to the specified value. -// Unlike counters, gauges do not track the last value for the metric. This method allows you to +// Unlike counters, gauges do not track the last value for the metric. This method allows us to // directly set the value of the gauge to the specified value. // // Usage: From c72905a7caab6851fcf63e44f328716160982793 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Jan 2025 11:34:58 +0530 Subject: [PATCH 10/22] Bump google.golang.org/api from 0.217.0 to 0.218.0 (#1449) --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 992926566..ecdf4c593 100644 --- a/go.mod +++ b/go.mod @@ -41,7 +41,7 @@ require ( golang.org/x/sync v0.10.0 golang.org/x/term v0.28.0 golang.org/x/text v0.21.0 - google.golang.org/api v0.217.0 + google.golang.org/api v0.218.0 google.golang.org/grpc v1.70.0 google.golang.org/protobuf v1.36.3 modernc.org/sqlite v1.34.5 @@ -96,7 +96,7 @@ require ( golang.org/x/time v0.9.0 // indirect google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250106144421-5f5ef82da422 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect gopkg.in/yaml.v3 v3.0.1 // indirect modernc.org/libc v1.55.3 // indirect modernc.org/mathutil v1.6.0 // indirect diff --git a/go.sum b/go.sum index c6eedf17a..560e51a04 100644 --- a/go.sum +++ b/go.sum @@ -345,8 +345,8 @@ 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.217.0 h1:GYrUtD289o4zl1AhiTZL0jvQGa2RDLyC+kX1N/lfGOU= -google.golang.org/api v0.217.0/go.mod h1:qMc2E8cBAbQlRypBTBWHklNJlaZZJBwDv81B1Iu8oSI= +google.golang.org/api v0.218.0 h1:x6JCjEWeZ9PFCRe9z0FBrNwj7pB7DOAqT35N+IPnAUA= +google.golang.org/api v0.218.0/go.mod h1:5VGHBAkxrA/8EFjLVEYmMUJ8/8+gWWQ3s4cFH0FxG2M= 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= @@ -357,8 +357,8 @@ google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 h1:ToEetK57OidYuqD google.golang.org/genproto v0.0.0-20241118233622-e639e219e697/go.mod h1:JJrvXBWRZaFMxBufik1a4RpFw4HhgVtBBWQeQgUj2cc= google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 h1:CkkIfIt50+lT6NHAVoRYEyAvQGFM7xEwXUUywFvEb3Q= google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250106144421-5f5ef82da422 h1:3UsHvIr4Wc2aW4brOaSCmcxh9ksica6fHEr8P1XhkYw= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250106144421-5f5ef82da422/go.mod h1:3ENsm/5D1mzDyhpzeRi1NR784I0BcofWBoSc5QqqMK4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50= 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= From a9a3453bcf441ec3ddc2ff395e1a5f6e7ebb66bc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Jan 2025 11:54:44 +0530 Subject: [PATCH 11/22] Bump go.opentelemetry.io/otel/exporters/zipkin from 1.33.0 to 1.34.0 (#1448) --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index ecdf4c593..7d2ee90dd 100644 --- a/go.mod +++ b/go.mod @@ -31,7 +31,7 @@ require ( go.opentelemetry.io/otel v1.34.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 go.opentelemetry.io/otel/exporters/prometheus v0.56.0 - go.opentelemetry.io/otel/exporters/zipkin v1.33.0 + go.opentelemetry.io/otel/exporters/zipkin v1.34.0 go.opentelemetry.io/otel/metric v1.34.0 go.opentelemetry.io/otel/sdk v1.34.0 go.opentelemetry.io/otel/sdk/metric v1.34.0 diff --git a/go.sum b/go.sum index 560e51a04..5653c9567 100644 --- a/go.sum +++ b/go.sum @@ -228,8 +228,8 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 h1:5pojm go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0/go.mod h1:57gTHJSE5S1tqg+EKsLPlTWhpHMsWlVmer+LA926XiA= go.opentelemetry.io/otel/exporters/prometheus v0.56.0 h1:GnCIi0QyG0yy2MrJLzVrIM7laaJstj//flf1zEJCG+E= go.opentelemetry.io/otel/exporters/prometheus v0.56.0/go.mod h1:JQcVZtbIIPM+7SWBB+T6FK+xunlyidwLp++fN0sUaOk= -go.opentelemetry.io/otel/exporters/zipkin v1.33.0 h1:aFexjEJIw5kVz6vQwnsqCG/nTV/UpsZh7MtQwGmH1eI= -go.opentelemetry.io/otel/exporters/zipkin v1.33.0/go.mod h1:aYsOzr/SZwZXJM6DJmSP/ST2P7MYxuc0R9RewkFVp9s= +go.opentelemetry.io/otel/exporters/zipkin v1.34.0 h1:GSjCkoYqsnvUMCjxF18j2tCWH8fhGZYjH3iYgechPTI= +go.opentelemetry.io/otel/exporters/zipkin v1.34.0/go.mod h1:h830hluwAqgSNnZbxL2rJhmAlE7/0SF9esoHVLU04Gc= go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= From cd6036f09e600eaf29bbc6c90874686ed4c2c6b1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Jan 2025 13:36:45 +0530 Subject: [PATCH 12/22] Bump google.golang.org/protobuf from 1.36.3 to 1.36.4 (#1446) --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 7d2ee90dd..b1fa81f7e 100644 --- a/go.mod +++ b/go.mod @@ -43,7 +43,7 @@ require ( golang.org/x/text v0.21.0 google.golang.org/api v0.218.0 google.golang.org/grpc v1.70.0 - google.golang.org/protobuf v1.36.3 + google.golang.org/protobuf v1.36.4 modernc.org/sqlite v1.34.5 ) diff --git a/go.sum b/go.sum index 5653c9567..65472bc2a 100644 --- a/go.sum +++ b/go.sum @@ -376,8 +376,8 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.36.3 h1:82DV7MYdb8anAVi3qge1wSnMDrnKK7ebr+I0hHRN1BU= -google.golang.org/protobuf v1.36.3/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM= +google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= From c747c6f334970df8e9170d5945f62dbbe50c1d4f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Jan 2025 14:52:32 +0530 Subject: [PATCH 13/22] Bump go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace (#1447) --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index b1fa81f7e..208b46d2b 100644 --- a/go.mod +++ b/go.mod @@ -26,7 +26,7 @@ require ( github.com/redis/go-redis/v9 v9.7.0 github.com/segmentio/kafka-go v0.4.47 github.com/stretchr/testify v1.10.0 - go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.58.0 + go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.59.0 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 go.opentelemetry.io/otel v1.34.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 diff --git a/go.sum b/go.sum index 65472bc2a..a3d234441 100644 --- a/go.sum +++ b/go.sum @@ -216,8 +216,8 @@ go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJyS go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 h1:r6I7RJCN86bpD/FQwedZ0vSixDpwuWREjW9oRMsmqDc= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0/go.mod h1:B9yO6b04uB80CzjedvewuqDhxJxi11s7/GtiGa8bAjI= -go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.58.0 h1:xwH3QJv6zL4u+gkPUu59NeT1Gyw9nScWT8FQpKLUJJI= -go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.58.0/go.mod h1:uosvgpqTcTXtcPQORTbEkZNDQTCDOgTz1fe6aLSyqrQ= +go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.59.0 h1:iQZYNQ7WwIcYXzOPR46FQv9O0dS1PW16RjvR0TjDOe8= +go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.59.0/go.mod h1:54CaSNqYEXvpzDh8KPjiMVoWm60t5R0dZRt0leEPgAs= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I= go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= From a6037bdd8b7f10379f4fd9d483597978049999f5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Jan 2025 16:29:54 +0530 Subject: [PATCH 14/22] Bump actions/setup-go from 4 to 5 (#1444) --- .github/workflows/go.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 390992f72..4cbcdf7ea 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -241,7 +241,7 @@ jobs: uses: actions/checkout@v4 - name: Set up Go environment - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: go-version: '1.22' cache: false From c34ef84256fe78ff17b3a7b94edfebc8666d56df Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Jan 2025 16:39:56 +0530 Subject: [PATCH 15/22] Bump golangci/golangci-lint-action from 3 to 6 (#1443) --- .github/workflows/go.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 4cbcdf7ea..1441e8825 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -251,7 +251,7 @@ jobs: go mod tidy - name: Lint Root Module - uses: golangci/golangci-lint-action@v3 + uses: golangci/golangci-lint-action@v6 with: version: v1.62 working-directory: . From 223ab74083b7225c99a788423cbf0111bbca134b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Feb 2025 14:56:44 +0530 Subject: [PATCH 16/22] Bump google.golang.org/api from 0.218.0 to 0.219.0 (#1464) Bumps [google.golang.org/api](https://github.com/googleapis/google-api-go-client) from 0.218.0 to 0.219.0. - [Release notes](https://github.com/googleapis/google-api-go-client/releases) - [Changelog](https://github.com/googleapis/google-api-go-client/blob/main/CHANGES.md) - [Commits](https://github.com/googleapis/google-api-go-client/compare/v0.218.0...v0.219.0) --- updated-dependencies: - dependency-name: google.golang.org/api dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 208b46d2b..98a20eb12 100644 --- a/go.mod +++ b/go.mod @@ -41,7 +41,7 @@ require ( golang.org/x/sync v0.10.0 golang.org/x/term v0.28.0 golang.org/x/text v0.21.0 - google.golang.org/api v0.218.0 + google.golang.org/api v0.219.0 google.golang.org/grpc v1.70.0 google.golang.org/protobuf v1.36.4 modernc.org/sqlite v1.34.5 @@ -96,7 +96,7 @@ require ( golang.org/x/time v0.9.0 // indirect google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250124145028-65684f501c47 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect modernc.org/libc v1.55.3 // indirect modernc.org/mathutil v1.6.0 // indirect diff --git a/go.sum b/go.sum index a3d234441..b7fc74025 100644 --- a/go.sum +++ b/go.sum @@ -345,8 +345,8 @@ 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.218.0 h1:x6JCjEWeZ9PFCRe9z0FBrNwj7pB7DOAqT35N+IPnAUA= -google.golang.org/api v0.218.0/go.mod h1:5VGHBAkxrA/8EFjLVEYmMUJ8/8+gWWQ3s4cFH0FxG2M= +google.golang.org/api v0.219.0 h1:nnKIvxKs/06jWawp2liznTBnMRQBEPpGo7I+oEypTX0= +google.golang.org/api v0.219.0/go.mod h1:K6OmjGm+NtLrIkHxv1U3a0qIf/0JOvAHd5O/6AoyKYE= 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= @@ -357,8 +357,8 @@ google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 h1:ToEetK57OidYuqD google.golang.org/genproto v0.0.0-20241118233622-e639e219e697/go.mod h1:JJrvXBWRZaFMxBufik1a4RpFw4HhgVtBBWQeQgUj2cc= google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 h1:CkkIfIt50+lT6NHAVoRYEyAvQGFM7xEwXUUywFvEb3Q= google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250124145028-65684f501c47 h1:91mG8dNTpkC0uChJUQ9zCiRqx3GEEFOWaRZ0mI6Oj2I= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250124145028-65684f501c47/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50= 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= From ec79604e7d6c992976406d5528743097405f628e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Feb 2025 15:12:17 +0530 Subject: [PATCH 17/22] Bump cloud.google.com/go/pubsub from 1.45.3 to 1.47.0 (#1463) --- go.mod | 15 +++++++-------- go.sum | 36 ++++++++++++++++++------------------ 2 files changed, 25 insertions(+), 26 deletions(-) diff --git a/go.mod b/go.mod index 98a20eb12..c58085863 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.22.7 toolchain go1.23.4 require ( - cloud.google.com/go/pubsub v1.45.3 + cloud.google.com/go/pubsub v1.47.0 github.com/DATA-DOG/go-sqlmock v1.5.2 github.com/XSAM/otelsql v0.36.0 github.com/alicebob/miniredis/v2 v2.34.0 @@ -48,11 +48,11 @@ require ( ) require ( - cloud.google.com/go v0.116.0 // indirect + cloud.google.com/go v0.118.1 // indirect cloud.google.com/go/auth v0.14.0 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect cloud.google.com/go/compute/metadata v0.6.0 // indirect - cloud.google.com/go/iam v1.2.2 // indirect + cloud.google.com/go/iam v1.3.1 // indirect filippo.io/edwards25519 v1.1.0 // indirect github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 // indirect github.com/beorn7/perks v1.0.1 // indirect @@ -64,7 +64,6 @@ require ( github.com/felixge/httpsnoop v1.0.4 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/s2a-go v0.1.9 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect @@ -84,18 +83,18 @@ require ( github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/yuin/gopher-lua v1.1.1 // indirect - go.einride.tech/aip v0.68.0 // indirect + go.einride.tech/aip v0.68.1 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 // indirect go.opentelemetry.io/proto/otlp v1.4.0 // indirect golang.org/x/crypto v0.32.0 // indirect golang.org/x/net v0.34.0 // indirect golang.org/x/sys v0.29.0 // indirect golang.org/x/time v0.9.0 // indirect - google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 // indirect + google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250124145028-65684f501c47 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect modernc.org/libc v1.55.3 // indirect diff --git a/go.sum b/go.sum index b7fc74025..fef2d49eb 100644 --- a/go.sum +++ b/go.sum @@ -1,20 +1,20 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.116.0 h1:B3fRrSDkLRt5qSHWe40ERJvhvnQwdZiHu0bJOpldweE= -cloud.google.com/go v0.116.0/go.mod h1:cEPSRWPzZEswwdr9BxE6ChEn01dWlTaF05LiC2Xs70U= +cloud.google.com/go v0.118.1 h1:b8RATMcrK9A4BH0rj8yQupPXp+aP+cJ0l6H7V9osV1E= +cloud.google.com/go v0.118.1/go.mod h1:CFO4UPEPi8oV21xoezZCrd3d81K4fFkDTEJu4R8K+9M= cloud.google.com/go/auth v0.14.0 h1:A5C4dKV/Spdvxcl0ggWwWEzzP7AZMJSEIgrkngwhGYM= cloud.google.com/go/auth v0.14.0/go.mod h1:CYsoRL1PdiDuqeQpZE0bP2pnPrGqFcOkI0nldEQis+A= cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M= cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc= cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I= cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= -cloud.google.com/go/iam v1.2.2 h1:ozUSofHUGf/F4tCNy/mu9tHLTaxZFLOUiKzjcgWHGIA= -cloud.google.com/go/iam v1.2.2/go.mod h1:0Ys8ccaZHdI1dEUilwzqng/6ps2YB6vRsjIe00/+6JY= -cloud.google.com/go/kms v1.20.1 h1:og29Wv59uf2FVaZlesaiDAqHFzHaoUyHI3HYp9VUHVg= -cloud.google.com/go/kms v1.20.1/go.mod h1:LywpNiVCvzYNJWS9JUcGJSVTNSwPwi0vBAotzDqn2nc= -cloud.google.com/go/longrunning v0.6.2 h1:xjDfh1pQcWPEvnfjZmwjKQEcHnpz6lHjfy7Fo0MK+hc= -cloud.google.com/go/longrunning v0.6.2/go.mod h1:k/vIs83RN4bE3YCswdXC5PFfWVILjm3hpEUlSko4PiI= -cloud.google.com/go/pubsub v1.45.3 h1:prYj8EEAAAwkp6WNoGTE4ahe0DgHoyJd5Pbop931zow= -cloud.google.com/go/pubsub v1.45.3/go.mod h1:cGyloK/hXC4at7smAtxFnXprKEFTqmMXNNd9w+bd94Q= +cloud.google.com/go/iam v1.3.1 h1:KFf8SaT71yYq+sQtRISn90Gyhyf4X8RGgeAVC8XGf3E= +cloud.google.com/go/iam v1.3.1/go.mod h1:3wMtuyT4NcbnYNPLMBzYRFiEfjKfJlLVLrisE7bwm34= +cloud.google.com/go/kms v1.20.5 h1:aQQ8esAIVZ1atdJRxihhdxGQ64/zEbJoJnCz/ydSmKg= +cloud.google.com/go/kms v1.20.5/go.mod h1:C5A8M1sv2YWYy1AE6iSrnddSG9lRGdJq5XEdBy28Lmw= +cloud.google.com/go/longrunning v0.6.4 h1:3tyw9rO3E2XVXzSApn1gyEEnH2K9SynNQjMlBi3uHLg= +cloud.google.com/go/longrunning v0.6.4/go.mod h1:ttZpLCe6e7EXvn9OxpBRx7kZEB0efv8yBO6YnVMfhJs= +cloud.google.com/go/pubsub v1.47.0 h1:Ou2Qu4INnf7ykrFjGv2ntFOjVo8Nloh/+OffF4mUu9w= +cloud.google.com/go/pubsub v1.47.0/go.mod h1:LaENesmga+2u0nDtLkIOILskxsfvn/BXX9Ak1NFxOs8= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -208,14 +208,14 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M= github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= -go.einride.tech/aip v0.68.0 h1:4seM66oLzTpz50u4K1zlJyOXQ3tCzcJN7I22tKkjipw= -go.einride.tech/aip v0.68.0/go.mod h1:7y9FF8VtPWqpxuAxl0KQWqaULxW4zFIesD6zF5RIHHg= +go.einride.tech/aip v0.68.1 h1:16/AfSxcQISGN5z9C5lM+0mLYXihrHbQ1onvYTr93aQ= +go.einride.tech/aip v0.68.1/go.mod h1:XaFtaj4HuA3Zwk9xoBtTWgNubZ0ZZXv9BZJCkuKuWbg= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 h1:r6I7RJCN86bpD/FQwedZ0vSixDpwuWREjW9oRMsmqDc= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0/go.mod h1:B9yO6b04uB80CzjedvewuqDhxJxi11s7/GtiGa8bAjI= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 h1:PS8wXpbyaDJQ2VDHHncMe9Vct0Zn1fEjpsjrLxGJoSc= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0/go.mod h1:HDBUsEjOuRC0EzKZ1bSaRGZWUBAzo+MhAcUUORSr4D0= go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.59.0 h1:iQZYNQ7WwIcYXzOPR46FQv9O0dS1PW16RjvR0TjDOe8= go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.59.0/go.mod h1:54CaSNqYEXvpzDh8KPjiMVoWm60t5R0dZRt0leEPgAs= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s= @@ -353,10 +353,10 @@ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoA 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-20241118233622-e639e219e697 h1:ToEetK57OidYuqD4Q5w+vfEnPvPpuTwedCNVohYJfNk= -google.golang.org/genproto v0.0.0-20241118233622-e639e219e697/go.mod h1:JJrvXBWRZaFMxBufik1a4RpFw4HhgVtBBWQeQgUj2cc= -google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 h1:CkkIfIt50+lT6NHAVoRYEyAvQGFM7xEwXUUywFvEb3Q= -google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08= +google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 h1:Pw6WnI9W/LIdRxqK7T6XGugGbHIRl5Q7q3BssH6xk4s= +google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4/go.mod h1:qbZzneIOXSq+KFAFut9krLfRLZiFLzZL5u2t8SV83EE= +google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47 h1:5iw9XJTD4thFidQmFVvx0wi4g5yOHk76rNRUxz1ZG5g= +google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47/go.mod h1:AfA77qWLcidQWywD0YgqfpJzf50w2VjzBml3TybHeJU= google.golang.org/genproto/googleapis/rpc v0.0.0-20250124145028-65684f501c47 h1:91mG8dNTpkC0uChJUQ9zCiRqx3GEEFOWaRZ0mI6Oj2I= google.golang.org/genproto/googleapis/rpc v0.0.0-20250124145028-65684f501c47/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= From 718de75f523bc443fdd0e31ece93d0a9bde95e71 Mon Sep 17 00:00:00 2001 From: Umang Mundhra Date: Tue, 4 Feb 2025 15:46:51 +0530 Subject: [PATCH 18/22] Fix/add rest handlers put (#1457) --- pkg/gofr/crud_handlers.go | 5 ++--- pkg/gofr/crud_handlers_test.go | 11 +++++------ 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/pkg/gofr/crud_handlers.go b/pkg/gofr/crud_handlers.go index 8aa85d275..4c132686f 100644 --- a/pkg/gofr/crud_handlers.go +++ b/pkg/gofr/crud_handlers.go @@ -283,6 +283,7 @@ func (e *entity) Get(c *Context) (any, error) { func (e *entity) Update(c *Context) (any, error) { newEntity := reflect.New(e.entityType).Interface() + id := c.PathParam(e.primaryKey) err := c.Bind(newEntity) if err != nil { @@ -299,11 +300,9 @@ func (e *entity) Update(c *Context) (any, error) { fieldValues = append(fieldValues, reflect.ValueOf(newEntity).Elem().Field(i).Interface()) } - id := c.PathParam("id") - stmt := sql.UpdateByQuery(c.SQL.Dialect(), e.tableName, fieldNames[1:], e.primaryKey) - _, err = c.SQL.ExecContext(c, stmt, append(fieldValues[1:], fieldValues[0])...) + _, err = c.SQL.ExecContext(c, stmt, append(fieldValues[1:], id)...) if err != nil { return nil, err } diff --git a/pkg/gofr/crud_handlers_test.go b/pkg/gofr/crud_handlers_test.go index 66e2e4a63..421803c47 100644 --- a/pkg/gofr/crud_handlers_test.go +++ b/pkg/gofr/crud_handlers_test.go @@ -10,7 +10,6 @@ import ( "net/http" "net/http/httptest" "reflect" - "strconv" "testing" "github.com/DATA-DOG/go-sqlmock" @@ -478,7 +477,7 @@ func Test_UpdateHandler(t *testing.T) { type testCase struct { desc string - id int + id string reqBody []byte mockErr error expectedResp any @@ -489,7 +488,7 @@ func Test_UpdateHandler(t *testing.T) { tests := []testCase{ { desc: "success case", - id: 1, + id: "1", reqBody: []byte(`{"id":1,"name":"goFr","isEmployed":true}`), mockErr: nil, expectedResp: "userEntity successfully updated with id: 1", @@ -497,7 +496,7 @@ func Test_UpdateHandler(t *testing.T) { }, { desc: "bind error", - id: 2, + id: "2", reqBody: []byte(`{"id":"2"}`), mockErr: nil, expectedResp: nil, @@ -505,7 +504,7 @@ func Test_UpdateHandler(t *testing.T) { }, { desc: "error From DB", - id: 3, + id: "3", reqBody: []byte(`{"id":3,"name":"goFr","isEmployed":false}`), mockErr: sqlmock.ErrCancelled, expectedResp: nil, @@ -518,7 +517,7 @@ func Test_UpdateHandler(t *testing.T) { for i, tc := range tests { t.Run(dc.dialect+" "+tc.desc, func(t *testing.T) { - ctx := createTestContext(http.MethodPut, "/user", strconv.Itoa(tc.id), tc.reqBody, c) + ctx := createTestContext(http.MethodPut, "/user", tc.id, tc.reqBody, c) mock.ExpectExec(dc.expectedQuery).WithArgs("goFr", true, tc.id). WillReturnResult(sqlmock.NewResult(1, 1)).WillReturnError(nil) From 8ef3bdf95675eafd1391eb613c9d8facaa19b2d1 Mon Sep 17 00:00:00 2001 From: Divya Darshana <98943137+coolwednesday@users.noreply.github.com> Date: Tue, 4 Feb 2025 16:15:32 +0530 Subject: [PATCH 19/22] Default HealthChecks in gRPC (#1458) --- docs/advanced-guide/grpc/page.md | 45 +++- .../grpc/grpc-client/client/health_client.go | 93 ++++++++ examples/grpc/grpc-client/client/hello.pb.go | 214 +++++++++++------ examples/grpc/grpc-client/client/hello.proto | 2 +- .../grpc/grpc-client/client/hello_client.go | 119 ++-------- .../grpc/grpc-client/client/hello_grpc.pb.go | 152 ++++++++++-- examples/grpc/grpc-client/main.go | 11 +- examples/grpc/grpc-server/configs/.env | 2 +- examples/grpc/grpc-server/main.go | 2 +- .../grpc/grpc-server/server/health_gofr.go | 99 ++++++++ examples/grpc/grpc-server/server/hello.pb.go | 216 ++++++++++++------ examples/grpc/grpc-server/server/hello.proto | 4 +- .../grpc/grpc-server/server/hello_gofr.go | 46 ++-- .../grpc/grpc-server/server/hello_grpc.pb.go | 148 ++++++++++-- .../grpc/grpc-server/server/hello_server.go | 13 +- pkg/gofr/grpc.go | 2 +- pkg/gofr/grpc/log.go | 64 ++++-- pkg/gofr/grpc/log_test.go | 4 +- 18 files changed, 909 insertions(+), 327 deletions(-) create mode 100644 examples/grpc/grpc-client/client/health_client.go create mode 100644 examples/grpc/grpc-server/server/health_gofr.go diff --git a/docs/advanced-guide/grpc/page.md b/docs/advanced-guide/grpc/page.md index 3bdedb174..bb34977f6 100644 --- a/docs/advanced-guide/grpc/page.md +++ b/docs/advanced-guide/grpc/page.md @@ -3,9 +3,10 @@ We have already seen how GoFr can help ease the development of HTTP servers, but there are cases where performance is primarily required sacrificing flexibility. In these types of scenarios gRPC protocol comes into picture. {% new-tab-link title="gRPC" href="https://grpc.io/docs/what-is-grpc/introduction/" /%} is an open-source RPC(Remote Procedure Call) framework initially developed by Google. GoFr streamlines the creation of gRPC servers and clients with unified GoFr's context support. -It provides built-in tracing, metrics, and logging to ensure seamless performance monitoring for both -gRPC servers and inter-service gRPC communication. Using GoFr's context, we can easily define custom metrics and -traces across gRPC handlers, enabling consistent observability and simplified debugging throughout your system. +It provides built-in tracing, metrics, and logging to ensure seamless performance monitoring for both gRPC servers and inter-service gRPC communication. +With GoFr's context, you can seamlessly define custom metrics and traces across gRPC handlers, ensuring consistent observability and streamlined debugging throughout +your system. Additionally, GoFr provides a built-in health check for all your services and supports inter-service +health checks, allowing gRPC services to monitor each other effortlessly. ## Prerequisites @@ -137,7 +138,7 @@ import ( func main() { app := gofr.New() - packageName.Register{serviceName}ServerWithGofr(app, &{packageName}.{serviceName}GoFrServer{}) + packageName.Register{serviceName}ServerWithGofr(app, &{packageName}.New{serviceName}GoFrServer()) app.Run() } @@ -145,7 +146,7 @@ func main() { >Note: By default, gRPC server will run on port 9000, to customize the port users can set `GRPC_PORT` config in the .env -## Generating tracing enabled gRPC Client using `gofr wrap grpc client` +## Generating gRPC Client using `gofr wrap grpc client` **1. Use the `gofr wrap grpc client` Command:** ```bash @@ -178,4 +179,38 @@ return nil, err return res, nil } ``` + +## HealthChecks in GoFr's gRPC Service/Clients +Health Checks in GoFr's gRPC Services + +GoFr provides built-in health checks for gRPC services, enabling observability, monitoring, and inter-service health verification. + +### Client Interface + +```go +type {serviceName}GoFrClient interface { +SayHello(*gofr.Context, *HelloRequest, ...grpc.CallOption) (*HelloResponse, error) +health +} + +type health interface { +Check(ctx *gofr.Context, in *grpc_health_v1.HealthCheckRequest, opts ...grpc.CallOption) (*grpc_health_v1.HealthCheckResponse, error) +Watch(ctx *gofr.Context, in *grpc_health_v1.HealthCheckRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[grpc_health_v1.HealthCheckResponse], error) +} +``` + +### Server Integration +```go +type {serviceName}GoFrServer struct { +health *healthServer +} +``` +Supported Methods for HealthCheck : +```go +func (h *healthServer) Check(ctx *gofr.Context, req *grpc_health_v1.HealthCheckRequest) (*grpc_health_v1.HealthCheckResponse, error) +func (h *healthServer) Watch(ctx *gofr.Context, in *grpc_health_v1.HealthCheckRequest, stream grpc_health_v1.Health_WatchServer) error +func (h *healthServer) SetServingStatus(ctx *gofr.Context, service string, status grpc_health_v1.HealthCheckResponse_ServingStatus) +func (h *healthServer) Shutdown(ctx *gofr.Context) +func (h *healthServer) Resume(ctx *gofr.Context) +``` > ##### Check out the example of setting up a gRPC server/client in GoFr: [Visit GitHub](https://github.com/gofr-dev/gofr/tree/main/examples/grpc) \ No newline at end of file diff --git a/examples/grpc/grpc-client/client/health_client.go b/examples/grpc/grpc-client/client/health_client.go new file mode 100644 index 000000000..11950667a --- /dev/null +++ b/examples/grpc/grpc-client/client/health_client.go @@ -0,0 +1,93 @@ +// Code generated by gofr.dev/cli/gofr. DO NOT EDIT. +package client + +import ( + "fmt" + "sync" + "time" + + "gofr.dev/pkg/gofr" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/grpc/health/grpc_health_v1" + "google.golang.org/grpc/metadata" + + gofrgRPC "gofr.dev/pkg/gofr/grpc" +) + +var ( + metricsOnce sync.Once + gRPCBuckets = []float64{0.005, 0.01, .05, .075, .1, .125, .15, .2, .3, .5, .75, 1, 2, 3, 4, 5, 7.5, 10} +) + +type HealthClient interface { + Check(ctx *gofr.Context, in *grpc_health_v1.HealthCheckRequest, opts ...grpc.CallOption) (*grpc_health_v1.HealthCheckResponse, error) + Watch(ctx *gofr.Context, in *grpc_health_v1.HealthCheckRequest, opts ...grpc.CallOption) ( + grpc.ServerStreamingClient[grpc_health_v1.HealthCheckResponse], error) +} + +type HealthClientWrapper struct { + client grpc_health_v1.HealthClient +} + +func NewHealthClient(conn *grpc.ClientConn) HealthClient { + return &HealthClientWrapper{ + client: grpc_health_v1.NewHealthClient(conn), + } +} + +func createGRPCConn(host string, serviceName string) (*grpc.ClientConn, error) { + serviceConfig := `{"loadBalancingPolicy": "round_robin"}` + + conn, err := grpc.Dial(host, + grpc.WithDefaultServiceConfig(serviceConfig), + grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + return nil, err + } + return conn, nil +} + +func invokeRPC(ctx *gofr.Context, rpcName string, rpcFunc func() (interface{}, error)) (interface{}, error) { + span := ctx.Trace("gRPC-srv-call: " + rpcName) + defer span.End() + + traceID := span.SpanContext().TraceID().String() + spanID := span.SpanContext().SpanID().String() + md := metadata.Pairs("x-gofr-traceid", traceID, "x-gofr-spanid", spanID) + + ctx.Context = metadata.NewOutgoingContext(ctx.Context, md) + transactionStartTime := time.Now() + + res, err := rpcFunc() + logger := gofrgRPC.NewgRPCLogger() + logger.DocumentRPCLog(ctx.Context, ctx.Logger, ctx.Metrics(), transactionStartTime, err, + rpcName, "app_gRPC-Client_stats") + + return res, err +} + +func (h *HealthClientWrapper) Check(ctx *gofr.Context, in *grpc_health_v1.HealthCheckRequest, + opts ...grpc.CallOption) (*grpc_health_v1.HealthCheckResponse, error) { + result, err := invokeRPC(ctx, fmt.Sprintf("/grpc.health.v1.Health/Check Service: %q", in.Service), func() (interface{}, error) { + return h.client.Check(ctx, in, opts...) + }) + + if err != nil { + return nil, err + } + return result.(*grpc_health_v1.HealthCheckResponse), nil +} + +func (h *HealthClientWrapper) Watch(ctx *gofr.Context, in *grpc_health_v1.HealthCheckRequest, + opts ...grpc.CallOption) (grpc.ServerStreamingClient[grpc_health_v1.HealthCheckResponse], error) { + result, err := invokeRPC(ctx, fmt.Sprintf("/grpc.health.v1.Health/Watch Service: %q", in.Service), func() (interface{}, error) { + return h.client.Watch(ctx, in, opts...) + }) + + if err != nil { + return nil, err + } + + return result.(grpc.ServerStreamingClient[grpc_health_v1.HealthCheckResponse]), nil +} diff --git a/examples/grpc/grpc-client/client/hello.pb.go b/examples/grpc/grpc-client/client/hello.pb.go index fda23297e..adbac3e5f 100644 --- a/examples/grpc/grpc-client/client/hello.pb.go +++ b/examples/grpc/grpc-client/client/hello.pb.go @@ -1,17 +1,17 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.26.0 -// protoc v3.7.0 +// protoc-gen-go v1.36.4 +// protoc v5.29.3 // source: hello.proto package client import ( - reflect "reflect" - sync "sync" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" ) const ( @@ -22,20 +22,17 @@ const ( ) type HelloRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` unknownFields protoimpl.UnknownFields - - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + sizeCache protoimpl.SizeCache } func (x *HelloRequest) Reset() { *x = HelloRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_hello_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_hello_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *HelloRequest) String() string { @@ -46,7 +43,7 @@ func (*HelloRequest) ProtoMessage() {} func (x *HelloRequest) ProtoReflect() protoreflect.Message { mi := &file_hello_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -69,20 +66,17 @@ func (x *HelloRequest) GetName() string { } type HelloResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` unknownFields protoimpl.UnknownFields - - Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` + sizeCache protoimpl.SizeCache } func (x *HelloResponse) Reset() { *x = HelloResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_hello_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_hello_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *HelloResponse) String() string { @@ -93,7 +87,7 @@ func (*HelloResponse) ProtoMessage() {} func (x *HelloResponse) ProtoReflect() protoreflect.Message { mi := &file_hello_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -115,46 +109,147 @@ func (x *HelloResponse) GetMessage() string { return "" } +type GoodbyeRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GoodbyeRequest) Reset() { + *x = GoodbyeRequest{} + mi := &file_hello_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GoodbyeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GoodbyeRequest) ProtoMessage() {} + +func (x *GoodbyeRequest) ProtoReflect() protoreflect.Message { + mi := &file_hello_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GoodbyeRequest.ProtoReflect.Descriptor instead. +func (*GoodbyeRequest) Descriptor() ([]byte, []int) { + return file_hello_proto_rawDescGZIP(), []int{2} +} + +func (x *GoodbyeRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type GoodbyeResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GoodbyeResponse) Reset() { + *x = GoodbyeResponse{} + mi := &file_hello_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GoodbyeResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GoodbyeResponse) ProtoMessage() {} + +func (x *GoodbyeResponse) ProtoReflect() protoreflect.Message { + mi := &file_hello_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GoodbyeResponse.ProtoReflect.Descriptor instead. +func (*GoodbyeResponse) Descriptor() ([]byte, []int) { + return file_hello_proto_rawDescGZIP(), []int{3} +} + +func (x *GoodbyeResponse) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + var File_hello_proto protoreflect.FileDescriptor -var file_hello_proto_rawDesc = []byte{ +var file_hello_proto_rawDesc = string([]byte{ 0x0a, 0x0b, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x22, 0x0a, 0x0c, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x29, 0x0a, 0x0d, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x32, 0x34, 0x0a, 0x05, - 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x2b, 0x0a, 0x08, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, - 0x6f, 0x12, 0x0d, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x0e, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x42, 0x32, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x76, 0x69, 0x6b, 0x61, 0x73, 0x68, 0x2f, 0x67, 0x6f, 0x66, 0x72, 0x2f, 0x65, 0x78, 0x61, - 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} + 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x24, 0x0a, 0x0e, + 0x47, 0x6f, 0x6f, 0x64, 0x62, 0x79, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x22, 0x2b, 0x0a, 0x0f, 0x47, 0x6f, 0x6f, 0x64, 0x62, 0x79, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x32, + 0x34, 0x0a, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x2b, 0x0a, 0x08, 0x53, 0x61, 0x79, 0x48, + 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x0d, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x32, 0x3c, 0x0a, 0x07, 0x47, 0x6f, 0x6f, 0x64, 0x62, 0x79, 0x65, + 0x12, 0x31, 0x0a, 0x0a, 0x53, 0x61, 0x79, 0x47, 0x6f, 0x6f, 0x64, 0x62, 0x79, 0x65, 0x12, 0x0f, + 0x2e, 0x47, 0x6f, 0x6f, 0x64, 0x62, 0x79, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x10, 0x2e, 0x47, 0x6f, 0x6f, 0x64, 0x62, 0x79, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x42, 0x2b, 0x5a, 0x29, 0x67, 0x6f, 0x66, 0x72, 0x2e, 0x64, 0x65, 0x76, 0x2f, + 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x67, 0x72, + 0x70, 0x63, 0x2d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +}) var ( file_hello_proto_rawDescOnce sync.Once - file_hello_proto_rawDescData = file_hello_proto_rawDesc + file_hello_proto_rawDescData []byte ) func file_hello_proto_rawDescGZIP() []byte { file_hello_proto_rawDescOnce.Do(func() { - file_hello_proto_rawDescData = protoimpl.X.CompressGZIP(file_hello_proto_rawDescData) + file_hello_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_hello_proto_rawDesc), len(file_hello_proto_rawDesc))) }) return file_hello_proto_rawDescData } -var file_hello_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_hello_proto_msgTypes = make([]protoimpl.MessageInfo, 4) var file_hello_proto_goTypes = []any{ - (*HelloRequest)(nil), // 0: HelloRequest - (*HelloResponse)(nil), // 1: HelloResponse + (*HelloRequest)(nil), // 0: HelloRequest + (*HelloResponse)(nil), // 1: HelloResponse + (*GoodbyeRequest)(nil), // 2: GoodbyeRequest + (*GoodbyeResponse)(nil), // 3: GoodbyeResponse } var file_hello_proto_depIdxs = []int32{ 0, // 0: Hello.SayHello:input_type -> HelloRequest - 1, // 1: Hello.SayHello:output_type -> HelloResponse - 1, // [1:2] is the sub-list for method output_type - 0, // [0:1] is the sub-list for method input_type + 2, // 1: Goodbye.SayGoodbye:input_type -> GoodbyeRequest + 1, // 2: Hello.SayHello:output_type -> HelloResponse + 3, // 3: Goodbye.SayGoodbye:output_type -> GoodbyeResponse + 2, // [2:4] is the sub-list for method output_type + 0, // [0:2] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name @@ -165,48 +260,21 @@ func file_hello_proto_init() { if File_hello_proto != nil { return } - if !protoimpl.UnsafeEnabled { - file_hello_proto_msgTypes[0].Exporter = func(v any, i int) any { - switch v := v.(*HelloRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_hello_proto_msgTypes[1].Exporter = func(v any, i int) any { - switch v := v.(*HelloResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_hello_proto_rawDesc, + RawDescriptor: unsafe.Slice(unsafe.StringData(file_hello_proto_rawDesc), len(file_hello_proto_rawDesc)), NumEnums: 0, - NumMessages: 2, + NumMessages: 4, NumExtensions: 0, - NumServices: 1, + NumServices: 2, }, GoTypes: file_hello_proto_goTypes, DependencyIndexes: file_hello_proto_depIdxs, MessageInfos: file_hello_proto_msgTypes, }.Build() File_hello_proto = out.File - file_hello_proto_rawDesc = nil file_hello_proto_goTypes = nil file_hello_proto_depIdxs = nil } diff --git a/examples/grpc/grpc-client/client/hello.proto b/examples/grpc/grpc-client/client/hello.proto index 97e35056e..74516517a 100644 --- a/examples/grpc/grpc-client/client/hello.proto +++ b/examples/grpc/grpc-client/client/hello.proto @@ -10,5 +10,5 @@ message HelloResponse { } service Hello { - rpc SayHello(HelloRequest) returns(HelloResponse) {} + rpc SayHello(HelloRequest) returns (HelloResponse) {} } \ No newline at end of file diff --git a/examples/grpc/grpc-client/client/hello_client.go b/examples/grpc/grpc-client/client/hello_client.go index f93a286d6..ea50c7fe3 100644 --- a/examples/grpc/grpc-client/client/hello_client.go +++ b/examples/grpc/grpc-client/client/hello_client.go @@ -2,125 +2,50 @@ package client import ( - "io" - "fmt" - "encoding/json" - "time" - "gofr.dev/pkg/gofr" "gofr.dev/pkg/gofr/metrics" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/grpc/metadata" -) - -const ( - statusCodeWidth = 3 - responseTimeWidth = 11 ) -type RPCLog struct { - ID string `json:"id"` -StartTime string `json:"startTime"` -ResponseTime int64 `json:"responseTime"` -Method string `json:"method"` -StatusCode int32 `json:"statusCode"` -} - type HelloGoFrClient interface { - SayHello(*gofr.Context, *HelloRequest) (*HelloResponse, error) + SayHello(*gofr.Context, *HelloRequest, ...grpc.CallOption) (*HelloResponse, error) + HealthClient } type HelloClientWrapper struct { - client HelloClient - HelloGoFrClient -} - -func createGRPCConn(host string) (*grpc.ClientConn, error) { - conn, err := grpc.Dial(host, grpc.WithTransportCredentials(insecure.NewCredentials())) - if err != nil { - return nil, err - } - return conn, nil + client HelloClient + HealthClient } -func NewHelloGoFrClient(host string, metrics metrics.Manager) (*HelloClientWrapper, error) { - conn, err := createGRPCConn(host) +func NewHelloGoFrClient(host string, metrics metrics.Manager) (HelloGoFrClient, error) { + conn, err := createGRPCConn(host, "Hello") if err != nil { - return &HelloClientWrapper{client: nil}, err + return &HelloClientWrapper{ + client: nil, + HealthClient: &HealthClientWrapper{client: nil}, // Ensure HealthClient is also implemented + }, err } - gRPCBuckets := []float64{0.005, 0.01, .05, .075, .1, .125, .15, .2, .3, .5, .75, 1, 2, 3, 4, 5, 7.5, 10} - metrics.NewHistogram("app_gRPC-Client_stats", - "Response time of gRPC client in milliseconds.", - gRPCBuckets...) - + metricsOnce.Do(func() { + metrics.NewHistogram("app_gRPC-Client_stats", "Response time of gRPC client in milliseconds.", gRPCBuckets...) + }) res := NewHelloClient(conn) + healthClient := NewHealthClient(conn) + return &HelloClientWrapper{ client: res, + HealthClient: healthClient, }, nil } +func (h *HelloClientWrapper) SayHello(ctx *gofr.Context, req *HelloRequest, +opts ...grpc.CallOption) (*HelloResponse, error) { + result, err := invokeRPC(ctx, "/Hello/SayHello", func() (interface{}, error) { + return h.client.SayHello(ctx.Context, req, opts...) + }) -func (h *HelloClientWrapper) SayHello(ctx *gofr.Context, req *HelloRequest) (*HelloResponse, error) { - span := ctx.Trace("gRPC-srv-call: SayHello") - defer span.End() - - traceID := span.SpanContext().TraceID().String() - spanID := span.SpanContext().SpanID().String() - md := metadata.Pairs("x-gofr-traceid", traceID, "x-gofr-spanid", spanID) - - ctx.Context = metadata.NewOutgoingContext(ctx.Context, md) - - var header metadata.MD - - transactionStartTime := time.Now() - - res, err := h.client.SayHello(ctx.Context, req, grpc.Header(&header)) if err != nil { return nil, err } - - duration := time.Since(transactionStartTime) - - ctx.Metrics().RecordHistogram(ctx, "app_gRPC-Client_stats", - float64(duration.Milliseconds())+float64(duration.Nanoseconds()%1e6)/1e6, - "gRPC_Service", "Hello", - "method", "SayHello") - - log := &RPCLog{} - - if values, ok := header["log"]; ok && len(values) > 0 { - errUnmarshal := json.Unmarshal([]byte(values[0]), log) - if errUnmarshal != nil { - return nil, fmt.Errorf("error while unmarshaling: %v", errUnmarshal) - } - } - - ctx.Logger.Info(log) - - return res, err -} - -func (l RPCLog) PrettyPrint(writer io.Writer) { - fmt.Fprintf(writer, "\u001B[38;5;8m%s \u001B[38;5;%dm%-*d"+ - "\u001B[0m %*d\u001B[38;5;8mµs\u001B[0m %s\n", - l.ID, colorForGRPCCode(l.StatusCode), - statusCodeWidth, l.StatusCode, - responseTimeWidth, l.ResponseTime, - l.Method) -} - -func colorForGRPCCode(s int32) int { - const ( - blue = 34 - red = 202 - ) - - if s == 0 { - return blue - } - - return red + return result.(*HelloResponse), nil } diff --git a/examples/grpc/grpc-client/client/hello_grpc.pb.go b/examples/grpc/grpc-client/client/hello_grpc.pb.go index d5dc6b564..39a5acfe6 100644 --- a/examples/grpc/grpc-client/client/hello_grpc.pb.go +++ b/examples/grpc/grpc-client/client/hello_grpc.pb.go @@ -1,7 +1,7 @@ -// Code generated by protoc-gen-go-client. DO NOT EDIT. +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-client v1.1.0 -// - protoc v3.7.0 +// - protoc-gen-go-grpc v1.5.1 +// - protoc v5.29.3 // source: hello.proto package client @@ -14,9 +14,13 @@ import ( ) // This is a compile-time assertion to ensure that this generated file -// is compatible with the client package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + Hello_SayHello_FullMethodName = "/Hello/SayHello" +) // HelloClient is the client API for Hello service. // @@ -34,30 +38,35 @@ func NewHelloClient(cc grpc.ClientConnInterface) HelloClient { } func (c *helloClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(HelloResponse) - err := c.cc.Invoke(ctx, "/Hello/SayHello", in, out, opts...) + err := c.cc.Invoke(ctx, Hello_SayHello_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } -// HelloServer is the client API for Hello service. +// HelloServer is the server API for Hello service. // All implementations must embed UnimplementedHelloServer -// for forward compatibility +// for forward compatibility. type HelloServer interface { SayHello(context.Context, *HelloRequest) (*HelloResponse, error) mustEmbedUnimplementedHelloServer() } -// UnimplementedHelloServer must be embedded to have forward compatible implementations. -type UnimplementedHelloServer struct { -} +// UnimplementedHelloServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedHelloServer struct{} func (UnimplementedHelloServer) SayHello(context.Context, *HelloRequest) (*HelloResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method SayHello not implemented") } func (UnimplementedHelloServer) mustEmbedUnimplementedHelloServer() {} +func (UnimplementedHelloServer) testEmbeddedByValue() {} // UnsafeHelloServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to HelloServer will @@ -67,10 +76,17 @@ type UnsafeHelloServer interface { } func RegisterHelloServer(s grpc.ServiceRegistrar, srv HelloServer) { + // If the following call pancis, it indicates UnimplementedHelloServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&Hello_ServiceDesc, srv) } -func _Hello_SayHello_Handler(srv any, ctx context.Context, dec func(any) error, interceptor grpc.UnaryServerInterceptor) (any, error) { +func _Hello_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(HelloRequest) if err := dec(in); err != nil { return nil, err @@ -80,16 +96,16 @@ func _Hello_SayHello_Handler(srv any, ctx context.Context, dec func(any) error, } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/Hello/SayHello", + FullMethod: Hello_SayHello_FullMethodName, } - handler := func(ctx context.Context, req any) (any, error) { + handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(HelloServer).SayHello(ctx, req.(*HelloRequest)) } return interceptor(ctx, in, info, handler) } -// Hello_ServiceDesc is the client.ServiceDesc for Hello service. -// It's only intended for direct use with client.RegisterService, +// Hello_ServiceDesc is the grpc.ServiceDesc for Hello service. +// It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var Hello_ServiceDesc = grpc.ServiceDesc{ ServiceName: "Hello", @@ -103,3 +119,105 @@ var Hello_ServiceDesc = grpc.ServiceDesc{ Streams: []grpc.StreamDesc{}, Metadata: "hello.proto", } + +const ( + Goodbye_SayGoodbye_FullMethodName = "/Goodbye/SayGoodbye" +) + +// GoodbyeClient is the client API for Goodbye service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type GoodbyeClient interface { + SayGoodbye(ctx context.Context, in *GoodbyeRequest, opts ...grpc.CallOption) (*GoodbyeResponse, error) +} + +type goodbyeClient struct { + cc grpc.ClientConnInterface +} + +func NewGoodbyeClient(cc grpc.ClientConnInterface) GoodbyeClient { + return &goodbyeClient{cc} +} + +func (c *goodbyeClient) SayGoodbye(ctx context.Context, in *GoodbyeRequest, opts ...grpc.CallOption) (*GoodbyeResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GoodbyeResponse) + err := c.cc.Invoke(ctx, Goodbye_SayGoodbye_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// GoodbyeServer is the server API for Goodbye service. +// All implementations must embed UnimplementedGoodbyeServer +// for forward compatibility. +type GoodbyeServer interface { + SayGoodbye(context.Context, *GoodbyeRequest) (*GoodbyeResponse, error) + mustEmbedUnimplementedGoodbyeServer() +} + +// UnimplementedGoodbyeServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedGoodbyeServer struct{} + +func (UnimplementedGoodbyeServer) SayGoodbye(context.Context, *GoodbyeRequest) (*GoodbyeResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SayGoodbye not implemented") +} +func (UnimplementedGoodbyeServer) mustEmbedUnimplementedGoodbyeServer() {} +func (UnimplementedGoodbyeServer) testEmbeddedByValue() {} + +// UnsafeGoodbyeServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to GoodbyeServer will +// result in compilation errors. +type UnsafeGoodbyeServer interface { + mustEmbedUnimplementedGoodbyeServer() +} + +func RegisterGoodbyeServer(s grpc.ServiceRegistrar, srv GoodbyeServer) { + // If the following call pancis, it indicates UnimplementedGoodbyeServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&Goodbye_ServiceDesc, srv) +} + +func _Goodbye_SayGoodbye_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GoodbyeRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GoodbyeServer).SayGoodbye(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Goodbye_SayGoodbye_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GoodbyeServer).SayGoodbye(ctx, req.(*GoodbyeRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// Goodbye_ServiceDesc is the grpc.ServiceDesc for Goodbye service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Goodbye_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "Goodbye", + HandlerType: (*GoodbyeServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "SayGoodbye", + Handler: _Goodbye_SayGoodbye_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "hello.proto", +} diff --git a/examples/grpc/grpc-client/main.go b/examples/grpc/grpc-client/main.go index 8ddbd76a9..b89a9083d 100644 --- a/examples/grpc/grpc-client/main.go +++ b/examples/grpc/grpc-client/main.go @@ -8,7 +8,7 @@ import ( func main() { app := gofr.New() - // Create a gRPC client for the Hello service + //Create a gRPC client for the Hello service helloGRPCClient, err := client.NewHelloGoFrClient(app.Config.Get("GRPC_SERVER_HOST"), app.Metrics()) if err != nil { app.Logger().Errorf("Failed to create Hello gRPC client: %v", err) @@ -40,6 +40,15 @@ func (g GreetHandler) Hello(ctx *gofr.Context) (interface{}, error) { userName = "World" } + //HealthCheck to SayHello Service. + //res, err := g.helloGRPCClient.Check(ctx, &grpc_health_v1.HealthCheckRequest{Service: "Hello"}) + //if err != nil { + // return nil, err + //} else if res.Status == grpc_health_v1.HealthCheckResponse_NOT_SERVING { + // ctx.Error("Hello Service is down") + // return nil, fmt.Errorf("Hello Service is down") + //} + // Make a gRPC call to the Hello service helloResponse, err := g.helloGRPCClient.SayHello(ctx, &client.HelloRequest{Name: userName}) if err != nil { diff --git a/examples/grpc/grpc-server/configs/.env b/examples/grpc/grpc-server/configs/.env index bc1dd2824..8bb5e0938 100644 --- a/examples/grpc/grpc-server/configs/.env +++ b/examples/grpc/grpc-server/configs/.env @@ -4,4 +4,4 @@ GRPC_PORT=9000 HTTP_PORT=8081 LOG_LEVEL=DEBUG -TRACE_EXPORTER=gofr \ No newline at end of file +TRACE_EXPORTER=gofr diff --git a/examples/grpc/grpc-server/main.go b/examples/grpc/grpc-server/main.go index 7c0f36749..dff1d1644 100644 --- a/examples/grpc/grpc-server/main.go +++ b/examples/grpc/grpc-server/main.go @@ -8,7 +8,7 @@ import ( func main() { app := gofr.New() - server.RegisterHelloServerWithGofr(app, &server.HelloGoFrServer{}) + server.RegisterHelloServerWithGofr(app, server.NewHelloGoFrServer()) app.Run() } diff --git a/examples/grpc/grpc-server/server/health_gofr.go b/examples/grpc/grpc-server/server/health_gofr.go new file mode 100644 index 000000000..e6ea82387 --- /dev/null +++ b/examples/grpc/grpc-server/server/health_gofr.go @@ -0,0 +1,99 @@ +// Code generated by gofr.dev/cli/gofr. DO NOT EDIT. +package server + +import ( + "fmt" + "google.golang.org/grpc" + "time" + + "gofr.dev/pkg/gofr" + + gofrGRPC "gofr.dev/pkg/gofr/grpc" + "google.golang.org/grpc/health" + healthpb "google.golang.org/grpc/health/grpc_health_v1" +) + +type healthServer struct { + *health.Server +} + +var globalHealthServer *healthServer +var healthServerRegistered bool // Global flag to track if health server is registered + +// getOrCreateHealthServer ensures only one health server is created and reused. +func getOrCreateHealthServer() *healthServer { + if globalHealthServer == nil { + globalHealthServer = &healthServer{health.NewServer()} + } + return globalHealthServer +} + +func registerServerWithGofr(app *gofr.App, srv any, registerFunc func(grpc.ServiceRegistrar, any)) { + var s grpc.ServiceRegistrar = app + h := getOrCreateHealthServer() + + // Register metrics and health server only once + if !healthServerRegistered { + gRPCBuckets := []float64{0.005, 0.01, .05, .075, .1, .125, .15, .2, .3, .5, .75, 1, 2, 3, 4, 5, 7.5, 10} + app.Metrics().NewHistogram("app_gRPC-Server_stats", "Response time of gRPC server in milliseconds.", gRPCBuckets...) + + healthpb.RegisterHealthServer(s, h.Server) + h.Server.SetServingStatus("", healthpb.HealthCheckResponse_SERVING) + healthServerRegistered = true + } + + // Register the provided server + registerFunc(s, srv) +} + +func (h *healthServer) Check(ctx *gofr.Context, req *healthpb.HealthCheckRequest) (*healthpb.HealthCheckResponse, error) { + start := time.Now() + span := ctx.Trace("/grpc.health.v1.Health/Check") + res, err := h.Server.Check(ctx.Context, req) + logger := gofrGRPC.NewgRPCLogger() + logger.DocumentRPCLog(ctx.Context, ctx.Logger, ctx.Metrics(), start, err, + fmt.Sprintf("/grpc.health.v1.Health/Check Service: %q", req.Service), "app_gRPC-Server_stats") + span.End() + return res, err +} + +func (h *healthServer) Watch(ctx *gofr.Context, in *healthpb.HealthCheckRequest, stream healthpb.Health_WatchServer) error { + start := time.Now() + span := ctx.Trace("/grpc.health.v1.Health/Watch") + err := h.Server.Watch(in, stream) + logger := gofrGRPC.NewgRPCLogger() + logger.DocumentRPCLog(ctx.Context, ctx.Logger, ctx.Metrics(), start, err, + fmt.Sprintf("/grpc.health.v1.Health/Watch Service: %q", in.Service), "app_gRPC-Server_stats") + span.End() + return err +} + +func (h *healthServer) SetServingStatus(ctx *gofr.Context, service string, servingStatus healthpb.HealthCheckResponse_ServingStatus) { + start := time.Now() + span := ctx.Trace("/grpc.health.v1.Health/SetServingStatus") + h.Server.SetServingStatus(service, servingStatus) + logger := gofrGRPC.NewgRPCLogger() + logger.DocumentRPCLog(ctx.Context, ctx.Logger, ctx.Metrics(), start, nil, + fmt.Sprintf("/grpc.health.v1.Health/SetServingStatus Service: %q", service), "app_gRPC-Server_stats") + span.End() +} + +func (h *healthServer) Shutdown(ctx *gofr.Context) { + start := time.Now() + span := ctx.Trace("/grpc.health.v1.Health/Shutdown") + h.Server.Shutdown() + logger := gofrGRPC.NewgRPCLogger() + logger.DocumentRPCLog(ctx.Context, ctx.Logger, ctx.Metrics(), start, nil, + "/grpc.health.v1.Health/Shutdown", "app_gRPC-Server_stats") + span.End() +} + +func (h *healthServer) Resume(ctx *gofr.Context) { + start := time.Now() + span := ctx.Trace("/grpc.health.v1.Health/Resume") + h.Server.Resume() + logger := gofrGRPC.NewgRPCLogger() + logger.DocumentRPCLog(ctx.Context, ctx.Logger, ctx.Metrics(), start, nil, + "/grpc.health.v1.Health/Resume", "app_gRPC-Server_stats") + span.End() +} diff --git a/examples/grpc/grpc-server/server/hello.pb.go b/examples/grpc/grpc-server/server/hello.pb.go index 33052d546..7c7e78197 100644 --- a/examples/grpc/grpc-server/server/hello.pb.go +++ b/examples/grpc/grpc-server/server/hello.pb.go @@ -1,17 +1,17 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.26.0 -// protoc v3.7.0 +// protoc-gen-go v1.36.4 +// protoc v5.29.3 // source: hello.proto package server import ( - reflect "reflect" - sync "sync" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" ) const ( @@ -22,20 +22,17 @@ const ( ) type HelloRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` unknownFields protoimpl.UnknownFields - - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + sizeCache protoimpl.SizeCache } func (x *HelloRequest) Reset() { *x = HelloRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_hello_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_hello_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *HelloRequest) String() string { @@ -46,7 +43,7 @@ func (*HelloRequest) ProtoMessage() {} func (x *HelloRequest) ProtoReflect() protoreflect.Message { mi := &file_hello_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -69,20 +66,17 @@ func (x *HelloRequest) GetName() string { } type HelloResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` unknownFields protoimpl.UnknownFields - - Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` + sizeCache protoimpl.SizeCache } func (x *HelloResponse) Reset() { *x = HelloResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_hello_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_hello_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *HelloResponse) String() string { @@ -93,7 +87,7 @@ func (*HelloResponse) ProtoMessage() {} func (x *HelloResponse) ProtoReflect() protoreflect.Message { mi := &file_hello_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -115,46 +109,147 @@ func (x *HelloResponse) GetMessage() string { return "" } +type GoodbyeRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GoodbyeRequest) Reset() { + *x = GoodbyeRequest{} + mi := &file_hello_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GoodbyeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GoodbyeRequest) ProtoMessage() {} + +func (x *GoodbyeRequest) ProtoReflect() protoreflect.Message { + mi := &file_hello_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GoodbyeRequest.ProtoReflect.Descriptor instead. +func (*GoodbyeRequest) Descriptor() ([]byte, []int) { + return file_hello_proto_rawDescGZIP(), []int{2} +} + +func (x *GoodbyeRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type GoodbyeResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GoodbyeResponse) Reset() { + *x = GoodbyeResponse{} + mi := &file_hello_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GoodbyeResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GoodbyeResponse) ProtoMessage() {} + +func (x *GoodbyeResponse) ProtoReflect() protoreflect.Message { + mi := &file_hello_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GoodbyeResponse.ProtoReflect.Descriptor instead. +func (*GoodbyeResponse) Descriptor() ([]byte, []int) { + return file_hello_proto_rawDescGZIP(), []int{3} +} + +func (x *GoodbyeResponse) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + var File_hello_proto protoreflect.FileDescriptor -var file_hello_proto_rawDesc = []byte{ +var file_hello_proto_rawDesc = string([]byte{ 0x0a, 0x0b, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x22, 0x0a, 0x0c, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x29, 0x0a, 0x0d, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x32, 0x34, 0x0a, 0x05, - 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x2b, 0x0a, 0x08, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, - 0x6f, 0x12, 0x0d, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x0e, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x42, 0x32, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x76, 0x69, 0x6b, 0x61, 0x73, 0x68, 0x2f, 0x67, 0x6f, 0x66, 0x72, 0x2f, 0x65, 0x78, 0x61, - 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} + 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x24, 0x0a, 0x0e, + 0x47, 0x6f, 0x6f, 0x64, 0x62, 0x79, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x22, 0x2b, 0x0a, 0x0f, 0x47, 0x6f, 0x6f, 0x64, 0x62, 0x79, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x32, + 0x34, 0x0a, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x2b, 0x0a, 0x08, 0x53, 0x61, 0x79, 0x48, + 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x0d, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x32, 0x3c, 0x0a, 0x07, 0x47, 0x6f, 0x6f, 0x64, 0x62, 0x79, 0x65, + 0x12, 0x31, 0x0a, 0x0a, 0x53, 0x61, 0x79, 0x47, 0x6f, 0x6f, 0x64, 0x62, 0x79, 0x65, 0x12, 0x0f, + 0x2e, 0x47, 0x6f, 0x6f, 0x64, 0x62, 0x79, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x10, 0x2e, 0x47, 0x6f, 0x6f, 0x64, 0x62, 0x79, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x42, 0x2b, 0x5a, 0x29, 0x67, 0x6f, 0x66, 0x72, 0x2e, 0x64, 0x65, 0x76, 0x2f, + 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x67, 0x72, + 0x70, 0x63, 0x2d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +}) var ( file_hello_proto_rawDescOnce sync.Once - file_hello_proto_rawDescData = file_hello_proto_rawDesc + file_hello_proto_rawDescData []byte ) func file_hello_proto_rawDescGZIP() []byte { file_hello_proto_rawDescOnce.Do(func() { - file_hello_proto_rawDescData = protoimpl.X.CompressGZIP(file_hello_proto_rawDescData) + file_hello_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_hello_proto_rawDesc), len(file_hello_proto_rawDesc))) }) return file_hello_proto_rawDescData } -var file_hello_proto_msgTypes = make([]protoimpl.MessageInfo, 2) -var file_hello_proto_goTypes = []interface{}{ - (*HelloRequest)(nil), // 0: HelloRequest - (*HelloResponse)(nil), // 1: HelloResponse +var file_hello_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_hello_proto_goTypes = []any{ + (*HelloRequest)(nil), // 0: HelloRequest + (*HelloResponse)(nil), // 1: HelloResponse + (*GoodbyeRequest)(nil), // 2: GoodbyeRequest + (*GoodbyeResponse)(nil), // 3: GoodbyeResponse } var file_hello_proto_depIdxs = []int32{ 0, // 0: Hello.SayHello:input_type -> HelloRequest - 1, // 1: Hello.SayHello:output_type -> HelloResponse - 1, // [1:2] is the sub-list for method output_type - 0, // [0:1] is the sub-list for method input_type + 2, // 1: Goodbye.SayGoodbye:input_type -> GoodbyeRequest + 1, // 2: Hello.SayHello:output_type -> HelloResponse + 3, // 3: Goodbye.SayGoodbye:output_type -> GoodbyeResponse + 2, // [2:4] is the sub-list for method output_type + 0, // [0:2] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name @@ -165,48 +260,21 @@ func file_hello_proto_init() { if File_hello_proto != nil { return } - if !protoimpl.UnsafeEnabled { - file_hello_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*HelloRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_hello_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*HelloResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_hello_proto_rawDesc, + RawDescriptor: unsafe.Slice(unsafe.StringData(file_hello_proto_rawDesc), len(file_hello_proto_rawDesc)), NumEnums: 0, - NumMessages: 2, + NumMessages: 4, NumExtensions: 0, - NumServices: 1, + NumServices: 2, }, GoTypes: file_hello_proto_goTypes, DependencyIndexes: file_hello_proto_depIdxs, MessageInfos: file_hello_proto_msgTypes, }.Build() File_hello_proto = out.File - file_hello_proto_rawDesc = nil file_hello_proto_goTypes = nil file_hello_proto_depIdxs = nil } diff --git a/examples/grpc/grpc-server/server/hello.proto b/examples/grpc/grpc-server/server/hello.proto index a62a2fe11..7da8fd661 100644 --- a/examples/grpc/grpc-server/server/hello.proto +++ b/examples/grpc/grpc-server/server/hello.proto @@ -10,5 +10,5 @@ message HelloResponse { } service Hello { - rpc SayHello(HelloRequest) returns(HelloResponse) {} -} \ No newline at end of file + rpc SayHello(HelloRequest) returns (HelloResponse) {} +} diff --git a/examples/grpc/grpc-server/server/hello_gofr.go b/examples/grpc/grpc-server/server/hello_gofr.go index 372668cee..d13f573e7 100644 --- a/examples/grpc/grpc-server/server/hello_gofr.go +++ b/examples/grpc/grpc-server/server/hello_gofr.go @@ -5,37 +5,46 @@ import ( "context" "fmt" "reflect" - "time" "gofr.dev/pkg/gofr" "gofr.dev/pkg/gofr/container" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + + healthpb "google.golang.org/grpc/health/grpc_health_v1" ) +// NewHelloGoFrServer creates a new instance of HelloGoFrServer +func NewHelloGoFrServer() *HelloGoFrServer { + return &HelloGoFrServer{ + health: getOrCreateHealthServer(), // Initialize the health server + } +} + +// HelloServerWithGofr is the interface for the server implementation type HelloServerWithGofr interface { SayHello(*gofr.Context) (any, error) } +// HelloServerWrapper wraps the server and handles request and response logic type HelloServerWrapper struct { HelloServer + *healthServer Container *container.Container server HelloServerWithGofr } -func (h *HelloServerWrapper) SayHello(ctx context.Context, req *HelloRequest) (*HelloResponse, error) { - gctx := h.GetGofrContext(ctx, &HelloRequestWrapper{ctx: ctx, HelloRequest: req}) - start := time.Now() +// +// SayHello wraps the method and handles its execution +func (h *HelloServerWrapper) SayHello(ctx context.Context, req *HelloRequest) (*HelloResponse, error) { + gctx := h.getGofrContext(ctx, &HelloRequestWrapper{ctx: ctx, HelloRequest: req}) res, err := h.server.SayHello(gctx) if err != nil { return nil, err } - duration := time.Since(start) - gctx.Metrics().RecordHistogram(ctx, "app_gRPC-Server_stats", float64(duration.Milliseconds())+float64(duration.Nanoseconds()%1e6)/1e6, "gRPC_Service", "Hello", "method", "SayHello") - resp, ok := res.(*HelloResponse) if !ok { return nil, status.Errorf(codes.Unknown, "unexpected response type %T", res) @@ -44,26 +53,29 @@ func (h *HelloServerWrapper) SayHello(ctx context.Context, req *HelloRequest) (* return resp, nil } +// mustEmbedUnimplementedHelloServer ensures that the server implements all required methods func (h *HelloServerWrapper) mustEmbedUnimplementedHelloServer() {} +// RegisterHelloServerWithGofr registers the server with the application func RegisterHelloServerWithGofr(app *gofr.App, srv HelloServerWithGofr) { - var s grpc.ServiceRegistrar = app - - wrapper := &HelloServerWrapper{server: srv} - - gRPCBuckets := []float64{0.005, 0.01, .05, .075, .1, .125, .15, .2, .3, .5, .75, 1, 2, 3, 4, 5, 7.5, 10} - app.Metrics().NewHistogram("app_gRPC-Server_stats", "Response time of gRPC server in milliseconds.", gRPCBuckets...) - - RegisterHelloServer(s, wrapper) + registerServerWithGofr(app, srv, func(s grpc.ServiceRegistrar, srv any) { + wrapper := &HelloServerWrapper{server: srv.(HelloServerWithGofr), healthServer: getOrCreateHealthServer()} + RegisterHelloServer(s, wrapper) + wrapper.Server.SetServingStatus("Hello", healthpb.HealthCheckResponse_SERVING) + }) } -func (h *HelloServerWrapper) GetGofrContext(ctx context.Context, req gofr.Request) *gofr.Context { +// GetGofrContext extracts the GoFr context from the original context +func (h *HelloServerWrapper) getGofrContext(ctx context.Context, req gofr.Request) *gofr.Context { return &gofr.Context{ Context: ctx, Container: h.Container, Request: req, } } + +// +// Request Wrappers type HelloRequestWrapper struct { ctx context.Context *HelloRequest @@ -90,10 +102,8 @@ func (h *HelloRequestWrapper) Bind(p interface{}) error { hValue := reflect.ValueOf(h.HelloRequest).Elem() ptrValue := ptr.Elem() - // Ensure we can set exported fields (skip unexported fields) for i := 0; i < hValue.NumField(); i++ { field := hValue.Type().Field(i) - // Skip the fields we don't want to copy (state, sizeCache, unknownFields) if field.Name == "state" || field.Name == "sizeCache" || field.Name == "unknownFields" { continue } diff --git a/examples/grpc/grpc-server/server/hello_grpc.pb.go b/examples/grpc/grpc-server/server/hello_grpc.pb.go index f1f09eab1..09a34c2bd 100644 --- a/examples/grpc/grpc-server/server/hello_grpc.pb.go +++ b/examples/grpc/grpc-server/server/hello_grpc.pb.go @@ -1,7 +1,7 @@ -// Code generated by protoc-gen-go-client. DO NOT EDIT. +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-client v1.1.0 -// - protoc v3.7.0 +// - protoc-gen-go-grpc v1.5.1 +// - protoc v5.29.3 // source: hello.proto package server @@ -14,9 +14,13 @@ import ( ) // This is a compile-time assertion to ensure that this generated file -// is compatible with the client package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + Hello_SayHello_FullMethodName = "/Hello/SayHello" +) // HelloClient is the client API for Hello service. // @@ -34,30 +38,35 @@ func NewHelloClient(cc grpc.ClientConnInterface) HelloClient { } func (c *helloClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(HelloResponse) - err := c.cc.Invoke(ctx, "/Hello/SayHello", in, out, opts...) + err := c.cc.Invoke(ctx, Hello_SayHello_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } -// HelloServer is the client API for Hello service. +// HelloServer is the server API for Hello service. // All implementations must embed UnimplementedHelloServer -// for forward compatibility +// for forward compatibility. type HelloServer interface { SayHello(context.Context, *HelloRequest) (*HelloResponse, error) mustEmbedUnimplementedHelloServer() } -// UnimplementedHelloServer must be embedded to have forward compatible implementations. -type UnimplementedHelloServer struct { -} +// UnimplementedHelloServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedHelloServer struct{} func (UnimplementedHelloServer) SayHello(context.Context, *HelloRequest) (*HelloResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method SayHello not implemented") } func (UnimplementedHelloServer) mustEmbedUnimplementedHelloServer() {} +func (UnimplementedHelloServer) testEmbeddedByValue() {} // UnsafeHelloServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to HelloServer will @@ -67,6 +76,13 @@ type UnsafeHelloServer interface { } func RegisterHelloServer(s grpc.ServiceRegistrar, srv HelloServer) { + // If the following call pancis, it indicates UnimplementedHelloServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&Hello_ServiceDesc, srv) } @@ -80,7 +96,7 @@ func _Hello_SayHello_Handler(srv interface{}, ctx context.Context, dec func(inte } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/Hello/SayHello", + FullMethod: Hello_SayHello_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(HelloServer).SayHello(ctx, req.(*HelloRequest)) @@ -88,8 +104,8 @@ func _Hello_SayHello_Handler(srv interface{}, ctx context.Context, dec func(inte return interceptor(ctx, in, info, handler) } -// Hello_ServiceDesc is the client.ServiceDesc for Hello service. -// It's only intended for direct use with client.RegisterService, +// Hello_ServiceDesc is the grpc.ServiceDesc for Hello service. +// It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var Hello_ServiceDesc = grpc.ServiceDesc{ ServiceName: "Hello", @@ -103,3 +119,105 @@ var Hello_ServiceDesc = grpc.ServiceDesc{ Streams: []grpc.StreamDesc{}, Metadata: "hello.proto", } + +const ( + Goodbye_SayGoodbye_FullMethodName = "/Goodbye/SayGoodbye" +) + +// GoodbyeClient is the client API for Goodbye service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type GoodbyeClient interface { + SayGoodbye(ctx context.Context, in *GoodbyeRequest, opts ...grpc.CallOption) (*GoodbyeResponse, error) +} + +type goodbyeClient struct { + cc grpc.ClientConnInterface +} + +func NewGoodbyeClient(cc grpc.ClientConnInterface) GoodbyeClient { + return &goodbyeClient{cc} +} + +func (c *goodbyeClient) SayGoodbye(ctx context.Context, in *GoodbyeRequest, opts ...grpc.CallOption) (*GoodbyeResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GoodbyeResponse) + err := c.cc.Invoke(ctx, Goodbye_SayGoodbye_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// GoodbyeServer is the server API for Goodbye service. +// All implementations must embed UnimplementedGoodbyeServer +// for forward compatibility. +type GoodbyeServer interface { + SayGoodbye(context.Context, *GoodbyeRequest) (*GoodbyeResponse, error) + mustEmbedUnimplementedGoodbyeServer() +} + +// UnimplementedGoodbyeServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedGoodbyeServer struct{} + +func (UnimplementedGoodbyeServer) SayGoodbye(context.Context, *GoodbyeRequest) (*GoodbyeResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SayGoodbye not implemented") +} +func (UnimplementedGoodbyeServer) mustEmbedUnimplementedGoodbyeServer() {} +func (UnimplementedGoodbyeServer) testEmbeddedByValue() {} + +// UnsafeGoodbyeServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to GoodbyeServer will +// result in compilation errors. +type UnsafeGoodbyeServer interface { + mustEmbedUnimplementedGoodbyeServer() +} + +func RegisterGoodbyeServer(s grpc.ServiceRegistrar, srv GoodbyeServer) { + // If the following call pancis, it indicates UnimplementedGoodbyeServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&Goodbye_ServiceDesc, srv) +} + +func _Goodbye_SayGoodbye_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GoodbyeRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GoodbyeServer).SayGoodbye(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Goodbye_SayGoodbye_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GoodbyeServer).SayGoodbye(ctx, req.(*GoodbyeRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// Goodbye_ServiceDesc is the grpc.ServiceDesc for Goodbye service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Goodbye_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "Goodbye", + HandlerType: (*GoodbyeServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "SayGoodbye", + Handler: _Goodbye_SayGoodbye_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "hello.proto", +} diff --git a/examples/grpc/grpc-server/server/hello_server.go b/examples/grpc/grpc-server/server/hello_server.go index 924422c6c..ca4f5e0f9 100644 --- a/examples/grpc/grpc-server/server/hello_server.go +++ b/examples/grpc/grpc-server/server/hello_server.go @@ -2,18 +2,18 @@ package server import ( "fmt" - "gofr.dev/pkg/gofr" ) // Register the gRPC service in your app using the following code in your main.go: // -// server.RegisterHelloServerWithGofr(app, &server.HelloGoFrServer{}) +// server.RegisterHelloServerWithGofr(app, &server.NewHelloGoFrServer()) // // HelloGoFrServer defines the gRPC server implementation. // Customize the struct with required dependencies and fields as needed. type HelloGoFrServer struct { + health *healthServer } func (s *HelloGoFrServer) SayHello(ctx *gofr.Context) (any, error) { @@ -29,6 +29,15 @@ func (s *HelloGoFrServer) SayHello(ctx *gofr.Context) (any, error) { name = "World" } + //Performing HealthCheck + //res, err := s.health.Check(ctx, &grpc_health_v1.HealthCheckRequest{ + // Service: "Hello", + //}) + //ctx.Log(res.String()) + + // Setting the serving status + //s.health.SetServingStatus(ctx, "Hello", grpc_health_v1.HealthCheckResponse_NOT_SERVING) + return &HelloResponse{ Message: fmt.Sprintf("Hello %s!", name), }, nil diff --git a/pkg/gofr/grpc.go b/pkg/gofr/grpc.go index 0e2b78064..64e53d16c 100644 --- a/pkg/gofr/grpc.go +++ b/pkg/gofr/grpc.go @@ -25,7 +25,7 @@ func newGRPCServer(c *container.Container, port int) *grpcServer { server: grpc.NewServer( grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer( grpc_recovery.UnaryServerInterceptor(), - gofr_grpc.LoggingInterceptor(c.Logger), + gofr_grpc.ObservabilityInterceptor(c.Logger, c.Metrics()), ))), port: port, } diff --git a/pkg/gofr/grpc/log.go b/pkg/gofr/grpc/log.go index 9c2932272..c789b276e 100644 --- a/pkg/gofr/grpc/log.go +++ b/pkg/gofr/grpc/log.go @@ -11,21 +11,30 @@ import ( "go.opentelemetry.io/otel/trace" "google.golang.org/grpc" "google.golang.org/grpc/codes" + "google.golang.org/grpc/health/grpc_health_v1" "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" ) const ( - statusCodeWidth = 3 - responseTimeWidth = 11 + statusCodeWidth = 3 + responseTimeWidth = 11 + nanosecondsPerMillisecond = 1e6 + debugMethod = "/grpc.health.v1.Health/SetServingStatus" + healthCheck = "/grpc.health.v1.Health/Check" ) type Logger interface { Info(args ...any) Errorf(string, ...any) + Debug(...any) } -type RPCLog struct { +type Metrics interface { + RecordHistogram(ctx context.Context, name string, value float64, labels ...string) +} + +type gRPCLog struct { ID string `json:"id"` StartTime string `json:"startTime"` ResponseTime int64 `json:"responseTime"` @@ -33,13 +42,18 @@ type RPCLog struct { StatusCode int32 `json:"statusCode"` } -func (l RPCLog) PrettyPrint(writer io.Writer) { +//nolint:revive // We intend to keep it non-exported for ease in future changes without any breaking change. +func NewgRPCLogger() gRPCLog { + return gRPCLog{} +} + +func (l gRPCLog) PrettyPrint(writer io.Writer) { fmt.Fprintf(writer, "\u001B[38;5;8m%s \u001B[38;5;%dm%-*d"+ - "\u001B[0m %*d\u001B[38;5;8mµs\u001B[0m %s\n", + "\u001B[0m %*d\u001B[38;5;8mµs\u001B[0m %s %s\n", l.ID, colorForGRPCCode(l.StatusCode), statusCodeWidth, l.StatusCode, responseTimeWidth, l.ResponseTime, - l.Method) + "GRPC", l.Method) } func colorForGRPCCode(s int32) int { @@ -55,12 +69,12 @@ func colorForGRPCCode(s int32) int { return red } -func (l RPCLog) String() string { +func (l gRPCLog) String() string { line, _ := json.Marshal(l) return string(line) } -func LoggingInterceptor(logger Logger) grpc.UnaryServerInterceptor { +func ObservabilityInterceptor(logger Logger, metrics Metrics) grpc.UnaryServerInterceptor { tracer := otel.GetTracerProvider().Tracer("gofr", trace.WithInstrumentationVersion("v0.1")) return func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) { @@ -74,13 +88,15 @@ func LoggingInterceptor(logger Logger) grpc.UnaryServerInterceptor { logger.Errorf("error while handling gRPC request to method %q: %q", info.FullMethod, err) } - md := documentRPCLog(ctx, logger, info.FullMethod, start, err) - - err = grpc.SendHeader(ctx, md) - if err != nil { - logger.Errorf("failed to send metadata in response to request method: %v, Error: %v", info.FullMethod, err) + if info.FullMethod == healthCheck { + service, ok := req.(*grpc_health_v1.HealthCheckRequest) + if ok { + info.FullMethod = fmt.Sprintf("%s Service: %q", healthCheck, service.Service) + } } + logRPC(ctx, logger, metrics, start, err, info.FullMethod, "app_gRPC-Server_stats") + span.End() return resp, err @@ -112,8 +128,13 @@ func initializeSpanContext(ctx context.Context) (context.Context, trace.SpanCont return ctx, spanContext } -func documentRPCLog(ctx context.Context, logger Logger, method string, start time.Time, err error) metadata.MD { - logEntry := RPCLog{ +func (gRPCLog) DocumentRPCLog(ctx context.Context, logger Logger, metrics Metrics, start time.Time, err error, method, name string) { + logRPC(ctx, logger, metrics, start, err, method, name) +} + +func logRPC(ctx context.Context, logger Logger, metrics Metrics, start time.Time, err error, method, name string) { + duration := time.Since(start) + logEntry := gRPCLog{ ID: trace.SpanFromContext(ctx).SpanContext().TraceID().String(), StartTime: start.Format("2006-01-02T15:04:05.999999999-07:00"), ResponseTime: time.Since(start).Microseconds(), @@ -129,10 +150,19 @@ func documentRPCLog(ctx context.Context, logger Logger, method string, start tim } if logger != nil { - logger.Info(logEntry) + if method == debugMethod { + logger.Debug(logEntry) + } else { + logger.Info(logEntry) + } } - return metadata.Pairs("log", logEntry.String()) + if metrics != nil { + metrics.RecordHistogram(ctx, name, + float64(duration.Milliseconds())+float64(duration.Nanoseconds()%nanosecondsPerMillisecond)/nanosecondsPerMillisecond, + "method", + method) + } } // Helper function to safely extract a value from metadata. diff --git a/pkg/gofr/grpc/log_test.go b/pkg/gofr/grpc/log_test.go index f147f424b..b0d275ad9 100644 --- a/pkg/gofr/grpc/log_test.go +++ b/pkg/gofr/grpc/log_test.go @@ -12,7 +12,7 @@ import ( ) func TestRPCLog_String(t *testing.T) { - l := RPCLog{ + l := gRPCLog{ ID: "123", StartTime: "2020-01-01T12:12:12", Method: http.MethodGet, @@ -46,7 +46,7 @@ func TestRPCLog_PrettyPrint(t *testing.T) { startTime := time.Now().String() log := testutil.StdoutOutputForFunc(func() { - l := RPCLog{ + l := gRPCLog{ ID: "1", StartTime: startTime, ResponseTime: 10, From dcce85faa59c29017868bd64f1e6a5eb92f8c5b1 Mon Sep 17 00:00:00 2001 From: umang01-hash Date: Wed, 5 Feb 2025 12:45:11 +0530 Subject: [PATCH 20/22] update release version to 1.33.0 --- pkg/gofr/version/version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/gofr/version/version.go b/pkg/gofr/version/version.go index 314d659ed..06c49c8b3 100644 --- a/pkg/gofr/version/version.go +++ b/pkg/gofr/version/version.go @@ -1,3 +1,3 @@ package version -const Framework = "v1.32.0" +const Framework = "v1.33.0" From cd522659382c34c0f1185e7bc1dadeba34783c4d Mon Sep 17 00:00:00 2001 From: Umang Mundhra Date: Thu, 6 Feb 2025 10:43:54 +0530 Subject: [PATCH 21/22] arango db support as external DB(#1429) --- CONTRIBUTING.md | 5 + .../injecting-databases-drivers/page.md | 193 ++++- pkg/gofr/container/container.go | 1 + pkg/gofr/container/datasources.go | 26 + pkg/gofr/container/mock_container.go | 3 + pkg/gofr/container/mock_datasources.go | 572 ++++++++++++++ pkg/gofr/datasource/README.md | 31 +- pkg/gofr/datasource/arangodb/arango.go | 263 +++++++ .../datasource/arangodb/arango_document.go | 190 +++++ .../arangodb/arango_document_test.go | 254 ++++++ pkg/gofr/datasource/arangodb/arango_graph.go | 71 ++ pkg/gofr/datasource/arangodb/arango_helper.go | 99 +++ .../datasource/arangodb/arango_helper_test.go | 239 ++++++ pkg/gofr/datasource/arangodb/arango_test.go | 132 ++++ pkg/gofr/datasource/arangodb/go.mod | 32 + pkg/gofr/datasource/arangodb/go.sum | 71 ++ pkg/gofr/datasource/arangodb/interface.go | 29 + pkg/gofr/datasource/arangodb/logger.go | 47 ++ pkg/gofr/datasource/arangodb/logger_test.go | 27 + pkg/gofr/datasource/arangodb/metrics.go | 9 + .../datasource/arangodb/mock_collection.go | 670 ++++++++++++++++ pkg/gofr/datasource/arangodb/mock_database.go | 538 +++++++++++++ .../datasource/arangodb/mock_interfaces.go | 724 ++++++++++++++++++ pkg/gofr/datasource/arangodb/mock_logger.go | 106 +++ pkg/gofr/datasource/arangodb/mock_metrics.go | 74 ++ pkg/gofr/datasource/arangodb/mock_user.go | 59 ++ pkg/gofr/external_db.go | 18 + pkg/gofr/external_db_test.go | 24 + 28 files changed, 4488 insertions(+), 19 deletions(-) create mode 100644 pkg/gofr/datasource/arangodb/arango.go create mode 100644 pkg/gofr/datasource/arangodb/arango_document.go create mode 100644 pkg/gofr/datasource/arangodb/arango_document_test.go create mode 100644 pkg/gofr/datasource/arangodb/arango_graph.go create mode 100644 pkg/gofr/datasource/arangodb/arango_helper.go create mode 100644 pkg/gofr/datasource/arangodb/arango_helper_test.go create mode 100644 pkg/gofr/datasource/arangodb/arango_test.go create mode 100644 pkg/gofr/datasource/arangodb/go.mod create mode 100644 pkg/gofr/datasource/arangodb/go.sum create mode 100644 pkg/gofr/datasource/arangodb/interface.go create mode 100644 pkg/gofr/datasource/arangodb/logger.go create mode 100644 pkg/gofr/datasource/arangodb/logger_test.go create mode 100644 pkg/gofr/datasource/arangodb/metrics.go create mode 100644 pkg/gofr/datasource/arangodb/mock_collection.go create mode 100644 pkg/gofr/datasource/arangodb/mock_database.go create mode 100644 pkg/gofr/datasource/arangodb/mock_interfaces.go create mode 100644 pkg/gofr/datasource/arangodb/mock_logger.go create mode 100644 pkg/gofr/datasource/arangodb/mock_metrics.go create mode 100644 pkg/gofr/datasource/arangodb/mock_user.go diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2eb4ad810..717f58a3a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -83,6 +83,11 @@ docker pull scylladb/scylla docker run --name scylla -d -p 2025:9042 scylladb/scylla docker pull surrealdb/surrealdb:latest docker run --name surrealdb -d -p 8000:8000 surrealdb/surrealdb:latest start --bind 0.0.0.0:8000 +docker run -d --name arangodb \ + -p 8529:8529 \ + -e ARANGO_ROOT_PASSWORD=rootpassword \ + --pull always \ + arangodb:latest diff --git a/docs/advanced-guide/injecting-databases-drivers/page.md b/docs/advanced-guide/injecting-databases-drivers/page.md index d9550e97a..259b43bf2 100644 --- a/docs/advanced-guide/injecting-databases-drivers/page.md +++ b/docs/advanced-guide/injecting-databases-drivers/page.md @@ -908,11 +908,11 @@ func main() { app.AddSurrealDB(client) // GET request to fetch person by ID - app.GET("/person/{id}", func(ctx *gofr.Context) (interface{}, error) { + app.GET("/person/{id}", func(ctx *gofr.Context) (any, error) { id := ctx.PathParam("id") query := "SELECT * FROM type::thing('person', $id)" - vars := map[string]interface{}{ + vars := map[string]any{ "id": id, } @@ -925,14 +925,14 @@ func main() { }) // POST request to create a new person - app.POST("/person", func(ctx *gofr.Context) (interface{}, error) { + app.POST("/person", func(ctx *gofr.Context) (any, error) { var person Person if err := ctx.Bind(&person); err != nil { return ErrorResponse{Message: "Invalid request body"}, nil } - result, err := ctx.SurrealDB.Create(ctx, "person", map[string]interface{}{ + result, err := ctx.SurrealDB.Create(ctx, "person", map[string]any{ "name": person.Name, "age": person.Age, "email": person.Email, @@ -949,3 +949,188 @@ func main() { } ``` + + +## ArangoDB + +GoFr supports injecting `ArangoDB` that implements the following interface. Any driver that implements the interface can be +added using the `app.AddArangoDB()` method, and users can use ArangoDB across the application with `gofr.Context`. + +```go +type ArangoDB interface { + // CreateDocument creates a new document in the specified collection. + CreateDocument(ctx context.Context, dbName, collectionName string, document any) (string, error) + // GetDocument retrieves a document by its ID from the specified collection. + GetDocument(ctx context.Context, dbName, collectionName, documentID string, result any) error + // UpdateDocument updates an existing document in the specified collection. + UpdateDocument(ctx context.Context, dbName, collectionName, documentID string, document any) error + // DeleteDocument deletes a document by its ID from the specified collection. + DeleteDocument(ctx context.Context, dbName, collectionName, documentID string) error + + // GetEdges retrieves all the edge documents connected to a specific vertex in an ArangoDB graph. + GetEdges(ctx context.Context, dbName, graphName, edgeCollection, vertexID string, resp any) error + + // Query executes an AQL query and binds the results + Query(ctx context.Context, dbName string, query string, bindVars map[string]any, result any) error + + HealthCheck(context.Context) (any, error) +} +``` + +Users can easily inject a driver that supports this interface, providing usability without compromising the extensibility to use multiple databases. + +Import the GoFr's external driver for ArangoDB: + +```shell +go get gofr.dev/pkg/gofr/datasource/arangodb@latest +``` + +### Example + +```go +package main + +import ( + "fmt" + + "gofr.dev/pkg/gofr" + "gofr.dev/pkg/gofr/datasource/arangodb" +) + +type Person struct { + Name string `json:"name"` + Age int `json:"age"` +} + +func main() { + app := gofr.New() + + // Configure the ArangoDB client + arangoClient := arangodb.New(arangodb.Config{ + Host: "localhost", + User: "root", + Password: "root", + Port: 8529, + }) + app.AddArangoDB(arangoClient) + + // Example routes demonstrating different types of operations + app.POST("/setup", Setup) + app.POST("/users/{name}", CreateUserHandler) + app.POST("/friends", CreateFriendship) + app.GET("/friends/{collection}/{vertexID}", GetEdgesHandler) + + app.Run() +} + +// Setup demonstrates database and collection creation +func Setup(ctx *gofr.Context) (interface{}, error) { + _, err := ctx.ArangoDB.CreateDocument(ctx, "social_network", "", nil) + if err != nil { + return nil, fmt.Errorf("failed to create database: %w", err) + } + + if err := createCollection(ctx, "social_network", "persons"); err != nil { + return nil, err + } + if err := createCollection(ctx, "social_network", "friendships"); err != nil { + return nil, err + } + + // Define and create the graph + edgeDefs := arangodb.EdgeDefinition{ + {Collection: "friendships", From: []string{"persons"}, To: []string{"persons"}}, + } + + _, err = ctx.ArangoDB.CreateDocument(ctx, "social_network", "social_graph", edgeDefs) + if err != nil { + return nil, fmt.Errorf("failed to create graph: %w", err) + } + + return "Setup completed successfully", nil +} + +// Helper function to create collections +func createCollection(ctx *gofr.Context, dbName, collectionName string) error { + _, err := ctx.ArangoDB.CreateDocument(ctx, dbName, collectionName, nil) + if err != nil { + return fmt.Errorf("failed to create collection %s: %w", collectionName, err) + } + return nil +} + +// CreateUserHandler demonstrates user management and document creation +func CreateUserHandler(ctx *gofr.Context) (interface{}, error) { + name := ctx.PathParam("name") + + // Create a person document + person := Person{ + Name: name, + Age: 25, + } + docID, err := ctx.ArangoDB.CreateDocument(ctx, "social_network", "persons", person) + if err != nil { + return nil, fmt.Errorf("failed to create person document: %w", err) + } + + return map[string]string{ + "message": "User created successfully", + "docID": docID, + }, nil +} + +// CreateFriendship demonstrates edge document creation +func CreateFriendship(ctx *gofr.Context) (interface{}, error) { + var req struct { + From string `json:"from"` + To string `json:"to"` + StartDate string `json:"startDate"` + } + + if err := ctx.Bind(&req); err != nil { + return nil, err + } + + edgeDocument := map[string]any{ + "_from": fmt.Sprintf("persons/%s", req.From), + "_to": fmt.Sprintf("persons/%s", req.To), + "startDate": req.StartDate, + } + + // Create an edge document for the friendship + edgeID, err := ctx.ArangoDB.CreateDocument(ctx, "social_network", "friendships", edgeDocument) + if err != nil { + return nil, fmt.Errorf("failed to create friendship: %w", err) + } + + return map[string]string{ + "message": "Friendship created successfully", + "edgeID": edgeID, + }, nil +} + +// GetEdgesHandler demonstrates fetching edges connected to a vertex +func GetEdgesHandler(ctx *gofr.Context) (interface{}, error) { + collection := ctx.PathParam("collection") + vertexID := ctx.PathParam("vertexID") + + fullVertexID := fmt.Sprintf("%s/%s", collection, vertexID) + + // Prepare a slice to hold edge details + edges := make(arangodb.EdgeDetails, 0) + + // Fetch all edges connected to the given vertex + err := ctx.ArangoDB.GetEdges(ctx, "social_network", "social_graph", "friendships", + fullVertexID, &edges) + if err != nil { + return nil, fmt.Errorf("failed to get edges: %w", err) + } + + return map[string]interface{}{ + "vertexID": vertexID, + "edges": edges, + }, nil +} +``` + + diff --git a/pkg/gofr/container/container.go b/pkg/gofr/container/container.go index 1f9d432cc..b46a90875 100644 --- a/pkg/gofr/container/container.go +++ b/pkg/gofr/container/container.go @@ -61,6 +61,7 @@ type Container struct { OpenTSDB OpenTSDB ScyllaDB ScyllaDB SurrealDB SurrealDB + ArangoDB ArangoDB KVStore KVStore diff --git a/pkg/gofr/container/datasources.go b/pkg/gofr/container/datasources.go index 8b75bb042..bb4bd0191 100644 --- a/pkg/gofr/container/datasources.go +++ b/pkg/gofr/container/datasources.go @@ -574,3 +574,29 @@ type ScyllaDBProvider interface { ScyllaDB provider } + +type ArangoDB interface { + // CreateDocument creates a new document in the specified collection. + CreateDocument(ctx context.Context, dbName, collectionName string, document any) (string, error) + // GetDocument retrieves a document by its ID from the specified collection. + GetDocument(ctx context.Context, dbName, collectionName, documentID string, result any) error + // UpdateDocument updates an existing document in the specified collection. + UpdateDocument(ctx context.Context, dbName, collectionName, documentID string, document any) error + // DeleteDocument deletes a document by its ID from the specified collection. + DeleteDocument(ctx context.Context, dbName, collectionName, documentID string) error + + // GetEdges retrieves all the edge documents connected to a specific vertex in an ArangoDB graph. + GetEdges(ctx context.Context, dbName, graphName, edgeCollection, vertexID string, resp any) error + + // Query executes an AQL query and binds the results + Query(ctx context.Context, dbName string, query string, bindVars map[string]any, result any) error + + HealthChecker +} + +// ArangoDBProvider is an interface that extends ArangoDB with additional methods for logging, metrics, and connection management. +type ArangoDBProvider interface { + ArangoDB + + provider +} diff --git a/pkg/gofr/container/mock_container.go b/pkg/gofr/container/mock_container.go index 8d36eb8c4..c2b04378c 100644 --- a/pkg/gofr/container/mock_container.go +++ b/pkg/gofr/container/mock_container.go @@ -87,6 +87,9 @@ func NewMockContainer(t *testing.T, options ...options) (*Container, *Mocks) { opentsdbMock := NewMockOpenTSDBProvider(ctrl) container.OpenTSDB = opentsdbMock + arangoMock := NewMockArangoDBProvider(ctrl) + container.ArangoDB = arangoMock + var httpMock *service.MockHTTP container.Services = make(map[string]service.HTTP) diff --git a/pkg/gofr/container/mock_datasources.go b/pkg/gofr/container/mock_datasources.go index 06d467705..c3f402321 100644 --- a/pkg/gofr/container/mock_datasources.go +++ b/pkg/gofr/container/mock_datasources.go @@ -9752,6 +9752,282 @@ func (mr *MockMongoProviderMockRecorder) UseTracer(tracer any) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UseTracer", reflect.TypeOf((*MockMongoProvider)(nil).UseTracer), tracer) } +// MockSurrealDB is a mock of SurrealDB interface. +type MockSurrealDB struct { + ctrl *gomock.Controller + recorder *MockSurrealDBMockRecorder + isgomock struct{} +} + +// MockSurrealDBMockRecorder is the mock recorder for MockSurrealDB. +type MockSurrealDBMockRecorder struct { + mock *MockSurrealDB +} + +// NewMockSurrealDB creates a new mock instance. +func NewMockSurrealDB(ctrl *gomock.Controller) *MockSurrealDB { + mock := &MockSurrealDB{ctrl: ctrl} + mock.recorder = &MockSurrealDBMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockSurrealDB) EXPECT() *MockSurrealDBMockRecorder { + return m.recorder +} + +// Create mocks base method. +func (m *MockSurrealDB) Create(ctx context.Context, table string, data any) (map[string]any, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Create", ctx, table, data) + ret0, _ := ret[0].(map[string]any) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Create indicates an expected call of Create. +func (mr *MockSurrealDBMockRecorder) Create(ctx, table, data any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockSurrealDB)(nil).Create), ctx, table, data) +} + +// Delete mocks base method. +func (m *MockSurrealDB) Delete(ctx context.Context, table, id string) (any, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Delete", ctx, table, id) + ret0, _ := ret[0].(any) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Delete indicates an expected call of Delete. +func (mr *MockSurrealDBMockRecorder) Delete(ctx, table, id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockSurrealDB)(nil).Delete), ctx, table, id) +} + +// HealthCheck mocks base method. +func (m *MockSurrealDB) 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 *MockSurrealDBMockRecorder) HealthCheck(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HealthCheck", reflect.TypeOf((*MockSurrealDB)(nil).HealthCheck), arg0) +} + +// Query mocks base method. +func (m *MockSurrealDB) Query(ctx context.Context, query string, vars map[string]any) ([]any, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Query", ctx, query, vars) + ret0, _ := ret[0].([]any) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Query indicates an expected call of Query. +func (mr *MockSurrealDBMockRecorder) Query(ctx, query, vars any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Query", reflect.TypeOf((*MockSurrealDB)(nil).Query), ctx, query, vars) +} + +// Select mocks base method. +func (m *MockSurrealDB) Select(ctx context.Context, table string) ([]map[string]any, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Select", ctx, table) + ret0, _ := ret[0].([]map[string]any) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Select indicates an expected call of Select. +func (mr *MockSurrealDBMockRecorder) Select(ctx, table any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Select", reflect.TypeOf((*MockSurrealDB)(nil).Select), ctx, table) +} + +// Update mocks base method. +func (m *MockSurrealDB) Update(ctx context.Context, table, id string, data any) (any, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Update", ctx, table, id, data) + ret0, _ := ret[0].(any) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Update indicates an expected call of Update. +func (mr *MockSurrealDBMockRecorder) Update(ctx, table, id, data any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockSurrealDB)(nil).Update), ctx, table, id, data) +} + +// MockSurrealBDProvider is a mock of SurrealBDProvider interface. +type MockSurrealBDProvider struct { + ctrl *gomock.Controller + recorder *MockSurrealBDProviderMockRecorder + isgomock struct{} +} + +// MockSurrealBDProviderMockRecorder is the mock recorder for MockSurrealBDProvider. +type MockSurrealBDProviderMockRecorder struct { + mock *MockSurrealBDProvider +} + +// NewMockSurrealBDProvider creates a new mock instance. +func NewMockSurrealBDProvider(ctrl *gomock.Controller) *MockSurrealBDProvider { + mock := &MockSurrealBDProvider{ctrl: ctrl} + mock.recorder = &MockSurrealBDProviderMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockSurrealBDProvider) EXPECT() *MockSurrealBDProviderMockRecorder { + return m.recorder +} + +// Connect mocks base method. +func (m *MockSurrealBDProvider) Connect() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Connect") +} + +// Connect indicates an expected call of Connect. +func (mr *MockSurrealBDProviderMockRecorder) Connect() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Connect", reflect.TypeOf((*MockSurrealBDProvider)(nil).Connect)) +} + +// Create mocks base method. +func (m *MockSurrealBDProvider) Create(ctx context.Context, table string, data any) (map[string]any, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Create", ctx, table, data) + ret0, _ := ret[0].(map[string]any) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Create indicates an expected call of Create. +func (mr *MockSurrealBDProviderMockRecorder) Create(ctx, table, data any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockSurrealBDProvider)(nil).Create), ctx, table, data) +} + +// Delete mocks base method. +func (m *MockSurrealBDProvider) Delete(ctx context.Context, table, id string) (any, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Delete", ctx, table, id) + ret0, _ := ret[0].(any) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Delete indicates an expected call of Delete. +func (mr *MockSurrealBDProviderMockRecorder) Delete(ctx, table, id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockSurrealBDProvider)(nil).Delete), ctx, table, id) +} + +// HealthCheck mocks base method. +func (m *MockSurrealBDProvider) 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 *MockSurrealBDProviderMockRecorder) HealthCheck(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HealthCheck", reflect.TypeOf((*MockSurrealBDProvider)(nil).HealthCheck), arg0) +} + +// Query mocks base method. +func (m *MockSurrealBDProvider) Query(ctx context.Context, query string, vars map[string]any) ([]any, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Query", ctx, query, vars) + ret0, _ := ret[0].([]any) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Query indicates an expected call of Query. +func (mr *MockSurrealBDProviderMockRecorder) Query(ctx, query, vars any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Query", reflect.TypeOf((*MockSurrealBDProvider)(nil).Query), ctx, query, vars) +} + +// Select mocks base method. +func (m *MockSurrealBDProvider) Select(ctx context.Context, table string) ([]map[string]any, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Select", ctx, table) + ret0, _ := ret[0].([]map[string]any) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Select indicates an expected call of Select. +func (mr *MockSurrealBDProviderMockRecorder) Select(ctx, table any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Select", reflect.TypeOf((*MockSurrealBDProvider)(nil).Select), ctx, table) +} + +// Update mocks base method. +func (m *MockSurrealBDProvider) Update(ctx context.Context, table, id string, data any) (any, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Update", ctx, table, id, data) + ret0, _ := ret[0].(any) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Update indicates an expected call of Update. +func (mr *MockSurrealBDProviderMockRecorder) Update(ctx, table, id, data any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockSurrealBDProvider)(nil).Update), ctx, table, id, data) +} + +// UseLogger mocks base method. +func (m *MockSurrealBDProvider) UseLogger(logger any) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "UseLogger", logger) +} + +// UseLogger indicates an expected call of UseLogger. +func (mr *MockSurrealBDProviderMockRecorder) UseLogger(logger any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UseLogger", reflect.TypeOf((*MockSurrealBDProvider)(nil).UseLogger), logger) +} + +// UseMetrics mocks base method. +func (m *MockSurrealBDProvider) UseMetrics(metrics any) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "UseMetrics", metrics) +} + +// UseMetrics indicates an expected call of UseMetrics. +func (mr *MockSurrealBDProviderMockRecorder) UseMetrics(metrics any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UseMetrics", reflect.TypeOf((*MockSurrealBDProvider)(nil).UseMetrics), metrics) +} + +// UseTracer mocks base method. +func (m *MockSurrealBDProvider) UseTracer(tracer any) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "UseTracer", tracer) +} + +// UseTracer indicates an expected call of UseTracer. +func (mr *MockSurrealBDProviderMockRecorder) UseTracer(tracer any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UseTracer", reflect.TypeOf((*MockSurrealBDProvider)(nil).UseTracer), tracer) +} + // Mockprovider is a mock of provider interface. type Mockprovider struct { ctrl *gomock.Controller @@ -11755,3 +12031,299 @@ func (mr *MockScyllaDBProviderMockRecorder) UseTracer(tracer any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UseTracer", reflect.TypeOf((*MockScyllaDBProvider)(nil).UseTracer), tracer) } + +// MockArangoDB is a mock of ArangoDB interface. +type MockArangoDB struct { + ctrl *gomock.Controller + recorder *MockArangoDBMockRecorder + isgomock struct{} +} + +// MockArangoDBMockRecorder is the mock recorder for MockArangoDB. +type MockArangoDBMockRecorder struct { + mock *MockArangoDB +} + +// NewMockArangoDB creates a new mock instance. +func NewMockArangoDB(ctrl *gomock.Controller) *MockArangoDB { + mock := &MockArangoDB{ctrl: ctrl} + mock.recorder = &MockArangoDBMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockArangoDB) EXPECT() *MockArangoDBMockRecorder { + return m.recorder +} + +// CreateDocument mocks base method. +func (m *MockArangoDB) CreateDocument(ctx context.Context, dbName, collectionName string, document any) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateDocument", ctx, dbName, collectionName, document) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateDocument indicates an expected call of CreateDocument. +func (mr *MockArangoDBMockRecorder) CreateDocument(ctx, dbName, collectionName, document any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateDocument", reflect.TypeOf((*MockArangoDB)(nil).CreateDocument), ctx, dbName, collectionName, document) +} + +// DeleteDocument mocks base method. +func (m *MockArangoDB) DeleteDocument(ctx context.Context, dbName, collectionName, documentID string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteDocument", ctx, dbName, collectionName, documentID) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteDocument indicates an expected call of DeleteDocument. +func (mr *MockArangoDBMockRecorder) DeleteDocument(ctx, dbName, collectionName, documentID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteDocument", reflect.TypeOf((*MockArangoDB)(nil).DeleteDocument), ctx, dbName, collectionName, documentID) +} + +// GetDocument mocks base method. +func (m *MockArangoDB) GetDocument(ctx context.Context, dbName, collectionName, documentID string, result any) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetDocument", ctx, dbName, collectionName, documentID, result) + ret0, _ := ret[0].(error) + return ret0 +} + +// GetDocument indicates an expected call of GetDocument. +func (mr *MockArangoDBMockRecorder) GetDocument(ctx, dbName, collectionName, documentID, result any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDocument", reflect.TypeOf((*MockArangoDB)(nil).GetDocument), ctx, dbName, collectionName, documentID, result) +} + +// GetEdges mocks base method. +func (m *MockArangoDB) GetEdges(ctx context.Context, dbName, graphName, edgeCollection, vertexID string, resp any) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetEdges", ctx, dbName, graphName, edgeCollection, vertexID, resp) + ret0, _ := ret[0].(error) + return ret0 +} + +// GetEdges indicates an expected call of GetEdges. +func (mr *MockArangoDBMockRecorder) GetEdges(ctx, dbName, graphName, edgeCollection, vertexID, resp any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEdges", reflect.TypeOf((*MockArangoDB)(nil).GetEdges), ctx, dbName, graphName, edgeCollection, vertexID, resp) +} + +// HealthCheck mocks base method. +func (m *MockArangoDB) 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 *MockArangoDBMockRecorder) HealthCheck(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HealthCheck", reflect.TypeOf((*MockArangoDB)(nil).HealthCheck), arg0) +} + +// Query mocks base method. +func (m *MockArangoDB) Query(ctx context.Context, dbName, query string, bindVars map[string]any, result any) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Query", ctx, dbName, query, bindVars, result) + ret0, _ := ret[0].(error) + return ret0 +} + +// Query indicates an expected call of Query. +func (mr *MockArangoDBMockRecorder) Query(ctx, dbName, query, bindVars, result any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Query", reflect.TypeOf((*MockArangoDB)(nil).Query), ctx, dbName, query, bindVars, result) +} + +// UpdateDocument mocks base method. +func (m *MockArangoDB) UpdateDocument(ctx context.Context, dbName, collectionName, documentID string, document any) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateDocument", ctx, dbName, collectionName, documentID, document) + ret0, _ := ret[0].(error) + return ret0 +} + +// UpdateDocument indicates an expected call of UpdateDocument. +func (mr *MockArangoDBMockRecorder) UpdateDocument(ctx, dbName, collectionName, documentID, document any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateDocument", reflect.TypeOf((*MockArangoDB)(nil).UpdateDocument), ctx, dbName, collectionName, documentID, document) +} + +// MockArangoDBProvider is a mock of ArangoDBProvider interface. +type MockArangoDBProvider struct { + ctrl *gomock.Controller + recorder *MockArangoDBProviderMockRecorder + isgomock struct{} +} + +// MockArangoDBProviderMockRecorder is the mock recorder for MockArangoDBProvider. +type MockArangoDBProviderMockRecorder struct { + mock *MockArangoDBProvider +} + +// NewMockArangoDBProvider creates a new mock instance. +func NewMockArangoDBProvider(ctrl *gomock.Controller) *MockArangoDBProvider { + mock := &MockArangoDBProvider{ctrl: ctrl} + mock.recorder = &MockArangoDBProviderMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockArangoDBProvider) EXPECT() *MockArangoDBProviderMockRecorder { + return m.recorder +} + +// Connect mocks base method. +func (m *MockArangoDBProvider) Connect() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Connect") +} + +// Connect indicates an expected call of Connect. +func (mr *MockArangoDBProviderMockRecorder) Connect() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Connect", reflect.TypeOf((*MockArangoDBProvider)(nil).Connect)) +} + +// CreateDocument mocks base method. +func (m *MockArangoDBProvider) CreateDocument(ctx context.Context, dbName, collectionName string, document any) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateDocument", ctx, dbName, collectionName, document) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateDocument indicates an expected call of CreateDocument. +func (mr *MockArangoDBProviderMockRecorder) CreateDocument(ctx, dbName, collectionName, document any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateDocument", reflect.TypeOf((*MockArangoDBProvider)(nil).CreateDocument), ctx, dbName, collectionName, document) +} + +// DeleteDocument mocks base method. +func (m *MockArangoDBProvider) DeleteDocument(ctx context.Context, dbName, collectionName, documentID string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteDocument", ctx, dbName, collectionName, documentID) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteDocument indicates an expected call of DeleteDocument. +func (mr *MockArangoDBProviderMockRecorder) DeleteDocument(ctx, dbName, collectionName, documentID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteDocument", reflect.TypeOf((*MockArangoDBProvider)(nil).DeleteDocument), ctx, dbName, collectionName, documentID) +} + +// GetDocument mocks base method. +func (m *MockArangoDBProvider) GetDocument(ctx context.Context, dbName, collectionName, documentID string, result any) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetDocument", ctx, dbName, collectionName, documentID, result) + ret0, _ := ret[0].(error) + return ret0 +} + +// GetDocument indicates an expected call of GetDocument. +func (mr *MockArangoDBProviderMockRecorder) GetDocument(ctx, dbName, collectionName, documentID, result any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDocument", reflect.TypeOf((*MockArangoDBProvider)(nil).GetDocument), ctx, dbName, collectionName, documentID, result) +} + +// GetEdges mocks base method. +func (m *MockArangoDBProvider) GetEdges(ctx context.Context, dbName, graphName, edgeCollection, vertexID string, resp any) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetEdges", ctx, dbName, graphName, edgeCollection, vertexID, resp) + ret0, _ := ret[0].(error) + return ret0 +} + +// GetEdges indicates an expected call of GetEdges. +func (mr *MockArangoDBProviderMockRecorder) GetEdges(ctx, dbName, graphName, edgeCollection, vertexID, resp any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEdges", reflect.TypeOf((*MockArangoDBProvider)(nil).GetEdges), ctx, dbName, graphName, edgeCollection, vertexID, resp) +} + +// HealthCheck mocks base method. +func (m *MockArangoDBProvider) 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 *MockArangoDBProviderMockRecorder) HealthCheck(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HealthCheck", reflect.TypeOf((*MockArangoDBProvider)(nil).HealthCheck), arg0) +} + +// Query mocks base method. +func (m *MockArangoDBProvider) Query(ctx context.Context, dbName, query string, bindVars map[string]any, result any) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Query", ctx, dbName, query, bindVars, result) + ret0, _ := ret[0].(error) + return ret0 +} + +// Query indicates an expected call of Query. +func (mr *MockArangoDBProviderMockRecorder) Query(ctx, dbName, query, bindVars, result any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Query", reflect.TypeOf((*MockArangoDBProvider)(nil).Query), ctx, dbName, query, bindVars, result) +} + +// UpdateDocument mocks base method. +func (m *MockArangoDBProvider) UpdateDocument(ctx context.Context, dbName, collectionName, documentID string, document any) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateDocument", ctx, dbName, collectionName, documentID, document) + ret0, _ := ret[0].(error) + return ret0 +} + +// UpdateDocument indicates an expected call of UpdateDocument. +func (mr *MockArangoDBProviderMockRecorder) UpdateDocument(ctx, dbName, collectionName, documentID, document any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateDocument", reflect.TypeOf((*MockArangoDBProvider)(nil).UpdateDocument), ctx, dbName, collectionName, documentID, document) +} + +// UseLogger mocks base method. +func (m *MockArangoDBProvider) UseLogger(logger any) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "UseLogger", logger) +} + +// UseLogger indicates an expected call of UseLogger. +func (mr *MockArangoDBProviderMockRecorder) UseLogger(logger any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UseLogger", reflect.TypeOf((*MockArangoDBProvider)(nil).UseLogger), logger) +} + +// UseMetrics mocks base method. +func (m *MockArangoDBProvider) UseMetrics(metrics any) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "UseMetrics", metrics) +} + +// UseMetrics indicates an expected call of UseMetrics. +func (mr *MockArangoDBProviderMockRecorder) UseMetrics(metrics any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UseMetrics", reflect.TypeOf((*MockArangoDBProvider)(nil).UseMetrics), metrics) +} + +// UseTracer mocks base method. +func (m *MockArangoDBProvider) UseTracer(tracer any) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "UseTracer", tracer) +} + +// UseTracer indicates an expected call of UseTracer. +func (mr *MockArangoDBProviderMockRecorder) UseTracer(tracer any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UseTracer", reflect.TypeOf((*MockArangoDBProvider)(nil).UseTracer), tracer) +} diff --git a/pkg/gofr/datasource/README.md b/pkg/gofr/datasource/README.md index dc9938cd8..75593d929 100644 --- a/pkg/gofr/datasource/README.md +++ b/pkg/gofr/datasource/README.md @@ -71,18 +71,19 @@ Therefore, GoFr utilizes a pluggable approach for new datasources by separating | Datasource | Health-Check | Logs | Metrics | Traces | As Driver | |------------------|:------------:|:----:|:-------:|:------:|:---------:| -| MySQL | ✅ | ✅ | ✅ | ✅ | | -| REDIS | ✅ | ✅ | ✅ | ✅ | | -| PostgreSQL | ✅ | ✅ | ✅ | ✅ | | -| MongoDB | ✅ | ✅ | ✅ | ✅ | ✅ | -| SQLite | ✅ | ✅ | ✅ | ✅ | | -| BadgerDB | ✅ | ✅ | ✅ | ✅ | ✅ | -| Cassandra | ✅ | ✅ | ✅ | ✅ | ✅ | -| ClickHouse | | ✅ | ✅ | ✅ | ✅ | -| FTP | | ✅ | | | ✅ | -| SFTP | | ✅ | | | ✅ | -| Solr | | ✅ | ✅ | ✅ | ✅ | -| DGraph | ✅ | ✅ | ✅ | ✅ | | -| Azure Event Hubs | | ✅ | ✅ | |✅ | -| OpenTSDB | ✅ | ✅ | | ✅ | ✅ | -| SurrealDB | ✅ | ✅ | | ✅ | ✅ | \ No newline at end of file +| MySQL | ✅ | ✅ | ✅ | ✅ | | +| REDIS | ✅ | ✅ | ✅ | ✅ | | +| PostgreSQL | ✅ | ✅ | ✅ | ✅ | | +| MongoDB | ✅ | ✅ | ✅ | ✅ | ✅ | +| SQLite | ✅ | ✅ | ✅ | ✅ | | +| BadgerDB | ✅ | ✅ | ✅ | ✅ | ✅ | +| Cassandra | ✅ | ✅ | ✅ | ✅ | ✅ | +| ClickHouse | | ✅ | ✅ | ✅ | ✅ | +| FTP | | ✅ | | | ✅ | +| SFTP | | ✅ | | | ✅ | +| Solr | | ✅ | ✅ | ✅ | ✅ | +| DGraph | ✅ | ✅ | ✅ | ✅ | | +| Azure Event Hubs | | ✅ | ✅ | |✅ | +| OpenTSDB | ✅ | ✅ | | ✅ | ✅ | +| SurrealDB | ✅ | ✅ | | ✅ | ✅ | +| ArangoDB | ✅| ✅ | ✅| ✅|✅| \ No newline at end of file diff --git a/pkg/gofr/datasource/arangodb/arango.go b/pkg/gofr/datasource/arangodb/arango.go new file mode 100644 index 000000000..fe8985a04 --- /dev/null +++ b/pkg/gofr/datasource/arangodb/arango.go @@ -0,0 +1,263 @@ +package arangodb + +import ( + "context" + "errors" + "fmt" + "time" + + "github.com/arangodb/go-driver/v2/arangodb" + arangoShared "github.com/arangodb/go-driver/v2/arangodb/shared" + "github.com/arangodb/go-driver/v2/connection" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" +) + +const ( + defaultTimeout = 5 * time.Second + arangoEdgeCollectionType = 3 +) + +// Client represents an ArangoDB client. +type Client struct { + client arangodb.Client + logger Logger + metrics Metrics + tracer trace.Tracer + config *Config + endpoint string + *Document + *Graph +} + +type EdgeDefinition []arangodb.EdgeDefinition + +type UserOptions struct { + Password string `json:"passwd,omitempty"` + Active *bool `json:"active,omitempty"` + Extra any `json:"extra,omitempty"` +} + +// Config holds the configuration for ArangoDB connection. +type Config struct { + Host string + User string + Password string + Port int +} + +var ( + errStatusDown = errors.New("status down") + errMissingField = errors.New("missing required field in config") + errInvalidResultType = errors.New("result must be a pointer to a slice of maps") + errInvalidUserOptionsType = errors.New("userOptions must be a *UserOptions type") +) + +// New creates a new ArangoDB client with the provided configuration. +func New(c Config) *Client { + client := &Client{ + config: &c, + } + + client.Document = &Document{client: client} + client.Graph = &Graph{client: client} + + return client +} + +// UseLogger sets the logger for the ArangoDB client. +func (c *Client) UseLogger(logger any) { + if l, ok := logger.(Logger); ok { + c.logger = l + } +} + +// UseMetrics sets the metrics for the ArangoDB client. +func (c *Client) UseMetrics(metrics any) { + if m, ok := metrics.(Metrics); ok { + c.metrics = m + } +} + +// UseTracer sets the tracer for the ArangoDB client. +func (c *Client) UseTracer(tracer any) { + if t, ok := tracer.(trace.Tracer); ok { + c.tracer = t + } +} + +// Connect establishes a connection to the ArangoDB server. +func (c *Client) Connect() { + if err := c.validateConfig(); err != nil { + c.logger.Errorf("config validation error: %v", err) + return + } + + c.endpoint = fmt.Sprintf("http://%s:%d", c.config.Host, c.config.Port) + c.logger.Debugf("connecting to ArangoDB at %s", c.endpoint) + + // Use HTTP connection instead of HTTP2 + endpoint := connection.NewRoundRobinEndpoints([]string{c.endpoint}) + conn := connection.NewHttpConnection(connection.HttpConfiguration{Endpoint: endpoint}) + + // Set authentication + auth := connection.NewBasicAuth(c.config.User, c.config.Password) + if err := conn.SetAuthentication(auth); err != nil { + c.logger.Errorf("authentication setup failed: %v", err) + return + } + + // Create ArangoDB client + client := arangodb.NewClient(conn) + c.client = client + + // Test connection by fetching server version + ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout) + defer cancel() + + _, err := c.client.Version(ctx) + if err != nil { + c.logger.Errorf("failed to verify connection: %v", err) + return + } + + // Initialize metrics + arangoBuckets := []float64{.05, .075, .1, .125, .15, .2, .3, .5, .75, 1, 2, 3, 4, 5, 7.5, 10} + c.metrics.NewHistogram("app_arango_stats", "Response time of ArangoDB operations in milliseconds.", arangoBuckets...) + + c.logger.Logf("Connected to ArangoDB successfully at %s", c.endpoint) +} + +func (c *Client) validateConfig() error { + if c.config.Host == "" { + return fmt.Errorf("%w: host is empty", errMissingField) + } + + if c.config.Port == 0 { + return fmt.Errorf("%w: port is empty", errMissingField) + } + + if c.config.User == "" { + return fmt.Errorf("%w: user is empty", errMissingField) + } + + if c.config.Password == "" { + return fmt.Errorf("%w: password is empty", errMissingField) + } + + return nil +} + +// Query executes an AQL query and binds the results. +func (c *Client) Query(ctx context.Context, dbName, query string, bindVars map[string]any, result any) error { + tracerCtx, span := c.addTrace(ctx, "query", map[string]string{"DB": dbName}) + startTime := time.Now() + + defer c.sendOperationStats(&QueryLog{Operation: "query", + Database: dbName, Query: query}, startTime, "query", span) + + db, err := c.client.Database(tracerCtx, dbName) + if err != nil { + return err + } + + cursor, err := db.Query(tracerCtx, query, &arangodb.QueryOptions{BindVars: bindVars}) + if err != nil { + return err + } + + defer cursor.Close() + + resultSlice, ok := result.(*[]map[string]any) + if !ok { + return errInvalidResultType + } + + for { + var doc map[string]any + + _, err = cursor.ReadDocument(tracerCtx, &doc) + if arangoShared.IsNoMoreDocuments(err) { + break + } + + if err != nil { + return err + } + + *resultSlice = append(*resultSlice, doc) + } + + return nil +} + +// addTrace adds tracing to context if tracer is configured. +func (c *Client) addTrace(ctx context.Context, operation string, attributes map[string]string) (context.Context, trace.Span) { + if c.tracer != nil { + contextWithTrace, span := c.tracer.Start(ctx, fmt.Sprintf("arangodb-%v", operation)) + + // Add default attributes + span.SetAttributes(attribute.String("arangodb.operation", operation)) + + // Add custom attributes if provided + for key, value := range attributes { + span.SetAttributes(attribute.String(fmt.Sprintf("arangodb.%s", key), value)) + } + + return contextWithTrace, span + } + + return ctx, nil +} + +func (c *Client) sendOperationStats(ql *QueryLog, startTime time.Time, method string, span trace.Span) { + duration := time.Since(startTime).Microseconds() + ql.Duration = duration + + c.logger.Debug(ql) + + c.metrics.RecordHistogram(context.Background(), "app_arango_stats", float64(duration), + "endpoint", c.endpoint, + "type", ql.Query, + ) + + if span != nil { + defer span.End() + span.SetAttributes(attribute.Int64(fmt.Sprintf("arangodb.%v.duration", method), duration)) + } +} + +// Health represents the health status of ArangoDB. +type Health struct { + Status string `json:"status,omitempty"` + Details map[string]any `json:"details,omitempty"` +} + +// HealthCheck performs a health check. +func (c *Client) HealthCheck(ctx context.Context) (any, error) { + h := Health{ + Details: map[string]any{ + "endpoint": c.endpoint, + }, + } + + version, err := c.client.Version(ctx) + if err != nil { + h.Status = "DOWN" + return &h, errStatusDown + } + + h.Status = "UP" + h.Details["version"] = version.Version + h.Details["server"] = version.Server + + return &h, nil +} + +func (uo *UserOptions) toArangoUserOptions() *arangodb.UserOptions { + return &arangodb.UserOptions{ + Password: uo.Password, + Active: uo.Active, + Extra: uo.Extra, + } +} diff --git a/pkg/gofr/datasource/arangodb/arango_document.go b/pkg/gofr/datasource/arangodb/arango_document.go new file mode 100644 index 000000000..02728ac4b --- /dev/null +++ b/pkg/gofr/datasource/arangodb/arango_document.go @@ -0,0 +1,190 @@ +package arangodb + +import ( + "context" + "errors" + "time" + + "github.com/arangodb/go-driver/v2/arangodb" +) + +var ( + errInvalidEdgeDocumentType = errors.New("document must be a map when creating an edge") + errMissingEdgeFields = errors.New("missing '_from' or '_to' field for edge document") + errInvalidFromField = errors.New("'_from' field must be a string") + errInvalidToField = errors.New("'_to' field must be a string") +) + +type Document struct { + client *Client +} + +// CreateDocument creates a new document in the specified collection. +// If the collection is an edge collection, the document must include `_from` and `_to`. +// Example for creating a regular document: +// +// doc := map[string]any{ +// "name": "Alice", +// "age": 30, +// } +// +// id, err := client.CreateDocument(ctx, "myDB", "users", doc) +// +// Example for creating an edge document: +// +// edgeDoc := map[string]any{ +// "_from": "users/123", +// "_to": "orders/456", +// "relation": "purchased", +// } +// +// id, err := client.CreateDocument(ctx, "myDB", "edges", edgeDoc). +func (d *Document) CreateDocument(ctx context.Context, dbName, collectionName string, document any) (string, error) { + collection, tracerCtx, err := executeCollectionOperation(ctx, *d, dbName, collectionName, + "createDocument", "") + if err != nil { + return "", err + } + + var isEdge bool + + // Check if the collection is an edge collection + isEdge, err = d.isEdgeCollection(ctx, dbName, collectionName) + if err != nil { + return "", err + } + + // Validate edge document if needed + if isEdge { + err = validateEdgeDocument(document) + if err != nil { + return "", err + } + } + + // Create the document in ArangoDB + meta, err := collection.CreateDocument(tracerCtx, document) + if err != nil { + return "", err + } + + return meta.Key, nil +} + +// GetDocument retrieves a document by its ID from the specified collection. +func (d *Document) GetDocument(ctx context.Context, dbName, collectionName, documentID string, result any) error { + collection, tracerCtx, err := executeCollectionOperation(ctx, *d, dbName, collectionName, + "getDocument", documentID) + if err != nil { + return err + } + + _, err = collection.ReadDocument(tracerCtx, documentID, result) + + return err +} + +// UpdateDocument updates an existing document in the specified collection. +func (d *Document) UpdateDocument(ctx context.Context, dbName, collectionName, documentID string, document any) error { + collection, tracerCtx, err := executeCollectionOperation(ctx, *d, dbName, collectionName, + "updateDocument", documentID) + if err != nil { + return err + } + + _, err = collection.UpdateDocument(tracerCtx, documentID, document) + + return err +} + +// DeleteDocument deletes a document by its ID from the specified collection. +func (d *Document) DeleteDocument(ctx context.Context, dbName, collectionName, documentID string) error { + collection, tracerCtx, err := executeCollectionOperation(ctx, *d, dbName, collectionName, + "deleteDocument", documentID) + if err != nil { + return err + } + + _, err = collection.DeleteDocument(tracerCtx, documentID) + + return err +} + +// isEdgeCollection checks if the given collection is an edge collection. +func (d *Document) isEdgeCollection(ctx context.Context, dbName, collectionName string) (bool, error) { + collection, err := d.getCollection(ctx, dbName, collectionName) + if err != nil { + return false, err + } + + properties, err := collection.Properties(ctx) + if err != nil { + return false, err + } + + // ArangoDB type: 3 = Edge Collection, 2 = Document Collection + return properties.Type == arangoEdgeCollectionType, nil +} + +func executeCollectionOperation(ctx context.Context, d Document, dbName, collectionName, + operation string, documentID string) (arangodb.Collection, context.Context, error) { + tracerCtx, span := d.client.addTrace(ctx, operation, map[string]string{"collection": collectionName}) + startTime := time.Now() + + ql := &QueryLog{Operation: operation, + Database: dbName, + Collection: collectionName} + + if documentID != "" { + ql.ID = documentID + } + + defer d.client.sendOperationStats(ql, startTime, operation, span) + + collection, err := d.getCollection(tracerCtx, dbName, collectionName) + if err != nil { + return nil, nil, err + } + + return collection, tracerCtx, nil +} + +// validateEdgeDocument ensures the document contains valid `_from` and `_to` fields when creating an edge. +func validateEdgeDocument(document any) error { + docMap, ok := document.(map[string]any) + if !ok { + return errInvalidEdgeDocumentType + } + + from, fromExists := docMap["_from"] + to, toExists := docMap["_to"] + + if !fromExists || !toExists { + return errMissingEdgeFields + } + + // Ensure `_from` and `_to` are strings + if _, ok := from.(string); !ok { + return errInvalidFromField + } + + if _, ok := to.(string); !ok { + return errInvalidToField + } + + return nil +} + +func (d *Document) getCollection(ctx context.Context, dbName, collectionName string) (arangodb.Collection, error) { + db, err := d.client.client.Database(ctx, dbName) + if err != nil { + return nil, err + } + + collection, err := db.Collection(ctx, collectionName) + if err != nil { + return nil, err + } + + return collection, nil +} diff --git a/pkg/gofr/datasource/arangodb/arango_document_test.go b/pkg/gofr/datasource/arangodb/arango_document_test.go new file mode 100644 index 000000000..d677e9e97 --- /dev/null +++ b/pkg/gofr/datasource/arangodb/arango_document_test.go @@ -0,0 +1,254 @@ +package arangodb + +import ( + "context" + "testing" + + "github.com/arangodb/go-driver/v2/arangodb" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" +) + +func Test_Client_CreateDocument(t *testing.T) { + client, mockArango, _, mockLogger, mockMetrics := setupDB(t) + mockDB := NewMockDatabase(gomock.NewController(t)) + mockCollection := NewMockCollection(gomock.NewController(t)) + + mockArango.EXPECT().Database(gomock.Any(), "testDB").Return(mockDB, nil).AnyTimes() + mockDB.EXPECT().Collection(gomock.Any(), "testCollection").Return(mockCollection, nil).AnyTimes() + mockCollection.EXPECT().Properties(gomock.Any()).Return(arangodb.CollectionProperties{}, nil) + mockCollection.EXPECT().CreateDocument(gomock.Any(), "testDocument"). + Return(arangodb.CollectionDocumentCreateResponse{DocumentMeta: arangodb.DocumentMeta{ + Key: "testDocument", ID: "1"}}, nil) + mockLogger.EXPECT().Debug(gomock.Any()).AnyTimes() + mockMetrics.EXPECT().RecordHistogram(gomock.Any(), "app_arango_stats", gomock.Any(), + gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + + docName, err := client.CreateDocument(context.Background(), "testDB", + "testCollection", "testDocument") + require.Equal(t, "testDocument", docName) + require.NoError(t, err, "Expected no error while truncating the collection") +} + +func Test_Client_CreateDocument_Error(t *testing.T) { + client, mockArango, _, mockLogger, mockMetrics := setupDB(t) + mockDB := NewMockDatabase(gomock.NewController(t)) + mockCollection := NewMockCollection(gomock.NewController(t)) + + mockArango.EXPECT().Database(gomock.Any(), "testDB").Return(mockDB, nil).AnyTimes() + mockDB.EXPECT().Collection(gomock.Any(), "testCollection").Return(mockCollection, nil).AnyTimes() + mockCollection.EXPECT().Properties(gomock.Any()).Return(arangodb.CollectionProperties{}, nil) + mockCollection.EXPECT().CreateDocument(gomock.Any(), "testDocument"). + Return(arangodb.CollectionDocumentCreateResponse{}, errDocumentNotFound) + mockLogger.EXPECT().Debug(gomock.Any()) + mockMetrics.EXPECT().RecordHistogram(gomock.Any(), "app_arango_stats", gomock.Any(), + gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) + + docName, err := client.CreateDocument(context.Background(), "testDB", + "testCollection", "testDocument") + require.Equal(t, "", docName) + require.ErrorIs(t, err, errDocumentNotFound, err, "Expected error when document not found") +} + +func Test_Client_GetDocument(t *testing.T) { + client, mockArango, _, mockLogger, mockMetrics := setupDB(t) + mockDB := NewMockDatabase(gomock.NewController(t)) + mockCollection := NewMockCollection(gomock.NewController(t)) + + mockArango.EXPECT().Database(gomock.Any(), "testDB").Return(mockDB, nil) + mockDB.EXPECT().Collection(gomock.Any(), "testCollection").Return(mockCollection, nil) + mockCollection.EXPECT().ReadDocument(gomock.Any(), "testDocument", "").Return(arangodb.DocumentMeta{ + Key: "testKey", ID: "1"}, nil) + mockLogger.EXPECT().Debug(gomock.Any()).AnyTimes() + mockMetrics.EXPECT().RecordHistogram(gomock.Any(), "app_arango_stats", gomock.Any(), + gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + + err := client.GetDocument(context.Background(), "testDB", + "testCollection", "testDocument", "") + require.NoError(t, err, "Expected no error while reading the document") +} + +func Test_Client_GetDocument_Error(t *testing.T) { + client, mockArango, _, mockLogger, mockMetrics := setupDB(t) + mockDB := NewMockDatabase(gomock.NewController(t)) + mockCollection := NewMockCollection(gomock.NewController(t)) + + mockArango.EXPECT().Database(gomock.Any(), "testDB").Return(mockDB, nil) + mockDB.EXPECT().Collection(gomock.Any(), "testCollection").Return(mockCollection, nil) + mockCollection.EXPECT().ReadDocument(gomock.Any(), "testDocument", ""). + Return(arangodb.DocumentMeta{}, errDocumentNotFound) + mockLogger.EXPECT().Debug(gomock.Any()) + mockMetrics.EXPECT().RecordHistogram(gomock.Any(), "app_arango_stats", gomock.Any(), + gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) + + err := client.GetDocument(context.Background(), "testDB", + "testCollection", "testDocument", "") + require.ErrorIs(t, err, errDocumentNotFound, err, "Expected error when document not found") +} + +func Test_Client_UpdateDocument(t *testing.T) { + client, mockArango, _, mockLogger, mockMetrics := setupDB(t) + mockDB := NewMockDatabase(gomock.NewController(t)) + mockCollection := NewMockCollection(gomock.NewController(t)) + testDocument := map[string]any{"field": "value"} + + mockArango.EXPECT().Database(gomock.Any(), "testDB").Return(mockDB, nil) + mockDB.EXPECT().Collection(gomock.Any(), "testCollection").Return(mockCollection, nil) + mockCollection.EXPECT().UpdateDocument(gomock.Any(), "testDocument", testDocument). + Return(arangodb.CollectionDocumentUpdateResponse{ + DocumentMeta: arangodb.DocumentMeta{Key: "testKey", ID: "1", Rev: ""}}, nil) + mockLogger.EXPECT().Debug(gomock.Any()).AnyTimes() + mockMetrics.EXPECT().RecordHistogram(gomock.Any(), "app_arango_stats", gomock.Any(), + gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + + err := client.UpdateDocument(context.Background(), "testDB", "testCollection", + "testDocument", testDocument) + require.NoError(t, err, "Expected no error while updating the document") +} + +func Test_Client_UpdateDocument_Error(t *testing.T) { + client, mockArango, _, mockLogger, mockMetrics := setupDB(t) + mockDB := NewMockDatabase(gomock.NewController(t)) + mockCollection := NewMockCollection(gomock.NewController(t)) + testDocument := map[string]any{"field": "value"} + + mockArango.EXPECT().Database(gomock.Any(), "testDB").Return(mockDB, nil) + mockDB.EXPECT().Collection(gomock.Any(), "testCollection").Return(mockCollection, nil) + mockCollection.EXPECT().UpdateDocument(gomock.Any(), "testDocument", testDocument). + Return(arangodb.CollectionDocumentUpdateResponse{}, errDocumentNotFound) + mockLogger.EXPECT().Debug(gomock.Any()).AnyTimes() + mockMetrics.EXPECT().RecordHistogram(gomock.Any(), "app_arango_stats", gomock.Any(), + gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + + err := client.UpdateDocument(context.Background(), "testDB", "testCollection", "testDocument", testDocument) + require.ErrorIs(t, err, errDocumentNotFound, "Expected error while updating the document") +} + +func Test_Client_DeleteDocument(t *testing.T) { + client, mockArango, _, mockLogger, mockMetrics := setupDB(t) + mockDB := NewMockDatabase(gomock.NewController(t)) + mockCollection := NewMockCollection(gomock.NewController(t)) + + mockArango.EXPECT().Database(gomock.Any(), "testDB").Return(mockDB, nil) + mockDB.EXPECT().Collection(gomock.Any(), "testCollection").Return(mockCollection, nil) + mockCollection.EXPECT().DeleteDocument(gomock.Any(), "testDocument"). + Return(arangodb.CollectionDocumentDeleteResponse{ + DocumentMeta: arangodb.DocumentMeta{Key: "testKey", ID: "1", Rev: ""}}, nil) + mockLogger.EXPECT().Debug(gomock.Any()).AnyTimes() + mockMetrics.EXPECT().RecordHistogram(gomock.Any(), "app_arango_stats", gomock.Any(), + gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + + err := client.DeleteDocument(context.Background(), "testDB", "testCollection", + "testDocument") + require.NoError(t, err, "Expected no error while updating the document") +} + +func Test_Client_DeleteDocument_Error(t *testing.T) { + client, mockArango, _, mockLogger, mockMetrics := setupDB(t) + mockDB := NewMockDatabase(gomock.NewController(t)) + mockCollection := NewMockCollection(gomock.NewController(t)) + + mockArango.EXPECT().Database(gomock.Any(), "testDB").Return(mockDB, nil) + mockDB.EXPECT().Collection(gomock.Any(), "testCollection").Return(mockCollection, nil) + mockCollection.EXPECT().DeleteDocument(gomock.Any(), "testDocument"). + Return(arangodb.CollectionDocumentDeleteResponse{}, errDocumentNotFound) + mockLogger.EXPECT().Debug(gomock.Any()).AnyTimes() + mockMetrics.EXPECT().RecordHistogram(gomock.Any(), "app_arango_stats", gomock.Any(), + gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + + err := client.DeleteDocument(context.Background(), "testDB", "testCollection", + "testDocument") + require.ErrorIs(t, err, errDocumentNotFound, "Expected error while updating the document") +} + +func TestExecuteCollectionOperation(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockLogger := NewMockLogger(ctrl) + mockMetrics := NewMockMetrics(ctrl) + mockArango := NewMockArango(ctrl) + mockDatabase := NewMockDatabase(ctrl) + mockCollection := NewMockCollection(ctrl) + + client := New(Config{Host: "localhost", Port: 8527, User: "root", Password: "root"}) + client.UseLogger(mockLogger) + client.UseMetrics(mockMetrics) + + client.client = mockArango + d := Document{client: client} + + ctx := context.Background() + dbName := "testDB" + collectionName := "testCollection" + operation := "createDocument" + documentID := "doc123" + + mockArango.EXPECT().Database(ctx, dbName).Return(mockDatabase, nil) + mockDatabase.EXPECT().Collection(ctx, collectionName).Return(mockCollection, nil) + mockLogger.EXPECT().Debug(gomock.Any()) + mockMetrics.EXPECT().RecordHistogram(ctx, "app_arango_stats", gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) + + _, _, err := executeCollectionOperation(ctx, d, dbName, collectionName, operation, documentID) + require.NoError(t, err) +} + +func TestValidateEdgeDocument(t *testing.T) { + tests := []struct { + name string + document any + expectedError error + }{ + { + name: "Success - Valid Edge Document", + document: map[string]any{ + "_from": "vertex1", + "_to": "vertex2", + }, + expectedError: nil, + }, + { + name: "Fail - Document is Not a Map", + document: "invalid", + expectedError: errInvalidEdgeDocumentType, + }, + { + name: "Fail - Missing _from Field", + document: map[string]any{ + "_to": "vertex2", + }, + expectedError: errMissingEdgeFields, + }, + { + name: "Fail - Missing _to Field", + document: map[string]any{ + "_from": "vertex1", + }, + expectedError: errMissingEdgeFields, + }, + { + name: "Fail - _from is Not a String", + document: map[string]any{ + "_from": 123, + "_to": "vertex2", + }, + expectedError: errInvalidFromField, + }, + { + name: "Fail - _to is Not a String", + document: map[string]any{ + "_from": "vertex1", + "_to": 123, + }, + expectedError: errInvalidToField, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + err := validateEdgeDocument(tc.document) + assert.Equal(t, tc.expectedError, err) + }) + } +} diff --git a/pkg/gofr/datasource/arangodb/arango_graph.go b/pkg/gofr/datasource/arangodb/arango_graph.go new file mode 100644 index 000000000..e9da26d9e --- /dev/null +++ b/pkg/gofr/datasource/arangodb/arango_graph.go @@ -0,0 +1,71 @@ +package arangodb + +import ( + "context" + "errors" + "fmt" + "time" + + "github.com/arangodb/go-driver/v2/arangodb" +) + +var ( + errInvalidInput = errors.New("invalid input parameter") + errInvalidResponseType = errors.New("invalid response type") +) + +type EdgeDetails []arangodb.EdgeDetails + +type Graph struct { + client *Client +} + +// GetEdges fetches all edges connected to a given vertex in the specified edge collection. +// +// Parameters: +// - ctx: Request context for tracing and cancellation. +// - dbName: Database name. +// - graphName: Graph name. +// - edgeCollection: Edge collection name. +// - vertexID: Full vertex ID (e.g., "persons/16563"). +// - resp: Pointer to `*EdgeDetails` to store results. +// +// Returns an error if input is invalid, `resp` is of the wrong type, or the query fails. +func (c *Client) GetEdges(ctx context.Context, dbName, graphName, edgeCollection, vertexID string, + resp any) error { + if vertexID == "" || edgeCollection == "" { + return errInvalidInput + } + + // Type check the response parameter + edgeResp, ok := resp.(*EdgeDetails) + if !ok { + return fmt.Errorf("%w: must be *[]arangodb.EdgeDetails", errInvalidResponseType) + } + + tracerCtx, span := c.addTrace(ctx, "getEdges", map[string]string{ + "DB": dbName, "Graph": graphName, "Collection": edgeCollection, "Vertex": vertexID, + }) + startTime := time.Now() + + defer c.sendOperationStats(&QueryLog{ + Operation: "getEdges", + Database: dbName, + Collection: edgeCollection, + }, startTime, "getEdges", span) + + db, err := c.client.Database(tracerCtx, dbName) + if err != nil { + return err + } + + edges, err := db.GetEdges(tracerCtx, edgeCollection, vertexID, nil) + if err != nil { + return err + } + + // Assign the result to the provided response parameter + *edgeResp = edges + + return nil +} diff --git a/pkg/gofr/datasource/arangodb/arango_helper.go b/pkg/gofr/datasource/arangodb/arango_helper.go new file mode 100644 index 000000000..9cb101148 --- /dev/null +++ b/pkg/gofr/datasource/arangodb/arango_helper.go @@ -0,0 +1,99 @@ +package arangodb + +import ( + "context" + "fmt" + "time" + + "github.com/arangodb/go-driver/v2/arangodb" +) + +func (c *Client) user(ctx context.Context, username string) (arangodb.User, error) { + return c.client.User(ctx, username) +} + +func (c *Client) database(ctx context.Context, name string) (arangodb.Database, error) { + return c.client.Database(ctx, name) +} + +func (c *Client) databases(ctx context.Context) ([]arangodb.Database, error) { + return c.client.Databases(ctx) +} + +func (c *Client) version(ctx context.Context) (arangodb.VersionInfo, error) { + return c.client.Version(ctx) +} + +// createUser creates a new user in ArangoDB. +func (c *Client) createUser(ctx context.Context, username string, options any) error { + tracerCtx, span := c.addTrace(ctx, "createUser", map[string]string{"user": username}) + startTime := time.Now() + + defer c.sendOperationStats(&QueryLog{Operation: "createUser", ID: username}, + startTime, "createUser", span) + + userOptions, ok := options.(UserOptions) + if !ok { + return fmt.Errorf("%w", errInvalidUserOptionsType) + } + + _, err := c.client.CreateUser(tracerCtx, username, userOptions.toArangoUserOptions()) + if err != nil { + return err + } + + return nil +} + +// dropUser deletes a user from ArangoDB. +func (c *Client) dropUser(ctx context.Context, username string) error { + tracerCtx, span := c.addTrace(ctx, "dropUser", map[string]string{"user": username}) + startTime := time.Now() + + defer c.sendOperationStats(&QueryLog{Operation: "dropUser", + ID: username}, startTime, "dropUser", span) + + err := c.client.RemoveUser(tracerCtx, username) + if err != nil { + return err + } + + return err +} + +// grantDB grants permissions for a database to a user. +func (c *Client) grantDB(ctx context.Context, database, username, permission string) error { + tracerCtx, span := c.addTrace(ctx, "grantDB", map[string]string{"DB": database}) + startTime := time.Now() + + defer c.sendOperationStats(&QueryLog{Operation: "grantDB", + Database: database, ID: username}, startTime, "grantDB", span) + + user, err := c.client.User(tracerCtx, username) + if err != nil { + return err + } + + err = user.SetDatabaseAccess(tracerCtx, database, arangodb.Grant(permission)) + + return err +} + +// grantCollection grants permissions for a collection to a user. +func (c *Client) grantCollection(ctx context.Context, database, collection, username, permission string) error { + tracerCtx, span := c.addTrace(ctx, "GrantCollection", map[string]string{"collection": collection}) + startTime := time.Now() + + defer c.sendOperationStats(&QueryLog{Operation: "GrantCollection", + Database: database, Collection: collection, ID: username}, startTime, + "GrantCollection", span) + + user, err := c.client.User(tracerCtx, username) + if err != nil { + return err + } + + err = user.SetCollectionAccess(tracerCtx, database, collection, arangodb.Grant(permission)) + + return err +} diff --git a/pkg/gofr/datasource/arangodb/arango_helper_test.go b/pkg/gofr/datasource/arangodb/arango_helper_test.go new file mode 100644 index 000000000..44581fe15 --- /dev/null +++ b/pkg/gofr/datasource/arangodb/arango_helper_test.go @@ -0,0 +1,239 @@ +package arangodb + +import ( + "context" + "testing" + + "github.com/arangodb/go-driver/v2/arangodb" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/otel" + "go.uber.org/mock/gomock" +) + +func Test_Client_CreateUser(t *testing.T) { + client, mockArango, _, mockLogger, mockMetrics := setupDB(t) + + mockArango.EXPECT().AddUser(gomock.Any(), "test", &arangodb.UserOptions{Password: "user123"}). + Return(nil, nil) + mockLogger.EXPECT().Debug(gomock.Any()) + mockMetrics.EXPECT().RecordHistogram(context.Background(), "app_arango_stats", + gomock.Any(), "endpoint", gomock.Any(), gomock.Any(), gomock.Any()) + + err := client.createUser(context.Background(), "test", UserOptions{ + Password: "user123", + Extra: nil, + }) + require.NoError(t, err, "Test_Arango_CreateUser: failed to create user") +} + +func Test_Client_DropUser(t *testing.T) { + client, mockArango, _, mockLogger, mockMetrics := setupDB(t) + + // Ensure the mock returns nil as an error type + mockArango.EXPECT().DropUser(gomock.Any(), "test").Return(nil) + mockLogger.EXPECT().Debug(gomock.Any()) + mockMetrics.EXPECT().RecordHistogram(context.Background(), "app_arango_stats", gomock.Any(), + gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) + + err := client.dropUser(context.Background(), "test") + require.NoError(t, err, "Test_Arango_DropUser: failed to drop user") +} + +func Test_Client_GrantDB(t *testing.T) { + client, mockArango, mockUser, mockLogger, mockMetrics := setupDB(t) + + // Test data + ctx := context.Background() + dbName := "testDB" + username := "testUser" + + // Expectations + mockLogger.EXPECT().Debug(gomock.Any()).AnyTimes() + mockMetrics.EXPECT().RecordHistogram( + ctx, "app_arango_stats", gomock.Any(), "endpoint", + gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + + // Expect user() call and return our mock user that implements the full interface + mockArango.EXPECT().User(gomock.Any(), username).Return(mockUser, nil).MaxTimes(2) + + // Test cases + testCases := []struct { + name string + dbName string + username string + permission string + expectErr bool + }{ + { + name: "Valid grant read-write", + dbName: dbName, + username: username, + permission: string(arangodb.GrantReadWrite), + expectErr: false, + }, + { + name: "Valid grant read-only", + dbName: dbName, + username: username, + permission: string(arangodb.GrantReadOnly), + expectErr: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + err := client.grantDB(ctx, tc.dbName, tc.username, tc.permission) + if tc.expectErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} + +func Test_Client_GrantDB_Errors(t *testing.T) { + client, mockArango, _, mockLogger, mockMetrics := setupDB(t) + + ctx := context.Background() + dbName := "testDB" + username := "testUser" + + // Expect user() call to return error + mockArango.EXPECT().User(gomock.Any(), username).Return(nil, errUserNotFound) + mockLogger.EXPECT().Debug(gomock.Any()).AnyTimes() + mockMetrics.EXPECT().RecordHistogram(ctx, "app_arango_stats", gomock.Any(), "endpoint", + gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + + err := client.grantDB(ctx, dbName, username, string(arangodb.GrantReadWrite)) + require.Error(t, err) +} + +func TestUser(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockArango := NewMockArango(ctrl) + mockUser := NewMockUser(ctrl) + client := &Client{client: mockArango} + + ctx := context.Background() + username := "testUser" + + t.Run("Successful user fetch", func(t *testing.T) { + mockArango.EXPECT(). + User(ctx, username). + Return(mockUser, nil) + + user, err := client.user(ctx, username) + require.NoError(t, err) + require.NotNil(t, user) + }) + + t.Run("user fetch error", func(t *testing.T) { + mockArango.EXPECT(). + User(ctx, username). + Return(nil, errUserNotFound) + + user, err := client.user(ctx, username) + require.Error(t, err) + require.Nil(t, user) + }) +} + +func TestClient_Database(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockLogger := NewMockLogger(ctrl) + mockMetrics := NewMockMetrics(ctrl) + mockArango := NewMockArango(ctrl) + + config := Config{Host: "localhost", Port: 8527, User: "root", Password: "root"} + client := New(config) + client.UseLogger(mockLogger) + client.UseMetrics(mockMetrics) + client.UseTracer(otel.GetTracerProvider().Tracer("gofr-arangodb")) + + client.client = mockArango + + mockDatabase := NewMockDatabase(gomock.NewController(t)) + + ctx := context.Background() + dbName := "testDB" + + t.Run("Get database Success", func(t *testing.T) { + mockArango.EXPECT(). + Database(ctx, dbName). + Return(mockDatabase, nil) + mockDatabase.EXPECT().Name().Return(dbName) + + db, err := client.database(ctx, dbName) + require.NoError(t, err) + require.NotNil(t, db) + require.Equal(t, dbName, db.Name()) + }) + + t.Run("Get database Error", func(t *testing.T) { + mockArango.EXPECT(). + Database(ctx, dbName). + Return(nil, errDBNotFound) + + db, err := client.database(ctx, dbName) + require.Error(t, err) + require.Nil(t, db) + }) + + // Test database operations + t.Run("database Operations", func(t *testing.T) { + mockArango.EXPECT(). + Database(ctx, dbName). + Return(mockDatabase, nil) + mockDatabase.EXPECT().Name().Return(dbName) + mockDatabase.EXPECT().Remove(ctx).Return(nil) + mockDatabase.EXPECT().Collection(ctx, "testCollection").Return(nil, nil) + + db, err := client.database(ctx, dbName) + require.NoError(t, err) + require.Equal(t, dbName, db.Name()) + + err = db.Remove(ctx) + require.NoError(t, err) + + coll, err := db.Collection(ctx, "testCollection") + require.NoError(t, err) + require.Nil(t, coll) + }) +} + +func Test_Client_GrantCollection(t *testing.T) { + client, mockArango, mockUser, mockLogger, mockMetrics := setupDB(t) + + mockLogger.EXPECT().Debug(gomock.Any()) + mockMetrics.EXPECT().RecordHistogram( + context.Background(), "app_arango_stats", gomock.Any(), "endpoint", + gomock.Any(), gomock.Any(), gomock.Any()) + + mockArango.EXPECT().User(gomock.Any(), "testUser").Return(mockUser, nil) + + err := client.grantCollection(context.Background(), "testDB", "testCollection", + "testUser", string(arangodb.GrantReadOnly)) + + require.NoError(t, err) +} + +func Test_Client_GrantCollection_Error(t *testing.T) { + client, mockArango, _, mockLogger, mockMetrics := setupDB(t) + + mockLogger.EXPECT().Debug(gomock.Any()).AnyTimes() + mockMetrics.EXPECT().RecordHistogram( + context.Background(), "app_arango_stats", gomock.Any(), "endpoint", + gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + + mockArango.EXPECT().User(gomock.Any(), "testUser").Return(nil, errUserNotFound) + + err := client.grantCollection(context.Background(), "testDB", "testCollection", + "testUser", string(arangodb.GrantReadOnly)) + + require.ErrorIs(t, errUserNotFound, err, "Expected error when user not found") +} diff --git a/pkg/gofr/datasource/arangodb/arango_test.go b/pkg/gofr/datasource/arangodb/arango_test.go new file mode 100644 index 000000000..0c8c2b7c3 --- /dev/null +++ b/pkg/gofr/datasource/arangodb/arango_test.go @@ -0,0 +1,132 @@ +package arangodb + +import ( + "errors" + "testing" + + "github.com/stretchr/testify/require" + "go.opentelemetry.io/otel" + "go.uber.org/mock/gomock" +) + +var ( + errUserNotFound = errors.New("user not found") + errDBNotFound = errors.New("database not found") + errDocumentNotFound = errors.New("document not found") +) + +func setupDB(t *testing.T) (*Client, *MockArango, *MockUser, *MockLogger, *MockMetrics) { + t.Helper() + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // Setup + mockLogger := NewMockLogger(ctrl) + mockMetrics := NewMockMetrics(ctrl) + mockArango := NewMockArango(ctrl) + mockUser := NewMockUser(ctrl) + + config := Config{Host: "localhost", Port: 8527, User: "root", Password: "root"} + client := New(config) + client.UseLogger(mockLogger) + client.UseMetrics(mockMetrics) + client.UseTracer(otel.GetTracerProvider().Tracer("gofr-arangodb")) + + client.client = mockArango + + return client, mockArango, mockUser, mockLogger, mockMetrics +} + +func Test_NewArangoClient_Error(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + metrics := NewMockMetrics(ctrl) + logger := NewMockLogger(ctrl) + + logger.EXPECT().Errorf("failed to verify connection: %v", gomock.Any()) + logger.EXPECT().Debugf(gomock.Any(), gomock.Any()) + + client := New(Config{Host: "localhost", Port: 8529, Password: "root", User: "admin"}) + + client.UseLogger(logger) + client.UseMetrics(metrics) + client.Connect() + + require.NotNil(t, client) +} + +func TestValidateConfig(t *testing.T) { + testCases := []struct { + name string + config Config + expectErr bool + errMsg string + }{ + { + name: "Valid config", + config: Config{ + Host: "localhost", + Port: 8529, + User: "root", + Password: "password", + }, + expectErr: false, + }, + { + name: "Empty host", + config: Config{ + Port: 8529, + User: "root", + Password: "password", + }, + expectErr: true, + errMsg: "missing required field in config: host is empty", + }, + { + name: "Empty port", + config: Config{ + Host: "localhost", + User: "root", + Password: "password", + }, + expectErr: true, + errMsg: "missing required field in config: port is empty", + }, + { + name: "Empty user", + config: Config{ + Host: "localhost", + Port: 8529, + Password: "password", + }, + expectErr: true, + errMsg: "missing required field in config: user is empty", + }, + { + name: "Empty password", + config: Config{ + Host: "localhost", + Port: 8529, + User: "root", + }, + expectErr: true, + errMsg: "missing required field in config: password is empty", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + client := &Client{config: &tc.config} + err := client.validateConfig() + + if tc.expectErr { + require.Error(t, err) + require.Contains(t, err.Error(), tc.errMsg) + } else { + require.NoError(t, err) + } + }) + } +} diff --git a/pkg/gofr/datasource/arangodb/go.mod b/pkg/gofr/datasource/arangodb/go.mod new file mode 100644 index 000000000..e0eb14123 --- /dev/null +++ b/pkg/gofr/datasource/arangodb/go.mod @@ -0,0 +1,32 @@ +module gofr.dev/pkg/gofr/datasource/arangodb + +go 1.23.4 + +require ( + github.com/arangodb/go-driver/v2 v2.1.2 + github.com/stretchr/testify v1.10.0 + go.opentelemetry.io/otel v1.34.0 + go.opentelemetry.io/otel/trace v1.34.0 + go.uber.org/mock v0.5.0 +) + +require ( + github.com/arangodb/go-velocypack v0.0.0-20200318135517-5af53c29c67e // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dchest/siphash v1.2.3 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/kkdai/maglev v0.2.0 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rs/zerolog v1.33.0 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/otel/metric v1.34.0 // indirect + golang.org/x/net v0.31.0 // indirect + golang.org/x/sys v0.27.0 // indirect + golang.org/x/text v0.20.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/pkg/gofr/datasource/arangodb/go.sum b/pkg/gofr/datasource/arangodb/go.sum new file mode 100644 index 000000000..f5f91c8d7 --- /dev/null +++ b/pkg/gofr/datasource/arangodb/go.sum @@ -0,0 +1,71 @@ +github.com/arangodb/go-driver/v2 v2.1.2 h1:3dxx97pjcJPajw4hnHJMXRz2bY/KizUj/ZrlAVEx10Q= +github.com/arangodb/go-driver/v2 v2.1.2/go.mod h1:POYSylTzBPej3qEouU3dSyfdVfo3WxawaRwzhA9mbJ4= +github.com/arangodb/go-velocypack v0.0.0-20200318135517-5af53c29c67e h1:Xg+hGrY2LcQBbxd0ZFdbGSyRKTYMZCfBbw/pMJFOk1g= +github.com/arangodb/go-velocypack v0.0.0-20200318135517-5af53c29c67e/go.mod h1:mq7Shfa/CaixoDxiyAAc5jZ6CVBAyPaNQCGS7mkj4Ho= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +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/dchest/siphash v1.2.2/go.mod h1:q+IRvb2gOSrUnYoPqHiyHXS0FOBBOdl6tONBlVnOnt4= +github.com/dchest/siphash v1.2.3 h1:QXwFc8cFOR2dSa/gE6o/HokBMWtLUaNDVd+22aKHeEA= +github.com/dchest/siphash v1.2.3/go.mod h1:0NvQU092bT0ipiFN++/rXm69QG9tVxLAlQHIXMPAkHc= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/kkdai/maglev v0.2.0 h1:w6DCW0kAA6fstZqXkrBrlgIC3jeIRXkjOYea/m6EK/Y= +github.com/kkdai/maglev v0.2.0/go.mod h1:d+mt8Lmt3uqi9aRb/BnPjzD0fy+ETs1vVXiGRnqHVZ4= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +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/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= +github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= +go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= +go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= +go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= +go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= +go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= +go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= +go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= +golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= +golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= +golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= +golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +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= diff --git a/pkg/gofr/datasource/arangodb/interface.go b/pkg/gofr/datasource/arangodb/interface.go new file mode 100644 index 000000000..ed8d9695a --- /dev/null +++ b/pkg/gofr/datasource/arangodb/interface.go @@ -0,0 +1,29 @@ +package arangodb + +import ( + "context" + + "github.com/arangodb/go-driver/v2/arangodb" +) + +type ArangoDB interface { + Connect() + + user(ctx context.Context, username string) (arangodb.User, error) + database(ctx context.Context, name string) (arangodb.Database, error) + databases(ctx context.Context) ([]arangodb.Database, error) + version(ctx context.Context) (arangodb.VersionInfo, error) + + CreateDocument(ctx context.Context, dbName, collectionName string, document any) (string, error) + GetDocument(ctx context.Context, dbName, collectionName, documentID string, result any) error + UpdateDocument(ctx context.Context, dbName, collectionName, documentID string, document any) error + DeleteDocument(ctx context.Context, dbName, collectionName, documentID string) error + + // GetEdges retrieves all the edge documents connected to a specific vertex in an ArangoDB graph. + GetEdges(ctx context.Context, dbName, graphName, edgeCollection, vertexID string, resp any) error + + // Query operations + Query(ctx context.Context, dbName string, query string, bindVars map[string]any, result any) error + + HealthCheck(ctx context.Context) (any, error) +} diff --git a/pkg/gofr/datasource/arangodb/logger.go b/pkg/gofr/datasource/arangodb/logger.go new file mode 100644 index 000000000..145174703 --- /dev/null +++ b/pkg/gofr/datasource/arangodb/logger.go @@ -0,0 +1,47 @@ +package arangodb + +import ( + "fmt" + "io" + "regexp" + "strings" +) + +type Logger interface { + Debug(args ...any) + Debugf(pattern string, args ...any) + Logf(pattern string, args ...any) + Errorf(pattern string, args ...any) +} + +type QueryLog struct { + Query string `json:"query"` + Duration int64 `json:"duration"` + Database string `json:"database,omitempty"` + Collection string `json:"collection,omitempty"` + Filter any `json:"filter,omitempty"` + ID any `json:"id,omitempty"` + Operation string `json:"operation,omitempty"` +} + +// PrettyPrint formats the QueryLog for output. +func (ql *QueryLog) PrettyPrint(writer io.Writer) { + if ql.Filter == nil { + ql.Filter = "" + } + + if ql.ID == nil { + ql.ID = "" + } + + fmt.Fprintf(writer, "\u001B[38;5;8m%-32s \u001B[38;5;206m%-6s\u001B[0m %8d\u001B[38;5;8mµs\u001B[0m %s %s\n", + clean(ql.Operation), "ARANGODB", ql.Duration, + clean(strings.Join([]string{ql.Database, ql.Collection, fmt.Sprint(ql.Filter), fmt.Sprint(ql.ID)}, " ")), clean(ql.Query)) +} + +func clean(query string) string { + // Replace multiple consecutive whitespace characters with a single space + query = regexp.MustCompile(`\s+`).ReplaceAllString(query, " ") + // Trim leading and trailing whitespace from the string + return strings.TrimSpace(query) +} diff --git a/pkg/gofr/datasource/arangodb/logger_test.go b/pkg/gofr/datasource/arangodb/logger_test.go new file mode 100644 index 000000000..8bfba6827 --- /dev/null +++ b/pkg/gofr/datasource/arangodb/logger_test.go @@ -0,0 +1,27 @@ +package arangodb + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_PrettyPrint(t *testing.T) { + queryLog := QueryLog{ + Query: "", + Duration: 12345, + Database: "test", + Collection: "test", + Filter: true, + ID: "12345", + Operation: "getDocument", + } + expected := "getDocument" + + var buf bytes.Buffer + + queryLog.PrettyPrint(&buf) + + assert.Contains(t, buf.String(), expected) +} diff --git a/pkg/gofr/datasource/arangodb/metrics.go b/pkg/gofr/datasource/arangodb/metrics.go new file mode 100644 index 000000000..868d30f89 --- /dev/null +++ b/pkg/gofr/datasource/arangodb/metrics.go @@ -0,0 +1,9 @@ +package arangodb + +import "context" + +// Metrics defines the interface for capturing metrics. +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/arangodb/mock_collection.go b/pkg/gofr/datasource/arangodb/mock_collection.go new file mode 100644 index 000000000..3a53ce518 --- /dev/null +++ b/pkg/gofr/datasource/arangodb/mock_collection.go @@ -0,0 +1,670 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: collection.go +// +// Generated by this command: +// +// mockgen -source=collection.go -destination=./mock_collection.go -package=arangodb +// + +// Package arangodb is a generated GoMock package. +package arangodb + +import ( + context "context" + reflect "reflect" + + arangodb "github.com/arangodb/go-driver/v2/arangodb" + gomock "go.uber.org/mock/gomock" +) + +// MockCollection is a mock of Collection interface. +type MockCollection struct { + ctrl *gomock.Controller + recorder *MockCollectionMockRecorder +} + +// MockCollectionMockRecorder is the mock recorder for MockCollection. +type MockCollectionMockRecorder struct { + mock *MockCollection +} + +// NewMockCollection creates a new mock instance. +func NewMockCollection(ctrl *gomock.Controller) *MockCollection { + mock := &MockCollection{ctrl: ctrl} + mock.recorder = &MockCollectionMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockCollection) EXPECT() *MockCollectionMockRecorder { + return m.recorder +} + +// Count mocks base method. +func (m *MockCollection) Count(ctx context.Context) (int64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Count", ctx) + ret0, _ := ret[0].(int64) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Count indicates an expected call of Count. +func (mr *MockCollectionMockRecorder) Count(ctx any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Count", reflect.TypeOf((*MockCollection)(nil).Count), ctx) +} + +// CreateDocument mocks base method. +func (m *MockCollection) CreateDocument(ctx context.Context, document any) (arangodb.CollectionDocumentCreateResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateDocument", ctx, document) + ret0, _ := ret[0].(arangodb.CollectionDocumentCreateResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateDocument indicates an expected call of CreateDocument. +func (mr *MockCollectionMockRecorder) CreateDocument(ctx, document any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateDocument", reflect.TypeOf((*MockCollection)(nil).CreateDocument), ctx, document) +} + +// CreateDocumentWithOptions mocks base method. +func (m *MockCollection) CreateDocumentWithOptions(ctx context.Context, document any, options *arangodb.CollectionDocumentCreateOptions) (arangodb.CollectionDocumentCreateResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateDocumentWithOptions", ctx, document, options) + ret0, _ := ret[0].(arangodb.CollectionDocumentCreateResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateDocumentWithOptions indicates an expected call of CreateDocumentWithOptions. +func (mr *MockCollectionMockRecorder) CreateDocumentWithOptions(ctx, document, options any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateDocumentWithOptions", reflect.TypeOf((*MockCollection)(nil).CreateDocumentWithOptions), ctx, document, options) +} + +// CreateDocuments mocks base method. +func (m *MockCollection) CreateDocuments(ctx context.Context, documents any) (arangodb.CollectionDocumentCreateResponseReader, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateDocuments", ctx, documents) + ret0, _ := ret[0].(arangodb.CollectionDocumentCreateResponseReader) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateDocuments indicates an expected call of CreateDocuments. +func (mr *MockCollectionMockRecorder) CreateDocuments(ctx, documents any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateDocuments", reflect.TypeOf((*MockCollection)(nil).CreateDocuments), ctx, documents) +} + +// CreateDocumentsWithOptions mocks base method. +func (m *MockCollection) CreateDocumentsWithOptions(ctx context.Context, documents any, opts *arangodb.CollectionDocumentCreateOptions) (arangodb.CollectionDocumentCreateResponseReader, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateDocumentsWithOptions", ctx, documents, opts) + ret0, _ := ret[0].(arangodb.CollectionDocumentCreateResponseReader) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateDocumentsWithOptions indicates an expected call of CreateDocumentsWithOptions. +func (mr *MockCollectionMockRecorder) CreateDocumentsWithOptions(ctx, documents, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateDocumentsWithOptions", reflect.TypeOf((*MockCollection)(nil).CreateDocumentsWithOptions), ctx, documents, opts) +} + +// Database mocks base method. +func (m *MockCollection) Database() arangodb.Database { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "database") + ret0, _ := ret[0].(arangodb.Database) + return ret0 +} + +// Database indicates an expected call of Database. +func (mr *MockCollectionMockRecorder) Database() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "database", reflect.TypeOf((*MockCollection)(nil).Database)) +} + +// DeleteDocument mocks base method. +func (m *MockCollection) DeleteDocument(ctx context.Context, key string) (arangodb.CollectionDocumentDeleteResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteDocument", ctx, key) + ret0, _ := ret[0].(arangodb.CollectionDocumentDeleteResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DeleteDocument indicates an expected call of DeleteDocument. +func (mr *MockCollectionMockRecorder) DeleteDocument(ctx, key any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteDocument", reflect.TypeOf((*MockCollection)(nil).DeleteDocument), ctx, key) +} + +// DeleteDocumentWithOptions mocks base method. +func (m *MockCollection) DeleteDocumentWithOptions(ctx context.Context, key string, opts *arangodb.CollectionDocumentDeleteOptions) (arangodb.CollectionDocumentDeleteResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteDocumentWithOptions", ctx, key, opts) + ret0, _ := ret[0].(arangodb.CollectionDocumentDeleteResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DeleteDocumentWithOptions indicates an expected call of DeleteDocumentWithOptions. +func (mr *MockCollectionMockRecorder) DeleteDocumentWithOptions(ctx, key, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteDocumentWithOptions", reflect.TypeOf((*MockCollection)(nil).DeleteDocumentWithOptions), ctx, key, opts) +} + +// DeleteDocuments mocks base method. +func (m *MockCollection) DeleteDocuments(ctx context.Context, keys []string) (arangodb.CollectionDocumentDeleteResponseReader, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteDocuments", ctx, keys) + ret0, _ := ret[0].(arangodb.CollectionDocumentDeleteResponseReader) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DeleteDocuments indicates an expected call of DeleteDocuments. +func (mr *MockCollectionMockRecorder) DeleteDocuments(ctx, keys any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteDocuments", reflect.TypeOf((*MockCollection)(nil).DeleteDocuments), ctx, keys) +} + +// DeleteDocumentsWithOptions mocks base method. +func (m *MockCollection) DeleteDocumentsWithOptions(ctx context.Context, documents any, opts *arangodb.CollectionDocumentDeleteOptions) (arangodb.CollectionDocumentDeleteResponseReader, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteDocumentsWithOptions", ctx, documents, opts) + ret0, _ := ret[0].(arangodb.CollectionDocumentDeleteResponseReader) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DeleteDocumentsWithOptions indicates an expected call of DeleteDocumentsWithOptions. +func (mr *MockCollectionMockRecorder) DeleteDocumentsWithOptions(ctx, documents, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteDocumentsWithOptions", reflect.TypeOf((*MockCollection)(nil).DeleteDocumentsWithOptions), ctx, documents, opts) +} + +// DeleteIndex mocks base method. +func (m *MockCollection) DeleteIndex(ctx context.Context, name string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteIndex", ctx, name) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteIndex indicates an expected call of DeleteIndex. +func (mr *MockCollectionMockRecorder) DeleteIndex(ctx, name any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteIndex", reflect.TypeOf((*MockCollection)(nil).DeleteIndex), ctx, name) +} + +// DeleteIndexByID mocks base method. +func (m *MockCollection) DeleteIndexByID(ctx context.Context, id string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteIndexByID", ctx, id) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteIndexByID indicates an expected call of DeleteIndexByID. +func (mr *MockCollectionMockRecorder) DeleteIndexByID(ctx, id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteIndexByID", reflect.TypeOf((*MockCollection)(nil).DeleteIndexByID), ctx, id) +} + +// DocumentExists mocks base method. +func (m *MockCollection) DocumentExists(ctx context.Context, key string) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DocumentExists", ctx, key) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DocumentExists indicates an expected call of DocumentExists. +func (mr *MockCollectionMockRecorder) DocumentExists(ctx, key any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DocumentExists", reflect.TypeOf((*MockCollection)(nil).DocumentExists), ctx, key) +} + +// EnsureGeoIndex mocks base method. +func (m *MockCollection) EnsureGeoIndex(ctx context.Context, fields []string, options *arangodb.CreateGeoIndexOptions) (arangodb.IndexResponse, bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EnsureGeoIndex", ctx, fields, options) + ret0, _ := ret[0].(arangodb.IndexResponse) + ret1, _ := ret[1].(bool) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// EnsureGeoIndex indicates an expected call of EnsureGeoIndex. +func (mr *MockCollectionMockRecorder) EnsureGeoIndex(ctx, fields, options any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureGeoIndex", reflect.TypeOf((*MockCollection)(nil).EnsureGeoIndex), ctx, fields, options) +} + +// EnsureInvertedIndex mocks base method. +func (m *MockCollection) EnsureInvertedIndex(ctx context.Context, options *arangodb.InvertedIndexOptions) (arangodb.IndexResponse, bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EnsureInvertedIndex", ctx, options) + ret0, _ := ret[0].(arangodb.IndexResponse) + ret1, _ := ret[1].(bool) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// EnsureInvertedIndex indicates an expected call of EnsureInvertedIndex. +func (mr *MockCollectionMockRecorder) EnsureInvertedIndex(ctx, options any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureInvertedIndex", reflect.TypeOf((*MockCollection)(nil).EnsureInvertedIndex), ctx, options) +} + +// EnsureMDIIndex mocks base method. +func (m *MockCollection) EnsureMDIIndex(ctx context.Context, fields []string, options *arangodb.CreateMDIIndexOptions) (arangodb.IndexResponse, bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EnsureMDIIndex", ctx, fields, options) + ret0, _ := ret[0].(arangodb.IndexResponse) + ret1, _ := ret[1].(bool) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// EnsureMDIIndex indicates an expected call of EnsureMDIIndex. +func (mr *MockCollectionMockRecorder) EnsureMDIIndex(ctx, fields, options any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureMDIIndex", reflect.TypeOf((*MockCollection)(nil).EnsureMDIIndex), ctx, fields, options) +} + +// EnsureMDIPrefixedIndex mocks base method. +func (m *MockCollection) EnsureMDIPrefixedIndex(ctx context.Context, fields []string, options *arangodb.CreateMDIPrefixedIndexOptions) (arangodb.IndexResponse, bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EnsureMDIPrefixedIndex", ctx, fields, options) + ret0, _ := ret[0].(arangodb.IndexResponse) + ret1, _ := ret[1].(bool) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// EnsureMDIPrefixedIndex indicates an expected call of EnsureMDIPrefixedIndex. +func (mr *MockCollectionMockRecorder) EnsureMDIPrefixedIndex(ctx, fields, options any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureMDIPrefixedIndex", reflect.TypeOf((*MockCollection)(nil).EnsureMDIPrefixedIndex), ctx, fields, options) +} + +// EnsurePersistentIndex mocks base method. +func (m *MockCollection) EnsurePersistentIndex(ctx context.Context, fields []string, options *arangodb.CreatePersistentIndexOptions) (arangodb.IndexResponse, bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EnsurePersistentIndex", ctx, fields, options) + ret0, _ := ret[0].(arangodb.IndexResponse) + ret1, _ := ret[1].(bool) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// EnsurePersistentIndex indicates an expected call of EnsurePersistentIndex. +func (mr *MockCollectionMockRecorder) EnsurePersistentIndex(ctx, fields, options any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsurePersistentIndex", reflect.TypeOf((*MockCollection)(nil).EnsurePersistentIndex), ctx, fields, options) +} + +// EnsureTTLIndex mocks base method. +func (m *MockCollection) EnsureTTLIndex(ctx context.Context, fields []string, expireAfter int, options *arangodb.CreateTTLIndexOptions) (arangodb.IndexResponse, bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EnsureTTLIndex", ctx, fields, expireAfter, options) + ret0, _ := ret[0].(arangodb.IndexResponse) + ret1, _ := ret[1].(bool) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// EnsureTTLIndex indicates an expected call of EnsureTTLIndex. +func (mr *MockCollectionMockRecorder) EnsureTTLIndex(ctx, fields, expireAfter, options any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureTTLIndex", reflect.TypeOf((*MockCollection)(nil).EnsureTTLIndex), ctx, fields, expireAfter, options) +} + +// EnsureZKDIndex mocks base method. +func (m *MockCollection) EnsureZKDIndex(ctx context.Context, fields []string, options *arangodb.CreateZKDIndexOptions) (arangodb.IndexResponse, bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EnsureZKDIndex", ctx, fields, options) + ret0, _ := ret[0].(arangodb.IndexResponse) + ret1, _ := ret[1].(bool) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// EnsureZKDIndex indicates an expected call of EnsureZKDIndex. +func (mr *MockCollectionMockRecorder) EnsureZKDIndex(ctx, fields, options any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureZKDIndex", reflect.TypeOf((*MockCollection)(nil).EnsureZKDIndex), ctx, fields, options) +} + +// Index mocks base method. +func (m *MockCollection) Index(ctx context.Context, name string) (arangodb.IndexResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Index", ctx, name) + ret0, _ := ret[0].(arangodb.IndexResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Index indicates an expected call of Index. +func (mr *MockCollectionMockRecorder) Index(ctx, name any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Index", reflect.TypeOf((*MockCollection)(nil).Index), ctx, name) +} + +// IndexExists mocks base method. +func (m *MockCollection) IndexExists(ctx context.Context, name string) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IndexExists", ctx, name) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// IndexExists indicates an expected call of IndexExists. +func (mr *MockCollectionMockRecorder) IndexExists(ctx, name any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IndexExists", reflect.TypeOf((*MockCollection)(nil).IndexExists), ctx, name) +} + +// Indexes mocks base method. +func (m *MockCollection) Indexes(ctx context.Context) ([]arangodb.IndexResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Indexes", ctx) + ret0, _ := ret[0].([]arangodb.IndexResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Indexes indicates an expected call of Indexes. +func (mr *MockCollectionMockRecorder) Indexes(ctx any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Indexes", reflect.TypeOf((*MockCollection)(nil).Indexes), ctx) +} + +// Name mocks base method. +func (m *MockCollection) Name() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Name") + ret0, _ := ret[0].(string) + return ret0 +} + +// Name indicates an expected call of Name. +func (mr *MockCollectionMockRecorder) Name() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Name", reflect.TypeOf((*MockCollection)(nil).Name)) +} + +// Properties mocks base method. +func (m *MockCollection) Properties(ctx context.Context) (arangodb.CollectionProperties, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Properties", ctx) + ret0, _ := ret[0].(arangodb.CollectionProperties) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Properties indicates an expected call of Properties. +func (mr *MockCollectionMockRecorder) Properties(ctx any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Properties", reflect.TypeOf((*MockCollection)(nil).Properties), ctx) +} + +// ReadDocument mocks base method. +func (m *MockCollection) ReadDocument(ctx context.Context, key string, result any) (arangodb.DocumentMeta, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ReadDocument", ctx, key, result) + ret0, _ := ret[0].(arangodb.DocumentMeta) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ReadDocument indicates an expected call of ReadDocument. +func (mr *MockCollectionMockRecorder) ReadDocument(ctx, key, result any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadDocument", reflect.TypeOf((*MockCollection)(nil).ReadDocument), ctx, key, result) +} + +// ReadDocumentWithOptions mocks base method. +func (m *MockCollection) ReadDocumentWithOptions(ctx context.Context, key string, result any, opts *arangodb.CollectionDocumentReadOptions) (arangodb.DocumentMeta, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ReadDocumentWithOptions", ctx, key, result, opts) + ret0, _ := ret[0].(arangodb.DocumentMeta) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ReadDocumentWithOptions indicates an expected call of ReadDocumentWithOptions. +func (mr *MockCollectionMockRecorder) ReadDocumentWithOptions(ctx, key, result, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadDocumentWithOptions", reflect.TypeOf((*MockCollection)(nil).ReadDocumentWithOptions), ctx, key, result, opts) +} + +// ReadDocuments mocks base method. +func (m *MockCollection) ReadDocuments(ctx context.Context, keys []string) (arangodb.CollectionDocumentReadResponseReader, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ReadDocuments", ctx, keys) + ret0, _ := ret[0].(arangodb.CollectionDocumentReadResponseReader) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ReadDocuments indicates an expected call of ReadDocuments. +func (mr *MockCollectionMockRecorder) ReadDocuments(ctx, keys any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadDocuments", reflect.TypeOf((*MockCollection)(nil).ReadDocuments), ctx, keys) +} + +// ReadDocumentsWithOptions mocks base method. +func (m *MockCollection) ReadDocumentsWithOptions(ctx context.Context, documents any, opts *arangodb.CollectionDocumentReadOptions) (arangodb.CollectionDocumentReadResponseReader, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ReadDocumentsWithOptions", ctx, documents, opts) + ret0, _ := ret[0].(arangodb.CollectionDocumentReadResponseReader) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ReadDocumentsWithOptions indicates an expected call of ReadDocumentsWithOptions. +func (mr *MockCollectionMockRecorder) ReadDocumentsWithOptions(ctx, documents, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadDocumentsWithOptions", reflect.TypeOf((*MockCollection)(nil).ReadDocumentsWithOptions), ctx, documents, opts) +} + +// Remove mocks base method. +func (m *MockCollection) Remove(ctx context.Context) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Remove", ctx) + ret0, _ := ret[0].(error) + return ret0 +} + +// Remove indicates an expected call of Remove. +func (mr *MockCollectionMockRecorder) Remove(ctx any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Remove", reflect.TypeOf((*MockCollection)(nil).Remove), ctx) +} + +// RemoveWithOptions mocks base method. +func (m *MockCollection) RemoveWithOptions(ctx context.Context, opts *arangodb.RemoveCollectionOptions) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RemoveWithOptions", ctx, opts) + ret0, _ := ret[0].(error) + return ret0 +} + +// RemoveWithOptions indicates an expected call of RemoveWithOptions. +func (mr *MockCollectionMockRecorder) RemoveWithOptions(ctx, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveWithOptions", reflect.TypeOf((*MockCollection)(nil).RemoveWithOptions), ctx, opts) +} + +// ReplaceDocument mocks base method. +func (m *MockCollection) ReplaceDocument(ctx context.Context, key string, document any) (arangodb.CollectionDocumentReplaceResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ReplaceDocument", ctx, key, document) + ret0, _ := ret[0].(arangodb.CollectionDocumentReplaceResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ReplaceDocument indicates an expected call of ReplaceDocument. +func (mr *MockCollectionMockRecorder) ReplaceDocument(ctx, key, document any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplaceDocument", reflect.TypeOf((*MockCollection)(nil).ReplaceDocument), ctx, key, document) +} + +// ReplaceDocumentWithOptions mocks base method. +func (m *MockCollection) ReplaceDocumentWithOptions(ctx context.Context, key string, document any, options *arangodb.CollectionDocumentReplaceOptions) (arangodb.CollectionDocumentReplaceResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ReplaceDocumentWithOptions", ctx, key, document, options) + ret0, _ := ret[0].(arangodb.CollectionDocumentReplaceResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ReplaceDocumentWithOptions indicates an expected call of ReplaceDocumentWithOptions. +func (mr *MockCollectionMockRecorder) ReplaceDocumentWithOptions(ctx, key, document, options any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplaceDocumentWithOptions", reflect.TypeOf((*MockCollection)(nil).ReplaceDocumentWithOptions), ctx, key, document, options) +} + +// ReplaceDocuments mocks base method. +func (m *MockCollection) ReplaceDocuments(ctx context.Context, documents any) (arangodb.CollectionDocumentReplaceResponseReader, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ReplaceDocuments", ctx, documents) + ret0, _ := ret[0].(arangodb.CollectionDocumentReplaceResponseReader) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ReplaceDocuments indicates an expected call of ReplaceDocuments. +func (mr *MockCollectionMockRecorder) ReplaceDocuments(ctx, documents any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplaceDocuments", reflect.TypeOf((*MockCollection)(nil).ReplaceDocuments), ctx, documents) +} + +// ReplaceDocumentsWithOptions mocks base method. +func (m *MockCollection) ReplaceDocumentsWithOptions(ctx context.Context, documents any, opts *arangodb.CollectionDocumentReplaceOptions) (arangodb.CollectionDocumentReplaceResponseReader, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ReplaceDocumentsWithOptions", ctx, documents, opts) + ret0, _ := ret[0].(arangodb.CollectionDocumentReplaceResponseReader) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ReplaceDocumentsWithOptions indicates an expected call of ReplaceDocumentsWithOptions. +func (mr *MockCollectionMockRecorder) ReplaceDocumentsWithOptions(ctx, documents, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplaceDocumentsWithOptions", reflect.TypeOf((*MockCollection)(nil).ReplaceDocumentsWithOptions), ctx, documents, opts) +} + +// SetProperties mocks base method. +func (m *MockCollection) SetProperties(ctx context.Context, options arangodb.SetCollectionPropertiesOptions) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetProperties", ctx, options) + ret0, _ := ret[0].(error) + return ret0 +} + +// SetProperties indicates an expected call of SetProperties. +func (mr *MockCollectionMockRecorder) SetProperties(ctx, options any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetProperties", reflect.TypeOf((*MockCollection)(nil).SetProperties), ctx, options) +} + +// Shards mocks base method. +func (m *MockCollection) Shards(ctx context.Context, details bool) (arangodb.CollectionShards, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Shards", ctx, details) + ret0, _ := ret[0].(arangodb.CollectionShards) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Shards indicates an expected call of Shards. +func (mr *MockCollectionMockRecorder) Shards(ctx, details any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Shards", reflect.TypeOf((*MockCollection)(nil).Shards), ctx, details) +} + +// Truncate mocks base method. +func (m *MockCollection) Truncate(ctx context.Context) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Truncate", ctx) + ret0, _ := ret[0].(error) + return ret0 +} + +// Truncate indicates an expected call of Truncate. +func (mr *MockCollectionMockRecorder) Truncate(ctx any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Truncate", reflect.TypeOf((*MockCollection)(nil).Truncate), ctx) +} + +// UpdateDocument mocks base method. +func (m *MockCollection) UpdateDocument(ctx context.Context, key string, document any) (arangodb.CollectionDocumentUpdateResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateDocument", ctx, key, document) + ret0, _ := ret[0].(arangodb.CollectionDocumentUpdateResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UpdateDocument indicates an expected call of UpdateDocument. +func (mr *MockCollectionMockRecorder) UpdateDocument(ctx, key, document any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateDocument", reflect.TypeOf((*MockCollection)(nil).UpdateDocument), ctx, key, document) +} + +// UpdateDocumentWithOptions mocks base method. +func (m *MockCollection) UpdateDocumentWithOptions(ctx context.Context, key string, document any, options *arangodb.CollectionDocumentUpdateOptions) (arangodb.CollectionDocumentUpdateResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateDocumentWithOptions", ctx, key, document, options) + ret0, _ := ret[0].(arangodb.CollectionDocumentUpdateResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UpdateDocumentWithOptions indicates an expected call of UpdateDocumentWithOptions. +func (mr *MockCollectionMockRecorder) UpdateDocumentWithOptions(ctx, key, document, options any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateDocumentWithOptions", reflect.TypeOf((*MockCollection)(nil).UpdateDocumentWithOptions), ctx, key, document, options) +} + +// UpdateDocuments mocks base method. +func (m *MockCollection) UpdateDocuments(ctx context.Context, documents any) (arangodb.CollectionDocumentUpdateResponseReader, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateDocuments", ctx, documents) + ret0, _ := ret[0].(arangodb.CollectionDocumentUpdateResponseReader) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UpdateDocuments indicates an expected call of UpdateDocuments. +func (mr *MockCollectionMockRecorder) UpdateDocuments(ctx, documents any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateDocuments", reflect.TypeOf((*MockCollection)(nil).UpdateDocuments), ctx, documents) +} + +// UpdateDocumentsWithOptions mocks base method. +func (m *MockCollection) UpdateDocumentsWithOptions(ctx context.Context, documents any, opts *arangodb.CollectionDocumentUpdateOptions) (arangodb.CollectionDocumentUpdateResponseReader, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateDocumentsWithOptions", ctx, documents, opts) + ret0, _ := ret[0].(arangodb.CollectionDocumentUpdateResponseReader) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UpdateDocumentsWithOptions indicates an expected call of UpdateDocumentsWithOptions. +func (mr *MockCollectionMockRecorder) UpdateDocumentsWithOptions(ctx, documents, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateDocumentsWithOptions", reflect.TypeOf((*MockCollection)(nil).UpdateDocumentsWithOptions), ctx, documents, opts) +} diff --git a/pkg/gofr/datasource/arangodb/mock_database.go b/pkg/gofr/datasource/arangodb/mock_database.go new file mode 100644 index 000000000..1693d8cc5 --- /dev/null +++ b/pkg/gofr/datasource/arangodb/mock_database.go @@ -0,0 +1,538 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: database.go +// +// Generated by this command: +// +// mockgen -source=database.go -destination=./mock_database.go -package=arangodb +// + +// Package arangodb is a generated GoMock package. +package arangodb + +import ( + context "context" + reflect "reflect" + + arangodb "github.com/arangodb/go-driver/v2/arangodb" + gomock "go.uber.org/mock/gomock" +) + +// MockArangoDatabase is a mock of database interface. +type MockDatabase struct { + ctrl *gomock.Controller + recorder *MockDatabaseMockRecorder +} + +// MockDatabaseMockRecorder is the mock recorder for MockArangoDatabase. +type MockDatabaseMockRecorder struct { + mock *MockDatabase +} + +// NewMockDatabase creates a new mock instance. +func NewMockDatabase(ctrl *gomock.Controller) *MockDatabase { + mock := &MockDatabase{ctrl: ctrl} + mock.recorder = &MockDatabaseMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockDatabase) EXPECT() *MockDatabaseMockRecorder { + return m.recorder +} + +// Analyzer mocks base method. +func (m *MockDatabase) Analyzer(ctx context.Context, name string) (arangodb.Analyzer, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Analyzer", ctx, name) + ret0, _ := ret[0].(arangodb.Analyzer) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Analyzer indicates an expected call of Analyzer. +func (mr *MockDatabaseMockRecorder) Analyzer(ctx, name any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Analyzer", reflect.TypeOf((*MockDatabase)(nil).Analyzer), ctx, name) +} + +// Analyzers mocks base method. +func (m *MockDatabase) Analyzers(ctx context.Context) (arangodb.AnalyzersResponseReader, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Analyzers", ctx) + ret0, _ := ret[0].(arangodb.AnalyzersResponseReader) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Analyzers indicates an expected call of Analyzers. +func (mr *MockDatabaseMockRecorder) Analyzers(ctx any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Analyzers", reflect.TypeOf((*MockDatabase)(nil).Analyzers), ctx) +} + +// BeginTransaction mocks base method. +func (m *MockDatabase) BeginTransaction(ctx context.Context, cols arangodb.TransactionCollections, opts *arangodb.BeginTransactionOptions) (arangodb.Transaction, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BeginTransaction", ctx, cols, opts) + ret0, _ := ret[0].(arangodb.Transaction) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// BeginTransaction indicates an expected call of BeginTransaction. +func (mr *MockDatabaseMockRecorder) BeginTransaction(ctx, cols, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BeginTransaction", reflect.TypeOf((*MockDatabase)(nil).BeginTransaction), ctx, cols, opts) +} + +// Collection mocks base method. +func (m *MockDatabase) Collection(ctx context.Context, name string) (arangodb.Collection, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Collection", ctx, name) + ret0, _ := ret[0].(arangodb.Collection) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Collection indicates an expected call of Collection. +func (mr *MockDatabaseMockRecorder) Collection(ctx, name any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Collection", reflect.TypeOf((*MockDatabase)(nil).Collection), ctx, name) +} + +// CollectionExists mocks base method. +func (m *MockDatabase) CollectionExists(ctx context.Context, name string) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CollectionExists", ctx, name) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CollectionExists indicates an expected call of CollectionExists. +func (mr *MockDatabaseMockRecorder) CollectionExists(ctx, name any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CollectionExists", reflect.TypeOf((*MockDatabase)(nil).CollectionExists), ctx, name) +} + +// Collections mocks base method. +func (m *MockDatabase) Collections(ctx context.Context) ([]arangodb.Collection, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Collections", ctx) + ret0, _ := ret[0].([]arangodb.Collection) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Collections indicates an expected call of Collections. +func (mr *MockDatabaseMockRecorder) Collections(ctx any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Collections", reflect.TypeOf((*MockDatabase)(nil).Collections), ctx) +} + +// CreateArangoSearchAliasView mocks base method. +func (m *MockDatabase) CreateArangoSearchAliasView(ctx context.Context, name string, options *arangodb.ArangoSearchAliasViewProperties) (arangodb.ArangoSearchViewAlias, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateArangoSearchAliasView", ctx, name, options) + ret0, _ := ret[0].(arangodb.ArangoSearchViewAlias) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateArangoSearchAliasView indicates an expected call of CreateArangoSearchAliasView. +func (mr *MockDatabaseMockRecorder) CreateArangoSearchAliasView(ctx, name, options any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateArangoSearchAliasView", reflect.TypeOf((*MockDatabase)(nil).CreateArangoSearchAliasView), ctx, name, options) +} + +// CreateArangoSearchView mocks base method. +func (m *MockDatabase) CreateArangoSearchView(ctx context.Context, name string, options *arangodb.ArangoSearchViewProperties) (arangodb.ArangoSearchView, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateArangoSearchView", ctx, name, options) + ret0, _ := ret[0].(arangodb.ArangoSearchView) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateArangoSearchView indicates an expected call of CreateArangoSearchView. +func (mr *MockDatabaseMockRecorder) CreateArangoSearchView(ctx, name, options any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateArangoSearchView", reflect.TypeOf((*MockDatabase)(nil).CreateArangoSearchView), ctx, name, options) +} + +// CreateCollection mocks base method. +func (m *MockDatabase) CreateCollection(ctx context.Context, name string, props *arangodb.CreateCollectionProperties) (arangodb.Collection, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateCollection", ctx, name, props) + ret0, _ := ret[0].(arangodb.Collection) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateCollection indicates an expected call of CreateCollection. +func (mr *MockDatabaseMockRecorder) CreateCollection(ctx, name, props any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateCollection", reflect.TypeOf((*MockDatabase)(nil).CreateCollection), ctx, name, props) +} + +// CreateCollectionWithOptions mocks base method. +func (m *MockDatabase) CreateCollectionWithOptions(ctx context.Context, name string, props *arangodb.CreateCollectionProperties, options *arangodb.CreateCollectionOptions) (arangodb.Collection, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateCollectionWithOptions", ctx, name, props, options) + ret0, _ := ret[0].(arangodb.Collection) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateCollectionWithOptions indicates an expected call of CreateCollectionWithOptions. +func (mr *MockDatabaseMockRecorder) CreateCollectionWithOptions(ctx, name, props, options any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateCollectionWithOptions", reflect.TypeOf((*MockDatabase)(nil).CreateCollectionWithOptions), ctx, name, props, options) +} + +// CreateGraph mocks base method. +func (m *MockDatabase) CreateGraph(ctx context.Context, name string, graph *arangodb.GraphDefinition, options *arangodb.CreateGraphOptions) (arangodb.Graph, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateGraph", ctx, name, graph, options) + ret0, _ := ret[0].(arangodb.Graph) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateGraph indicates an expected call of CreateGraph. +func (mr *MockDatabaseMockRecorder) CreateGraph(ctx, name, graph, options any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateGraph", reflect.TypeOf((*MockDatabase)(nil).CreateGraph), ctx, name, graph, options) +} + +// EnsureAnalyzer mocks base method. +func (m *MockDatabase) EnsureAnalyzer(ctx context.Context, analyzer *arangodb.AnalyzerDefinition) (bool, arangodb.Analyzer, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EnsureAnalyzer", ctx, analyzer) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(arangodb.Analyzer) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// EnsureAnalyzer indicates an expected call of EnsureAnalyzer. +func (mr *MockDatabaseMockRecorder) EnsureAnalyzer(ctx, analyzer any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnsureAnalyzer", reflect.TypeOf((*MockDatabase)(nil).EnsureAnalyzer), ctx, analyzer) +} + +// ExplainQuery mocks base method. +func (m *MockDatabase) ExplainQuery(ctx context.Context, query string, bindVars map[string]any, opts *arangodb.ExplainQueryOptions) (arangodb.ExplainQueryResult, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ExplainQuery", ctx, query, bindVars, opts) + ret0, _ := ret[0].(arangodb.ExplainQueryResult) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ExplainQuery indicates an expected call of ExplainQuery. +func (mr *MockDatabaseMockRecorder) ExplainQuery(ctx, query, bindVars, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExplainQuery", reflect.TypeOf((*MockDatabase)(nil).ExplainQuery), ctx, query, bindVars, opts) +} + +// GetCollection mocks base method. +func (m *MockDatabase) GetCollection(ctx context.Context, name string, options *arangodb.GetCollectionOptions) (arangodb.Collection, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetCollection", ctx, name, options) + ret0, _ := ret[0].(arangodb.Collection) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetCollection indicates an expected call of GetCollection. +func (mr *MockDatabaseMockRecorder) GetCollection(ctx, name, options any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCollection", reflect.TypeOf((*MockDatabase)(nil).GetCollection), ctx, name, options) +} + +// GetEdges mocks base method. +func (m *MockDatabase) GetEdges(ctx context.Context, name, vertex string, options *arangodb.GetEdgesOptions) ([]arangodb.EdgeDetails, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetEdges", ctx, name, vertex, options) + ret0, _ := ret[0].([]arangodb.EdgeDetails) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetEdges indicates an expected call of GetEdges. +func (mr *MockDatabaseMockRecorder) GetEdges(ctx, name, vertex, options any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEdges", reflect.TypeOf((*MockDatabase)(nil).GetEdges), ctx, name, vertex, options) +} + +// Graph mocks base method. +func (m *MockDatabase) Graph(ctx context.Context, name string, options *arangodb.GetGraphOptions) (arangodb.Graph, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Graph", ctx, name, options) + ret0, _ := ret[0].(arangodb.Graph) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Graph indicates an expected call of Graph. +func (mr *MockDatabaseMockRecorder) Graph(ctx, name, options any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Graph", reflect.TypeOf((*MockDatabase)(nil).Graph), ctx, name, options) +} + +// GraphExists mocks base method. +func (m *MockDatabase) GraphExists(ctx context.Context, name string) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GraphExists", ctx, name) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GraphExists indicates an expected call of GraphExists. +func (mr *MockDatabaseMockRecorder) GraphExists(ctx, name any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GraphExists", reflect.TypeOf((*MockDatabase)(nil).GraphExists), ctx, name) +} + +// Graphs mocks base method. +func (m *MockDatabase) Graphs(ctx context.Context) (arangodb.GraphsResponseReader, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Graphs", ctx) + ret0, _ := ret[0].(arangodb.GraphsResponseReader) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Graphs indicates an expected call of Graphs. +func (mr *MockDatabaseMockRecorder) Graphs(ctx any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Graphs", reflect.TypeOf((*MockDatabase)(nil).Graphs), ctx) +} + +// Info mocks base method. +func (m *MockDatabase) Info(ctx context.Context) (arangodb.DatabaseInfo, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Info", ctx) + ret0, _ := ret[0].(arangodb.DatabaseInfo) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Info indicates an expected call of Info. +func (mr *MockDatabaseMockRecorder) Info(ctx any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Info", reflect.TypeOf((*MockDatabase)(nil).Info), ctx) +} + +// ListTransactions mocks base method. +func (m *MockDatabase) ListTransactions(ctx context.Context) ([]arangodb.Transaction, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListTransactions", ctx) + ret0, _ := ret[0].([]arangodb.Transaction) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListTransactions indicates an expected call of ListTransactions. +func (mr *MockDatabaseMockRecorder) ListTransactions(ctx any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListTransactions", reflect.TypeOf((*MockDatabase)(nil).ListTransactions), ctx) +} + +// ListTransactionsWithStatuses mocks base method. +func (m *MockDatabase) ListTransactionsWithStatuses(ctx context.Context, statuses ...arangodb.TransactionStatus) ([]arangodb.Transaction, error) { + m.ctrl.T.Helper() + varargs := []any{ctx} + for _, a := range statuses { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ListTransactionsWithStatuses", varargs...) + ret0, _ := ret[0].([]arangodb.Transaction) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListTransactionsWithStatuses indicates an expected call of ListTransactionsWithStatuses. +func (mr *MockDatabaseMockRecorder) ListTransactionsWithStatuses(ctx any, statuses ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx}, statuses...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListTransactionsWithStatuses", reflect.TypeOf((*MockDatabase)(nil).ListTransactionsWithStatuses), varargs...) +} + +// Name mocks base method. +func (m *MockDatabase) Name() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Name") + ret0, _ := ret[0].(string) + return ret0 +} + +// Name indicates an expected call of Name. +func (mr *MockDatabaseMockRecorder) Name() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Name", reflect.TypeOf((*MockDatabase)(nil).Name)) +} + +// Query mocks base method. +func (m *MockDatabase) Query(ctx context.Context, query string, opts *arangodb.QueryOptions) (arangodb.Cursor, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Query", ctx, query, opts) + ret0, _ := ret[0].(arangodb.Cursor) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Query indicates an expected call of Query. +func (mr *MockDatabaseMockRecorder) Query(ctx, query, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Query", reflect.TypeOf((*MockDatabase)(nil).Query), ctx, query, opts) +} + +// QueryBatch mocks base method. +func (m *MockDatabase) QueryBatch(ctx context.Context, query string, opts *arangodb.QueryOptions, result any) (arangodb.CursorBatch, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "QueryBatch", ctx, query, opts, result) + ret0, _ := ret[0].(arangodb.CursorBatch) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// QueryBatch indicates an expected call of QueryBatch. +func (mr *MockDatabaseMockRecorder) QueryBatch(ctx, query, opts, result any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryBatch", reflect.TypeOf((*MockDatabase)(nil).QueryBatch), ctx, query, opts, result) +} + +// Remove mocks base method. +func (m *MockDatabase) Remove(ctx context.Context) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Remove", ctx) + ret0, _ := ret[0].(error) + return ret0 +} + +// Remove indicates an expected call of Remove. +func (mr *MockDatabaseMockRecorder) Remove(ctx any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Remove", reflect.TypeOf((*MockDatabase)(nil).Remove), ctx) +} + +// Transaction mocks base method. +func (m *MockDatabase) Transaction(ctx context.Context, id arangodb.TransactionID) (arangodb.Transaction, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Transaction", ctx, id) + ret0, _ := ret[0].(arangodb.Transaction) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Transaction indicates an expected call of Transaction. +func (mr *MockDatabaseMockRecorder) Transaction(ctx, id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Transaction", reflect.TypeOf((*MockDatabase)(nil).Transaction), ctx, id) +} + +// TransactionJS mocks base method. +func (m *MockDatabase) TransactionJS(ctx context.Context, options arangodb.TransactionJSOptions) (any, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "TransactionJS", ctx, options) + ret0, _ := ret[0].(any) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// TransactionJS indicates an expected call of TransactionJS. +func (mr *MockDatabaseMockRecorder) TransactionJS(ctx, options any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TransactionJS", reflect.TypeOf((*MockDatabase)(nil).TransactionJS), ctx, options) +} + +// ValidateQuery mocks base method. +func (m *MockDatabase) ValidateQuery(ctx context.Context, query string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ValidateQuery", ctx, query) + ret0, _ := ret[0].(error) + return ret0 +} + +// ValidateQuery indicates an expected call of ValidateQuery. +func (mr *MockDatabaseMockRecorder) ValidateQuery(ctx, query any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateQuery", reflect.TypeOf((*MockDatabase)(nil).ValidateQuery), ctx, query) +} + +// View mocks base method. +func (m *MockDatabase) View(ctx context.Context, name string) (arangodb.View, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "View", ctx, name) + ret0, _ := ret[0].(arangodb.View) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// View indicates an expected call of View. +func (mr *MockDatabaseMockRecorder) View(ctx, name any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "View", reflect.TypeOf((*MockDatabase)(nil).View), ctx, name) +} + +// ViewExists mocks base method. +func (m *MockDatabase) ViewExists(ctx context.Context, name string) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ViewExists", ctx, name) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ViewExists indicates an expected call of ViewExists. +func (mr *MockDatabaseMockRecorder) ViewExists(ctx, name any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ViewExists", reflect.TypeOf((*MockDatabase)(nil).ViewExists), ctx, name) +} + +// Views mocks base method. +func (m *MockDatabase) Views(ctx context.Context) (arangodb.ViewsResponseReader, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Views", ctx) + ret0, _ := ret[0].(arangodb.ViewsResponseReader) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Views indicates an expected call of Views. +func (mr *MockDatabaseMockRecorder) Views(ctx any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Views", reflect.TypeOf((*MockDatabase)(nil).Views), ctx) +} + +// ViewsAll mocks base method. +func (m *MockDatabase) ViewsAll(ctx context.Context) ([]arangodb.View, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ViewsAll", ctx) + ret0, _ := ret[0].([]arangodb.View) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ViewsAll indicates an expected call of ViewsAll. +func (mr *MockDatabaseMockRecorder) ViewsAll(ctx any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ViewsAll", reflect.TypeOf((*MockDatabase)(nil).ViewsAll), ctx) +} + +// WithTransaction mocks base method. +func (m *MockDatabase) WithTransaction(ctx context.Context, cols arangodb.TransactionCollections, opts *arangodb.BeginTransactionOptions, commitOptions *arangodb.CommitTransactionOptions, abortOptions *arangodb.AbortTransactionOptions, w arangodb.TransactionWrap) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "WithTransaction", ctx, cols, opts, commitOptions, abortOptions, w) + ret0, _ := ret[0].(error) + return ret0 +} + +// WithTransaction indicates an expected call of WithTransaction. +func (mr *MockDatabaseMockRecorder) WithTransaction(ctx, cols, opts, commitOptions, abortOptions, w any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WithTransaction", reflect.TypeOf((*MockDatabase)(nil).WithTransaction), ctx, cols, opts, commitOptions, abortOptions, w) +} diff --git a/pkg/gofr/datasource/arangodb/mock_interfaces.go b/pkg/gofr/datasource/arangodb/mock_interfaces.go new file mode 100644 index 000000000..aa958e59f --- /dev/null +++ b/pkg/gofr/datasource/arangodb/mock_interfaces.go @@ -0,0 +1,724 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: interface.go +// +// Generated by this command: +// +// mockgen -source=interface.go -destination=mock_interfaces.go -package=arangodb +// + +// Package arangodb is a generated GoMock package. +package arangodb + +import ( + "context" + "fmt" + "github.com/arangodb/go-driver/v2/connection" + reflect "reflect" + "strings" + + arangodb "github.com/arangodb/go-driver/v2/arangodb" + gomock "go.uber.org/mock/gomock" +) + +// MockArangoMockRecorder is the mock recorder for MockArango. +type MockArangoMockRecorder struct { + mock *MockArango +} + +// NewMockArango creates a new mock instance. +func NewMockArango(ctrl *gomock.Controller) *MockArango { + mock := &MockArango{ctrl: ctrl} + mock.recorder = &MockArangoMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockArango) EXPECT() *MockArangoMockRecorder { + return m.recorder +} + +// MockArango is a mock of ArangoDB interface. +type MockArango struct { + ctrl *gomock.Controller + recorder *MockArangoMockRecorder +} + +func (m *MockArango) Connection() connection.Connection { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Connection") + return ret[0].(connection.Connection) +} + +func (mr *MockArangoMockRecorder) Connection() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Connection", reflect.TypeOf((*MockArango)(nil).Connection)) +} + +func (m *MockArango) Get(ctx context.Context, output interface{}, urlParts ...string) (connection.Response, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", ctx, output, urlParts) + return ret[0].(connection.Response), ret[1].(error) +} + +func (mr *MockArangoMockRecorder) Get(ctx, output interface{}, urlParts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockArango)(nil).Get), ctx, output, urlParts) +} + +func (m *MockArango) Post(ctx context.Context, output, input interface{}, urlParts ...string) (connection.Response, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Post", ctx, output, input, urlParts) + return ret[0].(connection.Response), ret[1].(error) +} + +func (mr *MockArangoMockRecorder) Post(ctx, output, input interface{}, urlParts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Post", reflect.TypeOf((*MockArango)(nil).Post), ctx, output, input, urlParts) +} + +func (m *MockArango) Put(ctx context.Context, output, input interface{}, urlParts ...string) (connection.Response, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Put", ctx, output, input, urlParts) + return ret[0].(connection.Response), ret[1].(error) +} + +func (mr *MockArangoMockRecorder) Put(ctx, output, input interface{}, urlParts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Put", reflect.TypeOf((*MockArango)(nil).Put), ctx, output, input, urlParts) +} + +func (m *MockArango) Delete(ctx context.Context, output interface{}, urlParts ...string) (connection.Response, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Delete", ctx, output, urlParts) + return ret[0].(connection.Response), ret[1].(error) +} + +func (mr *MockArangoMockRecorder) Delete(ctx, output interface{}, urlParts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockArango)(nil).Delete), ctx, output, urlParts) +} + +func (m *MockArango) Head(ctx context.Context, output interface{}, urlParts ...string) (connection.Response, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Head", ctx, output, urlParts) + return ret[0].(connection.Response), ret[1].(error) +} + +func (mr *MockArangoMockRecorder) Head(ctx, output interface{}, urlParts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Head", reflect.TypeOf((*MockArango)(nil).Head), ctx, output, urlParts) +} + +func (m *MockArango) Patch(ctx context.Context, output, input interface{}, urlParts ...string) (connection.Response, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Patch", ctx, output, input, urlParts) + return ret[0].(connection.Response), ret[1].(error) +} + +func (mr *MockArangoMockRecorder) Patch(ctx, output, input interface{}, urlParts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Patch", reflect.TypeOf((*MockArango)(nil).Patch), ctx, output, input, urlParts) +} + +func (m *MockArango) CreateDatabase(ctx context.Context, name string, options *arangodb.CreateDatabaseOptions) (arangodb.Database, error) { + db := NewMockDatabase(m.ctrl) + if strings.Contains(name, "error") { + return nil,fmt.Errorf("database not found") + } + + return db,nil +} + +func (mr *MockArangoMockRecorder) CreateDatabase(ctx, name, options interface{}) *gomock.Call { + return mr.CreateDB(ctx, name) +} + +func (m *MockArango) GetDatabase(ctx context.Context, name string, options *arangodb.GetDatabaseOptions) (arangodb.Database, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetDatabase", ctx, name, options) + return ret[0].(arangodb.Database), ret[1].(error) +} + +func (mr *MockArangoMockRecorder) GetDatabase(ctx, name, options interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDatabase", reflect.TypeOf((*MockArango)(nil).GetDatabase), ctx, name, options) +} + +func (m *MockArango) DatabaseExists(ctx context.Context, name string) (bool, error) { + //TODO implement me + panic("implement me") +} + +func (m *MockArango) AccessibleDatabases(ctx context.Context) ([]arangodb.Database, error) { + //TODO implement me + panic("implement me") +} + + +func (m *MockArango) UserExists(ctx context.Context, name string) (bool, error) { + //TODO implement me + panic("implement me") +} + +func (m *MockArango) Users(ctx context.Context) ([]arangodb.User, error) { + //TODO implement me + panic("implement me") +} + +func (m *MockArango) ReplaceUser(ctx context.Context, name string, options *arangodb.UserOptions) (arangodb.User, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ReplaceUser", ctx, name, options) + return ret[0].(arangodb.User), ret[1].(error) +} + +// Mock recorder method for setting up expectations +func (mr *MockArangoMockRecorder) ReplaceUser(ctx, name, options interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplaceUser", reflect.TypeOf((*MockArango)(nil).ReplaceUser), ctx, name, options) +} + +func (m *MockArango) UpdateUser(ctx context.Context, name string, options *arangodb.UserOptions) (arangodb.User, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateUser", ctx, name, options) + return ret[0].(arangodb.User), ret[1].(error) +} + +// Mock recorder method for setting up expectations +func (mr *MockArangoMockRecorder) UpdateUser(ctx, name, options interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateUser", reflect.TypeOf((*MockArango)(nil).UpdateUser), ctx, name, options) +} + + +func (m *MockArango) RemoveUser(ctx context.Context, name string) error { + return m.DropUser(ctx, name) +} + +func (m *MockArango) VersionWithOptions(ctx context.Context, opts *arangodb.GetVersionOptions) (arangodb.VersionInfo, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "VersionWithOptions", ctx, opts) + return ret[0].(arangodb.VersionInfo), ret[1].(error) +} + +// Mock recorder method for setting up expectations +func (mr *MockArangoMockRecorder) VersionWithOptions(ctx, opts interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VersionWithOptions", reflect.TypeOf((*MockArango)(nil).VersionWithOptions), ctx, opts) +} + + +func (m *MockArango) ServerRole(ctx context.Context) (arangodb.ServerRole, error) { + //TODO implement me + panic("implement me") +} + +func (m *MockArango) ServerID(ctx context.Context) (string, error) { + //TODO implement me + panic("implement me") +} + +func (m *MockArango) GetLogLevels(ctx context.Context, opts *arangodb.LogLevelsGetOptions) (arangodb.LogLevels, error) { + //TODO implement me + panic("implement me") +} + +func (m *MockArango) SetLogLevels(ctx context.Context, logLevels arangodb.LogLevels, opts *arangodb.LogLevelsSetOptions) error { + //TODO implement me + panic("implement me") +} + +func (m *MockArango) BackupCreate(ctx context.Context, opt *arangodb.BackupCreateOptions) (arangodb.BackupResponse, error) { + //TODO implement me + panic("implement me") +} + +func (m *MockArango) BackupRestore(ctx context.Context, id string) (arangodb.BackupRestoreResponse, error) { + //TODO implement me + panic("implement me") +} + +func (m *MockArango) BackupDelete(ctx context.Context, id string) error { + //TODO implement me + panic("implement me") +} + +func (m *MockArango) BackupList(ctx context.Context, opt *arangodb.BackupListOptions) (arangodb.ListBackupsResponse, error) { + //TODO implement me + panic("implement me") +} + +func (m *MockArango) BackupUpload(ctx context.Context, backupId string, remoteRepository string, config interface{}) (arangodb.TransferMonitor, error) { + //TODO implement me + panic("implement me") +} + +func (m *MockArango) BackupDownload(ctx context.Context, backupId string, remoteRepository string, config interface{}) (arangodb.TransferMonitor, error) { + //TODO implement me + panic("implement me") +} + +func (m *MockArango) TransferMonitor(jobId string, transferType arangodb.TransferType) (arangodb.TransferMonitor, error) { + //TODO implement me + panic("implement me") +} + +func (m *MockArango) GetLicense(ctx context.Context) (arangodb.License, error) { + //TODO implement me + panic("implement me") +} + +func (m *MockArango) SetLicense(ctx context.Context, license string, force bool) error { + //TODO implement me + panic("implement me") +} + +func (m *MockArango) Health(ctx context.Context) (arangodb.ClusterHealth, error) { + //TODO implement me + panic("implement me") +} + +func (m *MockArango) DatabaseInventory(ctx context.Context, dbName string) (arangodb.DatabaseInventory, error) { + //TODO implement me + panic("implement me") +} + +func (m *MockArango) MoveShard(ctx context.Context, col arangodb.Collection, shard arangodb.ShardID, fromServer, toServer arangodb.ServerID) (string, error) { + //TODO implement me + panic("implement me") +} + +func (m *MockArango) CleanOutServer(ctx context.Context, serverID arangodb.ServerID) (string, error) { + //TODO implement me + panic("implement me") +} + +func (m *MockArango) ResignServer(ctx context.Context, serverID arangodb.ServerID) (string, error) { + //TODO implement me + panic("implement me") +} + +func (m *MockArango) NumberOfServers(ctx context.Context) (arangodb.NumberOfServersResponse, error) { + //TODO implement me + panic("implement me") +} + +func (m *MockArango) IsCleanedOut(ctx context.Context, serverID arangodb.ServerID) (bool, error) { + //TODO implement me + panic("implement me") +} + +func (m *MockArango) RemoveServer(ctx context.Context, serverID arangodb.ServerID) error { + //TODO implement me + panic("implement me") +} + +func (m *MockArango) ServerMode(ctx context.Context) (arangodb.ServerMode, error) { + //TODO implement me + panic("implement me") +} + +func (m *MockArango) SetServerMode(ctx context.Context, mode arangodb.ServerMode) error { + //TODO implement me + panic("implement me") +} + +func (m *MockArango) CheckAvailability(ctx context.Context, serverEndpoint string) error { + //TODO implement me + panic("implement me") +} + +func (m *MockArango) AsyncJobList(ctx context.Context, jobType arangodb.AsyncJobStatusType, opts *arangodb.AsyncJobListOptions) ([]string, error) { + //TODO implement me + panic("implement me") +} + +func (m *MockArango) AsyncJobStatus(ctx context.Context, jobID string) (arangodb.AsyncJobStatusType, error) { + //TODO implement me + panic("implement me") +} + +func (m *MockArango) AsyncJobCancel(ctx context.Context, jobID string) (bool, error) { + //TODO implement me + panic("implement me") +} + +func (m *MockArango) AsyncJobDelete(ctx context.Context, deleteType arangodb.AsyncJobDeleteType, opts *arangodb.AsyncJobDeleteOptions) (bool, error) { + //TODO implement me + panic("implement me") +} + +// Connect mocks base method. +func (m *MockArango) Connect() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Connect") +} + +// Connect indicates an expected call of Connect. +func (mr *MockArangoMockRecorder) Connect() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Connect", reflect.TypeOf((*MockArango)(nil).Connect)) +} + +// CreateCollection mocks base method. +func (m *MockArango) CreateCollection(ctx context.Context, database, collection string, isEdge bool) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateCollection", ctx, database, collection, isEdge) + ret0, _ := ret[0].(error) + return ret0 +} + +// CreateCollection indicates an expected call of CreateCollection. +func (mr *MockArangoMockRecorder) CreateCollection(ctx, database, collection, isEdge any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateCollection", reflect.TypeOf((*MockArango)(nil).CreateCollection), ctx, database, collection, isEdge) +} + +// CreateDB mocks base method. +func (m *MockArango) CreateDB(ctx context.Context, database string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateDB", ctx, database) + ret0, _ := ret[0].(error) + return ret0 +} + +// CreateDB indicates an expected call of CreateDB. +func (mr *MockArangoMockRecorder) CreateDB(ctx, database any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateDB", reflect.TypeOf((*MockArango)(nil).CreateDB), ctx, database) +} + +// CreateDocument mocks base method. +func (m *MockArango) CreateDocument(ctx context.Context, dbName, collectionName string, document any) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateDocument", ctx, dbName, collectionName, document) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateDocument indicates an expected call of CreateDocument. +func (mr *MockArangoMockRecorder) CreateDocument(ctx, dbName, collectionName, document any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateDocument", reflect.TypeOf((*MockArango)(nil).CreateDocument), ctx, dbName, collectionName, document) +} + +// CreateEdgeDocument mocks base method. +func (m *MockArango) CreateEdgeDocument(ctx context.Context, dbName, collectionName, from, to string, document any) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateEdgeDocument", ctx, dbName, collectionName, from, to, document) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateEdgeDocument indicates an expected call of CreateEdgeDocument. +func (mr *MockArangoMockRecorder) CreateEdgeDocument(ctx, dbName, collectionName, from, to, document any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateEdgeDocument", reflect.TypeOf((*MockArango)(nil).CreateEdgeDocument), ctx, dbName, collectionName, from, to, document) +} + +// CreateGraph mocks base method. +func (m *MockArango) CreateGraph(ctx context.Context, database, graph string, edgeDefinitions any) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateGraph", ctx, database, graph, edgeDefinitions) + ret0, _ := ret[0].(error) + return ret0 +} + +// CreateGraph indicates an expected call of CreateGraph. +func (mr *MockArangoMockRecorder) CreateGraph(ctx, database, graph, edgeDefinitions any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateGraph", reflect.TypeOf((*MockArango)(nil).CreateGraph), ctx, database, graph, edgeDefinitions) +} + +func (m *MockArango) CreateUser(ctx context. Context, name string, options *arangodb.UserOptions) (arangodb.User, error){ + return nil,m.AddUser(ctx, name, options) +} + +// CreateUser mocks base method. +func (m *MockArango) AddUser(ctx context.Context, username string, options any) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateUser", ctx, username, options) + ret0, _ := ret[0].(error) + return ret0 +} + +// CreateUser indicates an expected call of CreateUser. +func (mr *MockArangoMockRecorder) AddUser(ctx, username, options any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateUser", reflect.TypeOf((*MockArango)(nil).CreateUser), ctx, username, options) +} + +// Database mocks base method. +func (m *MockArango) Database(ctx context.Context, name string) (arangodb.Database, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "database", ctx, name) + ret0, _ := ret[0].(arangodb.Database) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Database indicates an expected call of Database. +func (mr *MockArangoMockRecorder) Database(ctx, name any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "database", reflect.TypeOf((*MockArango)(nil).Database), ctx, name) +} + +// Databases mocks base method. +func (m *MockArango) Databases(ctx context.Context) ([]arangodb.Database, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "databases", ctx) + ret0, _ := ret[0].([]arangodb.Database) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Databases indicates an expected call of Databases. +func (mr *MockArangoMockRecorder) Databases(ctx any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "databases", reflect.TypeOf((*MockArango)(nil).Databases), ctx) +} + +// DeleteDocument mocks base method. +func (m *MockArango) DeleteDocument(ctx context.Context, dbName, collectionName, documentID string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteDocument", ctx, dbName, collectionName, documentID) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteDocument indicates an expected call of DeleteDocument. +func (mr *MockArangoMockRecorder) DeleteDocument(ctx, dbName, collectionName, documentID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteDocument", reflect.TypeOf((*MockArango)(nil).DeleteDocument), ctx, dbName, collectionName, documentID) +} + +// DropCollection mocks base method. +func (m *MockArango) DropCollection(ctx context.Context, database, collection string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DropCollection", ctx, database, collection) + ret0, _ := ret[0].(error) + return ret0 +} + +// DropCollection indicates an expected call of DropCollection. +func (mr *MockArangoMockRecorder) DropCollection(ctx, database, collection any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DropCollection", reflect.TypeOf((*MockArango)(nil).DropCollection), ctx, database, collection) +} + +// DropDB mocks base method. +func (m *MockArango) DropDB(ctx context.Context, database string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DropDB", ctx, database) + ret0, _ := ret[0].(error) + return ret0 +} + +// DropDB indicates an expected call of DropDB. +func (mr *MockArangoMockRecorder) DropDB(ctx, database any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DropDB", reflect.TypeOf((*MockArango)(nil).DropDB), ctx, database) +} + +// DropGraph mocks base method. +func (m *MockArango) DropGraph(ctx context.Context, database, graph string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DropGraph", ctx, database, graph) + ret0, _ := ret[0].(error) + return ret0 +} + +// DropGraph indicates an expected call of DropGraph. +func (mr *MockArangoMockRecorder) DropGraph(ctx, database, graph any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DropGraph", reflect.TypeOf((*MockArango)(nil).DropGraph), ctx, database, graph) +} + +// DropUser mocks base method. +func (m *MockArango) DropUser(ctx context.Context, username string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DropUser", ctx, username) + ret0, _ := ret[0].(error) + return ret0 +} + +// DropUser indicates an expected call of DropUser. +func (mr *MockArangoMockRecorder) DropUser(ctx, username any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DropUser", reflect.TypeOf((*MockArango)(nil).DropUser), ctx, username) +} + +// GetDocument mocks base method. +func (m *MockArango) GetDocument(ctx context.Context, dbName, collectionName, documentID string, result any) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetDocument", ctx, dbName, collectionName, documentID, result) + ret0, _ := ret[0].(error) + return ret0 +} + +// GetDocument indicates an expected call of GetDocument. +func (mr *MockArangoMockRecorder) GetDocument(ctx, dbName, collectionName, documentID, result any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDocument", reflect.TypeOf((*MockArango)(nil).GetDocument), ctx, dbName, collectionName, documentID, result) +} + +// GrantCollection mocks base method. +func (m *MockArango) GrantCollection(ctx context.Context, database, collection, username, permission string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GrantCollection", ctx, database, collection, username, permission) + ret0, _ := ret[0].(error) + return ret0 +} + +// GrantCollection indicates an expected call of GrantCollection. +func (mr *MockArangoMockRecorder) GrantCollection(ctx, database, collection, username, permission any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GrantCollection", reflect.TypeOf((*MockArango)(nil).GrantCollection), ctx, database, collection, username, permission) +} + +// GrantDB mocks base method. +func (m *MockArango) GrantDB(ctx context.Context, database, username, permission string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GrantDB", ctx, database, username, permission) + ret0, _ := ret[0].(error) + return ret0 +} + +// GrantDB indicates an expected call of GrantDB. +func (mr *MockArangoMockRecorder) GrantDB(ctx, database, username, permission any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GrantDB", reflect.TypeOf((*MockArango)(nil).GrantDB), ctx, database, username, permission) +} + +// HealthCheck mocks base method. +func (m *MockArango) HealthCheck(ctx context.Context) (any, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HealthCheck", ctx) + ret0, _ := ret[0].(any) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// HealthCheck indicates an expected call of HealthCheck. +func (mr *MockArangoMockRecorder) HealthCheck(ctx any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HealthCheck", reflect.TypeOf((*MockArango)(nil).HealthCheck), ctx) +} + +// ListCollections mocks base method. +func (m *MockArango) ListCollections(ctx context.Context, database string) ([]string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListCollections", ctx, database) + ret0, _ := ret[0].([]string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListCollections indicates an expected call of ListCollections. +func (mr *MockArangoMockRecorder) ListCollections(ctx, database any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListCollections", reflect.TypeOf((*MockArango)(nil).ListCollections), ctx, database) +} + +// ListDBs mocks base method. +func (m *MockArango) ListDBs(ctx context.Context) ([]string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListDBs", ctx) + ret0, _ := ret[0].([]string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListDBs indicates an expected call of ListDBs. +func (mr *MockArangoMockRecorder) ListDBs(ctx any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListDBs", reflect.TypeOf((*MockArango)(nil).ListDBs), ctx) +} + +// ListGraphs mocks base method. +func (m *MockArango) ListGraphs(ctx context.Context, database string) ([]string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListGraphs", ctx, database) + ret0, _ := ret[0].([]string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListGraphs indicates an expected call of ListGraphs. +func (mr *MockArangoMockRecorder) ListGraphs(ctx, database any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListGraphs", reflect.TypeOf((*MockArango)(nil).ListGraphs), ctx, database) +} + +// Query mocks base method. +func (m *MockArango) Query(ctx context.Context, dbName, query string, bindVars map[string]any, result any) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Query", ctx, dbName, query, bindVars, result) + ret0, _ := ret[0].(error) + return ret0 +} + +// Query indicates an expected call of Query. +func (mr *MockArangoMockRecorder) Query(ctx, dbName, query, bindVars, result any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Query", reflect.TypeOf((*MockArango)(nil).Query), ctx, dbName, query, bindVars, result) +} + +// TruncateCollection mocks base method. +func (m *MockArango) TruncateCollection(ctx context.Context, database, collection string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "TruncateCollection", ctx, database, collection) + ret0, _ := ret[0].(error) + return ret0 +} + +// TruncateCollection indicates an expected call of TruncateCollection. +func (mr *MockArangoMockRecorder) TruncateCollection(ctx, database, collection any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TruncateCollection", reflect.TypeOf((*MockArango)(nil).TruncateCollection), ctx, database, collection) +} + +// UpdateDocument mocks base method. +func (m *MockArango) UpdateDocument(ctx context.Context, dbName, collectionName, documentID string, document any) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateDocument", ctx, dbName, collectionName, documentID, document) + ret0, _ := ret[0].(error) + return ret0 +} + +// UpdateDocument indicates an expected call of UpdateDocument. +func (mr *MockArangoMockRecorder) UpdateDocument(ctx, dbName, collectionName, documentID, document any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateDocument", reflect.TypeOf((*MockArango)(nil).UpdateDocument), ctx, dbName, collectionName, documentID, document) +} + +// User mocks base method. +func (m *MockArango) User(ctx context.Context, username string) (arangodb.User, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "user", ctx, username) + ret0, _ := ret[0].(arangodb.User) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// User indicates an expected call of User. +func (mr *MockArangoMockRecorder) User(ctx, username any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "user", reflect.TypeOf((*MockArango)(nil).User), ctx, username) +} + +// Version mocks base method. +func (m *MockArango) Version(ctx context.Context) (arangodb.VersionInfo, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "version", ctx) + ret0, _ := ret[0].(arangodb.VersionInfo) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Version indicates an expected call of Version. +func (mr *MockArangoMockRecorder) Version(ctx any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "version", reflect.TypeOf((*MockArango)(nil).Version), ctx) +} diff --git a/pkg/gofr/datasource/arangodb/mock_logger.go b/pkg/gofr/datasource/arangodb/mock_logger.go new file mode 100644 index 000000000..50450314b --- /dev/null +++ b/pkg/gofr/datasource/arangodb/mock_logger.go @@ -0,0 +1,106 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: logger.go +// +// Generated by this command: +// +// mockgen -source=logger.go -destination=mock_logger.go -package=arangodb +// + +// Package arangodb is a generated GoMock package. +package arangodb + +import ( + reflect "reflect" + + gomock "go.uber.org/mock/gomock" +) + +// MockLogger is a mock of Logger interface. +type MockLogger struct { + ctrl *gomock.Controller + recorder *MockLoggerMockRecorder +} + +// MockLoggerMockRecorder is the mock recorder for MockLogger. +type MockLoggerMockRecorder struct { + mock *MockLogger +} + +// NewMockLogger creates a new mock instance. +func NewMockLogger(ctrl *gomock.Controller) *MockLogger { + mock := &MockLogger{ctrl: ctrl} + mock.recorder = &MockLoggerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockLogger) EXPECT() *MockLoggerMockRecorder { + return m.recorder +} + +// Debug mocks base method. +func (m *MockLogger) Debug(args ...any) { + m.ctrl.T.Helper() + varargs := []any{} + for _, a := range args { + varargs = append(varargs, a) + } + m.ctrl.Call(m, "Debug", varargs...) +} + +// Debug indicates an expected call of Debug. +func (mr *MockLoggerMockRecorder) Debug(args ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Debug", reflect.TypeOf((*MockLogger)(nil).Debug), args...) +} + +// Debugf mocks base method. +func (m *MockLogger) Debugf(pattern string, args ...any) { + m.ctrl.T.Helper() + varargs := []any{pattern} + for _, a := range args { + varargs = append(varargs, a) + } + m.ctrl.Call(m, "Debugf", varargs...) +} + +// Debugf indicates an expected call of Debugf. +func (mr *MockLoggerMockRecorder) Debugf(pattern any, args ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{pattern}, args...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Debugf", reflect.TypeOf((*MockLogger)(nil).Debugf), varargs...) +} + +// Errorf mocks base method. +func (m *MockLogger) Errorf(pattern string, args ...any) { + m.ctrl.T.Helper() + varargs := []any{pattern} + for _, a := range args { + varargs = append(varargs, a) + } + m.ctrl.Call(m, "Errorf", varargs...) +} + +// Errorf indicates an expected call of Errorf. +func (mr *MockLoggerMockRecorder) Errorf(pattern any, args ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{pattern}, args...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Errorf", reflect.TypeOf((*MockLogger)(nil).Errorf), varargs...) +} + +// Logf mocks base method. +func (m *MockLogger) Logf(pattern string, args ...any) { + m.ctrl.T.Helper() + varargs := []any{pattern} + for _, a := range args { + varargs = append(varargs, a) + } + m.ctrl.Call(m, "Logf", varargs...) +} + +// Logf indicates an expected call of Logf. +func (mr *MockLoggerMockRecorder) Logf(pattern any, args ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{pattern}, args...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Logf", reflect.TypeOf((*MockLogger)(nil).Logf), varargs...) +} diff --git a/pkg/gofr/datasource/arangodb/mock_metrics.go b/pkg/gofr/datasource/arangodb/mock_metrics.go new file mode 100644 index 000000000..2ca4023ea --- /dev/null +++ b/pkg/gofr/datasource/arangodb/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=arangodb +// + +// Package arangodb is a generated GoMock package. +package arangodb + +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/datasource/arangodb/mock_user.go b/pkg/gofr/datasource/arangodb/mock_user.go new file mode 100644 index 000000000..7e2a999cc --- /dev/null +++ b/pkg/gofr/datasource/arangodb/mock_user.go @@ -0,0 +1,59 @@ +package arangodb + +import ( + "context" + + "github.com/arangodb/go-driver/v2/arangodb" + "go.uber.org/mock/gomock" +) + +// MockUser implements the complete arangodb.user interface. +type MockUser struct { + ctrl *gomock.Controller + name string + active bool +} + +func NewMockUser(ctrl *gomock.Controller) *MockUser { + return &MockUser{ + ctrl: ctrl, + name: "testUser", + active: true, + } +} + +func (m *MockUser) Name() string { return m.name } +func (m *MockUser) IsActive() bool { return m.active } +func (*MockUser) Extra(any) error { return nil } + +func (*MockUser) AccessibleDatabases(context.Context) (map[string]arangodb.Grant, error) { + return nil, nil +} + +func (*MockUser) AccessibleDatabasesFull(context.Context) (map[string]arangodb.DatabasePermissions, error) { + return nil, nil +} + +func (*MockUser) GetDatabaseAccess(context.Context, string) (arangodb.Grant, error) { + return arangodb.GrantNone, nil +} + +func (*MockUser) GetCollectionAccess(context.Context, string, string) (arangodb.Grant, error) { + return arangodb.GrantNone, nil +} + +func (*MockUser) SetDatabaseAccess(context.Context, string, arangodb.Grant) error { + return nil +} + +func (*MockUser) SetCollectionAccess(context.Context, string, string, arangodb.Grant) error { + return nil +} + +func (*MockUser) RemoveDatabaseAccess(context.Context, string) error { + return nil +} + +func (*MockUser) RemoveCollectionAccess(context.Context, string, string) error { + return nil +} diff --git a/pkg/gofr/external_db.go b/pkg/gofr/external_db.go index e8cc5112a..a6bda92ba 100644 --- a/pkg/gofr/external_db.go +++ b/pkg/gofr/external_db.go @@ -145,6 +145,7 @@ func (a *App) AddOpenTSDB(db container.OpenTSDBProvider) { a.container.OpenTSDB = db } +// AddScyllaDB sets the ScyllaDB datasource in the app's container. func (a *App) AddScyllaDB(db container.ScyllaDBProvider) { // Create the ScyllaDB client with the provided configuration db.UseLogger(a.Logger()) @@ -156,6 +157,23 @@ func (a *App) AddScyllaDB(db container.ScyllaDBProvider) { a.container.ScyllaDB = db } +// AddArangoDB sets the ArangoDB datasource in the app's container. +func (a *App) AddArangoDB(db container.ArangoDBProvider) { + // Set up logger, metrics, and tracer + db.UseLogger(a.Logger()) + db.UseMetrics(a.Metrics()) + + // Get tracer from OpenTelemetry + tracer := otel.GetTracerProvider().Tracer("gofr-arangodb") + db.UseTracer(tracer) + + // Connect to ArangoDB + db.Connect() + + // Add the ArangoDB provider to the container + a.container.ArangoDB = db +} + func (a *App) AddSurrealDB(db container.SurrealBDProvider) { db.UseLogger(a.Logger()) db.UseMetrics(a.Metrics()) diff --git a/pkg/gofr/external_db_test.go b/pkg/gofr/external_db_test.go index 4d5e4accd..f1bea39f0 100644 --- a/pkg/gofr/external_db_test.go +++ b/pkg/gofr/external_db_test.go @@ -1,6 +1,7 @@ package gofr import ( + "strconv" "testing" "github.com/stretchr/testify/assert" @@ -203,3 +204,26 @@ func TestApp_AddScyllaDB(t *testing.T) { assert.Equal(t, mock, app.container.ScyllaDB) }) } + +func TestApp_AddArangoDB(t *testing.T) { + t.Run("Adding ArangoDB", func(t *testing.T) { + port := testutil.GetFreePort(t) + t.Setenv("METRICS_PORT", strconv.Itoa(port)) + + app := New() + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mock := container.NewMockArangoDBProvider(ctrl) + + mock.EXPECT().UseLogger(app.Logger()) + mock.EXPECT().UseMetrics(app.Metrics()) + mock.EXPECT().UseTracer(otel.GetTracerProvider().Tracer("gofr-arangodb")) + mock.EXPECT().Connect() + + app.AddArangoDB(mock) + + assert.Equal(t, mock, app.container.ArangoDB) + }) +} From ceff783638a81df4673f422f4950d34e0a8d2751 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 Feb 2025 11:01:47 +0530 Subject: [PATCH 22/22] Bump golang.org/x/net (#1469) --- pkg/gofr/datasource/arangodb/go.mod | 6 +++--- pkg/gofr/datasource/arangodb/go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pkg/gofr/datasource/arangodb/go.mod b/pkg/gofr/datasource/arangodb/go.mod index e0eb14123..1b208d711 100644 --- a/pkg/gofr/datasource/arangodb/go.mod +++ b/pkg/gofr/datasource/arangodb/go.mod @@ -25,8 +25,8 @@ require ( github.com/rs/zerolog v1.33.0 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/otel/metric v1.34.0 // indirect - golang.org/x/net v0.31.0 // indirect - golang.org/x/sys v0.27.0 // indirect - golang.org/x/text v0.20.0 // indirect + golang.org/x/net v0.33.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/text v0.21.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/pkg/gofr/datasource/arangodb/go.sum b/pkg/gofr/datasource/arangodb/go.sum index f5f91c8d7..65ae0c84a 100644 --- a/pkg/gofr/datasource/arangodb/go.sum +++ b/pkg/gofr/datasource/arangodb/go.sum @@ -54,15 +54,15 @@ go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= -golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= -golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= -golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= -golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=