Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#97] List object paging #111

Merged
merged 1 commit into from
Jun 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions api/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const (
ErrEntityTooSmall
ErrEntityTooLarge
ErrPolicyTooLarge
ErrIllegalVersioningConfigurationException
ErrIncompleteBody
ErrInternalError
ErrInvalidAccessKeyID
Expand Down Expand Up @@ -383,6 +384,11 @@ var errorCodes = errorCodeMap{
Description: "Policy exceeds the maximum allowed document size.",
HTTPStatusCode: http.StatusBadRequest,
},
ErrIllegalVersioningConfigurationException: {
Code: "IllegalVersioningConfigurationException",
Description: "Indicates that the versioning configuration specified in the request is invalid.",
HTTPStatusCode: http.StatusBadRequest,
},
ErrIncompleteBody: {
Code: "IncompleteBody",
Description: "You did not provide the number of bytes specified by the Content-Length HTTP header.",
Expand Down
38 changes: 28 additions & 10 deletions api/handler/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,14 @@ import (
)

type listObjectsArgs struct {
Bucket string
Delimeter string
Encode string
Marker string
MaxKeys int
Prefix string
Version string
Bucket string
Delimeter string
Encode string
Marker string
StartAfter string
MaxKeys int
Prefix string
APIVersion int
}

// VersioningConfiguration contains VersioningConfiguration XML representation.
Expand Down Expand Up @@ -114,11 +115,17 @@ func (h *handler) listObjects(w http.ResponseWriter, r *http.Request) (*listObje
return nil, nil, err
}

marker := arg.Marker
if arg.APIVersion == 2 {
marker = arg.StartAfter
}

list, err := h.obj.ListObjects(r.Context(), &layer.ListObjectsParams{
Bucket: arg.Bucket,
Prefix: arg.Prefix,
MaxKeys: arg.MaxKeys,
Delimiter: arg.Delimeter,
Marker: marker,
})
if err != nil {
h.log.Error("something went wrong",
Expand Down Expand Up @@ -166,7 +173,7 @@ func encodeV1(arg *listObjectsArgs, list *layer.ListObjectsInfo) *ListObjectsRes
Delimiter: arg.Delimeter,

IsTruncated: list.IsTruncated,
NextMarker: list.NextContinuationToken,
NextMarker: list.NextMarker,
}

// fill common prefixes
Expand Down Expand Up @@ -221,8 +228,10 @@ func encodeV2(arg *listObjectsArgs, list *layer.ListObjectsInfo) *ListObjectsV2R
Name: arg.Bucket,
EncodingType: arg.Encode,
Prefix: arg.Prefix,
KeyCount: len(list.Objects),
MaxKeys: arg.MaxKeys,
Delimiter: arg.Delimeter,
StartAfter: arg.StartAfter,

IsTruncated: list.IsTruncated,

Expand Down Expand Up @@ -271,10 +280,19 @@ func parseListObjectArgs(r *http.Request) (*listObjectsArgs, error) {
}

res.Prefix = r.URL.Query().Get("prefix")
res.Marker = r.URL.Query().Get("key-marker")
res.Marker = r.URL.Query().Get("marker")
res.Delimeter = r.URL.Query().Get("delimiter")
res.Encode = r.URL.Query().Get("encoding-type")
res.Version = r.URL.Query().Get("version-id-marker")
res.StartAfter = r.URL.Query().Get("start-after")
apiVersionStr := r.URL.Query().Get("list-type")

res.APIVersion = 1
if len(apiVersionStr) != 0 {
if apiVersion, err := strconv.Atoi(apiVersionStr); err != nil || apiVersion != 2 {
return nil, api.GetAPIError(api.ErrIllegalVersioningConfigurationException)
}
res.APIVersion = 2
}

if info := api.GetReqInfo(r.Context()); info != nil {
res.Bucket = info.BucketName
Expand Down
1 change: 1 addition & 0 deletions api/layer/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ type (
Token string
Delimiter string
MaxKeys int
Marker string
}
)

Expand Down
14 changes: 13 additions & 1 deletion api/layer/layer.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"fmt"
"io"
"net/url"
"sort"
"time"

"github.com/nspcc-dev/neofs-api-go/pkg/client"
Expand Down Expand Up @@ -213,7 +214,6 @@ func (n *layer) ListObjects(ctx context.Context, p *ListObjectsParams) (*ListObj
// todo: check what happens if there is more than maxKeys objects
if ln > p.MaxKeys {
ln = p.MaxKeys
result.IsTruncated = true
}

result.Objects = make([]*ObjectInfo, 0, ln)
Expand Down Expand Up @@ -253,13 +253,25 @@ func (n *layer) ListObjects(ctx context.Context, p *ListObjectsParams) (*ListObj
if oi := objectInfoFromMeta(bkt, meta, p.Prefix); oi != nil {
// use only unique dir names
if _, ok := uniqNames[oi.Name]; !ok {
if len(p.Marker) > 0 && oi.Name <= p.Marker {
continue
}
uniqNames[oi.Name] = struct{}{}

result.Objects = append(result.Objects, oi)
}
}
}

sort.Slice(result.Objects, func(i, j int) bool {
return result.Objects[i].Name < result.Objects[j].Name
})

if len(result.Objects) > p.MaxKeys {
result.IsTruncated = true
result.Objects = result.Objects[:p.MaxKeys]
result.NextMarker = result.Objects[len(result.Objects)-1].Name
}
return &result, nil
}

Expand Down
4 changes: 4 additions & 0 deletions api/layer/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ type (
ContinuationToken string
NextContinuationToken string

// When response is truncated (the IsTruncated element value in the response is true),
// you can use the key name in this field as marker in the subsequent request to get next set of objects.
NextMarker string

// List of objects info for this request.
Objects []*ObjectInfo

Expand Down