Skip to content

Commit 1815d4e

Browse files
authored
fix: Handle remote url with trailing slash (#74)
1 parent e4ba5cf commit 1815d4e

File tree

2 files changed

+127
-1
lines changed

2 files changed

+127
-1
lines changed

internal/client/client.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import (
66
"crypto/x509"
77
"encoding/json"
88
"fmt"
9+
"net/url"
10+
"strings"
911
"time"
1012

1113
"github.com/observiq/bindplane-op-action/internal/client/config"
@@ -29,6 +31,18 @@ type BindPlane struct {
2931
client *resty.Client
3032
}
3133

34+
func buildBaseURL(remoteURL string) (string, error) {
35+
parsedURL, err := url.Parse(remoteURL)
36+
if err != nil {
37+
return "", fmt.Errorf("failed to parse remote URL: %w", err)
38+
}
39+
40+
// Ensure the path doesn't end with a slash
41+
parsedURL.Path = strings.TrimRight(parsedURL.Path, "/")
42+
43+
return parsedURL.String(), nil
44+
}
45+
3246
// NewBindPlane takes a config and logger and returns a configured BindPlane client
3347
func NewBindPlane(config *config.Config, logger *zap.Logger) (*BindPlane, error) {
3448
restryClient := resty.New()
@@ -43,7 +57,11 @@ func NewBindPlane(config *config.Config, logger *zap.Logger) (*BindPlane, error)
4357
restryClient.SetHeader(KeyHeader, config.Auth.APIKey)
4458
}
4559

46-
restryClient.SetBaseURL(fmt.Sprintf("%s/v1", config.Network.RemoteURL))
60+
baseURL, err := buildBaseURL(config.Network.RemoteURL)
61+
if err != nil {
62+
return nil, fmt.Errorf("failed to build base URL: %w", err)
63+
}
64+
restryClient.SetBaseURL(fmt.Sprintf("%s/v1", baseURL))
4765

4866
userAgent := DefaultUserAgent
4967
if config.Network.UserAgent != "" {

internal/client/client_test.go

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package client
2+
3+
import (
4+
"testing"
5+
)
6+
7+
func TestBuildBaseURL(t *testing.T) {
8+
tests := []struct {
9+
name string
10+
input string
11+
expected string
12+
wantErr bool
13+
}{
14+
{
15+
name: "URL without trailing slash",
16+
input: "https://example.com",
17+
expected: "https://example.com",
18+
wantErr: false,
19+
},
20+
{
21+
name: "URL with trailing slash",
22+
input: "https://example.com/",
23+
expected: "https://example.com",
24+
wantErr: false,
25+
},
26+
{
27+
name: "URL with multiple trailing slashes",
28+
input: "https://example.com///",
29+
expected: "https://example.com",
30+
wantErr: false,
31+
},
32+
{
33+
name: "URL with path and trailing slash",
34+
input: "https://example.com/api/",
35+
expected: "https://example.com/api",
36+
wantErr: false,
37+
},
38+
{
39+
name: "URL with path without trailing slash",
40+
input: "https://example.com/api",
41+
expected: "https://example.com/api",
42+
wantErr: false,
43+
},
44+
{
45+
name: "URL with query parameters",
46+
input: "https://example.com/api/?param=value",
47+
expected: "https://example.com/api?param=value",
48+
wantErr: false,
49+
},
50+
{
51+
name: "URL with fragment",
52+
input: "https://example.com/api/#section",
53+
expected: "https://example.com/api#section",
54+
wantErr: false,
55+
},
56+
{
57+
name: "URL with port",
58+
input: "https://example.com:8080/",
59+
expected: "https://example.com:8080",
60+
wantErr: false,
61+
},
62+
{
63+
name: "HTTP URL",
64+
input: "http://localhost:3000/",
65+
expected: "http://localhost:3000",
66+
wantErr: false,
67+
},
68+
{
69+
name: "Invalid URL (treated as relative path)",
70+
input: "not-a-valid-url",
71+
expected: "not-a-valid-url",
72+
wantErr: false,
73+
},
74+
{
75+
name: "Empty string (treated as relative path)",
76+
input: "",
77+
expected: "",
78+
wantErr: false,
79+
},
80+
}
81+
82+
for _, tt := range tests {
83+
t.Run(tt.name, func(t *testing.T) {
84+
result, err := buildBaseURL(tt.input)
85+
86+
if tt.wantErr {
87+
if err == nil {
88+
t.Errorf("buildBaseURL() expected error but got none")
89+
}
90+
return
91+
}
92+
93+
if err != nil {
94+
t.Errorf("buildBaseURL() unexpected error: %v", err)
95+
return
96+
}
97+
98+
if result != tt.expected {
99+
t.Errorf("buildBaseURL() = %v, want %v", result, tt.expected)
100+
}
101+
102+
// Additional check: ensure result doesn't end with trailing slash
103+
if len(result) > 0 && result[len(result)-1] == '/' {
104+
t.Errorf("buildBaseURL() result ends with trailing slash: %v", result)
105+
}
106+
})
107+
}
108+
}

0 commit comments

Comments
 (0)