package layer import ( "context" "sort" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data" s3errors "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors" ) func (n *layer) ListObjectVersions(ctx context.Context, p *ListObjectVersionsParams) (*ListObjectVersionsInfo, error) { var ( allObjects = make([]*data.ExtendedObjectInfo, 0, p.MaxKeys) res = &ListObjectVersionsInfo{} ) versions, err := n.getAllObjectsVersions(ctx, p.BktInfo, p.Prefix, p.Delimiter) if err != nil { return nil, err } sortedNames := make([]string, 0, len(versions)) for k := range versions { sortedNames = append(sortedNames, k) } sort.Strings(sortedNames) for _, name := range sortedNames { sortedVersions := versions[name] sort.Slice(sortedVersions, func(i, j int) bool { return sortedVersions[j].NodeVersion.Timestamp < sortedVersions[i].NodeVersion.Timestamp // sort in reverse order }) for i, version := range sortedVersions { version.IsLatest = i == 0 allObjects = append(allObjects, version) } } if allObjects, err = filterVersionsByMarker(allObjects, p); err != nil { return nil, err } res.CommonPrefixes, allObjects = triageExtendedObjects(allObjects) if len(allObjects) > p.MaxKeys { res.IsTruncated = true res.NextKeyMarker = allObjects[p.MaxKeys-1].ObjectInfo.Name res.NextVersionIDMarker = allObjects[p.MaxKeys-1].ObjectInfo.VersionID() allObjects = allObjects[:p.MaxKeys] res.KeyMarker = p.KeyMarker res.VersionIDMarker = p.VersionIDMarker } res.Version, res.DeleteMarker = triageVersions(allObjects) return res, nil } func filterVersionsByMarker(objects []*data.ExtendedObjectInfo, p *ListObjectVersionsParams) ([]*data.ExtendedObjectInfo, error) { if p.KeyMarker == "" { return objects, nil } for i, obj := range objects { if obj.ObjectInfo.Name == p.KeyMarker { for j := i; j < len(objects); j++ { if objects[j].ObjectInfo.Name != obj.ObjectInfo.Name { if p.VersionIDMarker == "" { return objects[j:], nil } break } if objects[j].ObjectInfo.VersionID() == p.VersionIDMarker { return objects[j+1:], nil } } return nil, s3errors.GetAPIError(s3errors.ErrInvalidVersion) } else if obj.ObjectInfo.Name > p.KeyMarker { if p.VersionIDMarker != "" { return nil, s3errors.GetAPIError(s3errors.ErrInvalidVersion) } return objects[i:], nil } } // don't use nil as empty slice to be consistent with `return objects[j+1:], nil` above // that can be empty return []*data.ExtendedObjectInfo{}, nil } func triageVersions(objVersions []*data.ExtendedObjectInfo) ([]*data.ExtendedObjectInfo, []*data.ExtendedObjectInfo) { if len(objVersions) == 0 { return nil, nil } var resVersion []*data.ExtendedObjectInfo var resDelMarkVersions []*data.ExtendedObjectInfo for _, version := range objVersions { if version.NodeVersion.IsDeleteMarker() { resDelMarkVersions = append(resDelMarkVersions, version) } else { resVersion = append(resVersion, version) } } return resVersion, resDelMarkVersions }