Skip to content

Commit

Permalink
feat: add option for json output (#109)
Browse files Browse the repository at this point in the history
  • Loading branch information
xoxys authored Nov 5, 2024
1 parent 6507c1d commit d79274f
Show file tree
Hide file tree
Showing 25 changed files with 361 additions and 161 deletions.
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ NAME:
url-parser - Parse URL and shows the part of it.

USAGE:
url-parser [global options] command [command options] [arguments...]
url-parser [global options] command [command options]

VERSION:
devel
Expand All @@ -45,7 +45,7 @@ COMMANDS:
user, u Get username from url
password, pw Get password from url
path, pt Get path from url
host, h Get hostname from url
host, ht Get hostname from url
port, p Get port from url
query, q Get query from url
fragment, f Get fragment from url
Expand Down Expand Up @@ -81,6 +81,10 @@ somevalue
# It is also possible to read the URL from stdin
$ echo "https://somedomain.com" | url-parser host
somedomain.com

# Get json output or all parsed parts
$ url-parser --url https://somedomain.com/?some-key=somevalue all --json
{"scheme":"https","hostname":"somedomain.com","port":"","path":"/","fragment":"","rawQuery":"some-key=somevalue","queryParams":[{"key":"some-key","value":"somevalue"}],"username":"","password":""}
```

## Contributors
Expand Down
1 change: 1 addition & 0 deletions cmd/url-parser/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ func main() {
Aliases: []string{"a"},
Usage: "Get all parts from url",
Action: command.Run(cfg),
Flags: command.AllFlags(cfg),
},
{
Name: "scheme",
Expand Down
97 changes: 93 additions & 4 deletions command/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,102 @@ import (
"github.com/thegeeklab/url-parser/config"
)

func parseURL(raw string) *url.URL {
urlString := strings.TrimSpace(raw)
type QueryParam struct {
Key string `json:"key"`
Value string `json:"value"`
}

type URL struct {
url *url.URL

Scheme string `json:"scheme"`
Hostname string `json:"hostname"`
Port string `json:"port"`
Path string `json:"path"`
Fragment string `json:"fragment"`
RawQuery string `json:"rawQuery"`
Query string `json:"-"`
QueryParams []QueryParam `json:"queryParams"`
Username string `json:"username"`
Password string `json:"password"`
}

url, err := url.Parse(urlString)
func (u *URL) String() string {
return u.url.String()
}

type Parser struct {
URL string
QueryField string
QuerySplit bool
}

func NewURLParser(url, queryField string, querySplit bool) *Parser {
return &Parser{
URL: url,
QueryField: queryField,
QuerySplit: querySplit,
}
}

func (p *Parser) parse() *URL {
urlString := strings.TrimSpace(p.URL)

parts, err := url.Parse(urlString)
if err != nil {
log.Fatal().Err(err).Msg(config.ErrParseURL.Error())
}

return url
extURL := &URL{
url: parts,
Scheme: parts.Scheme,
Hostname: parts.Hostname(),
Path: parts.Path,
Fragment: parts.Fragment,
QueryParams: []QueryParam{},
}

if len(parts.Scheme) > 0 {
extURL.Hostname = parts.Hostname()
extURL.Port = parts.Port()
}

if parts.User != nil {
if len(parts.User.Username()) > 0 {
extURL.Username = parts.User.Username()
}
}

if parts.User != nil {
pw, _ := parts.User.Password()
if len(pw) > 0 {
extURL.Password = pw
}
}

// Handle query field extraction
if parts.RawQuery != "" {
extURL.RawQuery = parts.RawQuery
}

if p.QueryField != "" {
if result := parts.Query().Get(p.QueryField); result != "" {
extURL.Query = result
}
} else {
extURL.Query = parts.RawQuery
}

// Handle query parameter splitting
values := parts.Query()
for k, v := range values {
if len(v) > 0 {
extURL.QueryParams = append(extURL.QueryParams, QueryParam{
Key: k,
Value: v[0],
})
}
}

return extURL
}
62 changes: 47 additions & 15 deletions command/commands_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,62 @@ package command
import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/thegeeklab/url-parser/config"
)

type TestParseData struct {
config *config.Config
expected string
}

func TestParseURL(t *testing.T) {
func TestParse(t *testing.T) {
//nolint:goconst
urlString := "postgres://user:pass@host.com:5432/path/to?key=value&other=other%20value#some-fragment"

tables := []TestParseData{
tests := []struct {
name string
config *config.Config
expected *URL
}{
{
config: &config.Config{URL: urlString},
expected: urlString,
name: "parse url",
config: &config.Config{
URL: urlString,
QuerySplit: true,
},
expected: &URL{
Scheme: "postgres",
Username: "user",
Password: "pass",
Hostname: "host.com",
Port: "5432",
Path: "/path/to",
Query: "key=value&other=other%20value",
RawQuery: "key=value&other=other%20value",
QueryParams: []QueryParam{
{
Key: "key",
Value: "value",
},
{
Key: "other",
Value: "other value",
},
},
Fragment: "some-fragment",
},
},
}

for _, table := range tables {
result := parseURL(urlString)

if result.String() != table.expected {
t.Fatalf("URL `%v`, should be `%v`", result, table.expected)
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := NewURLParser(urlString, "", false).parse()
assert.Equal(t, tt.expected.Scheme, result.Scheme)
assert.Equal(t, tt.expected.Username, result.Username)
assert.Equal(t, tt.expected.Password, result.Password)
assert.Equal(t, tt.expected.Hostname, result.Hostname)
assert.Equal(t, tt.expected.Port, result.Port)
assert.Equal(t, tt.expected.Path, result.Path)
assert.Equal(t, tt.expected.Fragment, result.Fragment)
assert.Equal(t, tt.expected.RawQuery, result.RawQuery)
assert.Equal(t, tt.expected.Query, result.Query)
assert.ElementsMatch(t, tt.expected.QueryParams, result.QueryParams)
})
}
}
2 changes: 1 addition & 1 deletion command/fragment.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
// Fragment prints out the fragment part from the url.
func Fragment(cfg *config.Config) cli.ActionFunc {
return func(_ *cli.Context) error {
parts := parseURL(cfg.URL)
parts := NewURLParser(cfg.URL, cfg.QueryField, cfg.QuerySplit).parse()

if len(parts.Scheme) > 0 {
fmt.Println(parts.Fragment)
Expand Down
24 changes: 12 additions & 12 deletions command/fragment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,34 @@ import (
"strings"
"testing"

"github.com/stretchr/testify/assert"
"github.com/thegeeklab/url-parser/config"
"github.com/urfave/cli/v2"
"github.com/zenizh/go-capturer"
)

type TestFragmentData struct {
config *config.Config
expected string
}

func TestFragment(t *testing.T) {
urlString := "postgres://user:pass@host.com:5432/path/to?key=value&other=other%20value#some-fragment"

tables := []TestFragmentData{
tests := []struct {
name string
config *config.Config
expected string
}{
{
name: "get fragment",
config: &config.Config{URL: urlString},
expected: "some-fragment",
},
}

for _, table := range tables {
for _, tt := range tests {
app := cli.NewApp()
ctx := cli.NewContext(app, nil, nil)

result := strings.TrimSpace(capturer.CaptureStdout(func() { _ = Fragment(table.config)(ctx) }))

if result != table.expected {
t.Fatalf("URL fragment `%v`, should be `%v`", result, table.expected)
}
t.Run(tt.name, func(t *testing.T) {
result := strings.TrimSpace(capturer.CaptureStdout(func() { _ = Fragment(tt.config)(ctx) }))
assert.Equal(t, tt.expected, result)
})
}
}
4 changes: 2 additions & 2 deletions command/host.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import (
// Host prints out the host part from the url.
func Host(cfg *config.Config) cli.ActionFunc {
return func(_ *cli.Context) error {
parts := parseURL(cfg.URL)
parts := NewURLParser(cfg.URL, cfg.QueryField, cfg.QuerySplit).parse()

if len(parts.Scheme) > 0 {
fmt.Println(parts.Hostname())
fmt.Println(parts.Hostname)
}

return nil
Expand Down
24 changes: 12 additions & 12 deletions command/host_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,34 @@ import (
"strings"
"testing"

"github.com/stretchr/testify/assert"
"github.com/thegeeklab/url-parser/config"
"github.com/urfave/cli/v2"
"github.com/zenizh/go-capturer"
)

type TestHostnameData struct {
config *config.Config
expected string
}

func TestHost(t *testing.T) {
urlString := "postgres://user:pass@host.com:5432/path/to?key=value&other=other%20value#some-fragment"

tables := []TestHostnameData{
tests := []struct {
name string
config *config.Config
expected string
}{
{
name: "get host",
config: &config.Config{URL: urlString},
expected: "host.com",
},
}

for _, table := range tables {
for _, tt := range tests {
app := cli.NewApp()
ctx := cli.NewContext(app, nil, nil)

result := strings.TrimSpace(capturer.CaptureStdout(func() { _ = Host(table.config)(ctx) }))

if result != table.expected {
t.Fatalf("URL host `%v`, should be `%v`", result, table.expected)
}
t.Run(tt.name, func(t *testing.T) {
result := strings.TrimSpace(capturer.CaptureStdout(func() { _ = Host(tt.config)(ctx) }))
assert.Equal(t, tt.expected, result)
})
}
}
9 changes: 3 additions & 6 deletions command/password.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,10 @@ import (
// Password prints out the password part from url.
func Password(cfg *config.Config) cli.ActionFunc {
return func(_ *cli.Context) error {
parts := parseURL(cfg.URL)
parts := NewURLParser(cfg.URL, cfg.QueryField, cfg.QuerySplit).parse()

if parts.User != nil {
pw, _ := parts.User.Password()
if len(pw) > 0 {
fmt.Println(pw)
}
if parts.Password != "" {
fmt.Println(parts.Password)
}

return nil
Expand Down
Loading

0 comments on commit d79274f

Please sign in to comment.