-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement the ListBasic API method, which calls bucketd route `/default/bucket/{bucketname}?listingType=Basic`. Additional improvements: - ListObjectVersions: use references for options - ListObjectVersions: replace allocation with new() with a local var - ListObjectVersions and CreateBucket: use url.URL object to create the full URL
- Loading branch information
1 parent
cf19c4c
commit 4c2df87
Showing
4 changed files
with
252 additions
and
26 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
package bucketclient | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
"net/url" | ||
"strconv" | ||
) | ||
|
||
type ListBasicOption func(*listBasicOptionSet) error | ||
|
||
// ListBasicGTOption only lists keys greater than the given argument | ||
func ListBasicGTOption(gt string) ListBasicOption { | ||
return func(opts *listBasicOptionSet) error { | ||
opts.gt = > | ||
return nil | ||
} | ||
} | ||
|
||
// ListBasicGTEOption only lists keys greater or equal to the given argument | ||
func ListBasicGTEOption(gte string) ListBasicOption { | ||
return func(opts *listBasicOptionSet) error { | ||
opts.gte = >e | ||
return nil | ||
} | ||
} | ||
|
||
// ListBasicLTOption only lists keys less than the given argument | ||
func ListBasicLTOption(lt string) ListBasicOption { | ||
return func(opts *listBasicOptionSet) error { | ||
opts.lt = < | ||
return nil | ||
} | ||
} | ||
|
||
// ListBasicLTEOption only lists keys less or equal to the given argument | ||
func ListBasicLTEOption(lte string) ListBasicOption { | ||
return func(opts *listBasicOptionSet) error { | ||
opts.lte = <e | ||
return nil | ||
} | ||
} | ||
|
||
// ListBasicMaxKeysOption limits the number of returned keys (default and maximum is 10000). | ||
func ListBasicMaxKeysOption(maxKeys int) ListBasicOption { | ||
return func(opts *listBasicOptionSet) error { | ||
if maxKeys < 0 || maxKeys > 10000 { | ||
return fmt.Errorf("maxKeys=%d is out of the valid range [0, 10000]", maxKeys) | ||
} | ||
opts.maxKeys = &maxKeys | ||
return nil | ||
} | ||
} | ||
|
||
// ListBasicNoKeysOption declares that keys are not needed in the | ||
// result entries and may be returned empty. | ||
// | ||
// Note: keys may still be returned until ARSN-438 is fixed. | ||
func ListBasicNoKeysOption() ListBasicOption { | ||
return func(opts *listBasicOptionSet) error { | ||
opts.noKeys = true | ||
return nil | ||
} | ||
} | ||
|
||
// ListBasicNoValuesOption declares that values are not needed in the | ||
// result entries and may be returned empty. | ||
// | ||
// Note: values may still be returned until ARSN-438 is fixed. | ||
func ListBasicNoValuesOption() ListBasicOption { | ||
return func(opts *listBasicOptionSet) error { | ||
opts.noValues = true | ||
return nil | ||
} | ||
} | ||
|
||
type ListBasicEntry struct { | ||
Key string `json:"key"` | ||
Value string `json:"value"` | ||
} | ||
|
||
type ListBasicResponse []ListBasicEntry | ||
|
||
type listBasicOptionSet struct { | ||
gt *string | ||
gte *string | ||
lt *string | ||
lte *string | ||
maxKeys *int | ||
noKeys bool | ||
noValues bool | ||
} | ||
|
||
func parseListBasicOptions(opts []ListBasicOption) (listBasicOptionSet, error) { | ||
parsedOpts := listBasicOptionSet{} | ||
for _, opt := range opts { | ||
err := opt(&parsedOpts) | ||
if err != nil { | ||
return parsedOpts, err | ||
} | ||
} | ||
return parsedOpts, nil | ||
} | ||
|
||
func (client *BucketClient) ListBasic(ctx context.Context, | ||
bucketName string, opts ...ListBasicOption) (*ListBasicResponse, error) { | ||
resource := fmt.Sprintf("/default/bucket/%s", bucketName) | ||
query := url.Values{} | ||
query.Set("listingType", "Basic") | ||
|
||
options, err := parseListBasicOptions(opts) | ||
if err != nil { | ||
return nil, &BucketClientError{ | ||
"ListBasic", "GET", client.Endpoint, resource, 0, "", err, | ||
} | ||
} | ||
if options.gt != nil { | ||
query.Set("gt", *options.gt) | ||
} | ||
if options.gte != nil { | ||
query.Set("gte", *options.gte) | ||
} | ||
if options.lt != nil { | ||
query.Set("lt", *options.lt) | ||
} | ||
if options.lte != nil { | ||
query.Set("lte", *options.lte) | ||
} | ||
if options.maxKeys != nil { | ||
query.Set("maxKeys", strconv.Itoa(*options.maxKeys)) | ||
} | ||
if options.noKeys { | ||
query.Set("keys", "false") | ||
} | ||
if options.noValues { | ||
query.Set("values", "false") | ||
} | ||
u, _ := url.Parse(resource) | ||
u.RawQuery = query.Encode() | ||
resource = u.String() | ||
responseBody, err := client.Request(ctx, "ListBasic", "GET", resource) | ||
if err != nil { | ||
return nil, err | ||
} | ||
var parsedResponse ListBasicResponse | ||
jsonErr := json.Unmarshal(responseBody, &parsedResponse) | ||
if jsonErr != nil { | ||
return nil, ErrorMalformedResponse("ListBasic", "GET", | ||
client.Endpoint, resource, jsonErr) | ||
} | ||
return &parsedResponse, 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,71 @@ | ||
package bucketclient_test | ||
|
||
import ( | ||
. "github.com/onsi/ginkgo/v2" | ||
. "github.com/onsi/gomega" | ||
|
||
"github.com/jarcoal/httpmock" | ||
|
||
"github.com/scality/bucketclient/go" | ||
) | ||
|
||
var _ = Describe("ListBasic()", func() { | ||
It("returns an empty listing result", func(ctx SpecContext) { | ||
httpmock.RegisterResponder( | ||
"GET", "/default/bucket/my-bucket?listingType=Basic", | ||
httpmock.NewStringResponder(200, "[]")) | ||
|
||
Expect(client.ListBasic(ctx, "my-bucket")).To(Equal( | ||
&bucketclient.ListBasicResponse{})) | ||
}) | ||
|
||
It("returns a non-empty listing result with no param", func(ctx SpecContext) { | ||
httpmock.RegisterResponder( | ||
"GET", "/default/bucket/my-bucket?listingType=Basic", | ||
httpmock.NewStringResponder(200, `[ | ||
{"key": "fop", "value": "fopvalue"}, | ||
{"key": "goo", "value": "goovalue"}, | ||
{"key": "hop", "value": "hopvalue"} | ||
] | ||
`)) | ||
|
||
Expect(client.ListBasic(ctx, "my-bucket")).To(Equal(&bucketclient.ListBasicResponse{ | ||
bucketclient.ListBasicEntry{Key: "fop", Value: "fopvalue"}, | ||
bucketclient.ListBasicEntry{Key: "goo", Value: "goovalue"}, | ||
bucketclient.ListBasicEntry{Key: "hop", Value: "hopvalue"}, | ||
})) | ||
}) | ||
|
||
It("returns a non-empty listing result with URL-encoded gt, lt, maxKeys params, without values", func(ctx SpecContext) { | ||
httpmock.RegisterResponder( | ||
"GET", "/default/bucket/my-bucket?gt=eee%2F123&listingType=Basic<=ijk+456&maxKeys=3&values=false", | ||
httpmock.NewStringResponder(200, `[ | ||
{"key": "fop"}, | ||
{"key": "goo"}, | ||
{"key": "hop"} | ||
] | ||
`)) | ||
|
||
Expect(client.ListBasic(ctx, "my-bucket", | ||
bucketclient.ListBasicGTOption("eee/123"), | ||
bucketclient.ListBasicLTOption("ijk 456"), | ||
bucketclient.ListBasicMaxKeysOption(3), | ||
bucketclient.ListBasicNoValuesOption(), | ||
)).To(Equal(&bucketclient.ListBasicResponse{ | ||
bucketclient.ListBasicEntry{Key: "fop"}, | ||
bucketclient.ListBasicEntry{Key: "goo"}, | ||
bucketclient.ListBasicEntry{Key: "hop"}, | ||
})) | ||
}) | ||
|
||
It("returns an error with malformed response", func(ctx SpecContext) { | ||
httpmock.RegisterResponder( | ||
"GET", "/default/bucket/my-bucket?listingType=Basic", | ||
httpmock.NewStringResponder(200, "[OOPS"), | ||
) | ||
|
||
_, err := client.ListBasic(ctx, "my-bucket") | ||
Expect(err).To(MatchError(ContainSubstring("malformed response body"))) | ||
}) | ||
|
||
}) |
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