From 9565641b9bc92ef467d06a9a8b5f6841b6de2da1 Mon Sep 17 00:00:00 2001 From: Harshavardhana Date: Thu, 21 Nov 2019 01:54:49 -0800 Subject: [PATCH] Enhance ListObjectsV2 API to return UserDefined metadata (#8539) --- cmd/api-response.go | 47 +++++++++++++++- cmd/api-router.go | 2 + cmd/bucket-handlers-listobjects.go | 90 +++++++++++++++++++++++++++++- cmd/bucket-handlers.go | 4 +- cmd/generic-handlers.go | 4 +- 5 files changed, 141 insertions(+), 6 deletions(-) diff --git a/cmd/api-response.go b/cmd/api-response.go index 7000c9bd9..e3a3db61d 100644 --- a/cmd/api-response.go +++ b/cmd/api-response.go @@ -239,6 +239,37 @@ type ObjectVersion struct { IsLatest bool } +// StringMap is a map[string]string. +type StringMap map[string]string + +// MarshalXML - StringMap marshals into XML. +func (s StringMap) MarshalXML(e *xml.Encoder, start xml.StartElement) error { + + tokens := []xml.Token{start} + + for key, value := range s { + t := xml.StartElement{} + t.Name = xml.Name{ + Space: "", + Local: key, + } + tokens = append(tokens, t, xml.CharData(value), xml.EndElement{Name: t.Name}) + } + + tokens = append(tokens, xml.EndElement{ + Name: start.Name, + }) + + for _, t := range tokens { + if err := e.EncodeToken(t); err != nil { + return err + } + } + + // flush to ensure tokens are written + return e.Flush() +} + // Object container for object metadata type Object struct { Key string @@ -251,6 +282,9 @@ type Object struct { // The class of storage used to store the object. StorageClass string + + // UserMetadata user-defined metadata + UserMetadata StringMap `xml:"UserMetadata,omitempty"` } // CopyObjectResponse container returns ETag and LastModified of the successfully copied object @@ -466,7 +500,7 @@ func generateListObjectsV1Response(bucket, prefix, marker, delimiter, encodingTy } // generates an ListObjectsV2 response for the said bucket with other enumerated options. -func generateListObjectsV2Response(bucket, prefix, token, nextToken, startAfter, delimiter, encodingType string, fetchOwner, isTruncated bool, maxKeys int, objects []ObjectInfo, prefixes []string) ListObjectsV2Response { +func generateListObjectsV2Response(bucket, prefix, token, nextToken, startAfter, delimiter, encodingType string, fetchOwner, isTruncated bool, maxKeys int, objects []ObjectInfo, prefixes []string, metadata bool) ListObjectsV2Response { var contents []Object var commonPrefixes []CommonPrefix var owner = Owner{} @@ -489,6 +523,17 @@ func generateListObjectsV2Response(bucket, prefix, token, nextToken, startAfter, content.Size = object.Size content.StorageClass = object.StorageClass content.Owner = owner + if metadata { + content.UserMetadata = make(StringMap) + for k, v := range CleanMinioInternalMetadataKeys(object.UserDefined) { + if hasPrefix(k, ReservedMetadataPrefix) { + // Do not need to send any internal metadata + // values to client. + continue + } + content.UserMetadata[k] = v + } + } contents = append(contents, content) } data.Name = bucket diff --git a/cmd/api-router.go b/cmd/api-router.go index 5ce073bf9..1fb3d9c5f 100644 --- a/cmd/api-router.go +++ b/cmd/api-router.go @@ -162,6 +162,8 @@ func registerAPIRouter(router *mux.Router, encryptionEnabled, allowSSEKMS bool) bucket.Methods(http.MethodGet).HandlerFunc(collectAPIStats("listenbucketnotification", httpTraceAll(api.ListenBucketNotificationHandler))).Queries("events", "{events:.*}") // ListMultipartUploads bucket.Methods(http.MethodGet).HandlerFunc(collectAPIStats("listmultipartuploads", httpTraceAll(api.ListMultipartUploadsHandler))).Queries("uploads", "") + // ListObjectsV2M + bucket.Methods(http.MethodGet).HandlerFunc(collectAPIStats("listobjectsv2M", httpTraceAll(api.ListObjectsV2MHandler))).Queries("list-type", "2", "metadata", "true") // ListObjectsV2 bucket.Methods(http.MethodGet).HandlerFunc(collectAPIStats("listobjectsv2", httpTraceAll(api.ListObjectsV2Handler))).Queries("list-type", "2") // ListBucketVersions diff --git a/cmd/bucket-handlers-listobjects.go b/cmd/bucket-handlers-listobjects.go index 3f2c5442a..6277109f0 100644 --- a/cmd/bucket-handlers-listobjects.go +++ b/cmd/bucket-handlers-listobjects.go @@ -126,6 +126,90 @@ func (api objectAPIHandlers) ListBucketObjectVersionsHandler(w http.ResponseWrit writeSuccessResponseXML(w, encodeResponse(response)) } +// ListObjectsV2MHandler - GET Bucket (List Objects) Version 2 with metadata. +// -------------------------- +// This implementation of the GET operation returns some or all (up to 10000) +// of the objects in a bucket. You can use the request parame