From 34256e9df4c11d33c9d6b097f46ac93861fd0772 Mon Sep 17 00:00:00 2001 From: Christian Muirhead Date: Mon, 3 Apr 2017 17:22:29 +1200 Subject: [PATCH] Make NewAuthenticatedClient take a version URL This enables creating a controller without doing version probing. --- client.go | 32 ++++++++++++++++++-------------- client_test.go | 16 +++++++--------- controller.go | 14 +++++++------- example/live_example.go | 3 ++- 4 files changed, 34 insertions(+), 31 deletions(-) diff --git a/client.go b/client.go index ef887e6..f736c19 100644 --- a/client.go +++ b/client.go @@ -11,6 +11,7 @@ import ( "mime/multipart" "net/http" "net/url" + "regexp" "strconv" "strings" "time" @@ -265,10 +266,12 @@ func (signer anonSigner) OAuthSign(request *http.Request) error { // *anonSigner implements the OAuthSigner interface. var _ OAuthSigner = anonSigner{} -func composeAPIURL(BaseURL string, apiVersion string) (*url.URL, error) { +// AddAPIVersionToURL will add the version// suffix to the +// given URL, handling trailing slashes. It shouldn't be called with a +// URL that already includes a version. +func AddAPIVersionToURL(BaseURL, apiVersion string) string { baseurl := EnsureTrailingSlash(BaseURL) - apiurl := fmt.Sprintf("%sapi/%s/", baseurl, apiVersion) - return url.Parse(apiurl) + return fmt.Sprintf("%sapi/%s/", baseurl, apiVersion) } // NewAnonymousClient creates a client that issues anonymous requests. @@ -276,20 +279,21 @@ func composeAPIURL(BaseURL string, apiVersion string) (*url.URL, error) { // http://my.maas.server.example.com/MAAS/ // apiVersion should contain the version of the MAAS API that you want to use. func NewAnonymousClient(BaseURL string, apiVersion string) (*Client, error) { - parsedBaseURL, err := composeAPIURL(BaseURL, apiVersion) + versionedURL := AddAPIVersionToURL(BaseURL, apiVersion) + parsedURL, err := url.Parse(versionedURL) if err != nil { return nil, err } - return &Client{Signer: &anonSigner{}, APIURL: parsedBaseURL}, nil + return &Client{Signer: &anonSigner{}, APIURL: parsedURL}, 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. -// BaseURL should refer to the root of the MAAS server path, e.g. -// http://my.maas.server.example.com/MAAS/ -// apiVersion should contain the version of the MAAS API that you want to use. -func NewAuthenticatedClient(BaseURL string, apiKey string, apiVersion string) (*Client, error) { +// 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. +// versionedURL should be the location of the versioned API root of +// the MAAS server, e.g.: +// http://my.maas.server.example.com/MAAS/api/2.0/ +func NewAuthenticatedClient(versionedURL, apiKey string) (*Client, error) { elements := strings.Split(apiKey, ":") if len(elements) != 3 { errString := fmt.Sprintf("invalid API key %q; expected \"::\"", apiKey) @@ -306,9 +310,9 @@ func NewAuthenticatedClient(BaseURL string, apiKey string, apiVersion string) (* if err != nil { return nil, err } - parsedBaseURL, err := composeAPIURL(BaseURL, apiVersion) + parsedURL, err := url.Parse(EnsureTrailingSlash(versionedURL)) if err != nil { return nil, err } - return &Client{Signer: signer, APIURL: parsedBaseURL}, nil + return &Client{Signer: signer, APIURL: parsedURL}, nil } diff --git a/client_test.go b/client_test.go index a042d27..6b81da3 100644 --- a/client_test.go +++ b/client_test.go @@ -128,7 +128,7 @@ func (suite *ClientSuite) TestClientdispatchRequestSignsRequest(c *gc.C) { expectedResult := "expected:result" server := newSingleServingServer(URI, expectedResult, http.StatusOK) defer server.Close() - client, err := NewAuthenticatedClient(server.URL, "the:api:key", "1.0") + client, err := NewAuthenticatedClient(server.URL, "the:api:key") c.Assert(err, jc.ErrorIsNil) request, err := http.NewRequest("GET", server.URL+URI, nil) c.Assert(err, jc.ErrorIsNil) @@ -277,7 +277,7 @@ func (suite *ClientSuite) TestNewAnonymousClientEnsuresTrailingSlash(c *gc.C) { } func (suite *ClientSuite) TestNewAuthenticatedClientEnsuresTrailingSlash(c *gc.C) { - client, err := NewAuthenticatedClient("http://example.com/", "a:b:c", "1.0") + client, err := NewAuthenticatedClient("http://example.com/api/1.0", "a:b:c") c.Assert(err, jc.ErrorIsNil) expectedURL, err := url.Parse("http://example.com/api/1.0/") c.Assert(err, jc.ErrorIsNil) @@ -293,7 +293,7 @@ func (suite *ClientSuite) TestNewAuthenticatedClientParsesApiKey(c *gc.C) { keyElements := []string{consumerKey, tokenKey, tokenSecret} apiKey := strings.Join(keyElements, ":") - client, err := NewAuthenticatedClient("http://example.com/", apiKey, "1.0") + client, err := NewAuthenticatedClient("http://example.com/api/1.0/", apiKey) c.Assert(err, jc.ErrorIsNil) signer := client.Signer.(*plainTextOAuthSigner) @@ -303,17 +303,15 @@ func (suite *ClientSuite) TestNewAuthenticatedClientParsesApiKey(c *gc.C) { } func (suite *ClientSuite) TestNewAuthenticatedClientFailsIfInvalidKey(c *gc.C) { - client, err := NewAuthenticatedClient("", "invalid-key", "1.0") + client, err := NewAuthenticatedClient("", "invalid-key") c.Check(err, gc.ErrorMatches, "invalid API key.*") c.Check(client, gc.IsNil) } -func (suite *ClientSuite) TestcomposeAPIURLReturnsURL(c *gc.C) { - apiurl, err := composeAPIURL("http://example.com/MAAS", "1.0") - c.Assert(err, jc.ErrorIsNil) - expectedURL, err := url.Parse("http://example.com/MAAS/api/1.0/") - c.Assert(err, jc.ErrorIsNil) +func (suite *ClientSuite) TestAddAPIVersionToURL(c *gc.C) { + apiurl := AddAPIVersionToURL("http://example.com/MAAS", "1.0") + expectedURL := "http://example.com/MAAS/api/1.0/" c.Assert(expectedURL, jc.DeepEquals, apiurl) } diff --git a/controller.go b/controller.go index c734687..5a9b846 100644 --- a/controller.go +++ b/controller.go @@ -59,7 +59,7 @@ func NewController(args ControllerArgs) (Controller, error) { if err != nil { return nil, errors.Errorf("bad version defined in supported versions: %q", apiVersion) } - client, err := NewAuthenticatedClient(args.BaseURL, args.APIKey, apiVersion) + client, err := NewAuthenticatedClient(AddAPIVersionToURL(args.BaseURL, apiVersion), args.APIKey) if err != nil { // If the credentials aren't valid, return now. if errors.IsNotValid(err) { @@ -73,9 +73,9 @@ func NewController(args ControllerArgs) (Controller, error) { Major: major, Minor: minor, } - controller := &controller{client: client} + controller := &controller{client: client, apiVersion: controllerVersion} // The controllerVersion returned from the function will include any patch version. - controller.capabilities, controller.apiVersion, err = controller.readAPIVersion(controllerVersion) + controller.capabilities, err = controller.readAPIVersionInfo() if err != nil { logger.Debugf("read version failed: %#v", err) continue @@ -805,10 +805,10 @@ func nextRequestID() int64 { return atomic.AddInt64(&requestNumber, 1) } -func (c *controller) readAPIVersion(apiVersion version.Number) (set.Strings, version.Number, error) { +func (c *controller) readAPIVersionInfo() (set.Strings, error) { parsed, err := c.get("version") if err != nil { - return nil, apiVersion, errors.Trace(err) + return nil, errors.Trace(err) } // As we care about other fields, add them. @@ -818,7 +818,7 @@ func (c *controller) readAPIVersion(apiVersion version.Number) (set.Strings, ver checker := schema.FieldMap(fields, nil) // no defaults coerced, err := checker.Coerce(parsed, nil) if err != nil { - return nil, apiVersion, WrapWithDeserializationError(err, "version response") + return nil, WrapWithDeserializationError(err, "version response") } // For now, we don't append any subversion, but as it becomes used, we // should parse and check. @@ -832,7 +832,7 @@ func (c *controller) readAPIVersion(apiVersion version.Number) (set.Strings, ver capabilities.Add(value.(string)) } - return capabilities, apiVersion, nil + return capabilities, nil } func parseAllocateConstraintsResponse(source interface{}, machine *machine) (ConstraintMatches, error) { diff --git a/example/live_example.go b/example/live_example.go index 64c10c5..01b9d58 100644 --- a/example/live_example.go +++ b/example/live_example.go @@ -50,7 +50,8 @@ func main() { getParams() // Create API server endpoint. - authClient, err := gomaasapi.NewAuthenticatedClient(apiURL, apiKey, apiVersion) + authClient, err := gomaasapi.NewAuthenticatedClient( + gomaasapi.AddAPIVersionToURL(apiURL, apiVersion), apiKey) checkError(err) maas := gomaasapi.NewMAAS(*authClient)