Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gitlab: Handle query parameters in REST trait #4632

Merged
merged 2 commits into from
Oct 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 25 additions & 4 deletions internal/providers/gitlab/gitlab_rest.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,11 @@ func (c *gitlabClient) GetBaseURL() string {

// NewRequest implements the REST provider interface
func (c *gitlabClient) NewRequest(method, requestPath string, body any) (*http.Request, error) {
base, err := url.Parse(c.glcfg.Endpoint)
u, err := getParsedURL(c.glcfg.Endpoint, requestPath)
if err != nil {
return nil, fmt.Errorf("failed to parse URL: %w", err)
return nil, err
}

u := base.JoinPath(requestPath)

var buf io.ReadWriter
if body != nil {
buf = &bytes.Buffer{}
Expand Down Expand Up @@ -118,3 +116,26 @@ func glRESTGet[T any](ctx context.Context, cli genericRESTClient, path string, o

return nil
}

func getParsedURL(endpoint, path string) (*url.URL, error) {
base, err := url.Parse(endpoint)
if err != nil {
return nil, fmt.Errorf("failed to parse URL: %w", err)
}

// Explicitly parse path and query parameters. This is to ensure that
// the path is properly escaped and that the query parameters are
// properly encoded.
parsedPathAndQuery, err := url.Parse(path)
if err != nil {
return nil, fmt.Errorf("failed to parse path: %w", err)
}

u := base.JoinPath(parsedPathAndQuery.Path)

// These have already been escaped by the URL parser
u.RawQuery = parsedPathAndQuery.RawQuery
u.Fragment = parsedPathAndQuery.Fragment

return u, nil
}
96 changes: 95 additions & 1 deletion internal/providers/gitlab/gitlab_rest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ func Test_gitlabClient_NewRequest(t *testing.T) {
name: "invalid URL gets cleaned up",
method: http.MethodGet,
requestPath: "..:/invalid-url",
wantErr: false,
wantErr: true,
},
{
name: "invalid URL parsing error",
Expand Down Expand Up @@ -273,3 +273,97 @@ func Test_glRESTGet(t *testing.T) {
})
}
}

func Test_getParsedURL(t *testing.T) {
t.Parallel()

type args struct {
endpoint string
// This may include query parameters
path string
}
tests := []struct {
name string
args args
want *url.URL
wantErr bool
}{
{
name: "valid URL",
args: args{
endpoint: "http://example.com/v1/api",
path: "/test",
},
want: &url.URL{
Scheme: "http",
Host: "example.com",
Path: "/v1/api/test",
},
},
{
name: "valid URL with query parameters",
args: args{
endpoint: "http://example.com/v1/api",
path: "/test?param1=value1&param2=value2",
},
want: &url.URL{
Scheme: "http",
Host: "example.com",
Path: "/v1/api/test",
RawQuery: "param1=value1&param2=value2",
},
},
{
name: "valid URL with fragment identifier",
args: args{
endpoint: "http://example.com/v1/api",
path: "/test#section",
},
want: &url.URL{
Scheme: "http",
Host: "example.com",
Path: "/v1/api/test",
Fragment: "section",
},
},
{
name: "invalid URL",
args: args{
endpoint: "://example.com:80:80",
path: "/test",
},
wantErr: true,
},
{
name: "query parameter that needs escaping",
args: args{
endpoint: "http://example.com",
path: "/test?param1=va lue1",
},
want: &url.URL{
Scheme: "http",
Host: "example.com",
Path: "test",
RawQuery: "param1=va%20lue1",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()

got, err := getParsedURL(tt.args.endpoint, tt.args.path)
if tt.wantErr {
assert.Error(t, err)
return
}

assert.NoError(t, err)
assert.Equal(t, tt.want.Scheme, got.Scheme, "Expected scheme to be equal")
assert.Equal(t, tt.want.Host, got.Host, "Expected host to be equal")
assert.Equal(t, tt.want.Path, got.Path, "Expected path to be equal")
assert.Equal(t, tt.want.Query(), got.Query(), "Expected query to be equal")
assert.Equal(t, tt.want.Fragment, got.Fragment, "Expected fragment to be equal")
})
}
}