From 9b9c3dce3ee10af5b6c4d070821bf47a861efd5b Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Sat, 23 Jan 2021 00:11:17 +0300 Subject: [PATCH] feat(storage): add projection parameter for BucketHandle.Objects() (#3549) Co-authored-by: Chris Cotter --- storage/bucket.go | 6 +++++- storage/integration_test.go | 37 +++++++++++++++++++++++++++++++++++++ storage/storage.go | 30 ++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 1 deletion(-) diff --git a/storage/bucket.go b/storage/bucket.go index 0d3be39eebc..7b1757b83de 100644 --- a/storage/bucket.go +++ b/storage/bucket.go @@ -1204,7 +1204,11 @@ func (it *ObjectIterator) Next() (*ObjectAttrs, error) { func (it *ObjectIterator) fetch(pageSize int, pageToken string) (string, error) { req := it.bucket.c.raw.Objects.List(it.bucket.name) setClientHeader(req.Header()) - req.Projection("full") + projection := it.query.Projection + if projection == ProjectionDefault { + projection = ProjectionFull + } + req.Projection(projection.String()) req.Delimiter(it.query.Delimiter) req.Prefix(it.query.Prefix) req.StartOffset(it.query.StartOffset) diff --git a/storage/integration_test.go b/storage/integration_test.go index c21ff8430a0..788aa41e44e 100644 --- a/storage/integration_test.go +++ b/storage/integration_test.go @@ -698,6 +698,7 @@ func TestIntegration_Objects(t *testing.T) { testObjectsIterateSelectedAttrs(t, bkt, objects) testObjectsIterateAllSelectedAttrs(t, bkt, objects) testObjectIteratorWithOffset(t, bkt, objects) + testObjectsIterateWithProjection(t, bkt) t.Run("testObjectsIterateSelectedAttrsDelimiter", func(t *testing.T) { query := &Query{Prefix: "", Delimiter: "/"} if err := query.SetAttrSelection([]string{"Name"}); err != nil { @@ -1236,6 +1237,42 @@ func testObjectsIterateAllSelectedAttrs(t *testing.T, bkt *BucketHandle, objects } } +func testObjectsIterateWithProjection(t *testing.T, bkt *BucketHandle) { + projections := map[Projection]bool{ + ProjectionDefault: true, + ProjectionFull: true, + ProjectionNoACL: false, + } + + for projection, expectACL := range projections { + query := &Query{Projection: projection} + it := bkt.Objects(context.Background(), query) + attrs, err := it.Next() + if err == iterator.Done { + t.Fatalf("iterator: no objects") + } + if err != nil { + t.Fatalf("iterator.Next: %v", err) + } + + if expectACL { + if attrs.Owner == "" { + t.Errorf("projection %q: Owner is empty, want nonempty Owner", projection) + } + if len(attrs.ACL) == 0 { + t.Errorf("projection %q: ACL is empty, want at least one ACL rule", projection) + } + } else { + if attrs.Owner != "" { + t.Errorf("projection %q: got Owner = %q, want empty Owner", projection, attrs.Owner) + } + if len(attrs.ACL) != 0 { + t.Errorf("projection %q: got %d ACL rules, want empty ACL", projection, len(attrs.ACL)) + } + } + } +} + func TestIntegration_SignedURL(t *testing.T) { if testing.Short() { // do not test during replay t.Skip("Integration tests skipped in short mode") diff --git a/storage/storage.go b/storage/storage.go index d4c25b83e0d..8852b9c6fc5 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -1306,6 +1306,31 @@ func encodeUint32(u uint32) string { return base64.StdEncoding.EncodeToString(b) } +// Projection is enumerated type for Query.Projection. +type Projection int + +const ( + // ProjectionDefault returns all fields of objects. + ProjectionDefault Projection = iota + + // ProjectionFull returns all fields of objects. + ProjectionFull + + // ProjectionNoACL returns all fields of objects except for Owner and ACL. + ProjectionNoACL +) + +func (p Projection) String() string { + switch p { + case ProjectionFull: + return "full" + case ProjectionNoACL: + return "noAcl" + default: + return "" + } +} + // Query represents a query to filter objects from a bucket. type Query struct { // Delimiter returns results in a directory-like fashion. @@ -1341,6 +1366,11 @@ type Query struct { // lexicographically before endOffset. If startOffset is also set, the objects // listed will have names between startOffset (inclusive) and endOffset (exclusive). EndOffset string + + // Projection defines the set of properties to return. It will default to ProjectionFull, + // which returns all properties. Passing ProjectionNoACL will omit Owner and ACL, + // which may improve performance when listing many objects. + Projection Projection } // attrToFieldMap maps the field names of ObjectAttrs to the underlying field