-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
268 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,152 @@ | ||
package bonsai | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
"net/http" | ||
"net/url" | ||
"path" | ||
"reflect" | ||
) | ||
|
||
const ReleaseAPIBasePath = "/releases" | ||
|
||
// Release is a placeholder for now. | ||
type Release struct { | ||
Name string `json:"name,omitempty"` | ||
Slug string `json:"slug"` | ||
ServiceType string `json:"service_type,omitempty"` | ||
Version string `json:"version,omitempty"` | ||
MultiTenant bool `json:"multi_tenant,omitempty"` | ||
MultiTenant bool `json:"multitenant,omitempty"` | ||
} | ||
|
||
// ReleasesResultList is a wrapper around a slice of | ||
// Releases for json unmarshaling. | ||
type ReleasesResultList struct { | ||
Releases []Release `json:"releases"` | ||
} | ||
|
||
// ReleaseClient is a client for the Releases API. | ||
type ReleaseClient struct { | ||
*Client | ||
} | ||
|
||
type releaseListOptions struct { | ||
listOpts | ||
} | ||
|
||
func (o releaseListOptions) values() url.Values { | ||
return o.listOpts.values() | ||
} | ||
|
||
// list returns a list of Releases for the page specified, | ||
// by performing a GET request against [spaceAPIBasePath]. | ||
// | ||
// Note: Pagination is not currently supported. | ||
func (c *ReleaseClient) list(ctx context.Context, opt releaseListOptions) ([]Release, *Response, error) { | ||
var ( | ||
req *http.Request | ||
reqURL *url.URL | ||
resp *Response | ||
err error | ||
) | ||
// Let's make some initial capacity to reduce allocations | ||
results := ReleasesResultList{ | ||
Releases: make([]Release, 0, defaultResponseCapacity), | ||
} | ||
|
||
reqURL, err = url.Parse(ReleaseAPIBasePath) | ||
if err != nil { | ||
return results.Releases, nil, fmt.Errorf("cannot parse relative url from basepath (%s): %w", ReleaseAPIBasePath, err) | ||
} | ||
|
||
// Conditionally set options if we received any | ||
if !reflect.ValueOf(opt).IsZero() { | ||
reqURL.RawQuery = opt.values().Encode() | ||
} | ||
|
||
req, err = c.NewRequest(ctx, "GET", reqURL.String(), nil) | ||
if err != nil { | ||
return results.Releases, nil, fmt.Errorf("creating new http request for URL (%s): %w", reqURL.String(), err) | ||
} | ||
|
||
resp, err = c.Do(ctx, req) | ||
if err != nil { | ||
return results.Releases, resp, fmt.Errorf("client.do failed: %w", err) | ||
} | ||
|
||
if err = json.Unmarshal(resp.BodyBuf.Bytes(), &results); err != nil { | ||
return results.Releases, resp, fmt.Errorf("json.Unmarshal failed: %w", err) | ||
} | ||
|
||
return results.Releases, resp, nil | ||
} | ||
|
||
// All lists all Releases from the Releases API. | ||
func (c *ReleaseClient) All(ctx context.Context) ([]Release, error) { | ||
var ( | ||
err error | ||
resp *Response | ||
) | ||
|
||
allResults := make([]Release, 0, defaultListResultSize) | ||
// No pagination support as yet, but support it for future use | ||
|
||
err = c.all(ctx, newEmptyListOpts(), func(opt listOpts) (*Response, error) { | ||
var listResults []Release | ||
|
||
listResults, resp, err = c.list(ctx, releaseListOptions{listOpts: opt}) | ||
if err != nil { | ||
return resp, fmt.Errorf("client.list failed: %w", err) | ||
} | ||
|
||
allResults = append(allResults, listResults...) | ||
if len(allResults) >= resp.PageSize { | ||
resp.MarkPaginationComplete() | ||
} | ||
return resp, err | ||
}) | ||
|
||
if err != nil { | ||
return allResults, fmt.Errorf("client.all failed: %w", err) | ||
} | ||
|
||
return allResults, err | ||
} | ||
|
||
// GetBySlug gets a Release from the Releases API by its slug. | ||
// | ||
//nolint:dupl // Allow duplicated code blocks in code paths that may change | ||
func (c *ReleaseClient) GetBySlug(ctx context.Context, slug string) (Release, error) { | ||
var ( | ||
req *http.Request | ||
reqURL *url.URL | ||
resp *Response | ||
err error | ||
result Release | ||
) | ||
|
||
reqURL, err = url.Parse(ReleaseAPIBasePath) | ||
if err != nil { | ||
return result, fmt.Errorf("cannot parse relative url from basepath (%s): %w", ReleaseAPIBasePath, err) | ||
} | ||
|
||
reqURL.Path = path.Join(reqURL.Path, slug) | ||
|
||
req, err = c.NewRequest(ctx, "GET", reqURL.String(), nil) | ||
if err != nil { | ||
return result, fmt.Errorf("creating new http request for URL (%s): %w", reqURL.String(), err) | ||
} | ||
|
||
resp, err = c.Do(ctx, req) | ||
if err != nil { | ||
return result, fmt.Errorf("client.do failed: %w", err) | ||
} | ||
|
||
if err = json.Unmarshal(resp.BodyBuf.Bytes(), &result); err != nil { | ||
return result, fmt.Errorf("json.Unmarshal failed: %w", err) | ||
} | ||
|
||
return result, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
package bonsai_test | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
"net/http" | ||
"net/url" | ||
|
||
"github.com/omc/bonsai-api-go/v1/bonsai" | ||
) | ||
|
||
func (s *ClientTestSuite) TestReleaseClient_All() { | ||
s.serveMux.HandleFunc(bonsai.ReleaseAPIBasePath, func(w http.ResponseWriter, _ *http.Request) { | ||
respStr := ` | ||
{ | ||
"releases": [ | ||
{ | ||
"name": "Elasticsearch 5.6.16", | ||
"slug": "elasticsearch-5.6.16", | ||
"service_type": "elasticsearch", | ||
"version": "5.6.16", | ||
"multitenant": true | ||
}, | ||
{ | ||
"name": "Elasticsearch 6.5.4", | ||
"slug": "elasticsearch-6.5.4", | ||
"service_type": "elasticsearch", | ||
"version": "6.5.4", | ||
"multitenant": true | ||
}, | ||
{ | ||
"name": "Elasticsearch 7.2.0", | ||
"slug": "elasticsearch-7.2.0", | ||
"service_type": "elasticsearch", | ||
"version": "7.2.0", | ||
"multitenant": true | ||
} | ||
] | ||
} | ||
` | ||
|
||
resp := &bonsai.ReleasesResultList{Releases: make([]bonsai.Release, 0, 3)} | ||
err := json.Unmarshal([]byte(respStr), resp) | ||
s.NoError(err, "unmarshal json into bonsai.ReleasesResultList") | ||
|
||
err = json.NewEncoder(w).Encode(resp) | ||
s.NoError(err, "encode bonsai.ReleasesResultList into json") | ||
}) | ||
|
||
expect := []bonsai.Release{ | ||
{ | ||
Name: "Elasticsearch 5.6.16", | ||
Slug: "elasticsearch-5.6.16", | ||
ServiceType: "elasticsearch", | ||
Version: "5.6.16", | ||
MultiTenant: true, | ||
}, | ||
{ | ||
Name: "Elasticsearch 6.5.4", | ||
Slug: "elasticsearch-6.5.4", | ||
ServiceType: "elasticsearch", | ||
Version: "6.5.4", | ||
MultiTenant: true, | ||
}, | ||
{ | ||
Name: "Elasticsearch 7.2.0", | ||
Slug: "elasticsearch-7.2.0", | ||
ServiceType: "elasticsearch", | ||
Version: "7.2.0", | ||
MultiTenant: true, | ||
}, | ||
} | ||
releases, err := s.client.Release.All(context.Background()) | ||
s.NoError(err, "successfully get all releases") | ||
s.Len(releases, 3) | ||
|
||
s.ElementsMatch(expect, releases, "elements in expect match elements in received releases") | ||
} | ||
|
||
func (s *ClientTestSuite) TestReleaseClient_GetBySlug() { | ||
const targetReleaseSlug = "elasticsearch-7.2.0" | ||
|
||
urlPath, err := url.JoinPath(bonsai.ReleaseAPIBasePath, targetReleaseSlug) | ||
s.NoError(err, "successfully resolved path") | ||
|
||
s.serveMux.HandleFunc(urlPath, func(w http.ResponseWriter, _ *http.Request) { | ||
respStr := fmt.Sprintf(` | ||
{ | ||
"name": "Elasticsearch 7.2.0", | ||
"slug": "%s", | ||
"service_type": "elasticsearch", | ||
"version": "7.2.0", | ||
"multitenant": true | ||
} | ||
`, targetReleaseSlug) | ||
|
||
resp := &bonsai.Release{} | ||
err = json.Unmarshal([]byte(respStr), resp) | ||
s.NoError(err, "unmarshals json into bonsai.Space") | ||
|
||
err = json.NewEncoder(w).Encode(resp) | ||
s.NoError(err, "encodes bonsai.Space into json on the writer") | ||
}) | ||
|
||
expect := bonsai.Release{ | ||
Slug: "elasticsearch-7.2.0", | ||
Name: "Elasticsearch 7.2.0", | ||
ServiceType: "elasticsearch", | ||
Version: "7.2.0", | ||
MultiTenant: true, | ||
} | ||
|
||
resultResp, err := s.client.Release.GetBySlug(context.Background(), targetReleaseSlug) | ||
s.NoError(err, "successfully get release by path") | ||
|
||
s.Equal(expect, resultResp, "elements in expect match elements in received release response") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters