Expose URL() on Request to allow building URLs

The client/Request type is the appropriate place to build
URLs, this allows callers to generate URLs for providing to
others (such as SelfLinks or relative links to objects).
This commit is contained in:
Clayton Coleman 2015-04-29 23:27:13 -04:00
parent 6f5e08114a
commit 53ca1fcc7a
2 changed files with 27 additions and 24 deletions

View File

@ -455,7 +455,8 @@ func (r *Request) Body(obj interface{}) *Request {
return r
}
func (r *Request) finalURL() string {
// URL returns the current working URL.
func (r *Request) URL() *url.URL {
p := r.path
if r.namespaceSet && !r.namespaceInQuery && len(r.namespace) > 0 {
p = path.Join(p, "namespaces", r.namespace)
@ -472,9 +473,9 @@ func (r *Request) finalURL() string {
p = path.Join(p, r.resourceName, r.subresource, r.subpath)
}
finalURL := url.URL{}
finalURL := &url.URL{}
if r.baseURL != nil {
finalURL = *r.baseURL
*finalURL = *r.baseURL
}
finalURL.Path = p
@ -494,16 +495,16 @@ func (r *Request) finalURL() string {
query.Set("timeout", r.timeout.String())
}
finalURL.RawQuery = query.Encode()
return finalURL.String()
return finalURL
}
// Similar to finalURL(), but if the request contains name of an object
// finalURLTemplate is similar to URL(), but if the request contains name of an object
// (e.g. GET for a specific Pod) it will be substited with "<name>".
func (r Request) finalURLTemplate() string {
func (r *Request) finalURLTemplate() string {
if len(r.resourceName) != 0 {
r.resourceName = "<name>"
}
return r.finalURL()
return r.URL().String()
}
// Watch attempts to begin watching the requested location.
@ -512,7 +513,8 @@ func (r *Request) Watch() (watch.Interface, error) {
if r.err != nil {
return nil, r.err
}
req, err := http.NewRequest(r.verb, r.finalURL(), r.body)
url := r.URL().String()
req, err := http.NewRequest(r.verb, url, r.body)
if err != nil {
return nil, err
}
@ -533,7 +535,7 @@ func (r *Request) Watch() (watch.Interface, error) {
if result := r.transformResponse(resp, req); result.err != nil {
return nil, result.err
}
return nil, fmt.Errorf("for request '%+v', got status: %v", req.URL, resp.StatusCode)
return nil, fmt.Errorf("for request '%+v', got status: %v", url, resp.StatusCode)
}
return watch.NewStreamWatcher(watchjson.NewDecoder(resp.Body, r.codec)), nil
}
@ -546,7 +548,8 @@ func (r *Request) Stream() (io.ReadCloser, error) {
if r.err != nil {
return nil, r.err
}
req, err := http.NewRequest(r.verb, r.finalURL(), nil)
url := r.URL().String()
req, err := http.NewRequest(r.verb, url, nil)
if err != nil {
return nil, err
}
@ -567,7 +570,7 @@ func (r *Request) Stream() (io.ReadCloser, error) {
// we have a decent shot at taking the object returned, parsing it as a status object and returning a more normal error
bodyBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("%v while accessing %v", resp.Status, r.finalURL())
return nil, fmt.Errorf("%v while accessing %v", resp.Status, url)
}
if runtimeObject, err := r.codec.Decode(bodyBytes); err == nil {
@ -579,7 +582,7 @@ func (r *Request) Stream() (io.ReadCloser, error) {
}
bodyText := string(bodyBytes)
return nil, fmt.Errorf("%s while accessing %v: %s", resp.Status, r.finalURL(), bodyText)
return nil, fmt.Errorf("%s while accessing %v: %s", resp.Status, url, bodyText)
}
return resp.Body, nil
@ -606,7 +609,7 @@ func (r *Request) Upgrade(config *Config, newRoundTripperFunc func(*tls.Config)
r.client = &http.Client{Transport: wrapper}
req, err := http.NewRequest(r.verb, r.finalURL(), nil)
req, err := http.NewRequest(r.verb, r.URL().String(), nil)
if err != nil {
return nil, fmt.Errorf("Error creating request: %s", err)
}
@ -647,8 +650,8 @@ func (r *Request) request(fn func(*http.Request, *http.Response)) error {
maxRetries := 10
retries := 0
for {
url := r.finalURL()
req, err := http.NewRequest(r.verb, r.finalURL(), r.body)
url := r.URL().String()
req, err := http.NewRequest(r.verb, url, r.body)
if err != nil {
return err
}

View File

@ -71,26 +71,26 @@ func TestRequestWithErrorWontChange(t *testing.T) {
func TestRequestPreservesBaseTrailingSlash(t *testing.T) {
r := &Request{baseURL: &url.URL{}, path: "/path/", namespaceInQuery: true}
if s := r.finalURL(); s != "/path/" {
if s := r.URL().String(); s != "/path/" {
t.Errorf("trailing slash should be preserved: %s", s)
}
}
func TestRequestAbsPathPreservesTrailingSlash(t *testing.T) {
r := (&Request{baseURL: &url.URL{}, namespaceInQuery: true}).AbsPath("/foo/")
if s := r.finalURL(); s != "/foo/" {
if s := r.URL().String(); s != "/foo/" {
t.Errorf("trailing slash should be preserved: %s", s)
}
r = (&Request{baseURL: &url.URL{}}).AbsPath("/foo/")
if s := r.finalURL(); s != "/foo/" {
if s := r.URL().String(); s != "/foo/" {
t.Errorf("trailing slash should be preserved: %s", s)
}
}
func TestRequestAbsPathJoins(t *testing.T) {
r := (&Request{baseURL: &url.URL{}, namespaceInQuery: true}).AbsPath("foo/bar", "baz")
if s := r.finalURL(); s != "foo/bar/baz" {
if s := r.URL().String(); s != "foo/bar/baz" {
t.Errorf("trailing slash should be preserved: %s", s)
}
}
@ -105,7 +105,7 @@ func TestRequestSetsNamespace(t *testing.T) {
if r.namespace == "" {
t.Errorf("namespace should be set: %#v", r)
}
if s := r.finalURL(); s != "?namespace=foo" {
if s := r.URL().String(); s != "?namespace=foo" {
t.Errorf("namespace should be in params: %s", s)
}
@ -114,7 +114,7 @@ func TestRequestSetsNamespace(t *testing.T) {
Path: "/",
},
}).Namespace("foo")
if s := r.finalURL(); s != "namespaces/foo" {
if s := r.URL().String(); s != "namespaces/foo" {
t.Errorf("namespace should be in path: %s", s)
}
}
@ -124,7 +124,7 @@ func TestRequestOrdersNamespaceInPath(t *testing.T) {
baseURL: &url.URL{},
path: "/test/",
}).Name("bar").Resource("baz").Namespace("foo")
if s := r.finalURL(); s != "/test/namespaces/foo/baz/bar" {
if s := r.URL().String(); s != "/test/namespaces/foo/baz/bar" {
t.Errorf("namespace should be in order in path: %s", s)
}
}
@ -134,7 +134,7 @@ func TestRequestOrdersSubResource(t *testing.T) {
baseURL: &url.URL{},
path: "/test/",
}).Name("bar").Resource("baz").Namespace("foo").Suffix("test").SubResource("a", "b")
if s := r.finalURL(); s != "/test/namespaces/foo/baz/bar/a/b/test" {
if s := r.URL().String(); s != "/test/namespaces/foo/baz/bar/a/b/test" {
t.Errorf("namespace should be in order in path: %s", s)
}
}
@ -1028,7 +1028,7 @@ func TestUintParam(t *testing.T) {
for _, item := range table {
c := NewOrDie(&Config{})
r := c.Get().AbsPath("").UintParam(item.name, item.testVal)
if e, a := item.expectStr, r.finalURL(); e != a {
if e, a := item.expectStr, r.URL().String(); e != a {
t.Errorf("expected %v, got %v", e, a)
}
}