launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #15026
[Merge] lp:~rvb/maas/gomaasapi-baseURL into lp:~maas-maintainers/maas/gomaasapi
Raphaël Badin has proposed merging lp:~rvb/maas/gomaasapi-baseURL into lp:~maas-maintainers/maas/gomaasapi.
Commit message:
Move BaseURL into the Client; add the AppendSlash utility to make sure we generate Django-compatible urls.
Requested reviews:
MAAS Maintainers (maas-maintainers)
For more details, see:
https://code.launchpad.net/~rvb/maas/gomaasapi-baseURL/+merge/145180
- Base URL is now a field in the Client objects (and not the Server object [i.e. a maasobject representing a MAAS server]).
- use url.URL internally to manipulate url objects.
- AppendSlash allows us, when using GetSubObject, to create urls ending with a slash. This is to keep Django [as configured by MAAS] happy (see https://docs.djangoproject.com/en/dev/ref/settings/#append-slash for details).
--
https://code.launchpad.net/~rvb/maas/gomaasapi-baseURL/+merge/145180
Your team MAAS Maintainers is requested to review the proposed merge of lp:~rvb/maas/gomaasapi-baseURL into lp:~maas-maintainers/maas/gomaasapi.
=== modified file 'client.go'
--- client.go 2013-01-28 08:18:15 +0000
+++ client.go 2013-01-28 13:30:59 +0000
@@ -13,7 +13,8 @@
)
type Client struct {
- Signer OAuthSigner
+ BaseURL *url.URL
+ Signer OAuthSigner
}
const (
@@ -37,7 +38,11 @@
return body, nil
}
-func (client Client) Get(URL string, operation string, parameters url.Values) ([]byte, error) {
+func (client Client) GetURL(URI *url.URL) *url.URL {
+ return client.BaseURL.ResolveReference(URI)
+}
+
+func (client Client) Get(URI *url.URL, operation string, parameters url.Values) ([]byte, error) {
opParameter := parameters.Get(operationParamName)
if opParameter != "" {
errString := fmt.Sprintf("The parameters contain a value for '%s' which is reserved parameter.")
@@ -46,8 +51,9 @@
if operation != "" {
parameters.Set(operationParamName, operation)
}
- queryUrl := URL + "?" + parameters.Encode()
- request, err := http.NewRequest("GET", queryUrl, nil)
+ queryUrl := client.GetURL(URI)
+ queryUrl.RawQuery = parameters.Encode()
+ request, err := http.NewRequest("GET", queryUrl.String(), nil)
if err != nil {
return nil, err
}
@@ -55,30 +61,29 @@
}
// nonIdempotentRequest is a utility method to issue a PUT or a POST request.
-func (client Client) nonIdempotentRequest(method string, URL string, parameters url.Values) ([]byte, error) {
- request, err := http.NewRequest(method, URL, strings.NewReader(string(parameters.Encode())))
+func (client Client) nonIdempotentRequest(method string, URI *url.URL, parameters url.Values) ([]byte, error) {
+ URL := client.GetURL(URI)
+ request, err := http.NewRequest(method, URL.String(), strings.NewReader(string(parameters.Encode())))
if err != nil {
return nil, err
}
request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
- if err != nil {
- return nil, err
- }
return client.dispatchRequest(request)
}
-func (client Client) Post(URL string, operation string, parameters url.Values) ([]byte, error) {
+func (client Client) Post(URI *url.URL, operation string, parameters url.Values) ([]byte, error) {
queryParams := url.Values{operationParamName: {operation}}
- queryURL := URL + "?" + queryParams.Encode()
- return client.nonIdempotentRequest("POST", queryURL, parameters)
-}
-
-func (client Client) Put(URL string, parameters url.Values) ([]byte, error) {
- return client.nonIdempotentRequest("PUT", URL, parameters)
-}
-
-func (client Client) Delete(URL string) error {
- request, err := http.NewRequest("DELETE", URL, strings.NewReader(""))
+ URI.RawQuery = queryParams.Encode()
+ return client.nonIdempotentRequest("POST", URI, parameters)
+}
+
+func (client Client) Put(URI *url.URL, parameters url.Values) ([]byte, error) {
+ return client.nonIdempotentRequest("PUT", URI, parameters)
+}
+
+func (client Client) Delete(URI *url.URL) error {
+ URL := client.GetURL(URI)
+ request, err := http.NewRequest("DELETE", URL.String(), strings.NewReader(""))
if err != nil {
return err
}
@@ -99,14 +104,18 @@
var _ OAuthSigner = (*anonSigner)(nil)
// NewAnonymousClient creates a client that issues anonymous requests.
-func NewAnonymousClient() (*Client, error) {
- return &Client{Signer: &anonSigner{}}, nil
+func NewAnonymousClient(BaseURL string) (*Client, error) {
+ parsedBaseURL, err := url.Parse(BaseURL)
+ if err != nil {
+ return nil, err
+ }
+ return &Client{Signer: &anonSigner{}, BaseURL: parsedBaseURL}, nil
}
// NewAuthenticatedClient parses the given MAAS API key into the individual
// OAuth tokens and creates an Client that will use these tokens to sign the
// requests it issues.
-func NewAuthenticatedClient(apiKey string) (*Client, error) {
+func NewAuthenticatedClient(BaseURL string, apiKey string) (*Client, error) {
elements := strings.Split(apiKey, ":")
if len(elements) != 3 {
errString := "Invalid API key. The format of the key must be \"<consumer secret>:<token key>:<token secret>\"."
@@ -124,5 +133,9 @@
if err != nil {
return nil, err
}
- return &Client{Signer: signer}, nil
+ parsedBaseURL, err := url.Parse(BaseURL)
+ if err != nil {
+ return nil, err
+ }
+ return &Client{Signer: signer, BaseURL: parsedBaseURL}, nil
}
=== modified file 'client_test.go'
--- client_test.go 2013-01-25 17:49:25 +0000
+++ client_test.go 2013-01-28 13:30:59 +0000
@@ -13,9 +13,9 @@
func (suite *GomaasapiTestSuite) TestClientdispatchRequestReturnsError(c *C) {
URI := "/some/url/?param1=test"
expectedResult := "expected:result"
- client, _ := NewAnonymousClient()
server := newSingleServingServer(URI, expectedResult, http.StatusBadRequest)
defer server.Close()
+ client, _ := NewAnonymousClient(server.URL)
request, err := http.NewRequest("GET", server.URL+URI, nil)
result, err := client.dispatchRequest(request)
@@ -27,9 +27,9 @@
func (suite *GomaasapiTestSuite) TestClientdispatchRequestSignsRequest(c *C) {
URI := "/some/url/?param1=test"
expectedResult := "expected:result"
- client, _ := NewAuthenticatedClient("the:api:key")
server := newSingleServingServer(URI, expectedResult, http.StatusOK)
defer server.Close()
+ client, _ := NewAuthenticatedClient(server.URL, "the:api:key")
request, err := http.NewRequest("GET", server.URL+URI, nil)
result, err := client.dispatchRequest(request)
@@ -40,44 +40,44 @@
}
func (suite *GomaasapiTestSuite) TestClientGetFormatsGetParameters(c *C) {
- URI := "/some/url"
+ URI, _ := url.Parse("/some/url")
expectedResult := "expected:result"
- client, _ := NewAnonymousClient()
params := url.Values{"test": {"123"}}
- fullURI := URI + "?test=123"
+ fullURI := URI.String() + "?test=123"
server := newSingleServingServer(fullURI, expectedResult, http.StatusOK)
defer server.Close()
+ client, _ := NewAnonymousClient(server.URL)
- result, err := client.Get(server.URL+URI, "", params)
+ result, err := client.Get(URI, "", params)
c.Check(err, IsNil)
c.Check(string(result), Equals, expectedResult)
}
func (suite *GomaasapiTestSuite) TestClientGetFormatsOperationAsGetParameter(c *C) {
- URI := "/some/url"
+ URI, _ := url.Parse("/some/url")
expectedResult := "expected:result"
- client, _ := NewAnonymousClient()
- fullURI := URI + "?op=list"
+ fullURI := URI.String() + "?op=list"
server := newSingleServingServer(fullURI, expectedResult, http.StatusOK)
defer server.Close()
+ client, _ := NewAnonymousClient(server.URL)
- result, err := client.Get(server.URL+URI, "list", url.Values{})
+ result, err := client.Get(URI, "list", url.Values{})
c.Check(err, IsNil)
c.Check(string(result), Equals, expectedResult)
}
func (suite *GomaasapiTestSuite) TestClientPostSendsRequest(c *C) {
- URI := "/some/url"
+ URI, _ := url.Parse("/some/url")
expectedResult := "expected:result"
- client, _ := NewAnonymousClient()
- fullURI := URI + "?op=list"
+ fullURI := URI.String() + "?op=list"
params := url.Values{"test": {"123"}}
server := newSingleServingServer(fullURI, expectedResult, http.StatusOK)
defer server.Close()
+ client, _ := NewAnonymousClient(server.URL)
- result, err := client.Post(server.URL+URI, "list", params)
+ result, err := client.Post(URI, "list", params)
c.Check(err, IsNil)
c.Check(string(result), Equals, expectedResult)
@@ -85,14 +85,14 @@
}
func (suite *GomaasapiTestSuite) TestClientPutSendsRequest(c *C) {
- URI := "/some/url"
+ URI, _ := url.Parse("/some/url")
expectedResult := "expected:result"
- client, _ := NewAnonymousClient()
params := url.Values{"test": {"123"}}
- server := newSingleServingServer(URI, expectedResult, http.StatusOK)
+ server := newSingleServingServer(URI.String(), expectedResult, http.StatusOK)
defer server.Close()
+ client, _ := NewAnonymousClient(server.URL)
- result, err := client.Put(server.URL+URI, params)
+ result, err := client.Put(URI, params)
c.Check(err, IsNil)
c.Check(string(result), Equals, expectedResult)
@@ -100,13 +100,13 @@
}
func (suite *GomaasapiTestSuite) TestClientDeleteSendsRequest(c *C) {
- URI := "/some/url"
+ URI, _ := url.Parse("/some/url")
expectedResult := "expected:result"
- client, _ := NewAnonymousClient()
- server := newSingleServingServer(URI, expectedResult, http.StatusOK)
+ server := newSingleServingServer(URI.String(), expectedResult, http.StatusOK)
defer server.Close()
+ client, _ := NewAnonymousClient(server.URL)
- err := client.Delete(server.URL + URI)
+ err := client.Delete(URI)
c.Check(err, IsNil)
}
@@ -120,7 +120,7 @@
keyElements := []string{consumerKey, tokenKey, tokenSecret}
apiKey := strings.Join(keyElements, ":")
- client, err := NewAuthenticatedClient(apiKey)
+ client, err := NewAuthenticatedClient("http://example.com/api", apiKey)
c.Check(err, IsNil)
signer := client.Signer.(_PLAINTEXTOAuthSigner)
@@ -130,7 +130,7 @@
}
func (suite *GomaasapiTestSuite) TestNewAuthenticatedClientFailsIfInvalidKey(c *C) {
- client, err := NewAuthenticatedClient("invalid-key")
+ client, err := NewAuthenticatedClient("", "invalid-key")
c.Check(err, ErrorMatches, "Invalid API key.*")
c.Check(client, IsNil)
=== modified file 'example/live_example.go'
--- example/live_example.go 2013-01-28 08:18:15 +0000
+++ example/live_example.go 2013-01-28 13:30:59 +0000
@@ -26,14 +26,14 @@
}
func main() {
- authClient, err := gomaasapi.NewAuthenticatedClient(apiKey)
+ authClient, err := gomaasapi.NewAuthenticatedClient(apiURL, apiKey)
if err != nil {
panic(err)
}
- maas, err := gomaasapi.NewMAAS(apiURL, *authClient)
+ maas, err := gomaasapi.NewMAAS(*authClient)
- nodeListing := maas.GetSubObject("/nodes/")
+ nodeListing := maas.GetSubObject("nodes")
// List nodes.
fmt.Println("Fetching list of nodes...")
@@ -46,7 +46,8 @@
for index, nodeObj := range listNodes {
node, _ := nodeObj.GetMAASObject()
hostname, _ := node.GetField("hostname")
- fmt.Printf("Node #%d is named '%v' (%v)\n", index, hostname, node.URL())
+ nodeURL, _ := node.URL()
+ fmt.Printf("Node #%d is named '%v' (%v)\n", index, hostname, nodeURL)
}
// Create a node.
@@ -58,7 +59,8 @@
}
newNode, _ := newNodeObj.GetMAASObject()
newNodeName, _ := newNode.GetField("hostname")
- fmt.Printf("New node created: %s (%s)\n", newNodeName, newNode.URL())
+ newNodeURL, _ := newNode.URL()
+ fmt.Printf("New node created: %s (%s)\n", newNodeName, newNodeURL)
// Update the new node.
fmt.Println("Updating the new node...")
=== modified file 'jsonobject.go'
--- jsonobject.go 2013-01-25 17:49:25 +0000
+++ jsonobject.go 2013-01-28 13:30:59 +0000
@@ -66,7 +66,7 @@
// JSONObject (with the appropriate implementation of course).
// This function is recursive. Maps and arrays are deep-copied, with each
// individual value being converted to a JSONObject type.
-func maasify(client Client, baseURL string, value interface{}) JSONObject {
+func maasify(client Client, value interface{}) JSONObject {
if value == nil {
return nil
}
@@ -81,19 +81,19 @@
original := value.(map[string]interface{})
result := make(map[string]JSONObject, len(original))
for key, value := range original {
- result[key] = maasify(client, baseURL, value)
+ result[key] = maasify(client, value)
}
if _, ok := result[resource_uri]; ok {
// If the map contains "resource-uri", we can treat
// it as a MAAS object.
- return jsonMAASObject{result, client, baseURL}
+ return jsonMAASObject{result, client}
}
return jsonMap(result)
case []interface{}:
original := value.([]interface{})
result := make([]JSONObject, len(original))
for index, value := range original {
- result[index] = maasify(client, baseURL, value)
+ result[index] = maasify(client, value)
}
return jsonArray(result)
case bool:
@@ -104,13 +104,13 @@
}
// Parse a JSON blob into a JSONObject.
-func Parse(client Client, baseURL string, input []byte) (JSONObject, error) {
+func Parse(client Client, input []byte) (JSONObject, error) {
var obj interface{}
err := json.Unmarshal(input, &obj)
if err != nil {
return nil, err
}
- return maasify(client, baseURL, obj), nil
+ return maasify(client, obj), nil
}
// Return error value for failed type conversion.
=== modified file 'jsonobject_test.go'
--- jsonobject_test.go 2013-01-25 17:49:25 +0000
+++ jsonobject_test.go 2013-01-28 13:30:59 +0000
@@ -9,37 +9,37 @@
// maasify() converts nil.
func (suite *GomaasapiTestSuite) TestMaasifyConvertsNil(c *C) {
- c.Check(maasify(Client{}, "", nil), Equals, nil)
+ c.Check(maasify(Client{}, nil), Equals, nil)
}
// maasify() converts strings.
func (suite *GomaasapiTestSuite) TestMaasifyConvertsString(c *C) {
const text = "Hello"
- c.Check(string(maasify(Client{}, "", text).(jsonString)), Equals, text)
+ c.Check(string(maasify(Client{}, text).(jsonString)), Equals, text)
}
// maasify() converts float64 numbers.
func (suite *GomaasapiTestSuite) TestMaasifyConvertsNumber(c *C) {
const number = 3.1415926535
- c.Check(float64(maasify(Client{}, "", number).(jsonFloat64)), Equals, number)
+ c.Check(float64(maasify(Client{}, number).(jsonFloat64)), Equals, number)
}
// Any number converts to float64, even integers.
func (suite *GomaasapiTestSuite) TestMaasifyConvertsIntegralNumber(c *C) {
const number = 1
- c.Check(float64(maasify(Client{}, "", number).(jsonFloat64)), Equals, float64(number))
+ c.Check(float64(maasify(Client{}, number).(jsonFloat64)), Equals, float64(number))
}
// maasify() converts array slices.
func (suite *GomaasapiTestSuite) TestMaasifyConvertsArray(c *C) {
original := []interface{}{3.0, 2.0, 1.0}
- output := maasify(Client{}, "", original).(jsonArray)
+ output := maasify(Client{}, original).(jsonArray)
c.Check(len(output), Equals, len(original))
}
// When maasify() converts an array slice, the result contains JSONObjects.
func (suite *GomaasapiTestSuite) TestMaasifyArrayContainsJSONObjects(c *C) {
- arr := maasify(Client{}, "", []interface{}{9.9}).(jsonArray)
+ arr := maasify(Client{}, []interface{}{9.9}).(jsonArray)
var entry JSONObject
entry = arr[0]
c.Check((float64)(entry.(jsonFloat64)), Equals, 9.9)
@@ -48,13 +48,13 @@
// maasify() converts maps.
func (suite *GomaasapiTestSuite) TestMaasifyConvertsMap(c *C) {
original := map[string]interface{}{"1": "one", "2": "two", "3": "three"}
- output := maasify(Client{}, "", original).(jsonMap)
+ output := maasify(Client{}, original).(jsonMap)
c.Check(len(output), Equals, len(original))
}
// When maasify() converts a map, the result contains JSONObjects.
func (suite *GomaasapiTestSuite) TestMaasifyMapContainsJSONObjects(c *C) {
- mp := maasify(Client{}, "", map[string]interface{}{"key": "value"}).(jsonMap)
+ mp := maasify(Client{}, map[string]interface{}{"key": "value"}).(jsonMap)
var entry JSONObject
entry = mp["key"]
c.Check((string)(entry.(jsonString)), Equals, "value")
@@ -66,67 +66,59 @@
"resource_uri": "http://example.com/foo",
"size": "3",
}
- output := maasify(Client{}, "", original).(jsonMAASObject)
+ output := maasify(Client{}, original).(jsonMAASObject)
c.Check(len(output.jsonMap), Equals, len(original))
c.Check((string)(output.jsonMap["size"].(jsonString)), Equals, "3")
}
-// maasify() passes its info (client and baseURL) to a MAASObject it creates.
+// maasify() passes its client to a MAASObject it creates.
func (suite *GomaasapiTestSuite) TestMaasifyPassesInfoToMAASObject(c *C) {
client := Client{}
original := map[string]interface{}{"resource_uri": "/foo"}
- baseURL := "http://example.com"
- output := maasify(client, baseURL, original).(jsonMAASObject)
+ output := maasify(client, original).(jsonMAASObject)
c.Check(output.client, Equals, client)
- c.Check(output.baseURL, Equals, baseURL)
}
-// maasify() passes its info (client and baseURL) into an array of MAASObjects it creates.
+// maasify() passes its client into an array of MAASObjects it creates.
func (suite *GomaasapiTestSuite) TestMaasifyPassesInfoIntoArray(c *C) {
client := Client{}
obj := map[string]interface{}{"resource_uri": "/foo"}
- baseURL := "http://example.com"
list := []interface{}{obj}
- output := maasify(client, baseURL, list).(jsonArray)
+ output := maasify(client, list).(jsonArray)
c.Check(output[0].(jsonMAASObject).client, Equals, client)
- c.Check(output[0].(jsonMAASObject).baseURL, Equals, baseURL)
}
-// maasify() passes its info (client and baseURL) into a map of MAASObjects it creates.
+// maasify() passes its client into a map of MAASObjects it creates.
func (suite *GomaasapiTestSuite) TestMaasifyPassesInfoIntoMap(c *C) {
client := Client{}
obj := map[string]interface{}{"resource_uri": "/foo"}
- baseURL := "http://example.com"
mp := map[string]interface{}{"key": obj}
- output := maasify(client, baseURL, mp).(jsonMap)
+ output := maasify(client, mp).(jsonMap)
c.Check(output["key"].(jsonMAASObject).client, Equals, client)
- c.Check(output["key"].(jsonMAASObject).baseURL, Equals, baseURL)
}
-// maasify() passes its info (client and baseURL) all the way down into any
-// MAASObjects in the object structure it creates.
+// maasify() passes its client all the way down into any MAASObjects in the
+// object structure it creates.
func (suite *GomaasapiTestSuite) TestMaasifyPassesInfoAllTheWay(c *C) {
client := Client{}
obj := map[string]interface{}{"resource_uri": "/foo"}
- baseURL := "http://example.com"
mp := map[string]interface{}{"key": obj}
list := []interface{}{mp}
- output := maasify(client, baseURL, list).(jsonArray)
+ output := maasify(client, list).(jsonArray)
maasobj := output[0].(jsonMap)["key"]
c.Check(maasobj.(jsonMAASObject).client, Equals, client)
- c.Check(maasobj.(jsonMAASObject).baseURL, Equals, baseURL)
}
// maasify() converts Booleans.
func (suite *GomaasapiTestSuite) TestMaasifyConvertsBool(c *C) {
- c.Check(bool(maasify(Client{}, "", true).(jsonBool)), Equals, true)
- c.Check(bool(maasify(Client{}, "", false).(jsonBool)), Equals, false)
+ c.Check(bool(maasify(Client{}, true).(jsonBool)), Equals, true)
+ c.Check(bool(maasify(Client{}, false).(jsonBool)), Equals, false)
}
// Parse takes you from a JSON blob to a JSONObject.
func (suite *GomaasapiTestSuite) TestParseMaasifiesJSONBlob(c *C) {
blob := []byte("[12]")
- obj, err := Parse(Client{}, "", blob)
+ obj, err := Parse(Client{}, blob)
c.Check(err, IsNil)
c.Check(float64(obj.(jsonArray)[0].(jsonFloat64)), Equals, 12.0)
}
=== modified file 'maasobject.go'
--- maasobject.go 2013-01-28 08:18:15 +0000
+++ maasobject.go 2013-01-28 13:30:59 +0000
@@ -18,9 +18,11 @@
// Utility method to extract a string field from this MAAS object.
GetField(name string) (string, error)
+ // URL for this MAAS object.
+ URL() (*url.URL, error)
// Resource URI for this MAAS object.
- URL() string
- // Retrieve the MAAS object located at thisObject.URL()+name.
+ URI() (*url.URL, error)
+ // Retrieve the MAAS object located at thisObject.URI()+name.
GetSubObject(name string) MAASObject
// Retrieve this MAAS object.
Get() (MAASObject, error)
@@ -43,8 +45,7 @@
// jsonMAASObject implements both JSONObject and MAASObject.
type jsonMAASObject struct {
jsonMap
- client Client
- baseURL string
+ client Client
}
var _ JSONObject = (*jsonMAASObject)(nil)
@@ -65,39 +66,48 @@
return obj.jsonMap[name].GetString()
}
-func (obj jsonMAASObject) _URI() (string, error) {
+func (obj jsonMAASObject) URI() (*url.URL, error) {
contents, err := obj.GetMap()
if err != nil {
panic("Unexpected failure converting jsonMAASObject to maasMap.")
}
- return contents[resource_uri].GetString()
+ urlString, err := contents[resource_uri].GetString()
+ if err != nil {
+ return &url.URL{}, err
+ }
+ return url.Parse(urlString)
}
-func (obj jsonMAASObject) URL() string {
- uri, err := obj._URI()
+func (obj jsonMAASObject) URL() (*url.URL, error) {
+ uri, err := obj.URI()
if err != nil {
- panic("Unexpected failure reading jsonMAASObject's URL.")
+ return &url.URL{}, err
}
- return obj.baseURL + uri
+ return obj.client.GetURL(uri), nil
}
func (obj jsonMAASObject) GetSubObject(name string) MAASObject {
- uri, err := obj._URI()
+ uri, err := obj.URI()
if err != nil {
panic("Unexpected failure reading jsonMAASObject's URL.")
}
- input := map[string]JSONObject{resource_uri: jsonString(uri + name)}
- return jsonMAASObject{jsonMap: jsonMap(input), client: obj.client, baseURL: obj.baseURL}
+ uri.Path = AppendSlash(JoinURLs(uri.Path, name))
+ input := map[string]JSONObject{resource_uri: jsonString(uri.String())}
+ return jsonMAASObject{jsonMap: jsonMap(input), client: obj.client}
}
var NotImplemented = errors.New("Not implemented")
func (obj jsonMAASObject) Get() (MAASObject, error) {
- result, err := obj.client.Get(obj.URL(), "", url.Values{})
- if err != nil {
- return nil, err
- }
- jsonObj, err := Parse(obj.client, obj.baseURL, result)
+ uri, err := obj.URI()
+ if err != nil {
+ return nil, err
+ }
+ result, err := obj.client.Get(uri, "", url.Values{})
+ if err != nil {
+ return nil, err
+ }
+ jsonObj, err := Parse(obj.client, result)
if err != nil {
return nil, err
}
@@ -105,19 +115,27 @@
}
func (obj jsonMAASObject) Post(params url.Values) (JSONObject, error) {
- result, err := obj.client.Post(obj.URL(), "", params)
- if err != nil {
- return nil, err
- }
- return Parse(obj.client, obj.baseURL, result)
+ uri, err := obj.URI()
+ if err != nil {
+ return nil, err
+ }
+ result, err := obj.client.Post(uri, "", params)
+ if err != nil {
+ return nil, err
+ }
+ return Parse(obj.client, result)
}
func (obj jsonMAASObject) Update(params url.Values) (MAASObject, error) {
- result, err := obj.client.Put(obj.URL(), params)
- if err != nil {
- return nil, err
- }
- jsonObj, err := Parse(obj.client, obj.baseURL, result)
+ uri, err := obj.URI()
+ if err != nil {
+ return nil, err
+ }
+ result, err := obj.client.Put(uri, params)
+ if err != nil {
+ return nil, err
+ }
+ jsonObj, err := Parse(obj.client, result)
if err != nil {
return nil, err
}
@@ -125,21 +143,33 @@
}
func (obj jsonMAASObject) Delete() error {
- return obj.client.Delete(obj.URL())
+ uri, err := obj.URI()
+ if err != nil {
+ return err
+ }
+ return obj.client.Delete(uri)
}
func (obj jsonMAASObject) CallGet(operation string, params url.Values) (JSONObject, error) {
- result, err := obj.client.Get(obj.URL(), operation, params)
- if err != nil {
- return nil, err
- }
- return Parse(obj.client, obj.baseURL, result)
+ uri, err := obj.URI()
+ if err != nil {
+ return nil, err
+ }
+ result, err := obj.client.Get(uri, operation, params)
+ if err != nil {
+ return nil, err
+ }
+ return Parse(obj.client, result)
}
func (obj jsonMAASObject) CallPost(operation string, params url.Values) (JSONObject, error) {
- result, err := obj.client.Post(obj.URL(), operation, params)
- if err != nil {
- return nil, err
- }
- return Parse(obj.client, obj.baseURL, result)
+ uri, err := obj.URI()
+ if err != nil {
+ return nil, err
+ }
+ result, err := obj.client.Post(uri, operation, params)
+ if err != nil {
+ return nil, err
+ }
+ return Parse(obj.client, result)
}
=== modified file 'maasobject_test.go'
--- maasobject_test.go 2013-01-28 08:18:15 +0000
+++ maasobject_test.go 2013-01-28 13:30:59 +0000
@@ -7,6 +7,7 @@
"fmt"
. "launchpad.net/gocheck"
"math/rand"
+ "net/url"
)
func makeFakeResourceURI() string {
@@ -45,19 +46,33 @@
}
func (suite *GomaasapiTestSuite) TestURL(c *C) {
+ baseURL, _ := url.Parse("http://example.com/")
uri := "http://example.com/a/resource"
+ resourceURL, _ := url.Parse(uri)
input := map[string]JSONObject{resource_uri: jsonString(uri)}
- obj := jsonMAASObject{jsonMap: jsonMap(input)}
- c.Check(obj.URL(), Equals, uri)
+ client := Client{BaseURL: baseURL}
+ obj := jsonMAASObject{jsonMap: jsonMap(input), client: client}
+
+ URL, err := obj.URL()
+
+ c.Check(err, IsNil)
+ c.Check(URL, DeepEquals, resourceURL)
}
func (suite *GomaasapiTestSuite) TestGetSubObject(c *C) {
- uri := "http://example.com/a/resource"
+ uri := "http://example.com/a/resource/"
input := map[string]JSONObject{resource_uri: jsonString(uri)}
obj := jsonMAASObject{jsonMap: jsonMap(input)}
subName := "/test"
+
subObj := obj.GetSubObject(subName)
- c.Check(subObj.URL(), Equals, uri+subName)
+ subURL, err := subObj.URL()
+
+ c.Check(err, IsNil)
+ // uri ends with a slash and subName starts with one, but the two paths
+ // should be concatenated as "http://example.com/a/resource/test/".
+ expectedSubURL, _ := url.Parse("http://example.com/a/resource/test/")
+ c.Check(subURL, DeepEquals, expectedSubURL)
}
func (suite *GomaasapiTestSuite) TestGetField(c *C) {
=== modified file 'server.go'
--- server.go 2013-01-28 08:18:15 +0000
+++ server.go 2013-01-28 13:30:59 +0000
@@ -3,19 +3,8 @@
package gomaasapi
-import (
- "fmt"
- "net/url"
-)
-
// NewMAAS returns an interface to the MAAS API as a MAASObject.
-func NewMAAS(URL string, client Client) (MAASObject, error) {
- parsed, err := url.Parse(URL)
- if err != nil {
- return nil, err
- }
- baseURL := fmt.Sprintf("%s://%s", parsed.Scheme, parsed.Host)
- resourceURI := parsed.Path
- input := map[string]JSONObject{resource_uri: jsonString(resourceURI)}
- return jsonMAASObject{jsonMap: jsonMap(input), client: client, baseURL: baseURL}, nil
+func NewMAAS(client Client) (MAASObject, error) {
+ input := map[string]JSONObject{resource_uri: jsonString(client.BaseURL.String())}
+ return jsonMAASObject{jsonMap: jsonMap(input), client: client}, nil
}
=== modified file 'server_test.go'
--- server_test.go 2013-01-28 08:18:15 +0000
+++ server_test.go 2013-01-28 13:30:59 +0000
@@ -5,16 +5,16 @@
import (
. "launchpad.net/gocheck"
+ "net/url"
)
-func (suite *GomaasapiTestSuite) TestNewMAASParsesURL(c *C) {
- maas, err := NewMAAS("https://server.com:888/path/to/api", Client{})
-
- c.Check(err, IsNil)
- c.Check(maas.URL(), Equals, "https://server.com:888/path/to/api")
- jsonObj := maas.(jsonMAASObject)
- uri, err := jsonObj._URI()
- c.Check(err, IsNil)
- c.Check(uri, Equals, "/path/to/api")
- c.Check(jsonObj.baseURL, Equals, "https://server.com:888")
+func (suite *GomaasapiTestSuite) TestNewMAASUsesBaseURLFromClient(c *C) {
+ baseURLString := "https://server.com:888/path/to/api"
+ baseURL, _ := url.Parse(baseURLString)
+ client := Client{BaseURL: baseURL}
+ maas, err := NewMAAS(client)
+ c.Check(err, IsNil)
+ URL, err := maas.URL()
+ c.Check(err, IsNil)
+ c.Check(URL, DeepEquals, baseURL)
}
=== modified file 'util.go'
--- util.go 2013-01-28 09:37:10 +0000
+++ util.go 2013-01-28 13:30:59 +0000
@@ -14,3 +14,15 @@
func JoinURLs(baseURL, path string) string {
return strings.TrimRight(baseURL, "/") + "/" + strings.TrimLeft(path, "/")
}
+
+// AppendSlash appends a slash at the end of the given string unless there
+// already is one.
+// This is used to create the kind of normalized URLs that Django expects.
+// (to avoid Django's redirection when an URL does not ends with a slash.)
+func AppendSlash(URL string) string {
+ length := len(URL)
+ if length > 0 && string(URL[length-1]) == "/" {
+ return URL
+ }
+ return URL + "/"
+}
=== modified file 'util_test.go'
--- util_test.go 2013-01-28 09:15:13 +0000
+++ util_test.go 2013-01-28 13:30:59 +0000
@@ -18,3 +18,15 @@
func (suite *GomaasapiTestSuite) TestJoinURLsNormalizesDoubleSlash(c *C) {
c.Check(JoinURLs("http://example.com/base/", "/szot"), Equals, "http://example.com/base/szot")
}
+
+func (suite *GomaasapiTestSuite) TestAppendSlashAppendsSlashIfMissing(c *C) {
+ c.Check(AppendSlash("test"), Equals, "test/")
+}
+
+func (suite *GomaasapiTestSuite) TestAppendSlashDoesNotAppendsIfPresent(c *C) {
+ c.Check(AppendSlash("test/"), Equals, "test/")
+}
+
+func (suite *GomaasapiTestSuite) TestAppendSlashReturnsSlashIfEmpty(c *C) {
+ c.Check(AppendSlash(""), Equals, "/")
+}
Follow ups