From eec755a4a48f40cd936bd9d880600b1a766894e6 Mon Sep 17 00:00:00 2001 From: Surajit Pore <97510117+surajit-zs@users.noreply.github.com> Date: Mon, 11 Nov 2024 12:45:28 +0530 Subject: [PATCH 1/8] Add Middleware with Container Access through UseMiddlewareWithContainer (#1179) --- docs/advanced-guide/middlewares/page.md | 60 ++++++++++++++++++++++++- pkg/gofr/gofr.go | 12 +++++ pkg/gofr/gofr_test.go | 51 +++++++++++++++++++++ 3 files changed, 121 insertions(+), 2 deletions(-) diff --git a/docs/advanced-guide/middlewares/page.md b/docs/advanced-guide/middlewares/page.md index 2529b4cbf..7369757bb 100644 --- a/docs/advanced-guide/middlewares/page.md +++ b/docs/advanced-guide/middlewares/page.md @@ -25,9 +25,12 @@ The CORS middleware provides the following overridable configs: By adding custom middleware to your GoFr application, user can easily extend its functionality and implement cross-cutting concerns in a modular and reusable way. -User can use the `UseMiddleware` method on your GoFr application instance to register your custom middleware. +User can use the `UseMiddleware` or `UseMiddlewareWithContainer` method on your GoFr application instance to register your custom middleware. -### Example: +### Using UseMiddleware method for Custom Middleware +The UseMiddleware method is ideal for simple middleware that doesn't need direct access to the application's container. + +#### Example: ```go import ( @@ -64,3 +67,56 @@ func main() { } ``` +### Using UseMiddlewareWithContainer for Custom Middleware with Container Access + +The UseMiddlewareWithContainer method allows middleware to access the application's container, providing access to +services like logging, configuration, and databases. This method is especially useful for middleware that needs access +to resources in the container to modify request processing flow. + +#### Example: + +```go +import ( + "fmt" + "net/http" + + "gofr.dev/pkg/gofr" + "gofr.dev/pkg/gofr/container" +) + +func main() { + // Create a new GoFr application instance + a := gofr.New() + + // Add custom middleware with container access + a.UseMiddlewareWithContainer(customMiddleware) + + // Define the application's routes + a.GET("/hello", HelloHandler) + + // Run the application + a.Run() +} + +// Define middleware with container access +func customMiddleware(c *container.Container, handler http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + c.Logger.Log("Hey! Welcome to GoFr") + + // Continue with the request processing + handler.ServeHTTP(w, r) + }) +} + +// Sample handler function +func HelloHandler(c *gofr.Context) (interface{}, error) { + name := c.Param("name") + if name == "" { + c.Log("Name came empty") + name = "World" + } + + return fmt.Sprintf("Hello %s!", name), nil +} +``` + diff --git a/pkg/gofr/gofr.go b/pkg/gofr/gofr.go index e182ee783..979983016 100644 --- a/pkg/gofr/gofr.go +++ b/pkg/gofr/gofr.go @@ -643,6 +643,18 @@ func (a *App) UseMiddleware(middlewares ...gofrHTTP.Middleware) { a.httpServer.router.UseMiddleware(middlewares...) } +// UseMiddlewareWithContainer adds a middleware that has access to the container +// and wraps the provided handler with the middleware logic. +// +// The `middleware` function receives the container and the handler, allowing +// the middleware to modify the request processing flow. +func (a *App) UseMiddlewareWithContainer(middlewareHandler func(c *container.Container, handler http.Handler) http.Handler) { + a.httpServer.router.Use(func(h http.Handler) http.Handler { + // Wrap the provided handler `h` with the middleware function `middlewareHandler` + return middlewareHandler(a.container, h) + }) +} + // AddCronJob registers a cron job to the cron table. // The cron expression can be either a 5-part or 6-part format. The 6-part format includes an // optional second field (in beginning) and others being minute, hour, day, month and day of week respectively. diff --git a/pkg/gofr/gofr_test.go b/pkg/gofr/gofr_test.go index 13b7df41a..dd68aa7e9 100644 --- a/pkg/gofr/gofr_test.go +++ b/pkg/gofr/gofr_test.go @@ -620,6 +620,57 @@ func Test_UseMiddleware(t *testing.T) { assert.Equal(t, "applied", testHeaderValue, "Test_UseMiddleware Failed! header value mismatch.") } +// Test the UseMiddlewareWithContainer function. +func TestUseMiddlewareWithContainer(t *testing.T) { + // Initialize the mock container + mockContainer := container.NewContainer(config.NewMockConfig(nil)) + + // Create a simple handler to test middleware functionality + handler := http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte("Hello, world!")) + }) + + // Middleware to modify response and test container access + middleware := func(c *container.Container, handler http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // Ensure the container is passed correctly (for this test, we are just logging) + assert.NotNil(t, c, "Container should not be nil in the middleware") + + // Continue with the handler execution + handler.ServeHTTP(w, r) + }) + } + + // Create a new App with a mock server + app := &App{ + httpServer: &httpServer{ + router: gofrHTTP.NewRouter(), + port: 8001, + }, + container: mockContainer, + Config: config.NewMockConfig(map[string]string{"REQUEST_TIMEOUT": "5"}), + } + + // Use the middleware with the container + app.UseMiddlewareWithContainer(middleware) + + // Register the handler to a route for testing + app.httpServer.router.Handle("/test", handler) + + // Create a test request + req := httptest.NewRequest(http.MethodGet, "/test", http.NoBody) + // Create a test response recorder + rr := httptest.NewRecorder() + + // Call the handler with the request and recorder + app.httpServer.router.ServeHTTP(rr, req) + + // Assert the status code and response body + assert.Equal(t, http.StatusOK, rr.Code) + assert.Equal(t, "Hello, world!", rr.Body.String()) +} + func Test_APIKeyAuthMiddleware(t *testing.T) { c, _ := container.NewMockContainer(t) From 368e3f5db21994be1a8b9a360567b689b23aec93 Mon Sep 17 00:00:00 2001 From: Divya Darshana <98943137+coolwednesday@users.noreply.github.com> Date: Tue, 12 Nov 2024 13:06:10 +0530 Subject: [PATCH 2/8] Support DB migrations For MongoDB (#1170) --- pkg/gofr/migration/datasource.go | 1 + pkg/gofr/migration/interface.go | 16 +++++ pkg/gofr/migration/migration.go | 10 +++ pkg/gofr/migration/mongo.go | 94 +++++++++++++++++++++++++ pkg/gofr/migration/mongo_test.go | 114 +++++++++++++++++++++++++++++++ 5 files changed, 235 insertions(+) create mode 100644 pkg/gofr/migration/mongo.go create mode 100644 pkg/gofr/migration/mongo_test.go diff --git a/pkg/gofr/migration/datasource.go b/pkg/gofr/migration/datasource.go index edbc9c871..8f855a4d4 100644 --- a/pkg/gofr/migration/datasource.go +++ b/pkg/gofr/migration/datasource.go @@ -12,6 +12,7 @@ type Datasource struct { PubSub PubSub Clickhouse Clickhouse Cassandra Cassandra + Mongo Mongo } // It is a base implementation for migration manager, on this other database drivers have been wrapped. diff --git a/pkg/gofr/migration/interface.go b/pkg/gofr/migration/interface.go index a253327f7..e6fe25857 100644 --- a/pkg/gofr/migration/interface.go +++ b/pkg/gofr/migration/interface.go @@ -47,6 +47,22 @@ type Cassandra interface { HealthCheck(ctx context.Context) (any, error) } +// Mongo is an interface representing a MongoDB database client with common CRUD operations. +type Mongo interface { + Find(ctx context.Context, collection string, filter any, results any) error + FindOne(ctx context.Context, collection string, filter any, result any) error + InsertOne(ctx context.Context, collection string, document any) (any, error) + InsertMany(ctx context.Context, collection string, documents []any) ([]any, error) + DeleteOne(ctx context.Context, collection string, filter any) (int64, error) + DeleteMany(ctx context.Context, collection string, filter any) (int64, error) + UpdateByID(ctx context.Context, collection string, id any, update any) (int64, error) + UpdateOne(ctx context.Context, collection string, filter any, update any) error + UpdateMany(ctx context.Context, collection string, filter any, update any) (int64, error) + Drop(ctx context.Context, collection string) error + CreateCollection(ctx context.Context, name string) error + StartSession() (any, error) +} + // keeping the migrator interface unexported as, right now it is not being implemented directly, by the externalDB drivers. // keeping the implementations for externalDB at one place such that if any change in migration logic, we would change directly here. type migrator interface { diff --git a/pkg/gofr/migration/migration.go b/pkg/gofr/migration/migration.go index 0628eb19b..d07b7e49c 100644 --- a/pkg/gofr/migration/migration.go +++ b/pkg/gofr/migration/migration.go @@ -162,6 +162,16 @@ func getMigrator(c *container.Container) (Datasource, migrator, bool) { c.Debug("initialized data source for Cassandra") } + if !isNil(c.Mongo) { + ok = true + + ds.Mongo = mongoDS{c.Mongo} + + mg = mongoDS{c.Mongo}.apply(mg) + + c.Debug("initialized data source for Mongo") + } + return ds, mg, ok } diff --git a/pkg/gofr/migration/mongo.go b/pkg/gofr/migration/mongo.go new file mode 100644 index 000000000..c04bc40bc --- /dev/null +++ b/pkg/gofr/migration/mongo.go @@ -0,0 +1,94 @@ +package migration + +import ( + "context" + "time" + + "gofr.dev/pkg/gofr/container" +) + +type mongoDS struct { + container.Mongo +} + +type mongoMigrator struct { + container.Mongo + migrator +} + +// apply initializes mongoMigrator using the Mongo interface. +func (ds mongoDS) apply(m migrator) migrator { + return mongoMigrator{ + Mongo: ds.Mongo, + migrator: m, + } +} + +const ( + mongoMigrationCollection = "gofr_migrations" +) + +// checkAndCreateMigrationTable initializes a MongoDB collection if it doesn't exist. +func (mg mongoMigrator) checkAndCreateMigrationTable(_ *container.Container) error { + err := mg.Mongo.CreateCollection(context.Background(), mongoMigrationCollection) + if err != nil { + return err + } + + return nil +} + +// getLastMigration retrieves the latest migration version from MongoDB. +func (mg mongoMigrator) getLastMigration(c *container.Container) int64 { + var lastMigration int64 + + var migrations []struct { + Version int64 `bson:"version"` + } + + filter := make(map[string]any) + + err := mg.Mongo.Find(context.Background(), mongoMigrationCollection, filter, &migrations) + if err != nil { + c.Errorf("Failed to fetch migrations from MongoDB: %v", err) + return 0 + } + + // Identify the highest migration version. + for _, migration := range migrations { + lastMigration = max(lastMigration, migration.Version) + } + + c.Debugf("MongoDB last migration fetched value is: %v", lastMigration) + + lm2 := mg.migrator.getLastMigration(c) + + return max(lm2, lastMigration) +} + +func (mg mongoMigrator) beginTransaction(c *container.Container) transactionData { + return mg.migrator.beginTransaction(c) +} + +func (mg mongoMigrator) commitMigration(c *container.Container, data transactionData) error { + migrationDoc := map[string]any{ + "version": data.MigrationNumber, + "method": "UP", + "start_time": data.StartTime, + "duration": time.Since(data.StartTime).Milliseconds(), + } + _, err := mg.Mongo.InsertOne(context.Background(), mongoMigrationCollection, migrationDoc) + + if err != nil { + return err + } + + c.Debugf("Inserted record for migration %v in MongoDB gofr_migrations collection", data.MigrationNumber) + + return mg.migrator.commitMigration(c, data) +} + +func (mg mongoMigrator) rollback(c *container.Container, data transactionData) { + mg.migrator.rollback(c, data) + c.Fatalf("Migration %v failed.", data.MigrationNumber) +} diff --git a/pkg/gofr/migration/mongo_test.go b/pkg/gofr/migration/mongo_test.go new file mode 100644 index 000000000..3c31cc1fe --- /dev/null +++ b/pkg/gofr/migration/mongo_test.go @@ -0,0 +1,114 @@ +package migration + +import ( + "context" + "errors" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "gofr.dev/pkg/gofr/container" +) + +var errMongoConn = errors.New("error connecting to mongo") + +func mongoSetup(t *testing.T) (migrator, *container.MockMongo, *container.Container) { + t.Helper() + + mockContainer, mocks := container.NewMockContainer(t) + + mockMongo := mocks.Mongo + + ds := Datasource{Mongo: mockContainer.Mongo} + + mongoDB := mongoDS{Mongo: mockMongo} + migratorWithMongo := mongoDB.apply(&ds) + + mockContainer.Mongo = mockMongo + + return migratorWithMongo, mockMongo, mockContainer +} + +func Test_MongoCheckAndCreateMigrationTable(t *testing.T) { + migratorWithMongo, mockMongo, mockContainer := mongoSetup(t) + + testCases := []struct { + desc string + err error + }{ + {"no error", nil}, + {"connection failed", errMongoConn}, + } + + for i, tc := range testCases { + mockMongo.EXPECT().CreateCollection(context.Background(), mongoMigrationCollection).Return(tc.err) + err := migratorWithMongo.checkAndCreateMigrationTable(mockContainer) + + assert.Equal(t, tc.err, err, "TEST[%v]\n %v Failed! ", i, tc.desc) + } +} + +func Test_MongoGetLastMigration(t *testing.T) { + migratorWithMongo, mockMongo, mockContainer := mongoSetup(t) + + testCases := []struct { + desc string + err error + resp int64 + }{ + {"no error", nil, 0}, + {"connection failed", errMongoConn, 0}, + } + + var migrations []struct { + Version int64 `bson:"version"` + } + + filter := make(map[string]any) + + for i, tc := range testCases { + mockMongo.EXPECT().Find(context.Background(), mongoMigrationCollection, filter, &migrations).Return(tc.err) + + resp := migratorWithMongo.getLastMigration(mockContainer) + + assert.Equal(t, tc.resp, resp, "TEST[%v]\n %v Failed! ", i, tc.desc) + } +} + +func Test_MongoCommitMigration(t *testing.T) { + migratorWithMongo, mockMongo, mockContainer := mongoSetup(t) + + // mockResult is not the same result type as that returned by InsertOne method in mongoDB, + // but has been used only for mocking the test for migrations in mongoDB. + mockResult := struct{}{} + + testCases := []struct { + desc string + err error + }{ + {"no error", nil}, + {"connection failed", errMongoConn}, + } + + timeNow := time.Now() + + td := transactionData{ + StartTime: timeNow, + MigrationNumber: 10, + } + + migrationDoc := map[string]interface{}{ + "version": td.MigrationNumber, + "method": "UP", + "start_time": td.StartTime, + "duration": time.Since(td.StartTime).Milliseconds(), + } + + for i, tc := range testCases { + mockMongo.EXPECT().InsertOne(context.Background(), mongoMigrationCollection, migrationDoc).Return(mockResult, tc.err) + + err := migratorWithMongo.commitMigration(mockContainer, td) + + assert.Equal(t, tc.err, err, "TEST[%v]\n %v Failed! ", i, tc.desc) + } +} From 3054b69e05224b4c5b7e5aa7529e2005552c13ce Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 13 Nov 2024 11:44:01 +0530 Subject: [PATCH 3/8] Bump golang.org/x/oauth2 from 0.23.0 to 0.24.0 (#1189) --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 54701577f..31cf72ef4 100644 --- a/go.mod +++ b/go.mod @@ -35,7 +35,7 @@ require ( go.opentelemetry.io/otel/sdk/metric v1.30.0 go.opentelemetry.io/otel/trace v1.31.0 go.uber.org/mock v0.5.0 - golang.org/x/oauth2 v0.23.0 + golang.org/x/oauth2 v0.24.0 golang.org/x/sync v0.8.0 golang.org/x/term v0.25.0 golang.org/x/text v0.19.0 diff --git a/go.sum b/go.sum index 7821ecc6b..b1755c8db 100644 --- a/go.sum +++ b/go.sum @@ -283,8 +283,8 @@ golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= -golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= +golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= From e05c6580dd3917f00dac4ea4bb5d1943295c3543 Mon Sep 17 00:00:00 2001 From: Divya Darshana <98943137+coolwednesday@users.noreply.github.com> Date: Wed, 13 Nov 2024 11:50:46 +0530 Subject: [PATCH 4/8] Fix: Empty Correlation ID for remote log-level (#1198) --- pkg/gofr/service/new.go | 10 ++++++++++ pkg/gofr/service/new_test.go | 4 ++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/pkg/gofr/service/new.go b/pkg/gofr/service/new.go index 9e2850dba..2fcca104c 100644 --- a/pkg/gofr/service/new.go +++ b/pkg/gofr/service/new.go @@ -154,6 +154,16 @@ func (h *httpService) createAndSendRequest(ctx context.Context, method string, p // encode the query parameters on the request encodeQueryParameters(req, queryParams) + if !trace.SpanFromContext(ctx).SpanContext().HasTraceID() { + // Start context and Tracing + ctx = req.Context() + + // extract the traceID and spanID from the headers and create a new context for the same + // this context will make a new span using the traceID and link the incoming SpanID as + // its parentID, thus connecting two spans + ctx = otel.GetTextMapPropagator().Extract(ctx, propagation.HeaderCarrier(req.Header)) + } + // inject the TraceParent header manually in the request headers otel.GetTextMapPropagator().Inject(spanContext, propagation.HeaderCarrier(req.Header)) diff --git a/pkg/gofr/service/new_test.go b/pkg/gofr/service/new_test.go index 5029d3c2d..10e35bfb9 100644 --- a/pkg/gofr/service/new_test.go +++ b/pkg/gofr/service/new_test.go @@ -68,7 +68,7 @@ func TestHTTPService_createAndSendRequest(t *testing.T) { ctx := context.Background() - metrics.EXPECT().RecordHistogram(ctx, "app_http_service_response", gomock.Any(), "path", server.URL, + metrics.EXPECT().RecordHistogram(gomock.Any(), "app_http_service_response", gomock.Any(), "path", server.URL, "method", http.MethodPost, "status", fmt.Sprintf("%v", http.StatusOK)) // when params value is of type []string then last value is sent in request @@ -508,7 +508,7 @@ func TestHTTPService_createAndSendRequestServerError(t *testing.T) { ctx := context.Background() - metrics.EXPECT().RecordHistogram(ctx, "app_http_service_response", gomock.Any(), "path", gomock.Any(), + metrics.EXPECT().RecordHistogram(gomock.Any(), "app_http_service_response", gomock.Any(), "path", gomock.Any(), "method", http.MethodPost, "status", fmt.Sprintf("%v", http.StatusInternalServerError)) // when params value is of type []string then last value is sent in request From 882298906ae79b16b77f80867f3ee542a45a319e Mon Sep 17 00:00:00 2001 From: Umang Mundhra Date: Wed, 13 Nov 2024 11:55:41 +0530 Subject: [PATCH 5/8] remove +build directives from GoFr (#1185) --- pkg/gofr/datasource/cassandra/cassandra_batch_test.go | 1 - pkg/gofr/datasource/cassandra/cassandra_test.go | 1 - pkg/gofr/datasource/cassandra/mock_interfaces.go | 1 - pkg/gofr/datasource/cassandra/mock_logger.go | 1 - pkg/gofr/datasource/cassandra/mock_metrics.go | 1 - pkg/gofr/datasource/clickhouse/clickhouse_test.go | 1 - pkg/gofr/datasource/clickhouse/mock_interface.go | 1 - pkg/gofr/datasource/clickhouse/mock_logger.go | 1 - pkg/gofr/datasource/clickhouse/mock_metrics.go | 1 - pkg/gofr/datasource/file/ftp/file_test.go | 1 - pkg/gofr/datasource/file/ftp/fs_test.go | 1 - pkg/gofr/datasource/file/ftp/mock_interface.go | 1 - 12 files changed, 12 deletions(-) diff --git a/pkg/gofr/datasource/cassandra/cassandra_batch_test.go b/pkg/gofr/datasource/cassandra/cassandra_batch_test.go index 4c8cb67cf..2a3669b75 100644 --- a/pkg/gofr/datasource/cassandra/cassandra_batch_test.go +++ b/pkg/gofr/datasource/cassandra/cassandra_batch_test.go @@ -1,5 +1,4 @@ //go:build exclude -// +build exclude package cassandra diff --git a/pkg/gofr/datasource/cassandra/cassandra_test.go b/pkg/gofr/datasource/cassandra/cassandra_test.go index 16c87a8ba..01e8b2201 100644 --- a/pkg/gofr/datasource/cassandra/cassandra_test.go +++ b/pkg/gofr/datasource/cassandra/cassandra_test.go @@ -1,5 +1,4 @@ //go:build exclude -// +build exclude package cassandra diff --git a/pkg/gofr/datasource/cassandra/mock_interfaces.go b/pkg/gofr/datasource/cassandra/mock_interfaces.go index f3649ec97..63ebd66eb 100644 --- a/pkg/gofr/datasource/cassandra/mock_interfaces.go +++ b/pkg/gofr/datasource/cassandra/mock_interfaces.go @@ -1,5 +1,4 @@ //go:build exclude -// +build exclude // Code generated by MockGen. DO NOT EDIT. // Source: interfaces.go diff --git a/pkg/gofr/datasource/cassandra/mock_logger.go b/pkg/gofr/datasource/cassandra/mock_logger.go index 5d2a61ee0..c29a7b4b3 100644 --- a/pkg/gofr/datasource/cassandra/mock_logger.go +++ b/pkg/gofr/datasource/cassandra/mock_logger.go @@ -1,5 +1,4 @@ //go:build exclude -// +build exclude // Code generated by MockGen. DO NOT EDIT. // Source: logger.go diff --git a/pkg/gofr/datasource/cassandra/mock_metrics.go b/pkg/gofr/datasource/cassandra/mock_metrics.go index 67795769d..e1348111c 100644 --- a/pkg/gofr/datasource/cassandra/mock_metrics.go +++ b/pkg/gofr/datasource/cassandra/mock_metrics.go @@ -1,5 +1,4 @@ //go:build exclude -// +build exclude // Code generated by MockGen. DO NOT EDIT. // Source: metrics.go diff --git a/pkg/gofr/datasource/clickhouse/clickhouse_test.go b/pkg/gofr/datasource/clickhouse/clickhouse_test.go index 964d9a80e..5471eb019 100644 --- a/pkg/gofr/datasource/clickhouse/clickhouse_test.go +++ b/pkg/gofr/datasource/clickhouse/clickhouse_test.go @@ -1,5 +1,4 @@ //go:build exclude -// +build exclude package clickhouse diff --git a/pkg/gofr/datasource/clickhouse/mock_interface.go b/pkg/gofr/datasource/clickhouse/mock_interface.go index dc091ce0b..d7344c31b 100644 --- a/pkg/gofr/datasource/clickhouse/mock_interface.go +++ b/pkg/gofr/datasource/clickhouse/mock_interface.go @@ -1,5 +1,4 @@ //go:build exclude -// +build exclude // Code generated by MockGen. DO NOT EDIT. // Source: interface.go diff --git a/pkg/gofr/datasource/clickhouse/mock_logger.go b/pkg/gofr/datasource/clickhouse/mock_logger.go index 411663b87..43eae2c76 100644 --- a/pkg/gofr/datasource/clickhouse/mock_logger.go +++ b/pkg/gofr/datasource/clickhouse/mock_logger.go @@ -1,5 +1,4 @@ //go:build exclude -// +build exclude // Code generated by MockGen. DO NOT EDIT. // Source: logger.go diff --git a/pkg/gofr/datasource/clickhouse/mock_metrics.go b/pkg/gofr/datasource/clickhouse/mock_metrics.go index aa7d3f46e..d76e77f0e 100644 --- a/pkg/gofr/datasource/clickhouse/mock_metrics.go +++ b/pkg/gofr/datasource/clickhouse/mock_metrics.go @@ -1,5 +1,4 @@ //go:build exclude -// +build exclude // Code generated by MockGen. DO NOT EDIT. // Source: metrics.go diff --git a/pkg/gofr/datasource/file/ftp/file_test.go b/pkg/gofr/datasource/file/ftp/file_test.go index 2d67bcc52..eb64b3feb 100644 --- a/pkg/gofr/datasource/file/ftp/file_test.go +++ b/pkg/gofr/datasource/file/ftp/file_test.go @@ -1,5 +1,4 @@ //go:build exclude -// +build exclude package ftp diff --git a/pkg/gofr/datasource/file/ftp/fs_test.go b/pkg/gofr/datasource/file/ftp/fs_test.go index 41456bbd4..1e4252881 100644 --- a/pkg/gofr/datasource/file/ftp/fs_test.go +++ b/pkg/gofr/datasource/file/ftp/fs_test.go @@ -1,5 +1,4 @@ //go:build exclude -// +build exclude package ftp diff --git a/pkg/gofr/datasource/file/ftp/mock_interface.go b/pkg/gofr/datasource/file/ftp/mock_interface.go index e45287af0..f82c2be0c 100644 --- a/pkg/gofr/datasource/file/ftp/mock_interface.go +++ b/pkg/gofr/datasource/file/ftp/mock_interface.go @@ -1,5 +1,4 @@ //go:build exclude -// +build exclude // Code generated by MockGen. DO NOT EDIT. // Source: interface.go From 0e22fccae3de65b5bed8df87294b02dd3b120ba7 Mon Sep 17 00:00:00 2001 From: Divya Darshana Date: Thu, 14 Nov 2024 12:21:40 +0530 Subject: [PATCH 6/8] document supported datatypes in add-rest-handlers --- docs/quick-start/add-rest-handlers/page.md | 30 +++++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/docs/quick-start/add-rest-handlers/page.md b/docs/quick-start/add-rest-handlers/page.md index 7b043bb96..616f782e5 100644 --- a/docs/quick-start/add-rest-handlers/page.md +++ b/docs/quick-start/add-rest-handlers/page.md @@ -114,9 +114,31 @@ In this example, we define a user struct representing a database entity. The `Ge This method can be used to implement custom logic for filtering, sorting, or retrieving additional data along with the entities. -> Few Points to consider: -> 1. The struct should always be passed by reference in the method `AddRESTHandlers`. -> 2. Field Naming Convention: GoFr assumes the struct fields in snake-case match the database column names. For example, `IsEmployed` field in the struct matches `is_employed` column in the database, `Age` field matches `age` column, etc. -> 3. Primary Key: The first field of the struct is typically used as the primary key for data operations. However, user can customize this behavior using GoFr's features. +## Few Points to Consider: +**1. Passing Struct by Reference** + +The struct should always be passed by reference in the method `AddRESTHandlers`. + +**2. Field Naming Convention** + +GoFr assumes that struct fields in snake_case match the database column names. + +* For example, the `IsEmployed` field in the struct matches the `is_employed` column in the database. +* Similarly, the `Age` field matches the `age` column. + +**3. Primary Key** + +The first field of the struct is typically used as the primary key for data operations. However, this behavior can be customized using GoFr's features. + +**4. Datatype Conversions** + +| Go Type | SQL Type | Description | +|---|---|---| +| `uuid.UUID` (from `github.com/google/uuid` or `github.com/satori/go.uuid`) | `CHAR(36)` or `VARCHAR(36)` | UUIDs are typically stored as 36-character strings in SQL databases. | +| `string` | `VARCHAR(n)` or `TEXT` | Use `VARCHAR(n)` for fixed-length strings, while `TEXT` is for longer, variable-length strings. | +| `int`, `int32`, `int64`, `uint`, `uint32`, `uint64` | `INT`, `BIGINT`, `SMALLINT`, `TINYINT`, `INTEGER` | Use `INT` for general integer values, `BIGINT` for large values, and `SMALLINT` or `TINYINT` for smaller ranges. | +| `bool` | `BOOLEAN` or `TINYINT(1)` | Use `BOOLEAN` (supported by most SQL databases like PostgreSQL, MySQL) or `TINYINT(1)` in MySQL (where `0` is false, and `1` is true). | +| `float32`, `float64` | `FLOAT`, `DOUBLE`, `DECIMAL` | Use `DECIMAL` for precise decimal numbers (e.g., financial data), `FLOAT` or `DOUBLE` for approximate floating-point numbers. | +| `time.Time` | `DATE`, `TIME`, `DATETIME`, `TIMESTAMP` | Use `DATE` for just the date, `TIME` for the time of day, and `DATETIME` or `TIMESTAMP` for both date and time. | > #### Check out the example on how to add REST Handlers in GoFr: [Visit Github](https://github.com/gofr-dev/gofr/tree/main/examples/using-add-rest-handlers) \ No newline at end of file From 8d1a0da21816a87933b9df0c451c2bca29a9ddfa Mon Sep 17 00:00:00 2001 From: Aryan Mehrotra <44036979+aryanmehrotra@users.noreply.github.com> Date: Fri, 15 Nov 2024 13:22:13 +0530 Subject: [PATCH 7/8] Fix SQL log for successful connection (#1205) --- pkg/gofr/datasource/logger.go | 4 ++-- pkg/gofr/datasource/redis/redis.go | 2 +- pkg/gofr/datasource/sql/sql.go | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pkg/gofr/datasource/logger.go b/pkg/gofr/datasource/logger.go index b4eaab064..6e34eda3f 100644 --- a/pkg/gofr/datasource/logger.go +++ b/pkg/gofr/datasource/logger.go @@ -9,8 +9,8 @@ package datasource type Logger interface { Debug(args ...interface{}) Debugf(format string, args ...interface{}) - Log(args ...interface{}) - Logf(format string, args ...interface{}) + Info(args ...interface{}) + Infof(format string, args ...interface{}) Error(args ...interface{}) Errorf(format string, args ...interface{}) Warn(args ...interface{}) diff --git a/pkg/gofr/datasource/redis/redis.go b/pkg/gofr/datasource/redis/redis.go index 3db974e5f..edcb9c6d5 100644 --- a/pkg/gofr/datasource/redis/redis.go +++ b/pkg/gofr/datasource/redis/redis.go @@ -55,7 +55,7 @@ func NewClient(c config.Config, logger datasource.Logger, metrics Metrics) *Redi logger.Errorf("could not add tracing instrumentation, error: %s", err) } - logger.Logf("connected to redis at %s:%d", redisConfig.HostName, redisConfig.Port) + logger.Infof("connected to redis at %s:%d", redisConfig.HostName, redisConfig.Port) } else { logger.Errorf("could not connect to redis at '%s:%d', error: %s", redisConfig.HostName, redisConfig.Port, err) } diff --git a/pkg/gofr/datasource/sql/sql.go b/pkg/gofr/datasource/sql/sql.go index 09e4c4b2b..90d0aacb2 100644 --- a/pkg/gofr/datasource/sql/sql.go +++ b/pkg/gofr/datasource/sql/sql.go @@ -108,7 +108,7 @@ func retryConnection(database *DB) { for { if database.DB.Ping() != nil { - database.logger.Log("retrying SQL database connection") + database.logger.Info("retrying SQL database connection") for { err := database.DB.Ping() @@ -208,9 +208,9 @@ func pushDBMetrics(db *sql.DB, metrics Metrics) { func printConnectionSuccessLog(status string, dbconfig *DBConfig, logger datasource.Logger) { if dbconfig.Dialect == sqlite { - logger.Debugf("%s to '%s' database", status, dbconfig.Database) + logger.Infof("%s to '%s' database", status, dbconfig.Database) } else { - logger.Debugf("%s to '%s' user to '%s' database at '%s:%s'", status, dbconfig.User, + logger.Infof("%s to '%s' user to '%s' database at '%s:%s'", status, dbconfig.User, dbconfig.Database, dbconfig.HostName, dbconfig.Port) } } From 4fccd2e14a4d5ed3ffd3f7cd4f16fa2e1baaaf25 Mon Sep 17 00:00:00 2001 From: mehrotra234 Date: Fri, 15 Nov 2024 16:27:44 +0530 Subject: [PATCH 8/8] update framwework version --- 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 4060342d1..dbceea8e0 100644 --- a/pkg/gofr/version/version.go +++ b/pkg/gofr/version/version.go @@ -1,3 +1,3 @@ package version -const Framework = "dev" +const Framework = "v1.27.0"