diff --git a/pkg/gofr/datasource/mongo/mongo.go b/pkg/gofr/datasource/mongo/mongo.go index ab0e109a9..b098e1a10 100644 --- a/pkg/gofr/datasource/mongo/mongo.go +++ b/pkg/gofr/datasource/mongo/mongo.go @@ -40,7 +40,12 @@ type Config struct { const defaultTimeout = 5 * time.Second -var errStatusDown = errors.New("status down") +var ( + errStatusDown = errors.New("status down") + errMissingField = errors.New("missing required field in config") + errIncorrectURI = errors.New("incorrect URI for mongo") + errParseHost = errors.New("failed to parse host from MongoDB URI") +) /* Developer Note: We could have accepted logger and metrics as part of the factory function `New`, but when mongo driver is @@ -84,25 +89,14 @@ func (c *Client) UseTracer(tracer any) { // Connect establishes a connection to MongoDB and registers metrics using the provided configuration when the client was Created. func (c *Client) Connect() { - var host string + uri, host, err := generateMongoURI(c.config) + if err != nil { + c.logger.Errorf("error generating mongo URI: %v", err) + return + } c.logger.Debugf("connecting to MongoDB at %v to database %v", c.config.Host, c.config.Database) - uri := c.config.URI - - if uri == "" { - uri = fmt.Sprintf("mongodb://%s:%s@%s:%d/%s?authSource=admin", - c.config.User, c.config.Password, c.config.Host, c.config.Port, c.config.Database) - - host = c.config.Host - } else { - host = getDBHost(uri) - - if host == "" { - c.logger.Debug("failed to parse URI: incorrect format provided") - } - } - timeout := c.config.ConnectionTimeout if timeout == 0 { timeout = defaultTimeout @@ -133,13 +127,46 @@ func (c *Client) Connect() { c.logger.Logf("connected to MongoDB at %v to database %v", host, c.Database) } -func getDBHost(uri string) (host string) { +func generateMongoURI(config *Config) (uri, host string, err error) { + if config.URI != "" { + host, err = getDBHost(config.URI) + if err != nil || host == "" { + return "", "", err + } + + return config.URI, host, nil + } + + switch { + case config.Host == "": + return "", "", fmt.Errorf("%w: host is empty", errMissingField) + case config.Port == 0: + return "", "", fmt.Errorf("%w: port is empty", errMissingField) + case config.Database == "": + return "", "", fmt.Errorf("%w: database is empty", errMissingField) + } + + uri = fmt.Sprintf("mongodb://%s:%s@%s:%d/%s?authSource=admin", + config.User, config.Password, config.Host, config.Port, config.Database) + + return uri, config.Host, nil +} + +func getDBHost(uri string) (host string, err error) { parsedURL, err := url.Parse(uri) if err != nil { - return "" + return "", err + } + + if parsedURL.Scheme != "mongodb" { + return "", errIncorrectURI + } + + if parsedURL.Hostname() == "" { + return "", errParseHost } - return parsedURL.Hostname() + return parsedURL.Hostname(), nil } // InsertOne inserts a single document into the specified collection. diff --git a/pkg/gofr/datasource/mongo/mongo_test.go b/pkg/gofr/datasource/mongo/mongo_test.go index 412c912ec..6b1bc6587 100644 --- a/pkg/gofr/datasource/mongo/mongo_test.go +++ b/pkg/gofr/datasource/mongo/mongo_test.go @@ -36,43 +36,141 @@ func Test_NewMongoClient(t *testing.T) { assert.NotNil(t, client) } +func TestGenerateMongoURI(t *testing.T) { + tests := []struct { + name string + config Config + expectedURI string + expectedHost string + expectedError string + }{ + { + name: "Valid Config", + config: Config{ + User: "admin", + Password: "password", + Host: "localhost", + Port: 27017, + Database: "mydb", + }, + expectedURI: "mongodb://admin:password@localhost:27017/mydb?authSource=admin", + expectedHost: "localhost", + expectedError: "", + }, + { + name: "Predefined URI", + config: Config{ + URI: "mongodb://admin:password@localhost:27017/mydb?authSource=admin", + }, + expectedURI: "mongodb://admin:password@localhost:27017/mydb?authSource=admin", + expectedHost: "localhost", + expectedError: "", + }, + { + name: "Empty Host", + config: Config{ + User: "admin", + Password: "password", + Port: 27017, + Database: "mydb", + }, + expectedURI: "", + expectedHost: "", + expectedError: "missing required field in config: host is empty", + }, + { + name: "Invalid Port", + config: Config{ + User: "admin", + Password: "password", + Host: "localhost", + Database: "mydb", + }, + expectedURI: "", + expectedHost: "", + expectedError: "missing required field in config: port is empty", + }, + { + name: "Empty Database", + config: Config{ + User: "admin", + Password: "password", + Host: "localhost", + Port: 27017, + }, + expectedURI: "", + expectedHost: "", + expectedError: "missing required field in config: database is empty", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + client := Client{config: &test.config} + uri, host, err := generateMongoURI(client.config) + + assert.Equal(t, test.expectedURI, uri, "Unexpected URI") + assert.Equal(t, test.expectedHost, host, "Unexpected Host") + + if test.expectedError != "" { + assert.EqualError(t, err, test.expectedError, "Unexpected error message") + } else { + assert.NoError(t, err, "Expected no error but got one") + } + }) + } +} + func TestGetDBHost(t *testing.T) { tests := []struct { - name string - uri string - expected string + name string + uri string + expected string + expectedErr string }{ { - name: "Valid URI with host and port", - uri: "mongodb://username:password@hostname:27017/database?authSource=admin", - expected: "hostname", + name: "Valid URI with host and port", + uri: "mongodb://username:password@hostname:27017/database?authSource=admin", + expected: "hostname", + expectedErr: "", }, { - name: "Valid URI with IP address as host", - uri: "mongodb://username:password@192.168.1.1:27017/database?authSource=admin", - expected: "192.168.1.1", + name: "Valid URI with IP address as host", + uri: "mongodb://username:password@192.168.1.1:27017/database?authSource=admin", + expected: "192.168.1.1", + expectedErr: "", }, { - name: "Invalid URI with no host", - uri: "mongodb://username:password@:27017/database?authSource=admin", - expected: "", + name: "Invalid URI with no host", + uri: "mongodb://username:password@:27017/database?authSource=admin", + expected: "", + expectedErr: "failed to parse host from MongoDB URI", }, { - name: "Empty URI", - uri: "", - expected: "", + name: "Empty URI", + uri: "", + expected: "", + expectedErr: "incorrect URI for mongo", }, { - name: "Malformed URI", - uri: "mongodb:/username:password@hostname:27017/database?authSource=admin", - expected: "", + name: "Malformed URI", + uri: "mongodb:/username:password@hostname:27017/database?authSource=admin", + expected: "", + expectedErr: "failed to parse host from MongoDB URI", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - result := getDBHost(tt.uri) - assert.Equal(t, tt.expected, result, "Test case: %s", tt.name) + host, err := getDBHost(tt.uri) + + assert.Equal(t, tt.expected, host, "Test case: %s", tt.name) + + if tt.expectedErr == "" { + assert.NoError(t, err, "Test case: %s", tt.name) + } else { + assert.EqualError(t, err, tt.expectedErr, "Test case: %s", tt.name) + } }) } }