Repository: danvergara/dblab (go)

Issue:
"Invalid db url" although it seems very correct
**Describe the bug**

I run `~/path/to/dblab -u postgres://postgres:postgres@localhost:5432/mydb?sslmode=disable` and get the following error (the db url is valid, I use it with `psql` and other tools.
```
Error: postgres://postgres:postgres@localhost:5432/openfindata?sslmode=disable: invalid url
Usage:
  dblab [flags]
  dblab [command]

Available Commands:
  help        Help about any command
  version     The version of the project

Flags:
      --config          get the connection data from a config file (default is $HOME/.dblab.yaml or the current directory)
      --db string       Database name
      --driver string   Database driver
  -h, --help            help for dblab
      --host string     Server host name or IP
      --pass string     Password for user
      --port string     Server port
      --ssl string      SSL mode
  -u, --url string      Database connection string
      --user string     Database user

Use "dblab [command] --help" for more information about a command.

Error: postgres://postgres:postgres@localhost:5432/openfindata?sslmode=disable: invalid url
```
As a bonus problem, my shell is messed up after the crash (typed text on the command line is invisible), and I need to `reset` it to be able to see my input again.

**To Reproduce**
Steps to reproduce the behavior:
1. run `~/path/to/dblab -u postgres://postgres:postgres@localhost:5432/mydb?sslmode=disable`
2. see error above

**Expected behavior**
A valid url should not fail, and the terminal should be left like it was.

**Screenshots**
N/A

**OS (please complete the following information):**
 - debian linux unstable
 - downloaded this release https://github.com/danvergara/dblab/releases/download/v0.12.0/dblab_0.12.0_linux_amd64.tar.gz

**Additional context**



Pull request:
Fix url flag
# Pull Request Template

## Description

As described in #74, the way dblab validates the `url` param wasn't working.

Fixes #74 

## Type of change

Please delete options that are not relevant.

- [X] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] This change requires a documentation update

## How Has This Been Tested?

Fix the test functions and QA this on my system.

## Checklist:

- [X] My code follows the style guidelines of this project
- [X] I have performed a self-review of my own code
- [X] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [X] I have added tests that prove my fix is effective or that my feature works
- [X] New and existing unit tests pass locally with my changes
- [X] I have checked my code and corrected any misspellings


Patch:
diff --git a/Makefile b/Makefile
index 1c2dfc2..906cd91 100644
--- a/Makefile
+++ b/Makefile
@@ -45,7 +45,11 @@ run: build
 run-mysql: build
 	./dblab --host localhost --user myuser --db mydb --pass 5@klkbN#ABC --ssl enable --port 3306 --driver mysql
 
-run: build
+.PHONY: run-url
+## run-url: Runs the app passing the url as paramenter
+run-url: build
+	./dblab --url postgres://postgres:password@localhost:5432/users?sslmode=disable
+
 .PHONY: up
 ## up: Runs all the containers listed in the docker-compose.yml file
 up:
diff --git a/pkg/client/client.go b/pkg/client/client.go
index 5d18504..011b8e7 100644
--- a/pkg/client/client.go
+++ b/pkg/client/client.go
@@ -23,7 +23,7 @@ type Client struct {
 
 // New return an instance of the client.
 func New(opts command.Options) (*Client, error) {
-	conn, err := connection.BuildConnectionFromOpts(opts)
+	conn, opts, err := connection.BuildConnectionFromOpts(opts)
 	if err != nil {
 		return nil, err
 	}
diff --git a/pkg/connection/connection.go b/pkg/connection/connection.go
index 137d7b1..2770e05 100644
--- a/pkg/connection/connection.go
+++ b/pkg/connection/connection.go
@@ -37,16 +37,23 @@ func init() {
 }
 
 // BuildConnectionFromOpts return the connection uri string given the options passed by the uses.
-func BuildConnectionFromOpts(opts command.Options) (string, error) {
+func BuildConnectionFromOpts(opts command.Options) (string, command.Options, error) {
 	if opts.URL != "" {
-		switch opts.Driver {
-		case "postgres":
-			return formatPostgresURL(opts)
-		case "mysql":
-			return formatMySQLURL(opts)
-		default:
-			return "", fmt.Errorf("%s: %w", opts.URL, ErrInvalidURLFormat)
+		if strings.HasPrefix(opts.URL, "postgres") {
+			opts.Driver = "postgres"
+
+			conn, err := formatPostgresURL(opts)
+
+			return conn, opts, err
 		}
+
+		if strings.HasPrefix(opts.URL, "mysql") {
+			opts.Driver = "mysql"
+			conn, err := formatMySQLURL(opts)
+			return conn, opts, err
+		}
+
+		return "", opts, fmt.Errorf("%s: %w", opts.URL, ErrInvalidURLFormat)
 	}
 
 	if opts.User == "" {
@@ -75,11 +82,11 @@ func BuildConnectionFromOpts(opts command.Options) (string, error) {
 			RawQuery: query.Encode(),
 		}
 
-		return connDB.String(), nil
+		return connDB.String(), opts, nil
 	case "mysql":
-		return fmt.Sprintf("%s:%s@tcp(%s:%s)/%s", opts.User, opts.Pass, opts.Host, opts.Port, opts.DBName), nil
+		return fmt.Sprintf("%s:%s@tcp(%s:%s)/%s", opts.User, opts.Pass, opts.Host, opts.Port, opts.DBName), opts, nil
 	default:
-		return "", fmt.Errorf("%s: %w", opts.URL, ErrInvalidDriver)
+		return "", opts, fmt.Errorf("%s: %w", opts.URL, ErrInvalidDriver)
 	}
 }
 


Test patch:
diff --git a/pkg/connection/connection_test.go b/pkg/connection/connection_test.go
index eee661b..41abbf5 100644
--- a/pkg/connection/connection_test.go
+++ b/pkg/connection/connection_test.go
@@ -27,8 +27,7 @@ func TestBuildConnectionFromOptsFromURL(t *testing.T) {
 			name: "valid postgres localhost",
 			given: given{
 				opts: command.Options{
-					Driver: "postgres",
-					URL:    "postgres://user:password@localhost:5432/db?sslmode=disable",
+					URL: "postgres://user:password@localhost:5432/db?sslmode=disable",
 				},
 			},
 			want: want{
@@ -39,8 +38,7 @@ func TestBuildConnectionFromOptsFromURL(t *testing.T) {
 			name: "valid postgres localhost but add sslmode",
 			given: given{
 				opts: command.Options{
-					Driver: "postgres",
-					URL:    "postgres://user:password@localhost:5432/db",
+					URL: "postgres://user:password@localhost:5432/db",
 				},
 			},
 			want: want{
@@ -51,8 +49,7 @@ func TestBuildConnectionFromOptsFromURL(t *testing.T) {
 			name: "valid postgres localhost postgresql as protocol",
 			given: given{
 				opts: command.Options{
-					Driver: "postgres",
-					URL:    "postgresql://user:password@localhost:5432/db",
+					URL: "postgresql://user:password@localhost:5432/db",
 				},
 			},
 			want: want{
@@ -63,26 +60,12 @@ func TestBuildConnectionFromOptsFromURL(t *testing.T) {
 			name: "error misspelled postgres",
 			given: given{
 				opts: command.Options{
-					Driver: "postgres",
-					URL:    "potgre://user:password@localhost:5432/db",
-				},
-			},
-			want: want{
-				hasError: true,
-				err:      ErrInvalidPostgresURLFormat,
-			},
-		},
-		{
-			name: "error invalid url",
-			given: given{
-				opts: command.Options{
-					Driver: "postgres",
-					URL:    "not-a-url",
+					URL: "potgre://user:password@localhost:5432/db",
 				},
 			},
 			want: want{
 				hasError: true,
-				err:      ErrInvalidPostgresURLFormat,
+				err:      ErrInvalidURLFormat,
 			},
 		},
 		// mysql
@@ -90,8 +73,7 @@ func TestBuildConnectionFromOptsFromURL(t *testing.T) {
 			name: "valid mysql localhost",
 			given: given{
 				opts: command.Options{
-					Driver: "mysql",
-					URL:    "mysql://user:password@tcp(localhost:3306)/db",
+					URL: "mysql://user:password@tcp(localhost:3306)/db",
 				},
 			},
 			want: want{
@@ -102,8 +84,7 @@ func TestBuildConnectionFromOptsFromURL(t *testing.T) {
 			name: "valid mysql localhost with params",
 			given: given{
 				opts: command.Options{
-					Driver: "mysql",
-					URL:    "mysql://user:password@tcp(localhost:3306)/db?charset=utf8",
+					URL: "mysql://user:password@tcp(localhost:3306)/db?charset=utf8",
 				},
 			},
 			want: want{
@@ -114,8 +95,7 @@ func TestBuildConnectionFromOptsFromURL(t *testing.T) {
 			name: "valid mysql remote url",
 			given: given{
 				opts: command.Options{
-					Driver: "mysql",
-					URL:    "mysql://user:password@tcp(your-amazonaws-uri.com:3306)/dbname",
+					URL: "mysql://user:password@tcp(your-amazonaws-uri.com:3306)/dbname",
 				},
 			},
 			want: want{
@@ -126,26 +106,24 @@ func TestBuildConnectionFromOptsFromURL(t *testing.T) {
 			name: "error misspelled mysql",
 			given: given{
 				opts: command.Options{
-					Driver: "mysql",
-					URL:    "mysq://user:password@tcp(localhost:3306)/db?charset=utf8",
+					URL: "mysq://user:password@tcp(localhost:3306)/db?charset=utf8",
 				},
 			},
 			want: want{
 				hasError: true,
-				err:      ErrInvalidMySQLURLFormat,
+				err:      ErrInvalidURLFormat,
 			},
 		},
 		{
 			name: "error invalid url",
 			given: given{
 				opts: command.Options{
-					Driver: "mysql",
-					URL:    "not-a-url",
+					URL: "not-a-url",
 				},
 			},
 			want: want{
 				hasError: true,
-				err:      ErrInvalidMySQLURLFormat,
+				err:      ErrInvalidURLFormat,
 			},
 		},
 	}
@@ -155,7 +133,7 @@ func TestBuildConnectionFromOptsFromURL(t *testing.T) {
 		t.Run(tc.name, func(t *testing.T) {
 			t.Parallel()
 
-			uri, err := BuildConnectionFromOpts(tc.given.opts)
+			uri, _, err := BuildConnectionFromOpts(tc.given.opts)
 
 			if tc.want.hasError {
 				assert.Error(t, err)
@@ -311,8 +289,7 @@ func TestFormatPostgresURL(t *testing.T) {
 			name: "valid postgres localhost",
 			given: given{
 				opts: command.Options{
-					Driver: "postgres",
-					URL:    "postgres://user:password@localhost:5432/db?sslmode=disable",
+					URL: "postgres://user:password@localhost:5432/db?sslmode=disable",
 				},
 			},
 			want: want{
@@ -323,8 +300,7 @@ func TestFormatPostgresURL(t *testing.T) {
 			name: "valid postgres localhost but add sslmode",
 			given: given{
 				opts: command.Options{
-					Driver: "postgres",
-					URL:    "postgres://user:password@localhost:5432/db",
+					URL: "postgres://user:password@localhost:5432/db",
 				},
 			},
 			want: want{
@@ -335,8 +311,7 @@ func TestFormatPostgresURL(t *testing.T) {
 			name: "valid postgres localhost postgresql as protocol",
 			given: given{
 				opts: command.Options{
-					Driver: "postgres",
-					URL:    "postgresql://user:password@localhost:5432/db",
+					URL: "postgresql://user:password@localhost:5432/db",
 				},
 			},
 			want: want{
@@ -347,8 +322,7 @@ func TestFormatPostgresURL(t *testing.T) {
 			name: "error misspelled postgres",
 			given: given{
 				opts: command.Options{
-					Driver: "postgres",
-					URL:    "potgre://user:password@localhost:5432/db",
+					URL: "potgre://user:password@localhost:5432/db",
 				},
 			},
 			want: want{
@@ -360,8 +334,7 @@ func TestFormatPostgresURL(t *testing.T) {
 			name: "error invalid url",
 			given: given{
 				opts: command.Options{
-					Driver: "postgres",
-					URL:    "not-a-url",
+					URL: "not-a-url",
 				},
 			},
 			want: want{
@@ -411,8 +384,7 @@ func TestFormatMySQLURL(t *testing.T) {
 			name: "valid mysql localhost",
 			given: given{
 				opts: command.Options{
-					Driver: "mysql",
-					URL:    "mysql://user:password@tcp(localhost:3306)/db",
+					URL: "mysql://user:password@tcp(localhost:3306)/db",
 				},
 			},
 			want: want{
@@ -423,8 +395,7 @@ func TestFormatMySQLURL(t *testing.T) {
 			name: "valid mysql localhost with params",
 			given: given{
 				opts: command.Options{
-					Driver: "mysql",
-					URL:    "mysql://user:password@tcp(localhost:3306)/db?charset=utf8",
+					URL: "mysql://user:password@tcp(localhost:3306)/db?charset=utf8",
 				},
 			},
 			want: want{
@@ -435,8 +406,7 @@ func TestFormatMySQLURL(t *testing.T) {
 			name: "valid mysql remote url",
 			given: given{
 				opts: command.Options{
-					Driver: "mysql",
-					URL:    "mysql://user:password@tcp(your-amazonaws-uri.com:3306)/dbname",
+					URL: "mysql://user:password@tcp(your-amazonaws-uri.com:3306)/dbname",
 				},
 			},
 			want: want{
@@ -447,8 +417,7 @@ func TestFormatMySQLURL(t *testing.T) {
 			name: "valid mysql traditional",
 			given: given{
 				opts: command.Options{
-					Driver: "mysql",
-					URL:    "mysql://user:password@/dbname?sql_mode=TRADITIONAL",
+					URL: "mysql://user:password@/dbname?sql_mode=TRADITIONAL",
 				},
 			},
 			want: want{
@@ -459,8 +428,7 @@ func TestFormatMySQLURL(t *testing.T) {
 			name: "valid mysql google cloud sql on app engine",
 			given: given{
 				opts: command.Options{
-					Driver: "mysql",
-					URL:    "mysql://user:password@unix(/cloudsql/project-id:region-name:instance-name)/dbname",
+					URL: "mysql://user:password@unix(/cloudsql/project-id:region-name:instance-name)/dbname",
 				},
 			},
 			want: want{
@@ -471,8 +439,7 @@ func TestFormatMySQLURL(t *testing.T) {
 			name: "error misspelled mysql",
 			given: given{
 				opts: command.Options{
-					Driver: "mysql",
-					URL:    "mysq://user:password@tcp(localhost:3306)/db?charset=utf8",
+					URL: "mysq://user:password@tcp(localhost:3306)/db?charset=utf8",
 				},
 			},
 			want: want{
@@ -484,8 +451,7 @@ func TestFormatMySQLURL(t *testing.T) {
 			name: "error invalid url",
 			given: given{
 				opts: command.Options{
-					Driver: "mysql",
-					URL:    "not-a-url",
+					URL: "not-a-url",
 				},
 			},
 			want: want{